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