
Dieses Repo enthält einen Blogbeitrag über meine Analyse zu CVE-2018-19987, einer authentifizierten OS-Befehlsinjektion, die mehrere D-Link-Router betrifft.
Hallo zusammen!
In diesem kurzen Blogbeitrag stelle ich eine schnelle Analyse vor, die ich durchgeführt habe, um die Details hinter CVE-2018-19987 besser zu verstehen. Zunächst bin ich dieser CVE nachgegangen, weil ich dachte, dass es keine öffentlichen Informationen oder Exploits dazu gäbe. Kurz nach einer ersten Analyse fand ich zwei öffentliche PoCs auf GitHub, die ihr hier und hier finden könnt.
Da ich den Großteil der Arbeit bereits erledigt hatte und keine vollständige Analyse gefunden hatte, beschloss ich, den Blogbeitrag trotzdem zu schreiben.
Ich hoffe, er gefällt euch!
Aktualisierung: Nun ja, nachdem ich gegoogelt hatte, während ich an diesem Blogbeitrag festhing, fand ich den folgenden Artikel, der eine sehr ähnliche Schwachstelle (CVE-2018-19986) im D-Link DIR-818 beschreibt. Router-Schwachstellenanalyse-Serie (5): CVE-2018-19986 DIR-818LW&828 Command-Injection-Schwachstellenanalyse und -Reproduktion (Google-Übersetzung) von Ogur1.
Ich begann meine Analyse, indem ich alle verfügbaren Informationen über das Gerät sammelte:
Hinweis: Ich habe die gesamte Analyse mit der Firmware v3.01B02 durchgeführt.
Öffentliche Informationen zu dieser Schwachstelle, verfügbar auf Mitres Seite, lieferten genügend Informationen für den Einstieg:
D-Link DIR-822 Rev.B 202KRb06, DIR-822 Rev.C 3.10B06, DIR-860L Rev.B 2.03.B03, DIR-868L Rev.B 2.05B02, DIR-880L Rev.A 1.20B01_01_i3se_BETA, and DIR-890L Rev.A 1.21B02_BETA devices mishandle IsAccessPoint in /HNAP1/SetAccessPointMode. In the SetAccessPointMode.php source code, the IsAccessPoint parameter is saved in the ShellPath script file without any regex checking. After the script file is executed, the command injection occurs. A vulnerable /HNAP1/SetAccessPointMode XML message could have shell metacharacters in the IsAccessPoint element such as the `telnetd` string.
Um die Analyse zu beginnen, brauchte ich etwas, das ich untersuchen konnte, also extrahierte ich das Dateisystem des Routers.
binwalk -eM DIR822C1_FW301WWb02.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/1"
10380 0x288C LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 4213444 bytes
1376372 0x150074 PackImg section delimiter tag, little endian size: 10505216 bytes; big endian size: 5021696 bytes
1376404 0x150094 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 5019773 bytes, 2282 inodes, blocksize: 131072 bytes, created: 2016-03-18 09:35:31
Wie wir sehen können, erkennt Binwalk ein Linux-Dateisystem. Wenn wir die vorhandenen Informationen genauer betrachten, sehen wir, dass das Advisory den Pfad /HNAP1 erwähnt. Wir können also damit beginnen zu untersuchen, wie der Router diese URLs verarbeitet.
Wie in dem exzellenten /dev/tty0-Blogbeitrag erklärt, werden diese URLs letztendlich von einer Binärdatei namens cgibin verarbeitet, die sich unter htdocs/cgibin befindet. Ich wollte jedoch besser verstehen, wie dies konfiguriert war, und das führte mich schließlich dazu, verstehen zu müssen, wie der HTTP-Server konfiguriert war, um Anfragen zu verarbeiten. Ich suchte nach dem Dateinamen httpd.conf und nahm an, dass er zur Konfiguration des HTTP-Servers verwendet wird. Dabei fand ich eine Datei namens HTTP.php im Ordner /etc/services/, die unter anderem die folgenden interessanten Zeilen enthielt:
$httpd_conf = "/var/run/httpd.conf";
fwrite("a",$START, "xmldbc -P /etc/services/HTTP/httpcfg.php > ".$httpd_conf."\n");
fwrite("a",$START, "event PREFWUPDATE add /etc/scripts/prefwupdate.sh\n");
fwrite("a",$START, "httpd -f ".$httpd_conf."\n");
fwrite("a",$START, "event HTTP.UP\n");
fwrite("a",$START, "exit 0\n");
Ich nahm an, dass diese Datei irgendwann ausgeführt wird und die Datei /var/run/httpd.conf schreibt. Um zu erfahren, wie der Webserver konfiguriert war, analysierte ich die Datei /etc/services/HTTP/httpcfg.php (ich habe nur die relevanten Teile aufgenommen):
if ($hnap > 0)
{
echo
" Control". "\n".
" {". "\n".
" Alias /HNAP1". "\n".
" Location /htdocs/HNAP1". "\n".
" External". "\n".
" {". "\n".
" /usr/sbin/hnap { hnap }". "\n".
" }". "\n".
" IndexNames { index.hnap }". "\n".
" }". "\n";
}
Jetzt haben wir alle Puzzleteile! Wir können annehmen, dass der Webserver so konfiguriert ist, dass er HTTP-Anfragen an /HNAP1 mithilfe von /usr/sbin/hnap verarbeitet, und basierend auf dem /dev/tty0-Blogbeitrag wissen wir, dass es sich um einen Link auf die Binärdatei htdocs/cgibin handelt.
Der nächste Schritt war, die Binärdatei cgibin zu untersuchen, um zu verstehen, wie sie HNAP-Anfragen verarbeitet. Im folgenden Bild sehen wir einen Teil der dekompilierten main-Funktion, in der der übergebene URL-Pfad mit verschiedenen Pfaden verglichen wird (z. B. session.cgi, authentication.cgi, captcha.cgi, um nur einige zu nennen), bis hnap gefunden wird und unsere Funktion hnap_main aufgerufen wird.
Hinweis: Bei der Analyse der main-Funktion hatte ich einige Probleme, den hnap-String zu identifizieren, da Ghidra ihn während der ersten Analyse nicht als String erkannt hatte.
Der nächste Schritt war zu verstehen, was diese Funktion tut. Ich werde nur Codeausschnitte für die relevanten Teile bereitstellen, die helfen, besser zu verstehen, was die Funktion tut. Außerdem werde ich andere relevante Teile mit etwas Pseudocode vervollständigen — ich habe einige Variablen umbenannt, um ihren Zweck zu verdeutlichen.
...
HTTP_SOAPACTION = getenv("HTTP_SOAPACTION");
REQUEST_METHOD = getenv("REQUEST_METHOD");
HNAP_AUTH = getenv("HTTP_HNAP_AUTH");
__haystack = getenv("HTTP_COOKIE");
pcVar1 = getenv("HTTP_REFERER");