Pull to refresh
0

Итоги и разбор заданий онлайн-тура NeoQUEST-2014

Reading time 19 min
Views 7.6K
Настала пора подвести итоги онлайн-тура NeoQUEST-2014, в том числе:
— разобрать все 7 заданий квеста (на каждый день недели!);
— поведать о победителях и их наградах;
— рассказать, что ждет участников и гостей мероприятия в очном туре NeoQUEST-2014, который пройдет 3 июля в Санкт-Петербурге в Политехническом Университете.


О победителях


«Золото», «серебро» и «бронзу» онлайн-тура получили AV1ct0r, n0n3m4 и Dor1s соответственно. Борьба за первое место была очень жаркой, на протяжении трех дней лидер непрерывно менялся — AV1ct0r и n0n3m4 вырывали друг у друга первое место, и разница между ними составляла всего пару-тройку очков. Но затем AV1ct0r прошел задание про «фальшивый цитрус» и укрепил свое лидерство, оторвавшись на 113 баллов! Мы не можем не отметить волю n0n3m4 к победе и его несомненный талант (второе по сложности задание квеста он прошел первым). «Бронзовый» призер Dor1s ненамного отстал от второго места, зато реализовал большой отрыв от следующих участников.

Поздравляем победителей, их ждут главные призы и очный тур, победа в котором принесет победителю поездку на одну из международных хакерских конференций! AV1ct0r и n0n3m4 награждаются смарт-часами от Samsung и Sonyвот еще интересный обзор на Хабре) соответственно, а Dor1s получает почти трехкилограммового робота, тоже отнюдь не глупого! Кстати, всех прошедших хотя бы одно задание NeoQUEST-2014 также ждет небольшой приз. Поскольку стандартные идеи памятных сувениров (чашки, блокноты, флешки, ручки, шоколадки и прочее) уже исчерпали себя, наш сувенир крайне удивит участников, но большинство, скорее всего, рано или поздно им воспользуются! Мы связываемся со всеми призерами. Уважаемые участники, проверяйте почту на наличие писем от info@neoquest.ru!

Разбор заданий


NeoQUEST-2014 состоял из 7 заданий из различных областей кибербезопасности:
1) «Моя твоя не понимать» — reverse engineering приложения на C#;
2) «Hasta la vista» — reverse engineering Android-приложения;
3) «TimeShift2. Revenge» — временная атака на RSA;
4) «Отмороженный компьютер» — получение видеопамяти виртуальной машины из дампа оперативной памяти;
5) «Обнаружено неизвестное дымящееся устройство» — анализ дампа синхронизации по USB Android-телефона с компьютером;
6) «Игродром» — использование port knocking для игры в Pac-Man;
7) «В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!» — задание на reverse engineering, знание технологий обхода DEP и ASLR, основ криптографии и умения эксплуатировать бинарные уязвимости.

Задания «Обнаружено неизвестное дымящееся устройство», «Игродром», «В чащах юга жил бы цитрус?» будут детально рассмотрены в майском номере журнала «Хакер». К тому же, наши участники написали достаточное количество writeup'ов, в которых можно почитать, как они прошли то или иное задание. Здесь writeup от AV1ct0r'а, тут — от n0n3m4 и там — от Dor1s.

1 — Моя твоя не понимать

В основе задания лежала технология, позволяющая встраивать приложения, написанные на C#, в Интернет-сайты. Возможно это только в том случае, если сайт написан на asp.NET. Правда, и тут есть определенные ограничения. Только Internet Explorer сможет отобразить графическую оболочку апплета, остальные браузеры могут лишь использовать экспортируемые им функции.
Участник квеста из всех исходных данных получал только IP-адрес сайта, при переходе по которому загружалась такая вот страница:



На странице выводился адрес пользователя, зашедшего на сайт, и еще непонятные иероглифы. Адрес, подсвеченный белым цветом, намекает, что сайту небезразлична локация, из которой пользователь на него заходит.
Если ввести выводимые сайтом иероглифы в сервис translate.google.ru, получится вот что:



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



Пользователю выводится форма для ввода данных, но что туда нужно ввести? Вот в чем вопрос. Для этого нужно покопаться в исходном коде страницы, тогда обнаруживается такой любопытный кусочек:



