Управление на Angr

Имах много малко време за тазгодишното Defcon CTF. Успях само достатъчно време, за да си поиграя с първото RE предизвикателство на бебето и, за съжаление, в крайна сметка го реших по дългия път. След като завърших предизвикателството, се чудех как другите са го решили и намерих хубав инструмент за символно изпълнение/двоичен анализ, който не бях виждал преди.

Дългият път

Отваряйки двоичния файл в IDA, можем да видим програмата:

  • Чете 13 числови стойности със scanf от stdin.
  • Извиква функция, наречена „CheckSolution“, като предава указател към прочетените стойности.
  • Тества върнатата стойност от „CheckSolution“.
  • Отпечатва „грешно“, ако върнатата стойност е 0 или отпечатва ascii представянето на числовите стойности (по ред), ако върнатата стойност е различна от нула.

Така че, за да преминем функцията CheckSolution, трябва вече да знаем ключа. Можем да направим някои прости корекции, за да видим, че нашите входни стойности всъщност не са променени, преди да бъдат отпечатани. Така че каквото и да подадем вероятно е в рамките на нормалния ASCII диапазон, така че имаме сравнително малко пространство за търсене. Задълбавайки се във функцията за проверка на решението, можем да видим някакъв инициализиращ код, последван от 13 отделни кодови блока, които всички са преминали/неуспели (давайки хубавата функция за стъпка в графиката по-долу).

След бърз преглед изглежда, че нашият ключ трябва да премине 13 проверки, за да бъде приет. Трябваше да въведем 13 стойности, за да започнем, така че... може би всеки кодов блок е отговорен за проверката дали всяка символна стойност е в някакви граници или отговаря на някакво предварително дефинирано условие?? Това би било лесно, можем просто да атакуваме всеки блок с груба сила и да намерим ключа! Но няма такъв късмет. Както се оказва, всеки знак се използва във всеки кодов блок, за да се създаде окончателна сума (контролна сума или хеш), която се сравнява с крайна статика, тъй като провереният резултат се записва в кода и не се променя при извиквания, стойност. Така че дори и да успеем да наложим грубо решение за първия кодов блок, то трябва да бъде и решението за втория кодов блок и така нататък (правилният отговор каскадно се спуска от дясната страна на графиката по-горе). Сега разчитаме на способността си да създадем атака на сблъсък в 13 различни хеш функции... майната му все пак.

И сега какво? Обратно към разглеждането на disasm. Е, ако всичките 13 кодови блока използват всичките 13 променливи, за да създадат сума (те го правят) и ние вече знаем решението на всяка сума (ние знаем), тогава можем просто да третираме всеки кодов блок като уравнение в система на едновременни уравнения, като така:

a*37485 - b*21621 - c*1874 - d*46273 … + m*54757 = 21399379 (блок1)

a*50936 + b*4809 - c*6019 - d*38962 … + m*53228 = 1453872 (блок 2)

….

a*51735 + b*35879 - c*63890 + d*4102 … +m*7314 = 1788229 (блок 13)

За щастие, решаването на системи от симутанни уравнения е просто приложение на основна линейна алгебра. Можем да третираме всеки набор от коефициенти като ред в матрица, а решенията като колонен вектор. След това можем да разделим вектора на решението на матрицата и стойностите за променливите, които решават системата от уравнения, веднага изскачат!

Бърз тест с октава и виждаме отговора си ясно: Математиката е трудна! :)

Пътят на Angr

Това беше всичко, за което имах време, когато стана дума за Defcon, но ми беше интересно да видя как другите са решили baby-re. Изглеждаше, че моят подход е метод, който отнема много време за първото предизвикателство на бебето. След като се огледах в Github, забелязах няколко решения, които използваха angr tool за решаване на предизвикателството. Разглеждайки използваните скриптове, изглежда, че можете лесно да разрешите предизвикателството само с няколко реда на Python и време за изпълнение под минута. След като си поиграх със скриптовете за решение на виртуална машина, нямах търпение да изпробвам angr на друго предизвикателство. Забелязах, че CTF на securityfest идва и реших да изпробвам angr на някои от техните предизвикателства.

Разглеждайки откритите предизвикателства, забелязах тяхното предизвикателство RE с 250 точки: fairlight. Разглеждайки го с IDA, забелязах познат модел, въвеждане на 14 знака, което след това беше обработено чрез 14 функции за проверка.

Исках да опитам да атакувам програмата с angr, като същевременно отделям възможно най-малко време за обръщане. Гледайки check_0 беше ясно, че програмата ще се спаси вътре във функциите за проверка, така че да няма „окончателна“ проверка на входа, към който можем да се насочим. Това обаче не беше проблем, тъй като можехме просто да използваме angr за насочване към последния клон на check_13.

Както се оказа, това беше доста добро решение. Написах най-бързия angr скрипт, който можах да измисля (без да съм направил опит да разбера функциите за проверка). Всичко, което скриптът прави, е да се насочи към безусловния jmp в check_13, изброен по-горе, като същевременно избягва разклонението fail (наличен също в angr-docs):

import angr
proj = angr.Project('./fairlight', load_options={“auto_load_libs”: False})
argv1 = angr.claripy.BVS(“argv1”, 0xE * 8)
първоначално_състояние = proj.factory.entry_state(args=[“./fairlight”, argv1])

initial_path = proj.factory.path(initial_state)
path_group = proj.factory.path_group(initial_state)
path_group.explore(find=0x4018f7, avoid=0x4018f9)
found = path_group.found[ 0]
print found.state.se.any_str(argv1)

Отговорът се появи след по-малко от минута: 4ngrman4gem3nt.

Явно и организаторите са фенове. ›:)