We figured out that the H1N337 viruses has a virtual variant too, and a sample has been encapsulated in this sandbox. Can you write a code to pass through the sandbox without being infected?
Перед нами классическая пвнка, в описании ничего интересного. Взглянем на бинарь в IDA:
Функция main в бинаре (уже причёсанная)
read возвращает количество реально прочитанных байт, и если оно больше, чем 12, то нам кроме надписи “too big!” ничего не светит. Далее вызывается enable_seccomp, mprotect на наш шеллкод с правами rx (чтение + исполнение) и собственно сам шеллкод.
enable_seccomp запрещает все сисколы, кроме read, write, open, mprotect, exit, exit_group.
Вроде не так и страшно — по крайней мере мы можем читать файлы.
На этом статический анализ можно считать завершенным — больше в бинаре ничего интересного нет.
Для начала можно отослать \\xeb\\xfe
— jmp, который прыгает сам на себя. Результат предсказуемый — сервис зависает, что означает, что наш код действительно исполняется на сервере.
Следующим, более разумным шеллкодом мы должны решить одну из следующих задач:
Очевидно, что впихнуть чтение файла в 12 байт сложно (если вообще возможно). На ум приходит сделать mprotect с rwx и ещё один read, чтобы прочитать в буфер шеллкод больше 12 байт. Чтобы не писать лишний код, попробуем переиспользовать то, что уже есть в регистрах и на стеке. Для этого остановим бинарь в gdb на начале шеллкода:
Да, мне было лень ставить брейкпоинт и я просто ввёл фигню, чтобы бинарь крашнулся прям на начале шеллкода. На скрине gdb с плагином peda, для удобства отладки.
Для того чтобы сделать mprotect(buf, 0x1000, 7)
нам достаточно записать в rdx 7 (третий аргумент) и в rax 10 (номер сискола), после чего вызвать syscall
:
mov dl, 7 /* для экономии места пишем в однобайтовый регистр */
mov al, 10
syscall
Компилируем любым удобным способом (я предпочитаю pwntools), проверяем размер. 6 байт — ровно половина отведённого места. Осталось впихнуть read. Основная проблема заключается в том, что у mprotect и у read разный порядок аргументов:
int mprotect(void *addr, size_t len, int prot);
ssize_t read(int fd, void *buf, size_t count);