Надо пройти все проверки, чтобы приложение отобразило флаг.

Бинари:

Solution

Посмотрю инфу о файле:

Info:
    File name: /spbctf_rev/task3/task3
    Size: 7628
    File type: ELF32
    String: ELF(386)
    Extension: elf
    Operation system: Ubuntu Linux(16.04.4,ABI: 2.6.32)
    Architecture: 386
    Mode: 32-bit
    Type: EXEC
    Endianness: LE

Закидываю бинарь в Binary Ninja. Угадайте, что я иду смотреть?) Правильно, функцию main)

int32_t main(int32_t argc, char** argv, char** envp) {
    void* const __return_addr_1 = __return_addr
    int32_t* var_c = &argc
    char** argv_1 = argv
    void* gsbase
    int32_t eax_1 = *(gsbase + 0x14)
    FILE* fp = fopen(filename: "Hello1", mode: "r")
    int32_t result
    
    if (fp != 0)
        void buf
        fgets(&buf, n: 0xff, fp)
        fclose(fp)
        FILE* fp_1 = fopen(filename: "Hello2", mode: "r")
        
        if (fp_1 != 0)
            void buf_1
            fgets(buf: &buf_1, n: 0xff, fp: fp_1)
            fclose(fp: fp_1)
            
            if (check(&buf, &buf_1) == 0)
                if (check2(&buf) == 0)
                    printf(format: "Flag{%s}\n", &buf)
                
                result = 0
            else
                puts(str: "Nope")
                result = 1
        else
            puts(str: "NOT AGAIN!")
            result = 3
    else
        puts(str: "NOOOOO")
        result = 5
    
    if (eax_1 == *(gsbase + 0x14))
        return result
    
    __stack_chk_fail()
    noreturn
}

Сперва обратил внимание на данный кусок кода:

FILE* fp = fopen(filename: "Hello1", mode: "r")
int32_t result
    
if (fp != 0)
	void buf
    fgets(&buf, n: 0xff, fp)
    fclose(fp)
    FILE* fp_1 = fopen(filename: "Hello2", mode: "r")
        
	if (fp_1 != 0)
		void buf_1
        fgets(buf: &buf_1, n: 0xff, fp: fp_1)
        fclose(fp: fp_1)

В нем открывается 2 файла для чтения — Hello1 и Hello2. В buf и buf_1 происходит чтение их содержимого. Значит для решения лабы нужно будет создать эти файлы.

Далее нужно разобрать функции check и check2. Начну рассматривать их по порядку:

int32_t check(char* arg1, char* arg2) {
    size_t len1 = strnlen(arg1, 256);
    size_t len2 = strnlen(arg2, 256);
    
    if (len1 != len2)
        return -1;
    
    int32_t var_18_1 = 0;
    int32_t i = 0;
    
    while (true)
    {
        if (i >= len1)
            return 0;
        
        if (arg1[i] != arg2[((len2 - 1) - i)])
            break;
        
        i += 1;
    }
    
    return -1;
}

После пары переименований и преобразований становится ясно, что строки должны быть одинаковой длины, а также что в Hello1 хранится прямое представление строки, а в Hello2 — перевернутая строка.

Перейду к check2:

int32_t check2(char* arg1) {
    if (strnlen(arg1, 256) != 8)
        return -1;
    
    if (*(uint8_t*)arg1 != arg1[6])
        return -1;
    
    if (arg1[2] != arg1[3])
        return -1;
    
    if (arg1[3] != arg1[7])
        return -1;
    
    if (arg1[1] != 'f')
        return -1;
    
    if (arg1[7] != 's')
        return -1;
    
    if (*(uint8_t*)arg1 != 'z')
        return -1;
    
    if ((((int32_t)arg1[5]) - ((int32_t)arg1[4])) != 8)
        return -1;
    
    if (arg1[5] == 'i')
        return 0;
    
    return -1;
}

Такс… Давайте разбираться:

    if (strnlen(arg1, 256) != 8)
        return -1;

Значит длина строки — 8.

    if (*(uint8_t*)arg1 != arg1[6])
        return -1;
    
    if (arg1[2] != arg1[3])
        return -1;
    
    if (arg1[3] != arg1[7])
        return -1;

Группы элементов 0 и 6, а также 2, 3, 7 — совпадают.

	if (arg1[1] != 'f')
        return -1;
    
    if (arg1[7] != 's')
        return -1;
    
    if (*(uint8_t*)arg1 != 'z')
        return -1;
        
    if ((((int32_t)arg1[5]) - ((int32_t)arg1[4])) != 8)
        return -1;

    if (arg1[5] == 'i')
        return 0;

1 элемент — f, 7s, 0z, 5i.

Ну а 4 элемент меньше 5 на 8.

Представим ключ:

zfssaizs

Создам файлы:

IMG

Запущу бинарь:

root@6a23fdb0da11:/rev# ./task3
Flag{zfssaizs}