Очередной лабиринт. Чтобы получить флаг надо выиграть и собрать 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}