Создано при поддержке моего канала. Все задачи можно найти на rev-kids20.forkbomb.ru
Запускаем игру и ловим непонятную ошибку:
Закидываем бинарь в иду и по xref-ам находим место, где вылетает ошибка:
Патчим на
И, о чудо, нас ждет уже другая ошибка
Запатчив игру еще пару раз подобным образом, она заработает и выдаст флаг.
Полный список адресов, по которым нужно запатчить:
Пропрыгав по xref-ам находим алгоритм получения флага:
int sub_138D()
{
int v1; // eax
int v2; // eax
char v3[136]; // [rsp+0h] [rbp-C0h] BYREF
FILE *stream; // [rsp+88h] [rbp-38h]
int v5; // [rsp+94h] [rbp-2Ch]
char *check_st; // [rsp+98h] [rbp-28h]
int i; // [rsp+A4h] [rbp-1Ch]
int res; // [rsp+A8h] [rbp-18h]
int v9; // [rsp+ACh] [rbp-14h]
int v10; // [rsp+B0h] [rbp-10h]
int v11; // [rsp+B4h] [rbp-Ch]
int v12; // [rsp+B8h] [rbp-8h]
int v13; // [rsp+BCh] [rbp-4h]
if ( !strcmp("/", s2) && !strcmp("GET", http_method) )
{
puts("HTTP/1.1 200 OK\r\n\r");
return puts("Welcome to our fast web-server\r");
}
else if ( strcmp(s2, "/secret/") == 1 && !strcmp("GET", http_method) )
{
check_st = get_header_content("X-Auth");
if ( check_st )
{
st_len = strlen(check_st);
if ( st_len == 8 )
{
v13 = check_st[4] + *check_st - check_st[6];
v12 = check_st[6] + check_st[3] - check_st[2];
v11 = check_st[7] + check_st[1] - check_st[3];
v10 = check_st[2] + check_st[5] - check_st[7];
v9 = check_st[3] + check_st[1] - check_st[2];
res = 0;
for ( i = 0; i <= 7; ++i )
{
if ( (v12 & 1) != 0 )
{
v10 -= v13;
if ( (v10 & 1) != 0 )
{
v11 -= v9;
v2 = v12 + v13;
}
else
{
v12 -= v9;
v2 = v11 + v12;
}
res += v2;
}
else
{
v9 -= v11;
if ( (v13 & 1) != 0 )
{
v13 -= v12;
v1 = v10 + v9;
}
else
{
v10 -= v11;
v1 = v13 + v12;
}
res += v1;
}
v13 ^= v12;
v12 ^= v11;
v11 ^= v10;
v10 ^= v9;
v9 ^= v13;
}
if ( res == 444 )
{
stream = fopen("flag.txt", "r");
fgets(v3, 128, stream);
fclose(stream);
puts("HTTP/1.1 200 OK\r\n\r");
return printf("Flag: %s\r\n", v3);
}
}
}
}
}
Попытка решить все это дело с помощью z3 не увенчалась успехом, поэтому пришлось брутить:
import requests,random
arr = [0]*5
while True:
st = [random.randint(32,126) for _ in range(8)]
arr[4] = st[4] + st[0] - st[6]
arr[3] = st[6] + st[3] - st[2]
arr[2] = st[7] + st[1] - st[3]
arr[1] = st[2] + st[5] - st[7]
arr[0] = st[3] + st[1] - st[2]
check_var_444 = 0
for i in range(8):
if ( (arr[3] & 1) != 0 ):
arr[1] -= arr[4]
if ( (arr[1] & 1) != 0 ):
arr[2] -= arr[0]
v1 = arr[3] + arr[4]
else:
arr[3] -= arr[0]
v1 = arr[2] + arr[3]
else:
arr[0] -= arr[2];
if ( (arr[4] & 1) != 0 ):
arr[4] -= arr[3]
v1 = arr[1] + arr[0]
else:
arr[1] -= arr[2];
v1 = arr[4] + arr[3]
check_var_444 += v1
arr[4] ^= arr[3]
arr[3] ^= arr[2]
arr[2] ^= arr[1]
arr[1] ^= arr[0]
arr[0] ^= arr[4]
if check_var_444 == 444:
print(requests.get('http://109.233.56.90:11544/secret/',headers = {"X-Auth": ''.join(map(chr,st))}).content)
break
P.S.: Нужно было сразу брутфорсить, а не загонять в z3, но я подумал, что брутить будет слишком долго. Вот так неверная оценка сложности шифрования может привести к нескольким потерянным часам.
О нет голанг Закидываем в иду и наслаждаемся псевдокодом.
Алгоритм работы бинаря прост - дропнуть __init__.pyc
на диск и запустить ее (в агрументах при этом передается что-то похожее на ключ - HzURFvGeN7sClEVLJDBnZVnlrTUBDw41RFefNmcB7bdfFg==
).
Вытащить адрес проживания пики можно strace-ом:
Декомпилируем пику и получаем следующие:
import base64, hashlib, os, sys, urllib.request
KEY = b'K[\x87\xb7\xddR\x1e\xc0I\x1d/\xa2\xea\xa4\xadGW?Z\xffN\x80k\x9d\xe1\xfc4\xffs\xb2\x86k1'
def byte_xor(ba1, ba2):
return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)])
def encrypt(key, plaintext):
while len(key) < len(plaintext):
key += hashlib.sha256(key).digest()
key = key[::-1]
return byte_xor(key[:len(plaintext)], plaintext)
def check(params):
if len(params) != 1:
return False
else:
return hashlib.sha256(encrypt(KEY, base64.b64decode(params[0]))).hexdigest() == 'fb0d5faa2b54bc25b1654a90106913bd1dd7e5a0400986f7676ec1e0afa93d71'
try:
if check(sys.argv[1:]):
r = urllib.request.urlopen('https://forkbomb.ru/?answer=' + str(encrypt(KEY, str(os.getuid()).encode())))
with open('1.txt', 'w') as (f):
f.write(r.read(1024).decode('utf-8'))
except:
pass
with open(__file__, 'wb') as (f):
f.write('')
В check передаются argv, то есть тот странный ключ.
На этом моменте я долго тупил и не понимал куда идти дальше. Спустя пару дней решение пришло само собой:
encrypt(KEY, base64.b64decode('HzURFvGeN7sClEVLJDBnZVnlrTUBDw41RFefNmcB7bdfFg=='))
Задача состоит в получении премиум доступа в приложении для Андроида.
Исследование апк в jadx заводит в com.safonov.speedreading.application.util.PremiumUtil
:
Видно, что уровень доступа(премиум или нет) проверяется только на клиенте, соответственно мы можем запатчить этот самый клиент.
Извлечем байткод из classes.dex
, который находится внутри апк: apktool d ./SpeedRead.apk
.
Откроем PremiumUtil.smali
и изменим код функции isPremiumUser
с
.method public isPremiumUser()Z
.locals 1
.prologue
.line 22
iget-boolean v0, p0, Lcom/safonov/speedreading/application/util/PremiumUtil;->premiumUser:Z
return v0
.end method
На
.method public isPremiumUser()Z
.locals 1
.prologue
.line 22
const/4 v0, 0x1 # v0 = true
return v0
.end method
Пересоберем приложение: apktool b ./SpeedRead -o SpeedRead_Patched.apk
Запускаем приложение и получаем желаемое: