
Cisco RV110w UPnP Stack-Überlauf
In letzter Zeit ist UPnP ziemlich beliebt, und ich habe zufällig einen Cisco RV110W. Im August 2021 veröffentlichte Cisco offiziell einen 0-Day für die Cisco RV-Serie bezüglich UPnP, aber die genauen Details wurden nicht veröffentlicht. Also wollte ich die Schwachstelle mit dem Gerät debuggen und ausgraben. Die Ankündigung der Schwachstelle kann auf der offiziellen Website eingesehen werden.
Aktualisieren Sie zuerst die Firmware auf die neueste Version 1.2.2.8. Das nächste Problem ist, wie man die Schwachstelle debuggt und lokalisiert. Zuerst muss das Debuggen gelöst werden. Die Hauptaufgabe beim Debuggen ist es, eine Shell auf dem Gerät zu erhalten. Die neueste Firmware bietet jedoch keine Debug-Schnittstelle. Der Autor hat hier über die UART-Schnittstelle und durch Modifikation des Firmware-Pakets Debug-Zugriff auf die neueste Firmware erhalten. Die spezifische Methode finden Sie in einem zuvor geschriebenen Artikel Router-Debuggen: GetShell.
Der Cisco RV110W basiert auf der mipsel-Architektur, daher muss zunächst ein entsprechender gdb-Server gefunden werden. Man kann ihn selbst cross-kompilieren oder vorkompilierte Versionen verwenden. Hier wird gdb-static-cros empfohlen.
Die offizielle Ankündigung besagt, dass die Schwachstelle im UPnP-Dienst vorhanden ist. Gehen Sie zuerst zur Verwaltungsoberfläche und öffnen Sie unter FireWall -> Basic Settings die UPnP-Konfiguration.

Dann scannen Sie die Ports mit nmap. Es wurde kein UPnP-Port gefunden, aber Tests zeigen, dass UPnP tatsächlich geöffnet ist.

Hier verwendet der Autor UPnPy zum Testen und Ausnutzen der Schwachstelle.
import socket
msg = \
b'M-SEARCH * HTTP/1.1\r\n' \
b'HOST:239.255.255.250:1900\r\n' \
b'ST:upnp:rootdevice\r\n' \
b'MX:2\r\n' \
b'MAN:"ssdp:discover"\r\n' \
b'\r\n'
# Set up UDP socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.bind((b"192.168.2.100",23333)) #本机IP
s.settimeout(2)
s.sendto(msg, (b'239.255.255.250', 1900))
addr = ('192.168.2.1', 1900) # 网关IP
try:
while True:
data, addr = s.recvfrom(65507)
print(addr,data)
except socket.timeout:
pass
Es wurde tatsächlich eine UPnP-Antwort erhalten, und im Prozess des Geräts gibt es einen entsprechenden UPnP-Prozess.

UPnP ist ein universeller Protokollstandard, die meisten Hersteller implementieren ihn nach Standard, d.h. viele Aktionen sind gleich. Es ist jedoch notwendig, die vom Gerät bereitgestellten Dienste zu analysieren, um die Schwachstelle zu lokalisieren und auszunutzen. Auch hier wird UPnPy zur Informationssammlung verwendet.
import upnpy
import socket
import requests
from upnpy.ssdp.SSDPDevice import SSDPDevice
msg = \
b'M-SEARCH * HTTP/1.1\r\n' \
b'HOST:239.255.255.250:1900\r\n' \
b'ST:upnp:rootdevice\r\n' \
b'MX:2\r\n' \
b'MAN:"ssdp:discover"\r\n' \
b'\r\n'
# Set up UDP socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.bind((b"192.168.2.100",23333))
s.settimeout(2)
s.sendto(msg, (b'239.255.255.250', 1900))
addr = ('192.168.2.1', 1900)
data = b""
try:
while True:
data, addr = s.recvfrom(65507)
print(addr,data)
except socket.timeout:
pass
# data = b'HTTP/1.1 200 OK\r\nCache-Control: max-age=120\r\nDate: Fri, 01 Jan 2010 00:44:16 GMT\r\nExt: \r\nLocation: http://192.168.2.1:1780/InternetGatewayDevice.xml\r\nServer: POSIX UPnP/1.0 linux/5.70.48.16\r\nST: upnp:rootdevice\r\nUSN: uuid:31474a87-67ea-dae4-2f73-f157fb06d22b::upnp:rootdevice\r\n\r\n'
# data = b'HTTP/1.1 200 OK\r\nCache-Control: max-age=3600\r\nST: upnp:rootdevice\r\nUSN: uuid:824ff22b-8c7d-41c5-a131-8c3bad401726::upnp:rootdevice\r\nEXT:\r\nServer: Unspecified, UPnP/1.0, Unspecified\r\nLocation: http://192.168.3.1:56688/rootDesc.xml\r\n\r\n'
device = SSDPDevice(addr, data.decode())
services = device.get_services()
services_id = [services[i].id.split(":")[-1] for i in range(len(services))]
for id in services_id:
service = device[id]
actions = service.get_actions()
for action in actions:
for argument in action.arguments:
print(id,action.name,argument.name)
Es können eine Reihe von Dienstinformationen abgerufen werden, ein Teil davon ist unten aufgeführt:
WANIPConn1 AddPortMapping NewRemoteHost
WANIPConn1 AddPortMapping NewExternalPort
WANIPConn1 AddPortMapping NewProtocol
WANIPConn1 AddPortMapping NewInternalPort
WANIPConn1 AddPortMapping NewInternalClient
WANIPConn1 AddPortMapping NewEnabled
WANIPConn1 AddPortMapping NewPortMappingDescription
WANIPConn1 AddPortMapping NewLeaseDuration
WANIPConn1 DeletePortMapping NewRemoteHost
WANIPConn1 DeletePortMapping NewExternalPort
WANIPConn1 DeletePortMapping NewProtocol
WANIPConn1 GetExternalIPAddress NewExternalIPAddress
Diese Dienste können grob in get- und set-Klassen sowie einige wenige delete-Klassen unterteilt werden. Da eines der Hauptziele von UPnP darin besteht, interne Geräte für externe Geräte freizugeben, sind bestimmte Konfigurationen (Portweiterleitung usw.) erforderlich. Da eine Konfiguration benötigt wird, sind die Parameter und Informationen der Konfiguration unverzichtbar, und diese Parameter sind tatsächlich die Parameter in den set-Klassendiensten.
Da nicht alle Dienste vom Hersteller implementiert werden, muss die Firmware selbst reverse-engineered werden. Lokalisieren Sie zuerst upnp_mainloop.

Die gesamte Verarbeitungslogik wird in upnp_dispatch implementiert. Bei der weiteren Analyse wurden SSDP- und HTTP-Anforderungsverarbeitungsteile gefunden.

ssdp_process entspricht der M-Search-Anforderung während der Adressierung, upnp_http_process entspricht der Verarbeitung von HTTP-Anforderungen. Da normale Dienstaufrufe HTTP-Anforderungen sind, wird angenommen, dass upnp_http_process möglicherweise Schwachstellen aufweist. Ein Beispiel für einen Dienstaufruf ist unten dargestellt.

In upnp_http_process wird weiter upnp_http_fsm_emgine aufgerufen.

Bei der weiteren Analyse wurde festgestellt, dass mehrere Funktionen an der Adresse off_45ab80 ausgeführt werden.

Diese Funktionen umfassen Initialisierung und Parsen von Protokollheadern. Die letzte Funktion ist upnp_http_fsm_dispatch, es wird vermutet, dass sie die entsprechenden Dienstfunktionen ausführt.

Bei der weiteren Analyse von upnp_http_fsm_dispatch wurde festgestellt, dass tatsächlich Funktionsmethoden aufgerufen werden, jedoch basierend auf a1 und a2. Die genaue aufgerufene Funktion kann nicht bestimmt werden, dynamisches Debuggen ist erforderlich.

Wenn es sich um eine normale SSDP-Adressierungsanforderung handelt, werden SUB_405B34 und description_process aufgerufen. Wenn es sich um eine dienstbezogene Anforderung handelt, wird soap_process aufgerufen. In soap_process wird basierend auf den Anforderungsheaderinformationen entweder query_process oder action_process aufgerufen.
Konzentrieren Sie sich hauptsächlich auf action_process. Basierend auf dem Anforderungsheader des Dienstaufrufs wird vermutet, dass soap_control in action_process dem Dienstaufruf entspricht.

In soap_control ist dennoch dynamisches Debuggen erforderlich, um die genauen Funktionsinformationen zu bestimmen.

Schließlich wurde durch dynamisches Debuggen festgestellt, dass sub_414C28 der Aktion AddPortMapping entspricht, dessen Funktionsaufrufkette lautet:
sub_414c28->upnp_portmap_add->upnp_osl_nat_config->strcpy(stack overflow)
In upnp_portmap_add wird jedoch die WAN-Port-Adresse des Geräts überprüft. Da der Autor beim Testen keinen WAN-Port konfiguriert hatte, wurde die WAN-IP direkt mit nvram gesetzt.

Beim Stack-Überlauf muss der Programmfluss in den roten Block gelenkt werden, da die Funktion im blauen Block auf den überschriebenen Stack zugreift und das Programm vor der RCE zum Absturz bringt. Daher muss der Wert von *(a2+11) auf 0 gesetzt werden. Glücklicherweise ist dieser Wert steuerbar.

Da die neueste Firmware kein telnetd hat, kann eine Reverse-Shell gestartet werden, dann wird ein utelnetd hochgeladen und Telnet aktiviert.

Referenz: Reverse-Shell-Methoden