Discuz3.4-SSRF-从触发点到构造payload

SSRF逆向分析

0x00 前言

之前有复现过一些漏洞,但是每次按照别人的思路复现完了之后感觉还是有很多疑问,知道了怎么做但是不知道为什么这么做,所以这次我尝试自己从补丁一步步找到攻击链,构造poc。

0x01 收集情报

补丁地址:
https://gitee.com/ComsenzDiscuz/DiscuzX/commit/41eb5bb0a3a716f84b0ce4e4feb41e6f25a980a3

查看补丁,发现如下:
1077935-20190428231036229-1883155460.png

删去了followlocation,也就是说对于301/302请求,curl不会去跟踪跳转。
既然这里存在一个跳转ssrf,下面就是逆向调用链,找到程序的入口。

0x02 尝试逆向找到触发点

首先这个存在漏洞的函数是_dfsockopen,通过Ctrl+Alt+F大法找到了位于function_core.php的dfsockopen方法。

1077935-20190428231044685-8726578.png

继续向上找,找到了一处import_block方法。

1077935-20190428231050576-1823625711.png

通过对dfsockopen的第一个参数进行分析,发现其刚好是import_block的第一个参数经过一些处理之后的结果。
由于参数可控,继续向上。

1077935-20190428231100575-46652462.png

鸡冻人心的发现!第一个参数直接以$_GET传了进去!

0x03 尝试构造payload

下面看一下如何访问到这个语句:

1077935-20190428231106501-1581435964.png

首先,直接通过文件肯定是访问不了的(L10-12)。下面根据L19和L21确定url中基本要必须存在的参数。经过一系列的尝试和Ctrl+Alt+F,终于找到了入口:
/upload/admin.php?action=blockxml&operation=add

1077935-20190428231113458-424155583.png

跟进一下submitcheck()

1077935-20190428231118816-656138023.png

继续跟进getgpc()

1077935-20190428231123665-1682617257.png

大概就是返回$_GET[$k],由于这里的$k就是从前面的submitcheck('addsubmit')传进来的,所以这里只要保证$_GET['addsubmit']即可,构成的url如下:
/upload/admin.php?action=blockxml&operation=add&addsubmit=test

继续跟,

1077935-20190428231130918-1426329754.png

可以看到getgpc返回了$_GET['addsubmit']的值,由于我们的url参数中有此参数,因此进入到了else语句块。继续跟进submitcheck

1077935-20190428231136640-1575887569.png

这里又有一个相同的getgpc(),由于参数跟刚刚也相同,就不继续跟了,直接进入到else语句块。可以看到,首先22行有个if语句,必须把条件满足成True,否则是False的话就直接进入Else语句块,这条链就直接中断掉了。仔细看一下这个if条件:

$allowget || ($_SERVER['REQUEST_METHOD'] == 'POST' && 
!empty($_GET['formhash']) && $_GET['formhash'] == formhash() && 
empty($_SERVER['HTTP_X_FLASH_VERSION']) && 
(empty($_SERVER['HTTP_REFERER']) ||      strncmp($_SERVER['HTTP_REFERER'], 'http://wsq.discuz.com/', 22) 
=== 0 || preg_replace("/https?:\/\/([^\:\/]+).*/i", "\\1", 
$_SERVER['HTTP_REFERER']) == preg_replace("/([^\:]+).*/", "\\1", 
$_SERVER['HTTP_HOST'])))

最外层是个or,如果$allowget是True就直接省事儿了,可是这是此方法的第二个参数,默认为0,pass。剩下的逻辑如下:
1)必须是POST请求 &&
2)GET请求中必须有formhash参数 &&
3)formhash的值必须等于formhash() &&
4)请求头中没有HTTP_X_FLASH_VERSION &&
5.1)refer为空 ||
5.2)referer的值以http://wsq.discuz.com/开头 ||
5.3)referer与host的主机名部分必须相同

第1、4、5条件好满足,直接抓包改即可。主要看第二个请求和第三个请求,即如何获取这个formhash。看一下函数定义:

1077935-20190428231145045-2065481122.png

其大致是计算一个数的MD5,这个数由几个$_G变量组成。既然不是一个固定的值,那么首先肯定是服务端先发给客户端,然后客户端才能带着这个$_GET['formhash']来进行请求,下面全局搜一下formhash,发现很多页面中都有这个字段:

1077935-20190428231152042-1413742906.png

然后随手在页面上查找一下,没想到真找到了:

1077935-20190428231158635-399271097.png

(经过一些测试,这里有个比较坑的点是这个formhash在同一个session请求中是不会变的,不过前台和后台的formhash不是同一个,你不能拿前台获取的formhash作为参数去访问后台的接口)。

formhash的问题到这里就解决了,会看一下上面的条件,构成的url暂时如下:

POST ..../upload/admin.php?action=blockxml&operation=add&addsubmit=test&formhash=2b23ba6f

并且去掉referer头。
请求之后可以发现,成功进入了if语句,然后顺其自然的到了return True。

1077935-20190428231206325-471744371.png

然后就终于回到了最开始的地方,成功调用import_block()

1077935-20190428231212797-315057022.png

由于这里需要$_GET['xmlurl'],我们暂且传入http://127.0.0.1:2222。

1077935-20190428231218981-1777202150.png

可以看到,最后url赋值给了$signurl,其值变成:
http://127.0.0.1:2222?charset=utf-8&clientid=&op=getconfig&sign=

没有什么太大的变化,继续跟进后就到了最开始说的那个可能存在ssrf的_dfsockopen方法了。通过下图可以看到,先是在33行调用parse_url对用户传来的url进行解析,然后调用_isLocalip()来检查host是否是内网地址,如果是内网地址则直接return掉。所以就算这里存在ssrf,我们的url中也是不能直接传内网地址进来的。

1077935-20190428231229014-1897835831.png

1077935-20190428231237407-62866231.png

接着看,这里在88行发送了请求,我在这次请求中传入的url是:upload/admin.php?action=blockxml&operation=add&addsubmit=test&formhash=2b23ba6f&xmlurl=http://127.0.0.1:2222

1077935-20190428231244005-2140595686.png

这里看一下我本地监听的2222端口:

1077935-20190428231250470-108285756.png

访问成功了。

下面的整理下思路,由于程序对内网地址进行了限制,导致了除127.0.0.1之外的内网地址都会直接return掉,因此这里我们需要通过一个301跳转,来实现绕过程序对内网url的限制。
可是如果想要curl自动重定向到第一个url返回的地址中去,就必须先要将此curl的CURLOPT_FOLLOWLOCATION属性设置为true才行。然而这一点在本文一开始就已经确认了:

1077935-20190428231257281-1731664201.png

下面就可以通过在vps上上传一个301跳转的php脚本,内容如下:

1077935-20190428231303457-500795946.png

下面把我们之前的payload中的xmlurl改成我的公网vps的ip,然后重放,同时在本地监听9999端口。

1077935-20190428231308815-278281377.png

请求结果如下,可以发现,本地的9999端口果然收到了discuz-curl发来的请求!

1077935-20190428231316744-1032684932.png

我的vps的http日志:

1077935-20190428231323346-817798913.png

至此,这条ssrf的攻击链就已经形成了。

0x04 总结

这次跟下来还是学到了一些东西的,比如构造payload时会遇到的一些坑,然后自己对ssrf也有了跟深入的一些理解。

转载于:https://www.cnblogs.com/litlife/p/10787682.html

### Java 安全编码中的 SSRF 漏洞练习与测试环境 SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种常见的安全漏洞,攻击者可以通过该漏洞诱导应用程序向内部网络或其他外部目标发送HTTP请求。为了帮助开发者更好地理解和防范此类漏洞,在Java环境中可以使用一些专门设计的实验室或工具来模拟和实践。 #### 常见的SSRF漏洞练习平台 OWASP Juice Shop 是一个流行的开源项目,它故意包含了多种Web应用安全漏洞供学习者研究,其中包括SSRF漏洞实例[^2]。通过部署这个程序,你可以体验到真实世界中可能出现的各种攻击场景并尝试修复它们。 另一个推荐的是 DVWA (Damn Vulnerable Web Application),虽然主要面向PHP开发人员,但也提供了基础级别的SSRF挑战,适合初学者了解基本概念[^3]。 对于更专注于Java生态系统的训练资源,则有 OWASP WebGoat ,这是一个专门为教育目的而构建的应用程序,其中涵盖了多个主题的安全课程,包括但不限于SQL注入、跨站脚本(XSS)以及我们关心的SSRF等问题领域[^4]。 以下是设置这些环境的一个简单指南: ```bash # 更新系统包管理器索引 sudo apt update && sudo apt upgrade -y # 安装必要的依赖项 sudo apt install git docker-compose -y # 下载Juice Shop源码仓库 git clone https://github.com/juiceshop/juiceshop.git cd juiceshop/ # 使用Docker启动服务 docker-compose up -d ``` 上述命令展示了如何基于Linux操作系统快速搭建起OWASP Juice Shop 的运行环境。完成之后访问 `http://localhost:3000` 即可进入界面开始探索其内置的功能模块及其背后隐藏的风险点。 请注意,在实际操作过程中应当遵循合法合规的原则,仅限于授权范围内的实验活动;同时也要定期更新所使用的软件版本以获得最新的功能改进和支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值