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

Идея метода для обхо­да филь­тров кода на PHP воз­никла у меня еще дав­но, но под­сказ­ка под­верну­лась недав­но. При­мер­но год назад, читая статью про оче­ред­ные уяз­вимос­ти в обра­зова­тель­ной плат­форме Moodle, я нат­кнул­ся на статью, в которой иссле­дова­телям приш­лось при­думать, как выпол­нить код без квад­ратных ско­бок и тек­ста, исполь­зуя толь­ко матема­тичес­кие фор­мулы. У метода, пред­ложен­ного авто­рами ори­гиналь­ной статьи, были огра­ниче­ния, поэто­му я нем­ного изме­нил под­ход и дорабо­тал его.

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

www

Ори­гиналь­ный метод опуб­ликован его авто­рами в их ре­пози­тории на GitHub и отно­сит­ся к уяз­вимос­ти CVE-2024-43425.

warning

Статья име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для спе­циалис­тов по безопас­ности, про­водя­щих тес­тирова­ние в рам­ках кон­трак­та. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Рас­простра­нение вре­донос­ных прог­рамм, наруше­ние работы сис­тем и наруше­ние тай­ны перепис­ки прес­леду­ются по закону.

 

Исходный метод

Сра­зу ого­ворюсь, что этот метод был соз­дан спе­циаль­но для экс­плу­ата­ции Moodle, име­ет ряд недос­татков и огра­ниче­ний и не рас­счи­тан на более гиб­кое при­мене­ние где‑то еще. Моя доработ­ка, которую я покажу чуть поз­же, тоже неидеаль­на, но зна­читель­но рас­ширя­ет воз­можнос­ти.

Да­вай фор­мализу­ем задачу: нуж­но добить­ся RCE, исполь­зуя толь­ко сим­волы, которые встре­чают­ся в фор­мулах. Это авто­мати­чес­ки зап­реща­ет квад­ратные скоб­ки и подоб­ные хаки, которые обыч­но при­меня­ют в таких слу­чаях.

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

php > print(base64_encode("printf"));
cHJpbnRm
php > base64_decode("cHJpbnRm")("hello");
hello

Как вид­но из при­мера, имя фун­кции printf кодиру­ется в Base64, а если рас­кодиро­вать его обратно и выз­вать стро­ку с аргу­мен­том, толь­ко что рас­кодиро­ван­ная стро­ка будет интер­пре­тиро­вана как фун­кция, которая тут же будет выпол­нена. Для вызова фун­кции мож­но исполь­зовать любой спо­соб получе­ния стро­ки, а интер­пре­татор PHP сам най­дет и выпол­нит нуж­ную фун­кцию.

Здесь важ­но, что это дол­жна быть имен­но фун­кция, то есть eval или print таким спо­собом выз­вать не получит­ся, потому что это спе­циаль­ные язы­ковые конс­трук­ции, а не фун­кции. Такие конс­трук­ции PHP обра­баты­вает перед собс­твен­но выпол­нени­ем кода. Если поп­робовать про­вер­нуть такой трюк с eval, то получим ошиб­ку:

php > print(base64_encode("eval"));
ZXZhbA==
php > base64_decode("ZXZhbA==")("phpinfo();");
PHP Warning: Uncaught Error: Call to undefined function eval() in php shell code:1
Stack trace:
#0 {main}
thrown in php shell code on line 1
php >

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

Плав­но перей­дем ко вто­рой задаче — избавле­нию от замет­ных и оче­вид­но немате­мати­чес­ких фун­кций вро­де base64_decode. По усло­вию задачи исполь­зовать мож­но толь­ко матема­тичес­кие фун­кции, а, как извес­тно, при матема­тичес­ких вычис­лени­ях обыч­но получа­ются чис­ла.

Вто­рой трюк зак­люча­ется в том, что­бы пос­ле опе­раций с чис­лами получить стро­ку. Для это­го в PHP исполь­зует­ся опе­ратор ., который может быть как десятич­ной точ­кой в чис­ле, так и опе­рато­ром кон­катена­ции. Что­бы извлечь из чис­ла стро­ку, нуж­но сна­чала получить любую дру­гую стро­ку. В ори­гиналь­ной статье авто­ры исполь­зуют acos(2), который даст NAN, так как это выраже­ние невоз­можно вычис­лить. Давай поп­робу­ем:

php > print(gettype(acos(2)));
double

По­лучил­ся double, а нуж­но string. Самый прос­той спо­соб — кон­катена­ция. При кон­катена­ции чего угод­но даже с пус­той стро­кой получит­ся стро­ка, а мы сло­жим два чис­ла‑стро­ки NAN:

php > print(gettype(acos(2)));
double
php > print(gettype(acos(2) . acos(2)));
string
php > print(acos(2) . acos(2));
NANNAN

При кон­катена­ции двух acos(2) получа­ется стро­ка NANNAN. Даль­ше мож­но соб­рать жела­емую стро­ку, исполь­зуя опе­рацию XOR:

(acos(2) . 1) ^ (0 . 0 . 0) ^ (1 . 1 . 1)

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    2 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии