
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.