В исходнике страницы в явном виде указан путь к C#-апплету. Но, как уже говорилось выше, он не отображается ни в каких браузерах кроме Internet Explorer. Вот так выглядит сайт в IE при использовании прокси-сервера:



Однако на ход выполнение задания отображение графической части апплета не влияет. Кнопки в его интерфейсе не несут никакой полезной нагрузки.
Дальнейшая задача участника – изучение исходного кода апплета. Приложения, написанные на языке C# достаточно легко декомпилируются. Например, можно использовать приложение .NET Reflector. Получаем исходный код апплета:



В декомпилированном приложении несложно найти класс с говорящим названием KeyStore. В нем хранится ключ, который необходимо ввести в форму на сайте.
К слову, функция проверки ключа реализована так же в апплете. Средствами asp.NET она вызывается и проверяет вводимую информацию на правильность. Этот функционал работает в любом браузере. Задание пройдено!

2 — Hasta la vista

В задании участники получали некий файл MyGreenManController.zip, при открытии которого становилось понятно, что это не что иное, как приложение для Android (да, собственно, и название файла подсказывало):



Участникам нужно было пореверсить этот файл. Для этого требовалось знать, что представляет собой приложение для операционной системы Android. Это ни что иное, как архив, упакованный zip-ом и имеющий расширение «.apk». В данном архиве содержатся ресурсы приложения, файл AndroidManifest.xml, который определяет права приложения, его имя и т.д., и файл classes.dex. Последний является байт-кодом программы, скомпилированной для виртуальной машины Dalvik, использующейся в Android. Получить из него исходный код на java просто так не получится, однако можно получить набор команд для виртуальной машины – dalvik opcodes. Данный способ не особо пригоден для анализа приложений, поскольку существует другой, более простой и удобный, который заключается в преобразовании dex-файла в jar, который можно декомпилировать и получить вполне читаемый код на java.

Реверсинг сводится к получению файла classes.dex, с использованием утилиты Apk Manager или простого архиватора. Затем, при помощи утилиты dex2jar, получаем файл classes.dex.dex2jar.jar, который очень удобно изучать при помощи программы jd-gui.
Входной точкой является класс Code:

public class Code extends Activity
{
  public static String aA = "";

  public void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130837504);
    TextView localTextView = (TextView)findViewById(2130968577);
    ((Button)findViewById(2130968578)).setOnClickListener(new View.OnClickListener(localTextView)
    {
      public void onClick(View paramView)
      {
        Code.aA = Code.this.getApplicationContext().getFilesDir().getAbsolutePath();
        String str = new AA().a(this.val$aaa.getText().toString(), Code.aA);
        if (str == null)
          Toast.makeText(Code.this.getApplicationContext(), "Error", 0).show();
        do
          return;
        while (!str.equals("Correct command"));
        Toast.makeText(Code.this.getApplicationContext(), "Correct command", 0).show();
        A localA = new A();
        try
        {
          localA.a((TelephonyManager)Code.this.getSystemService("phone"));
          return;
        }
        catch (Exception localException)
        {
          localException.printStackTrace();
        }
      }
    });
  }
}


Что делает класс Code? При нажатии на кнопку в переменную aA записывается путь до данного приложения (и да, однотипные названия методов и классов были сделаны исключительно с целью немного запутать участников квеста):

Code.aA = Code.this.getApplicationContext().getFilesDir().getAbsolutePath();


Затем в переменную str записывается результат выполнения метода a из класса AA.
Метод a прост до невозможности:

public String a(String paramString1, String paramString2)
  {
    if (!paramString1.equals("download_image"))
      return null;
    aa("http://10.0.31.111/index.php", "neoquest_2014", paramString2 + File.separator + "neoquest_2014");
    return "Correct command";
  }


В случае если в текстовое поле введена команда, отличная от «download_image» возвращается null, иначе вызывается метод aa со следующими параметрами:

aa("http://hastalavistababy.ru/index.php", "neoquest_2014", paramString2 + File.separator + "neoquest_2014");


Метод aa формирует строку вида:
«cmd=1&time=xxx&command_name=download_image&path=neoquest_2014»


А затем вызывает метод getExampleInFile, который сохраняет ответ от сервера в файл:

getExampleInFile("http://hastalavistababy.ru/index.php", cmd=1&time=xxx&command_name=download_image&path=neoquest_2014, /data/data/com.example.NeoQUEST2014/files/neoquest_2014);


В результате в папке files нашего приложения окажется файл, загруженный с сервера. Ввиду того, что достать этот файл на телефоне можно только с правами root, следует воспользоваться утилитой wget и скачать файл следующей командой:

wget.exe http://hastalavistababy.ru/index.php?cmd=1&time=xxx&command_name=download_image&path=neoquest_2014


Что же это за странный файл? На этот вопрос ответит класс Code. После скачивания файла вызывается метод a класса A c параметром TelephonyManager:

A localA = new A();
        try{
          localA.a((TelephonyManager)Code.this.getSystemService("phone"));
          return;
        }


Метод a делает следующее:

public String a(TelephonyManager paramTelephonyManager)
    throws Exception
  {
    StringBuffer localStringBuffer = new StringBuffer();
    localStringBuffer.append(paramTelephonyManager.getDeviceId());
    if (!paramTelephonyManager.getDeviceId().equals("352276054393855"));
    do
    {
      return null;
      localStringBuffer.append(paramTelephonyManager.getSimOperator());
    }
    while (!paramTelephonyManager.getSimOperator().equals("25001"));
    localStringBuffer.append(aaaa("neoquest_2014"));
    String str = aaaa(localStringBuffer.toString());
    localStringBuffer.setLength(0);
    localStringBuffer.append(str);
    aa(localStringBuffer.toString().substring(0, 16).getBytes());
    return "Success";
  }


Для начала он берет ID телефона и проверяет его на соответствие строке «352276054393855», а ID оператора связи проверяет на соответствие строке «25001» и добавляет эти строки в переменную localStringBuffer:

if (!paramTelephonyManager.getDeviceId().equals("352276054393855"));
    do
    {
      return null;
      localStringBuffer.append(paramTelephonyManager.getSimOperator());
    }
    while (!paramTelephonyManager.getSimOperator().equals("25001"));


Затем в переменную localStringBuffer добавляется строка «neoquest_2014»:

localStringBuffer.append(aaaa("neoquest_2014"));


Затем вызывается метод aaaa с параметром – сформированной строкой. Данный метод считает хэш по алгоритму MD5:

String str = aaaa(localStringBuffer.toString());


Далее вызывается метод aa c параметром – первые 16 байт полученного хэша:

aa(localStringBuffer.toString().substring(0, 16).getBytes());


Метод aa осуществляет расшифрование загруженного файла neoquest_2014 с ключом, состоящим из первых 16-ти байт того самого хэша, а результат записывает в файл neoquest_2014_original:

public static void aa(byte[] paramArrayOfByte)
    throws Exception
  {
    SecretKeySpec localSecretKeySpec = new SecretKeySpec(paramArrayOfByte, "AES");
    byte[] arrayOfByte1 = IOUtils.toByteArray(new FileInputStream(Code.aA + File.separator + "neoquest_2014"));
    Cipher localCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    localCipher.init(1, localSecretKeySpec);
    localCipher.init(2, localSecretKeySpec);
    byte[] arrayOfByte2 = localCipher.doFinal(arrayOfByte1);
    FileOutputStream localFileOutputStream = new FileOutputStream(Code.aA + File.separator + "neoquest_2014_original");
    localFileOutputStream.write(arrayOfByte2);
    localFileOutputStream.close();
  }


Таким образом, участникам нужно было скачать файл на компьютер (или с телефона, для любителей извращений), а затем восстановить ключ и расшифровать загруженный файл.

3 — TimeShift 2. Revenge

Задание с очного тура NeoQUEST-2013, тогда его никто не смог пройти. Мы его доработали для онлайн-тура, и в этот раз многие с ним справились.
Из легенды имеем два IP-адреса с портом (213.170.102.196:4001, 213.170.102.197:4002) и ключ B4365F2.
Установив соединение с первым сервером и отправив произвольные данные (например, программой netcat), получаем ответ:

ilya@debian:~$ netcat 213.170.102.196 4001
hi
ЖїAlert! Expected client hello message.
Format:
	1 byte		type	NEOSSL_HANDSHAKE			0x16
	2 byte		version	NEOSSL1_VERSION		0x01
	3-4 bytes	length (excluding header)
	5 byte		data	NEOSSL_CLIENT_HELLO			0x01
