Новости C.I.P.H.E.R. 5 : глазами php-кодера.

blackzert
, 14 июля 2009
Преамбула.

Начало для меня было вовсе необычным — я пришёл с работы. Уставший и ленивый. Там я весь день изучал большую работу пытливых умов над проблематикой разработки, воплощённую в виде php-скриптов. Это сыграло свою роль на моей концентрации над пых-пышном сервисом.

Амбула.

Итак, пришёл я в 203 аудиторию где-то к 20:30. Народу было достаточно, чтобы разобрать и обратно собрать один кубический метр китайской стены (далее — человеко-кубометр), однако занимались совсем не тем. Зачем-то настраивали маршрутизаторы, разворачивали ноуты и подбирали плейлист (примечательно, брутфорсом) — игра все же долгая. Сел я в центре, чтобы никому не мешать. Рядом Алебастр (это ник, а не стройматериал) и Жека (aka ЖК, ака Progressor). Пока не было доступно сервисов, я, дожидаясь когда же, считал количество мух, собравшихся из открытого окна на стене от проектора.

3… 2… 1… Вроде что-то началось. Я дождался сервисов, и начал тыркаться что где и как. Кстати сказать, добрались мы до них где-то за полчаса до открытия сегментов или позже. Итак, нашёл сервис на пресловутом пхп. Назывался он marmot и осуществлял обычную для цтфа схему сохранения/выдачи флагов. Пользователи регистрировались, авторизировались, писали что-то, и только они могли видеть что было написано. Админ же всегда существовал и без него сервис не хотел работать (все как всегда разрешимо фиксом). Админ мог смотреть имена пользователь и первые 16 символов их сообщений, не более.

Сразу же после изучения я наткнулся на скрипт insert.php, который добавлял сказанное пользователем. Примечательно было то, что параметр бравшийся из куков не фильтровался:

if (isset ($_COOKIE['sessid'])) {
  $biscuit = $_COOKIE['sessid'];
  //mysanitize
  $result = $db->query ("SELECT username FROM users WHERE cookie = '$biscuit'");
  err ($result);
  $res = db2lista ($result);
  $usern = $res[0][0];
}

Ну даже слепой увидит, что значение переменной $usern теперь ограничено только нашей фантазией. Например она могла нам подсказать следующую строку в качестве $_COOKIE['sessid']: "' AND -1 union select body from memos order by id desc limit 1-" и тогда мы напишем от имени флага что-нибудь. А потом под админом глянем, какой же там флаг. У админа стандартный пароль был — admin. Это первый вектор атаки.

К сожалению моя фантазия страдала на тот момент и говорила более заумно. Я нашёл следующее. Дальше в коде было так:

$result = $db->query ("SELECT * FROM memos WHERE username = '$usern' and title = '$title'");
err ($result);
if (count (db2lista ($result)) > 0) {
    exit ("Note already inserted!");
}
$result = $db->query ("INSERT INTO memos VALUES ('$usern', '$title', '$body')");
err ($result);

Я подумал, а что если в переменную $usern записать ещё один под запрос, проверяющий какое-либо условие? Тогда в случае true число вернувшихся столбцов окажется >0 и мы увидим «Note already inserted!». А если false, то  из-за структуры запроса проверки, insert нас не поймёт и свалится с ошибкой. Таким образом мы например можем перебирать флаги — сначала есть ли те у которых буква, а первая, потом те у которых есть a с начала строки и так далее дихотомией. вытаскиваем флаги брутфорсом. Я написал именно такое адвизори, за что и получил первые 1,5 балла.

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

Итак, фильтровалось не всё:

function mysanitize ($str) {
  $bad_words = array (" ", "or", "and", "like", "union", "select", "where", "from");
  $str = str_replace ($bad_words, "", $str);
  return $str;
}

Учитывая регистр, используя знаки '+', '%09'(табуляция), и "/**/" мы можем внедрить инъекцию и авторизироваться сначала как админ — получить список userов, потом как пользователи - получить список их сообщений…

Закрываться следует правильной фильтрацией — mysql_real_escape_string

Тут тоже адвайзори на 3 бала, и  238 флагов (Спасибо Дмитрисимусу за бота)

Итак, я пытался копать потом сервисы на питоне… Но  что-то нет) А реверсинг Argenet уже уделал. Поэтому я пытался быть полезным имитацией бурной деятельности (простите команда, я старался:)).

Ну как бе, а потом я пошёл на работу… это был очень длинный день… спал с 19 в поезде целых 13 часов. Аминь.

PS. Окончательные гвозди в гроб сервису.

WARNING: Дальнейшее лишь пища для ума. Людей, боящихся за свою психику, просьба не читать. Итак, механизм генерации cookies, которые хранились в базе данных, в студию:

$cookie = random_gen (32);
function random_gen ($len) {
  $random_string = "";
  for ($i = 1; $i <= $len; $i++)
    $random_string .= chr (rand (97, 122));
  return $random_string;
}

Во первых, это диапазон — от 97 до 122, всего из 26 букавок. Это мало;).

Прошу обратится к статьям: raz0r

Предсказываем случайные числа в PHP

Магия случайных чисел (часть 2)

и при определённой фазе луны, можно будет перехватить сессию пользователя.