apache cve 2021-40438
前言
做题遇到了,但是发现不理解的话,做不了题
URL中的Hostname VS RequestHeader中的Host字段
http://hostname:port_no |
proxypass
<VirtualHost *> |
payload
http://localhost:80/?unix:(A*4096)|http://localhost:7891/ |
过程
漏洞点
漏洞点出现在proxy_utils.c
中的fix_uds_filename
函数中对url过滤的不严格,以及对错误处理的逻辑存在问题导致的ssrf漏洞
1.传入fix_uds_filenameurl
的hostname
已经被mod_proxy
模块重新修改为配置文件中指定的后端`hostname(http://localhost:4554/)
(在本题中:相当于此时是www.geogle.com?xxx)
2.fix_uds_filename
函数的本意主要是用于处理形如unix:/home/www.socket|http://localhost/whatever/
含有unix套接字的请求
3.在第二个if
条件中,fix_uds_filename首先判断了
r->filename的开头是否为
proxy:
4.接着将r->filename
分为了ptr
和ptr2
两部分,ptr2
根据unix:
关键字对url
进行划分,ptr
则根据|
符号,从ptr2
中进一步划分,提取目标url
并赋值给变量rurl
。
5.而r->filename
来源于proxy_http_canon
函数。这个函数主要的功能是将传入的url进行解析,并拆分为scheme
,host
,port
,path
,和query_args(search)
等部分,最后拼接"proxy:", scheme, "://", host, sport,"/", path, (search)
,赋值给r->filename
,此值来源于以下部分
此时schema=http: |
7.回到fix_uds_filename
函数,在对r->filename
进行拆分后,接下来就是本次漏洞最核心的部分uds_path
的赋值操作。这里使用ap_runtime_dir_relative
对urisock.path
进行处理,并将结果赋值给sockpath
,以键值对的形式(usd_path: sockpath
)存储到r->notes
中。
8.跟进ap_runtime_dir_relative
函数,这里引用了arp库中的apr_filepath_merge
函数。望文生义,这个函数是用来处理文件路径的合并操作的。而传入该函数的两个重要参数:runtime_dir = "/usr/local/apache2/logs"
, file = urisock.path
。
9.apr_filepath_merge
函数计算了strlen(rootpath) + strlen(addpath) + 4
的值,并与APR_PATH_MAX
作比较。如果比APR_PATH_MAX
大,则返回APR_ENAMETOOLONG
常量。这使得上面ap_runtime_dir_relative
在处理完路径后,返回的rv
状态不为if
条件中的状态,从而进入else
分支,返回NULL
。
此时rootpath就是runtime_dir传入的值
10.APR_PATH_MAX
定义在arp.h头文件中,而在linux系统上,PATH_MAX
定义在linux/limits.h
中,值为4096。因此需要填充的addpath
的最小长度为4096 - strlen(rootpath) - 4 = 4069
。
11.接下来,在ap_proxy_determine_connection
函数中对uds_path
进行了初始化。这里我将比较关键的变量都加入到左边的调试窗口中。可以看到,*worker->s->uds_path
最开始并没有被初始化,因此uds_path = apr_table_get(r->notes, "uds_path")
。而这里,恰恰是在fix_uds_filename
中设置过的键值对
12.如果这里的uds_path != null
,那么就会使用unix socket
进行后序的通信。因此作者在fix_uds_filename
函数中通过使用超长的payload这样巧妙的方法来设置uds_path = null
的原因就在于此。只要uds_path = null
,那么就会进入2553行的else
分支,也就回退到tcp连接。
13.此时conn->hostname
和conn->port
都由uri->hostname
和uri->port
进行控制。在此之前,已经调用过apr_uri_parse
处理url
,得到的hostname
和port
正是|
后的目标url
,而这部分内容正是攻击者可控的,因此造成了ssrf漏洞。
payload处理过程
分析payload
http://localhost:80/?unix:(A*4096)|http://localhost:7891/ |
传入以后变成
http://proxypass/?unix:(A*4096)|http://localhost:7891/ |
然后经过由于unix过长,再次提取,访问以下内容
http://localhost:7891/ |
payload
curl --header 'Host: geogle.com' "http://httpd.summ3r.top:60010/proxy?unix:$(python3 -c 'print("A"*4901, end="")')|http://internal.host/flag" |
apache cve 2021-40438