---DEBUG INFO---
Ubuntu Release 10.04 (lucid)
Kernel Linux 2.6.32-21-generic
Memory 1001.9 MiB
Processor Intel(R) Core(TM) i3 CPU
Processing time 1471 cycles
Processing threads - 1 thread
Public-key cryptography algorithm - RSA (with Montgomery multiplication)
Symmetric-key cryptography algorithm - AES-128 (zero IV)
------


Хост отвечает сообщением об ошибке в котором содержится формат ожидаемого сообщения и отладочная информация. Сервер ожидает получить сообщение client hello: {0x16, 0x01, 0x00, 0x01, 0x01}. Из 5-ти байт 4 входит в заголовок сообщения, один (NEOSSL_CLIENT_HELLO) в данные.
Проверим второй сервер.

ilya@debian:~$ netcat 213.170.102.197 4002
hi

뤲t! Expected server hello message.
Format:
	1 byte		type	NEOSSL_HANDSHAKE	0x16
	2 byte		version	NEOSSL1_VERSION		0x01
	3-4 bytes	length (excluding header)
	5 byte		data	NEOSSL_SERVER_HELLO			0x02
	6 byte		data	RSA_WITH_AES_128_CBC		0x01
	7-n bytes	data	Certificate
---DEBUG INFO---
Ubuntu Release 10.04 (lucid)
Kernel Linux 2.6.32-21-generic
Memory 1001.9 MiB
Processor Intel(R) Core(TM) i3 CPU
Processing time 1531 cycles
Processing threads - 1 thread
Public-key cryptography algorithm - RSA (with Montgomery multiplication)
Symmetric-key cryptography algorithm - AES-128 (zero IV)
------


Второй хост отвечает сообщением client hello и ожидает сообщение server hello, содержащее сертификат сервера. Таким образом, первый хост выполняет роль сервера, второй — клиента. Пересылая их сообщения друг другу и анализируя сообщения об ошибках, можно понять протокол общения.
1. Клиент посылает NEOSSL_CLIENT_HELLO сообщение.
2. Сервер отвечает сообщением NEOSSL_SERVER_HELLO, содержащим параметры защищенного соединения (RSA_WITH_AES_128_CBC) и сертификат.
3. Клиент отправляет NEOSSL_KEY_EXCHANGE сообщение, содержащее зашифрованный открытым ключом сервера сессионный ключ AES-128.
4. Сервер отвечает NEOSSL_FINISHED.
5. Клиент отправляет зашифрованные данные.
Зашифрованные данные также отправляются с заголовком и имеют следующий формат:

1 byte		type	NEOSSL_DATA				0x17
2 byte		version	NEOSSL1_VERSION		0x01
3-4 bytes	length (excluding header)
5-n bytes	data


Поскольку в схеме отсутствует аутентификация клиента, можно попробовать отправлять сообщения серверу, однако, без знания протокола дальнейшего общения, это не принесет никаких результатов.
Нужно получить данные, которые клиент посылает серверу на 5-м шаге. Можно попытаться сгенерировать сертификат и с его помощью установить соединение с клиентом. Однако клиент проверяет получаемый сертификат и не принимает сгенерированный. Про шифрование AES-ом практически ничего не известно (ключ каждый раз разный, сообщение, вероятно, тоже может меняться и его содержание неизвестно). Значит, надо взламывать RSA.

Необходимо получить закрытый ключ сервера. Имеется сертификат сервера (содержащий открытый ключ) и доступ к шифратору (на шаге 3 клиент посылает данные, которые сервер расшифровывает и интерпретирует как сессионный ключ).

Среди атак на RSA есть интересный класс атак, связанный с реализацией шифрования. Наиболее интересна нам атака по времени выполнения, потому как в отладочной информации находится время, которое сервер затратил на расшифровку сообщения (а еще – название задания намекает!). Еще про шифрование известно то, что оно использует умножение Монтгомери для увеличения скорости шифрования.
Для вскрытия секретного ключа можно воспользоваться атакой, описанной в статье Harshman Singh «Timing Attacks on software implementation of RSA». Атака позволяет вычислить один из множителей модуля n. Вскрытие множителя выполняется побитово, начиная со старших бит. Для вскрытия шифратору отправляются запросы, сформированные из текущего известного значения модуля и вычисляется разница времени в случае если выбран бит 0 или 1. Если разница большая – устанавливается нулевой бит, иначе «один». Первые три бита множителя при этом нужно угадать. После вскрытия множителя легко вычислить закрытый ключ сервера.

