Флаг — это ввод, который заставит программу ответить 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^

Работает точно так же.