Конкурс [Большой ку$hk на PHDays райтап

Конкурс [Большой ку$hk на PHDays райтап


Буквально пару дней назад завершился PHDays, познакомился со многими интересными людьми, в целом форум оставил самые позитивные впечатления, за что огромный респект организаторам, компании Positive Technologies. Уже появилось довольно много отчетов ( shr , d0znpp , toxa , asintsov ), напишу лишь о том, что больше всего запомнилось лично для меня – о конкурсе Snatch, он же «Большой ку$h».



За день до конкурса всем участникам выдали по флешке с PHP-исходниками системы дистанционного банковского обслуживания, нужно было найти в них уязвимости и на следующий день, используя баги, слить как можно больше денег со счетов в этой системе. Причем, обналичка украденных полученных средств производилась на реальном банкомате с реальными картами. В итоге, удалось занять лишь третье место, первое досталось Gifts’у, а второе – Глебу Чербову из Digital Security.


Никаких SQL-инъекций, LFI/RFI и прочих традиционных для PHP уязвимостей в коде не было – присутствовали баги, которые, по словам организаторов, встречаются в ходе пентеста реальных ДБО. Система была подготовлена очень грамотно, разработчики потрудились на славу. Всего присутствовало три уязвимости, самую жирную успел использовать Gifts благодаря своему многопоточному скрипту на Python, который мне впоследствии удалось посмотреть, за что спасибо cyber-punk’у. Вторую уязвимость нашли все, так как была самой простой, и, соответственно, давала меньше всего денег. Третий баг позволял получать доступ к аккаунтам с помощью подбора сессии. Как ни странно парни, занявшие первое и второе место, как впоследствии выяснилось, ее не использовали. Буквально за час до конкурса я нашел этот баг, но использовать его мне удалось, до сих пор очень обидно.


1. Уязвимость через helpdesk


helpdesk/pswdcheck.php

helpdesk/index.php



$user = $auth->checkSession();
if(!$user) {
/* ... */
}

class/HelpdeskAuth.php



public function checkSession() {

if(isset($_SERVER["HTTP_BANKOFFICEUSER"])) {
$userId = base64_decode($_SERVER["HTTP_BANKOFFICEUSER"]);
$userInfo = $this->user->getUserInfoById($userId);
$this->user->setupUserInfo($userInfo);
return $this->user;
}

При отправке HTTP-хэдера BANKOFFICEUSER с любым значением можно было обойти авторизацию и получить доступ к скрипту, который выводит пользователей со слабыми числовыми паролями. Для брута юзера с 5-символьным паролем требовалось 100 тысяч запросов. Была captcha, но она нисколько не мешала, так как был обход:


login.php


if($_POST["code"] != $captcha->decodeCaptchaCode($_POST["_code"])) {

class/Captcha.php



public function decodeCaptchaCode($code) {
return @base64_decode(@strrev(@base64_decode($code)));
}

Здесь все просто, для обхода нужно было отправлять в POST, например, такие параметры: code=»12345″ и _code=»PVVETnpJVE0=».


Получив доступ к аккаунту, для перевода денег на свой счет было необходимо использовать еще одну уязвимость. На одних аккаунтах транзакции могли проводиться без защиты, а на других с помощью одноразовых паролей. Эти одноразовые пароли генерились следующим алгоритмом:



protected function generateOTP() {
$OTPs = array();
$s = 44553 + $this->userInfo["id"];
for($n = 10; $n < 24; $n++) {
$OTP = "";
$j = rand(20,39);
$j = substr($j, 0, 1);
$OTP = $n*$s*$j;
$OTP = substr($OTP, 0, 5);
$OTPs[] = $OTP;
}
return $OTPs;
}

Понятно, что сбрутить тут очень просто – всего 19 вариантов всего 2 варианта (thx BECHED). Был еще один способ защиты транзакций, но сгенерированный OTP было невозможно подобрать при переходе на третий шаг авторизации OTP не проверялся (thx Gifts).


2. Уязвимость в восстановлении пароля


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


$key = md5($login.rand(1, 250));

Собственно, все использовали именно этот баг, хорошо, что в своем брутере я начал с конца диапазона аккаунтов, а не с начала, иначе вообще ничего не досталось бы.


3. Слабые сессии


class/Auth.php



private function getSpecialHash($password) {
$hash = sprintf("%u", crc32($password));
if(strlen($hash) > 4) {
$hash = substr($hash, 0, 4);
}
if(strlen($hash) < 4) {
while(strlen($hash) < 4) {
$hash .= "1";
}
}
return $hash;
}

У некоторых пользователей, для которых в БД поле type соответствовало значению «contractor», можно было сбрутить сессию, так как рандомный хэш состоял лишь из 4 символов, т.е. требовалось лишь 10 тысяч запросов. Почему я не догадался попробовать уязвимость на аккаунтах, на которых Дима Евтеев демонстрировал для публики возможности ДБО, для меня остается загадкой.


Помимо конкурса на PHDays понравилась настоящая хек-атмосфера, интересные доклады и халявный Гиннес в баре :) Остается поблагодарить Positive Technologies за организацию мероприятия, было очень круто.


Остальное phdays php взлом
Alt text

Арсений Реутов

блог о web-безопасности