Далее, установив соединение с клиентом, попробуем расшифровать сессионный ключ и сообщение, зашифрованное шифром AES. В отладочной информации сказано, что используется нулевой инициализирующий вектор AES. Вот полученное от клиента сообщение:

To obtain the access to the missile control system send a message: "XXXXXXX:Connect".
XXXXXXX - ID


В качестве ID нужно использовать значение из задания — B4365F2. Необходимо зашифровать строку «B4365F2:Connect» с использованием сессионного ключа и отправить её клиенту. Расшифровав очередной его ответ, получаем хэш – f9e8ceee19e980bd68e3193d6d0de2d3, это и есть наш ключ. Задание пройдено!

4 — Отмороженный компьютер

Это задание было посвящено анализу данных дампа оперативной памяти, который был выдан участникам в виде файла размером в 1GB. Дамп был получен путем простого копирования файла vmem от запущенной и настроенной заранее виртуальной машины. Особенностью задания заключалась в том, что внутри одной виртуальной машины VMware, от которой и предоставлялся дамп, была создана и запущена другая виртуальная машина под управлением Oracle Virtual Box. Схема задания выглядела вот так:



Обе виртуальные машины были под управлением одинаковых версий Windows 7, поэтому в дампе памяти присутствовало одновременно две операционные системы, страницы памяти которых были сильно разбросаны по дампу. Участникам NeoQUEST требовалось найти в дампе ключ. Наличие двух виртуальных машин и определение их типов выяснялось достаточно легко, путем анализа всех текстовых строк, находящихся в памяти. Интересно отметить, что при использовании CMAT на выданном участникам дампе программа отказывалась нормально работать, в то время как Volatility Framework успешно справлялся со своей работой и находил сразу две операционные системы.
Победитель AV1ct0r для решения этого задания использовал именно Volatility Framework, про что он подробно написал в своем блоге. Еще одной особенностью задания NeoQUEST было то, что ключ, который нужно было найти участникам, был только в виде графического изображения в видеопамяти виртуальной машины Virtual Box. Поэтому для решения необходимо было восстановить видеопамять этой виртуалки. Для решения существует два пути, поскольку оперативная память и видеопамять виртуальной машины Virtual Box замаплена одновременно и в физическую память виртуальной машины VMware, и в виртуальную память процесса Virtual Box с эмуляторами (как ни странно, VirtualBox.exe).



AV1ct0r решил задание с использованием дампа памяти процесса VirtualBox.exe, выделив в нем по виртуальному адресу, найденному в логах, видеопамять и сделав из нее картинку. Получение дампа процесса осуществлялось им так же, при помощи Volatility Framework. Вот алгоритм решения задания с получением видеопамяти виртуальной машины Virtual Box и формированием из нее картинки, с использованием маппинга видеопамяти в физическую память виртуальной машины VMware.



Шаг 1. Получаем все читаемые строки из дампа памяти виртуальной машины VMware при помощи утилиты strings.exe. Файл со строками получается внушительным: 196 MB.
Шаг 2. Узнаем, что запущена виртуальная машина Virtual Box и получаем кусок лога от ее запуска с более подробной и необходимой нам информацией:

:2013122720131228: komsomol@file:///C:/Users/komsomol/VirtualBox%20VMs/KP-2/Logs/VBox.log
00:00:02.305000 
00:00:02.305001 [/DBGF/] (level 1)
00:00:02.305002   Path <string>  = "C:\Users\komsomol\VirtualBox VMs\KP-2/debug/;C:\Users\komsomol\VirtualBox VMs\KP-2/;C:\Users\komsomol/" (cb=103)
00:00:02.897225 HWACCM: TPR shadow physaddr           = 000000003dd5f000
00:00:02.897227 HWACCM: VCPU0: MSR bitmap physaddr    = 000000003dd67000
00:00:02.897229 HWACCM: VCPU0: VMCS physaddr          = 000000003dd65000
00:00:02.897653 CPUMSetGuestCpuIdFeature: Enabled sysenter/exit
00:00:02.897655 HWACCM: 32-bit guests supported.
00:00:02.897656 HWACCM: VMX enabled!
00:00:02.897656 HWACCM: Enabled nested paging
00:00:02.897658 HWACCM: EPT root page                 = 000000003dd8b000


