SSRF:CVE-2021-40438
SSRF本质:应用把外部输入的URL/地址直接当作目标去请求,而没有严格检查,攻击者利用这个缺陷,让服务器去请求攻击者想让它访问的资源
CVE-2021-40438:环境:Apache+mod_proxy
- mod_proxy是一个代理模块,它的作用是把收到的请求转发到其他服务器
- 漏洞出现在URL-PATH解析逻辑:当服务器收到请求时,mod_proxy会解析URL PATH,决定把请求代理到哪里,攻击者通过构造特殊的路径,让mod_proxy把目标服务器改成攻击者可控的地址,或者直接改为内网/metadata地址
1.开启环境:docker-compose up -d
2.docker ps用于查看端口映射状态
3.可以去访问一下该页面
4.用burp Suite抓包
5.放入构造的payload
why?
1.下载apache 2.4.48版本的源码:https://launchpad.net/debian/+source/apache2/2.4.48-2
2.相关源码文件:modules/proxy/proxy_util.c,modules/proxy/mod_proxy.c
3.proxy_utils.c:fix_uds_filename()
这段函数用于反向代理,把请求转发给后端服务时,如果后端服务用的是Unix Socket而不是TCP,这个函数就会处理这种URL,把其中的Socket路径解析出来并存储到请求对象里
判断条件:
- r->filename前六个字符是proxy
- URL中包含unix
- unix后面有|分隔符
ptr2指向“unix”的位置,ptr指向“|”的地址,将|替换为\0,将它拆成两部分
调用apr_uri_parse解析socket URL:rurl指向|后买你的URL,ap_runtime_dir_relative()将socket路径转换为运行时绝对路径,
4.ap_runtime_dir_relative()
通过apr_filepath_merge()将runtime_dir和file拼接为绝对路径,APR_FILEPATH_TRUENAME选项解析路径中的符号链接,保证路径是真实存在的,然后检查结果并返回。
-
如果拼接成功,或者虽然路径不存在但允许(
ENOENT
/ENOTDIR
/通配符情况),返回newpath
-
否则返回
NULL
5.apr_filepath_merge()
if (maxlen > APR_PATH_MAX) {return APR_ENAMETOOLONG;
}
path = (char *)apr_palloc(p, maxlen);
可能导致错误的方法就是文件名过长,那么构造超长的文件名使其返回NULL
所以这个代码本应该使用UDS,但是现在uds_path为NULL,绕过UDS逻辑,服务器最终将请求转发到攻击者指定的URL,实现了SSRF
修复:
把原来在整个r->filename中任意位置搜索unix:的逻辑,改为只在proxy:前缀之后,严格见擦汗开头就是unix:,从而避免把query参数里任意unix:当成UDS URL解析
未修补代码:
if (!strncmp(r->filename, "proxy:", 6) &&(ptr2 = ap_strcasestr(r->filename, "unix:")) &&(ptr = ap_strchr(ptr2, '|'))) {...
}
问题:ap_strcasestr(r->filename, "unix:")
会在整个 r->filename
(包含 query)中任意位置匹配 unix:
,攻击者可以把 unix:
放在参数里触发该分支。
修补后的代码:
if (!strncmp(r->filename, "proxy:", 6) &&!ap_cstr_casecmpn(r->filename + 6, "unix:", 5) &&(ptr2 = r->filename + 6 + 5, ptr = ap_strchr(ptr2, '|'))) {...
}
修补将unix:的检查限定在proxy前缀之后