Podczas turnieju pingCTF 2023 zorganizowanego przez Koło Naukowe PING z Politechniki Gdańskiej, rozwiązałem wyzwanie “Hangover” plik.
Write-up
Analizę programu rozpoczynamy od sprawdzenia sekcji .rodata, gdzie znajdujemy dwa interesujące ciągi znaków: “wrong” oraz “good”.
Zostały one użyte w dwóch funkcjach. Plik wykonywalny nie zawiera symboli, więc nazwy funkcji trzeba nadać ręcznie(skrót klawiszowy “N”).
Funkcję main rozpoczyna wywołanie funkcji signal
(man 2 signal). Rejestrowana jest funkcja, która obsługuje sygnał 5 - SIGTRAP.
Funkcja sigtrap_handler(ponownie nadajemy funkcji nazwę), ustawia wartości pięciu zmiennych globanych.
Wracamy do funkcji main. Na końcu znajduje się breakpoint
, czyli instrukcja używana przez debuggery do zatrzymania wykonania programu. Zastępujemy breakpointa
NOPem
aby Binary Ninja kontynuował analizę funkcji main.
W poprzednio niewidocznej wcześniej części programu sprawdzany jest czas. Jeśli program byłby debugowany ustawione przez signal_handler
wartości zmiennych zostały by nadpisane.
Funkcja sprawdzająca aktualny czas:
Po próbie wykrycia debuggera program rozpoczyna sprawdzanie poprawności podanej przez użytkownika flagi.
Używając User Informed Data Flow ustawiamy wartości czterech zmiennych lokalnych (rdi_6
, rdx_1
, r9
i rsi_1
). Wartości powinny być takie jak te co zostają ustawione po wywołaniu sigtrap_handler
w odpowiedzi na wykonanie instrukcji breakpoint.
Efekt przekazania dodatkowych informacji do dekompilatora:
W równaniach została jeszcze jedna zmienna, której wartości Binary Ninja nie zna - r14
. Program kopiuje ciąg znaków “ding{ring_ding_ding_daa_baa_baaaa}” do std::string a następnie wskaźnik do skopiowanego ciągu znaków jest przypisywany do r14.
Ponownie używamy User Informed Data Flow, tym razem do ustawienia wartość zmiennej r14 jako wskaźnik z wartością 0x403198.
Dzięki wykorzystaniu Binary Ninja oraz jego funkcji User Informed Data Flow, zdołaliśmy zdeobfuskować program.
Binary Ninja Python API pozwoli nam wyświetlić flagę, wystarczy zaledwie jedna linia kodu Pythona. Używamy zmiennych current_variable
i current_hlil
, więc istotne jest aby przed wykonaniem kodu kliknąć na nazwę zmiennej __str_1
w linii gdzie została zdefiniowana.
>>> from itertools import islice
>>> "".join(chr(current_hlil[xref.expr_id].condition.instruction_operands[1].constant) for xref in islice(current_function.get_hlil_var_refs(current_variable),1,None))
'ping{r3m3mb3r_70_h1d3_ur_d3bu993r}'