Из лога мы узнаем, что включена аппаратная виртуализация и что включена аппаратная поддержка таблиц страниц nested paging. Последнее означает, что Virtual Box настраивает таблицы EPT (Extended Page Tables), которые используются затем процессором для преобразования физических адресов виртуальной машины в физические адреса хостовой машины. В нашем случае, физические адреса виртуальной машины Virtual Box в смещения в файле дампа памяти. Чтобы узнать побольше про формат EPT (и про то, что он соответствует PML4), нужно почитать мануал процессора.
Адрес корневой таблицы виден в логе и он равен 3dd8b000. Как и любые таблицы трансляций в них используются исключительно физические адреса, которые соответствуют абсолютным смещениям в файле дампа, поэтому осталось проичтать видеопамять, которая также расположена в физическом адресном пространстве виртуальной машины Virtual Box и маппится при помощи EPT.
Эмуляторы устройства VGA (видеоадаптера) используют хостовую оперативную память для имитации видеопамяти для виртуальной машины, а для выполнения непосредственного рисования картинки на экран просто копируют обладсть оперативной памяти в хостовую видеопамять периодически – в точности как работает классический алгоритм двойной буферизации, использующийся во многих графических движках.
Шаг 3. Узнаем физический адрес видеопамяти для виртуальной машины Virtual Box.
В дампе его нет, поэтому, чтобы его узнать, достаточно запустить Virtual Box у себя на компьютере и посмотреть во вкладке ресурсов видеоадаптера значение (кстати, размер оперативной памяти в дампе есть):

00:00:02.305353   VRamSize         <integer> = 0x0000000001000000 (16 777 216, 16 MB)


Шаг 4. Определяем формат и размер видеоизображения. В видеопамяти графические данные хранятся в определенном формате, который схож с обычным BMP форматом: последовательно байты RGBXRGBX…на каждый пиксель по 3 или 4 байта. Разрешение экрана и битность узнаем из лога:

00:01:14.565973 Display::handleDisplayResize(): uScreenId = 0, pvVRAM=065c0000 w=800 h=600 bpp=32 cbLine=0xC80, flags=0x1


Шаг 5. Теперь нужно написать программу, которая прочитает из дампа только видеопамять. Для этого программа должна разбирать таблицы страниц в заданном диапазоне (начиная с физического адреса 0xE0000000 длиной размер картинки 800*600*4). Основная часть кода программы выглядит так:

DWORD TranslateGPA2HPA( DWORD gpa )
{
    vmxGuestPysicalAddress addr;
    addr.Value = gpa;

    vmxEPTP eptp;
    eptp.Value = EPT_base;
    
    DWORD64 pml4_base = (eptp.PML4ShiftedAddr << 12);
    vmxEptPML4Entry pml4_ent;
    pml4_ent.Value = DumpRead64(pml4_base + 8 * addr.eptPML4EntryOffset);
    
    DWORD64 pdpt_base = (pml4_ent.eptPDPTShiftedAddr << 12);
    vmxEptPDPTEntry pdpt_ent;
    pdpt_ent.Value = DumpRead64(pdpt_base + 8 * addr.eptPDPTEntryOffset);

    DWORD64 pd_base = (pdpt_ent.eptrfPDShiftedAddr << 12);
    vmxEptPDEntry pd_ent;
    pd_ent.Value = DumpRead64(pd_base + 8 * addr.eptPDEntryOffset);

    DWORD64 pt_base = (pd_ent.eptrfPTShiftedAddr << 12);
    vmxEptPTEntry pt_ent;
    pt_ent.Value = DumpRead64(pt_base + 8 * addr.eptPTEntryOffset);

    return (pt_ent.Shifted4KPageAddr << 12) | addr.eptByteOffset;
}
void ReadPage(void *data, DWORD gpa)
{
    DWORD offset = TranslateGPA2HPA(gpa & (~0xFFF));
    fseek(g_fdump, offset, SEEK_SET);
    fread(data, 4096, 1, g_fdump);
}
HBITMAP ReadVideoMemory( DWORD width, DWORD height, DWORD bpp )
{
    DWORD size = ((width * height * (bpp / 8) / 4096) + 1) * 4096;
    char *bmp = (char *)malloc(size);

    for (int i = 0; i < size; i+=4096)
    {
        ReadPage(bmp + i, 0xE0000000 + i);
    }

   return CreateBitmap(width, height, 1, 32, bmp);
}


