SSRF逆向分析

0x00 前言

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

0x01 收集情报

补丁地址:

https://gitee.com/ComsenzDiscuz/DiscuzX/commit/41eb5bb0a3a716f84b0ce4e4feb41e6f25a980a3

查看补丁,发现如下:

删去了followlocation,也就是说对于301/302请求,curl不会去跟踪跳转。

既然这里存在一个跳转ssrf,下面就是逆向调用链,找到程序的入口。

0x02 尝试逆向找到触发点

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

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

通过对dfsockopen的第一个参数进行分析,发现其刚好是import_block的第一个参数经过一些处理之后的结果。

由于参数可控,继续向上。

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

0x03 尝试构造payload

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

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

/upload/admin.php?action=blockxml&operation=add

跟进一下submitcheck()

继续跟进getgpc()

大概就是返回$_GET[$k],由于这里的$k就是从前面的submitcheck('addsubmit')传进来的,所以这里只要保证$_GET['addsubmit']即可,构成的url如下:

/upload/admin.php?action=blockxml&operation=add&addsubmit=test

继续跟,

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

这里又有一个相同的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。看一下函数定义:

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

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

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

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

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

并且去掉referer头。

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

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

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

可以看到,最后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中也是不能直接传内网地址进来的。

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

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

访问成功了。

下面的整理下思路,由于程序对内网地址进行了限制,导致了除127.0.0.1之外的内网地址都会直接return掉,因此这里我们需要通过一个301跳转,来实现绕过程序对内网url的限制。

可是如果想要curl自动重定向到第一个url返回的地址中去,就必须先要将此curl的CURLOPT_FOLLOWLOCATION属性设置为true才行。然而这一点在本文一开始就已经确认了:

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

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

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

我的vps的http日志:

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

0x04 总结

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

Discuz3.4-SSRF-从触发点到构造payload的更多相关文章

  1. HGAME-week2-web-wp

    hgame第二周总结 1.webpack-engine 我不懂,但是真的刚打开就出来了,一脸懵逼(wp说是sourcemap没关 hgame{D0nt_f0r9et_2_ClOs3_S0urce_m@ ...

  2. 学习xss模拟构造攻击(第一篇)

    本文作者:i春秋签约作家——rosectow 0×00前言 XSS又名叫CSS全程(cross site scriptting),中文名跨站脚本攻击,目前网站的常见漏洞之一,它的危害没有像上传漏洞,s ...

  3. discuz 3.x ssrf分析

    discuz 3.x版本ssrf漏洞分析 漏洞促发点\souce\module\forum\forum_ajax.php 最后看到了这里 ***$_GET['action']='downremotei ...

  4. XSS Payload深入分析整理

    几种加载XSS Payload的不常见标签 众所周知,一种调用JavaScript的方法就是在元素类型上使用事件处理器(Event Handler),通常的一种方法类似: <img src=x ...

  5. CTF SSRF(服务器端伪造请求)

    目录 CTF SSRF(服务器端伪造请求) 一.概念 二.危害 三.漏洞挖掘与判断 四.相关函数 五.IP绕过 六.Gopher协议 1.使用限制 2.构造payload CTF SSRF(服务器端伪 ...

  6. SSRF绕过姿势

    0x00 什么是SSRF? SSRF(Server-Side Request Forgery,服务器端请求伪造):是一种由攻击者构造形成由服务器端发起请求的一个漏洞. SSRF 攻击的目标是从外网无法 ...

  7. SSRF和XSS-filter_var(), preg_match() 和 parse_url()绕过学习

    0x01:url标准的灵活性导致绕过filter_var与parse_url进行ssrf filter_var() (PHP 5 >= 5.2.0, PHP 7) filter_var — 使用 ...

  8. Vue中的Xss构造

    首发tools:https://www.t00ls.net/thread-59512-1-1.html 存储型XSS 最近做测试的时候碰到了一个前端页面使用了Vue框架的项目 在测试XSS漏洞的过程中 ...

  9. CTFHub-技能树-SSRF

    SSRF 目录 SSRF 1.内网访问 2.伪协议读取文件 3.端口扫描 4.POST请求 5.上传文件 6.FastCGI协议 7.Redis 8.URL Bypass 9.数字IP Bypass ...

随机推荐

  1. java监听器之实现在线人数显示

    在码农的世界里只有bug才能让人成长,The more bugs you encounter, the more efficient you will be! java中的监听器分为三种:Servle ...

  2. 文本分类学习(六) AdaBoost和SVM

    直接从特征提取,跳到了BoostSVM,是因为自己一直在写程序,分析垃圾文本,和思考文本分类用于识别垃圾文本的短处.自己学习文本分类就是为了识别垃圾文本. 中间的博客待自己研究透彻后再补上吧. 因为获 ...

  3. 基于分支限界法的旅行商问题(TSP)二

    和上篇一样,考前写写伪代码,考完了补上具体的解释和代码. 状态{矩阵,结果集,下界} 全局结果集列表,全局上界初始为Infinite 建立一个heap,存储状态,出堆规则为拥有最小的下界. 利用red ...

  4. mysql 基本命令操作

    1. 查看存储引擎 show engines; 2. 查看数据存储位置 show variables like 'datadir': 3. 存储引擎 create table mytest engin ...

  5. 使用xshell链接虚拟机的方法

    给大家介绍一下虚拟机和Xshell5连接的基本配置1.安装虚拟机,跟着提示一步一步安装即可,注意添加镜像文件,虚拟机就完成了.2.下载一个Xshell5,安装好之后.要修改虚拟机的网卡状态    1) ...

  6. Python_ jiba、snownlp中文分词、pypinyin中文转拼音

    import jieba #导入jieba模块 x = '分词的准确度直接影响了后续文本处理和挖掘算法的最终效果.' jieba.cut(x) #使用默认词库进行分词 print(list(jieba ...

  7. JSTL varStatus属性

    JSTL核心标签库中c:forEach 的 varStatus属性 varStatus属性  类型:String   描述:循环的状态信息,可以取值index\count\first\last\cur ...

  8. String的lastIndexOf()用于获取字符串中某个子字符串最后一次出现的位置

    String还定义有lastIndexOf(String str,int from) 意思为str在字符串多次出现时将返回最后一次出现的位置. eg: String str = "I can ...

  9. 【java错误】Non-terminating decimal expansion; no exact representable decimal result

    问题描述 意思是“无法结束的除法表达式:没有精确的除结果”.当时输入的10/3,结果应该是3.3333....333. 下面这种处理方式有问题. BigDecimal num3 = new BigDe ...

  10. xml序列化和反序列化(二)

    上篇讲到关于xml入参实体序列化,下面给出出参实体反序列化,代码如下: /// <summary> /// 反序列化 /// </summary> /// <param ...