题目地址:https://buuoj.cn/challenges
解题思路
第一步:进入题目,看到一个提交框和源码
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return "我扌 your problem? 111"
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"
</code>
<!-- Dont worry about the suctf.cc. Go on! -->
<!-- Do you know the nginx? -->
第二步:代码审计
- 从/getUrl页面取出参数,并使用parse.urlparse(url)对参数进行划分得到元组,获取其中的hostname值(现已改为netloc),并对host值进行判断若为suctf.cc则报错
- 将元组变为列表,并取出第二项对其判断若为suctf.cc,则报错
- 对列表里第二项的值以‘.’分割后进行idna编码后使用utf8解码,再用‘.’进行连接
- 对列表里的值去除空白字符串后使用urlunsplit函数重新生成URL
- 最后对重新生成URL取出host进行判断,若为suctf.cc,读出URL指定的文件内容
第三步:构建漏洞
- URL开头可以是http,可以是https,还可以是file。前两者用于访问页面,后者用于读取文件内容,所以我们可以使用file开头读取提示的nginx
- 由于使用函数parse.urlparse(url)对url各部分进行划分,当url协议后面满足
://
的格式时会将后面的第一级目录划定为hostname,当斜杠/
数量不是2个时,即使是hostname,也不会识别
url = 'https://a9dedbc8-7d8c-4550-ac62-9e99eb016317.node3.buuoj.cn/'
print(parse.urlparse(url))
结果为:ParseResult(scheme='https', netloc='a9dedbc8-7d8c-4550-ac62-9e99eb016317.node3.buuoj.cn', path='/', params='', query='', fragment='')
url = 'https:/a9dedbc8-7d8c-4550-ac62-9e99eb016317.node3.buuoj.cn/'
print(parse.urlparse(url))
结果为:ParseResult(scheme='https', netloc='', path='/a9dedbc8-7d8c-4550-ac62-9e99eb016317.node3.buuoj.cn/', params='', query='', fragment='')
netloc值不一样
- 由于后面使用urlunsplit函数把URL剔除空格后还原,协议后面缺少的://会自动填补上,但是当协议后面的参数开头有两个//时,只补上
:
- 经过2,3分析我们可以使file后面跟的
/
数量为4个,file:
,这样使用parse.urlparse划分时,将hostname划分到path里面去了,在使用urlunsplit还原时,由于path里面有两个//,则不进行添加,最后由https:a9dedbc8-7d8c-4550-ac62-9e99eb016317.node3.buuoj.cn/
变成了https://a9dedbc8-7d8c-4550-ac62-9e99eb016317.node3.buuoj.cn/
,而file协议与路径之间有多少个/
都不会影响file协议读取文件,file:///D:/jdk/1.txt
能读取到1.txt,file:/D:/jdk/1.txt
这样也能读取到
第四步:获取nginx配置文件
输入file:suctf.cc/usr/local/nginx/conf/nginx.conf
,得到提示:/usr/fffffflag
第五步:获取flag
输入file:suctf.cc/usr/fffffflag
获取flag