Флаг — это ввод, который заставит программу ответить Yes.
Solution
Анализ main
Начну с разбора функции main:
int32_t main(int32_t argc, char** argv, char** envp)
{
void* const __return_addr_1 = __return_addr;
int32_t* var_c = &argc;
int32_t argc_1 = argc;
char** argv_1 = argv;
void* gsbase;
int32_t var_14 = *(uint32_t*)((char*)gsbase + 0x14);
print_banner();
printf("[*] Enter something: ");
char buf[0x100];
get_user_input(&buf, 0x100);
if (check(&buf))
puts("[-] No...");
else
{
puts("[+] Yes...");
print_flag(&buf);
}
exit(0);
/* no return */
}
Ничего особо интересного не вижу. Читаем в буфер размера 256 байт ввод пользователя через get_user_input и проверяем его в функции check.
Анализ check
После несложных манипуляций я получил вот такой код:
int32_t check(char* buf)
{
void* gsbase;
int32_t canary = *(uint32_t*)((char*)gsbase + 0x14);
int32_t result = 0;
size_t len = strlen(buf);
if (len <= 4 || !(len & 1))
result = 1;
if (*(uint8_t*)buf != '^' || buf[len >> 1] != '_' || buf[len - 1] != '^')
result = 1;
for (void* i = 1; len >> 1 > i; i += 1)
{
if (*(uint8_t*)((char*)i + buf) <= ' ' || *(uint8_t*)((char*)i + buf) > '/')
result = 1;
}
for (void* i_1 = (len >> 1) + 1; len - 1 > i_1; i_1 += 1)
{
if (*(uint8_t*)((char*)i_1 + buf) <= '/' || *(uint8_t*)((char*)i_1 + buf) > '9')
result = 1;
}
if (canary == *(uint32_t*)((char*)gsbase + 0x14))
return result;
__stack_chk_fail();
/* no return */
}
По сути нам нужно подобрать такой ввод, который пройдет эти проверки. Вижу 2 способа: брутфорс и разобрать функцию проверки. Пойдем сложным путем.
Длина должна быть больше 3 и нечётной:
if (len <= 4 || !(len & 1))
result = 1;
Первый и последний символ — ^, а в середине _:
if (*(uint8_t*)buf != '^' || buf[len >> 1] != '_' || buf[len - 1] != '^')
result = 1;
В первой половине символы должны находиться в (0x20, 0x2f], а во второй — (0x2f, 0x39]:
for (void* i = 1; len >> 1 > i; i += 1)
{
if (*(uint8_t*)((char*)i + buf) <= 0x20 || *(uint8_t*)((char*)i + buf) > 0x2f)
result = 1;
}
for (void* i_1 = (len >> 1) + 1; len - 1 > i_1; i_1 += 1)
{
if (*(uint8_t*)((char*)i_1 + buf) <= 0x2f || *(uint8_t*)((char*)i_1 + buf) > 0x39)
result = 1;
}
>>> chr(0x21)
'!'
>>> chr(0x30)
'0'
Значит нам должен подойти флаг ^!_0^:
root@96c8041d7c99:/rev# ./venkata.elf
+------------------------------------+
| TASK #2 |
| by sirroko |
+------------------------------------+
[*] Enter something: ^!_0^
[+] Yes...
[*] FLAG{^!_0^}
Brute force
Для ленивых есть другой путь. Закидываем код функции check в нейронку и просим воспроизвести на Python. Далее просто перебираем все возможные значения начиная с последовательности длиной 5:
import itertools
import ctypes
def check(buf: str) -> bool:
b = buf.encode()
length = len(b)
if length <= 4 or not (length & 1):
return False
half = length // 2
if b[0] != ord('^') or b[half] != ord('_') or b[-1] != ord('^'):
return False
for c in b[1:half]:
if not (0x20 < c <= 0x2F):
return False
for c in b[half+1:-1]:
if not (0x2F < c <= 0x39):
return False
return True
def brute_force():
left_chars = [chr(c) for c in range(0x21, 0x30)]
right_chars = [chr(c) for c in range(0x30, 0x3a)]
for length in range(5, 256, 2):
half = length // 2
for left in itertools.product(left_chars, repeat=half - 1):
for right in itertools.product(right_chars, repeat=half - 1):
candidate = "^" + "".join(left) + "_" + "".join(right) + "^"
if check(candidate):
print(candidate)
return candidate
if __name__ == '__main__':
brute_force()
root@96c8041d7c99:/rev# python3 solver.py
^!_0^
Работает точно так же.