Объявление структур можно найти в документации к процессору. Функцию CreateBitmap можно найти в Интернете.
После компиляции и запуска программы получается такая картинка:



Ключ найден! В результате задание можно решить и без использования CMAT и Volatility Framework, используя знания об устройстве видеопамяти и технологии виртуализации.

5 — Обнаружено неизвестное дымящееся устройство

По легенде, участникам надо было, разобравшись, что же за непонятное устройство они «синхронизировали с ноутбуком», получить ключ из файла неизвестного формата.
AV1ct0r в своем writeup'е определил, что этот файл — ни что иное, как дамп USB-трафика Android-устройства, и совершенно правильно отметил, что удобнее всего с ним работать в HEX-редакторе, а не в Wireshark.
Поанализировав дамп, находим имена людей, телефонные номера и SMS-сообщения. Помня, что важная информация может храниться в SMS, ищем подсказку и находим:



Информация о датах рождения хранилась в записной книжке, она начиналась с VCARD, там обнаружились 4 претендентки на роль жены. Оставался открытым вопрос: а что должен открывать этот пароль? Повнимательнее просмотрев дамп, обнаруживаем файл с названием look_at_this.7z. Гуглим сигнатуру 7z файлов:

37 7A BC AF 27 1C


Дальше остается всего-то ничего — вытащить архив из дампа и перебрать 4 полученные ранее даты рождения. Внутри архива лежит файл win.txt с флагом key. Задание пройдено!

6 — Игродром

В этом задании от участников требовалось поиграть в старый добрый Pac-Man, но особым образом, реализуя своеобразный port knocking.
Первоначально — надо было догадаться, что сервер принимает запросы только с тегом . Методом проб и ошибок, используя подсказки от сервера, приходящие с порта 1898, участник, наконец, должен получить такое приветственное сообщение:

<pacman gameid="7e09d6f13c5fd6086e3cc374e2bb8857">
    <info message='Hello, Mr. Struve. Waiting for you at the next congress. Your
 move. Use action "newstep" and node <direction> [UP, DOWN, LEFT
or  RIGHT]</direction>; for step' />
</pacman>


Получается, нас ждут на каком-то непонятном следующем конгрессе. Тут самое время включить логику и собрать воедино разрозненные куски информации:
— порт 1898;
— Струве;
— конгресс.
Воспользовавшись поисковыми системами, участники должны были прийти к тому, что порт — это год съезда КПСС (или же ранних ее форм), и «нас ждут» на 1903-ем порту. соответствующем дате следующего съезда. Несложно также найти даты съездов КПСС:



Теперь уже делаем ход на правильный порт, и получаем ответ от сервера, закодированный с помощью Base64. После расшифровки получаем дамп, начинающийся строкой "%PNG", и понимаем, что это PNG-картинка. Открываем изображение — и вот он, Pac-Man!



Остается только пройти игру! После прохождения получаем ответ от сервера с ключом:

<pacman>
    <info message="You Win! Key is 584cc0bdfb44ac2dc00ec03ae6a5d937" />
</pacman>


7 — В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!

Самое сложное задание (его прошел только AV1ct0r), требующее знаний reverse engineering, основ криптографии, технологий обхода DEP и ASLR, а также умения эксплуатировать бинарные уязвимости. Кто захочет вникнуть глубже — в майском номере «Хакера» большая и подробная статья по его прохождению.

Участнику предоставлялись файлы:
1. atl110.dll;
2. NeoQuestDocument.docx;
3. NeoQuestActiveX.dll.

В документе NeoQuestDocument.docx загружается ActiveX и результатом запуска является Decryption Error.
В модуле ActiveX, реализованном в бинарном файле NeoQuestActiveX.dll, содержится уязвимость в сеттере свойства EncryptedTextBlob, которую можно проэксплуатировать, и которая позволит выполнить произвольный код на машине пользователя.
В результате реверс-инжиниринга NeoQuestActiveX.dll получаем следующий алгоритм работы сеттера свойства EncryptedTextBlob ActiveX-компоненты NeoQuestActiveX.dll:



Немного поясним алгоритм работы словами, в совокупности с картинкой это должно прояснить план действий.
При выставлении свойства EncryptedTextBlob вызывается сеттер. В него передаётся строка, которая, в свою очередь, декодируется из Base64. Далее полученный массив байт делится на отрезки и берутся первые два байта, содержащие в себе длину второго отрезка. Затем в стековый 16-байтный буфер копируется второй отрезок. Здесь-то и происходит ошибка и падение Word в случае, если длина указана более 16. С помощью функции memmove память копируется без проверки количества копируемых байт.

Затем, в случае нормального хода программы, берётся второй отрезок байт и парсится, как серийный номер сертификата. В хранилище сертификатов ищется сертификат с указанным серийным номером. И в случае, если сертификат найден, на нём при помощи функции CryptDecryptMessage расшифровывается третий отрезок и в результате этого мы должны получить ключ для прохождения задания.

Из всего вышесказанного делаем вывод, что нам в систему нужно установить сертификат, на котором был зашифрован ключ. После этого при открытии документа NeoQuestDocument.docx мы увидим не «!!!Decryption Error!!!», а искомый ключ.
Откуда взять сертификат? В задании дан некий сервер. Кроме как оттуда, больше взять неоткуда.

На этом шаге уже можно полностью понять стратегию прохождения задания:
1. Создать документ, эксплуатирующий уязвимость в NeoQuestActiveX.dll и выполняющий полезную нагрузки в виде скачивания нужного сертификата;
2. Загрузить его на сервер указанный в задании (в задании сказано: «ресурс 213.170.102.198 на котором он установлен», а значит эксплойт должен там отработать);
3. Скачать сертификат;
4. Установить его на свою машину;
5. Открыть документ NeoQuestDocument.docx и получить расшифрованный ключ прохождения задания.

Перейдём к эксплуатации уязвимости. В процессе WINWORD.exe включён permanent-DEP и ASLR. Лишь для одного модуля ASLR отключен — NeoQuestActiveX.dll. Для обхода приведённых ограничений необходимо переполнить буфер, перезаписав адрес возврата, который в свою очередь будет являться первым ROP-гаджетом в программе. В результате выполнения ROP-программы память с шеллкодом (стек) должна стать исполняемой, и мы перейдём к исполнению шеллкода.

Для упрощения построения ROP-программы воспользуемся Immunity Debugger’ом и скриптом на питоне mona.py. Запустив mona.py, получаем примерную ROP-цепочку. Сразу после ROP-программы следует payload. В качестве полезной нагрузки мы используем shell_reverse_tcp (подходит только он, потому что на сервере все порты фильтруются и открыт только 80-й порт), при этом необходимо иметь внешний IP-адрес или использовать хостинг. Генерируем полезную нагрузку с помощью того же Metasploit’а.

Загружаем сформированный docx документ на сервер, получаем «отстук» полезной нагрузки в консоль Metasploit’а, скачиваем сертификат и пароль на него, устанавливаем в своё хранилище сертификатов и открываем документ NeoQuestDocument.docx. Перед нами открывается ключ прохождения задания. Победа!

А дальше — это главное...


В скором времени победители сойдутся в схватке на очном туре NeoQUEST под единой благородной целью — остановить ракету, которую они же сами и запустили (случайно). Так что, дорогие хакеры, вылезаем из-под земли и начинаем смотреть в небо, выискивая признаки приближающейся ракеты.

Если вы интересуетесь информационной безопасностью, и неважно, хобби это для вас или работа, — ждем вас на очный тур NeoQUEST-2014 3 июля в Питере! Вход свободный! На самом мероприятии можно будет понаблюдать за сражением наших участников, принять участие в интересных хак-конкурсах (и уйти с подарками!), послушать увлекательные доклады из практики кибербезопасности, пообщаться с коллегами и просто весело провести время! Следите за новостями на нашем сайте!
Tags:
Hubs:
+6
Comments 0
Comments Leave a comment

Articles

Information

Website
neobit.ru
Registered
Employees
51–100 employees
Location
Россия