安全矩阵

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

记一次不停自我追问的学习(下)——有趣的探索之旅

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-7-2 14:11:35 | 显示全部楼层 |阅读模式
原文链接:记一次不停自我追问的学习(下)——有趣的探索之旅

前言:
上篇>>一个文件上传 1day 的 PoC 编写,从简单的 GUI 编写,不满足于是选择用 Go 语言编写,再到逐个使用 Goby 自带 API 优化 PoC,最终实现一键反弹 shell。不仅学习了 Go,也对 EXP 进一步的完善。
下篇>>代码审计,从一个为什么产生出发,不断地问自己问题,虽然是一次简单的代审,延伸出一次溯源,最后的收获远不止一次代码审计,而是学习方法!
01
代码审计 TL;DR
  •         为什么文件名后缀使用.<>php就可以绕过?
  •         为什么我尝试.<php的后缀绕过方式不行?
  •         为什么有这种神奇的绕过方法?
  •         什么原因导致的?

十万个为什么,最终促使了我进行代码审计!
不会代码审计,只能一步步的断点跟踪分析,笨办法
1.1 showdoc 如何限制文件上传的
1.1.1 黑名单检测检测文件名中是否包含 php 字样

strstr(strtolower($_FILES['editormd-image-file']['name']), ".php")

1.1.2 白名单检测检测文件后缀名是否在 upload 类的$ext数组白名单中。

return empty($this->config['exts']) ? true : in_array(strtolower($ext), $this->exts);

1.1.3 图片内容检测仅对图像文件进行进一步检测,其他类型则放行(有些鸡肋)

1.2 绕过方法一:畸形后缀绕过
1.2.1 绕过方式文件名为:test.<>php
1.2.2 原理.<>php的方式绕过了黑名单对.php检测
而在之后的 showdoc\server\ThinkPHP\Library\Think\Upload.class.php 的upload()函数中对文件名使用了strip_tags()函数进行处理,去掉了<>标签,还原后缀名为 php。

1.2.3 修复方案在进行黑名单检测时,先用strip_tags()函数对输入文件名进行处理(有趣的是我在另外一个路径下的同名文件中发现了这种修复方案):
  1. //漏洞版本:showdoc\server\Application\Home\Controller\PageController.class.php

  2. strstr(strtolower($_FILES['editormd-image-file']['name']), ".php")

  3. //另外一个路径下的修复版本:showdoc\server\Application\Api\Controller\PageController.class.php

  4. strstr(strip_tags(strtolower($_FILES['editormd-image-file']['name'])), ".php")
复制代码


1.2.4 追问:不是后面还会进行白名单检测吗?
重头戏,这个引发后续一系列问题
虽然前面绕过了黑名单检测,但是后面还有白名单检测,那么这里怎么绕过的呢?
赋值问题:后缀的白名单数组赋值给了 upload 类中的allowExts变量:$upload->allowExts = array('jpg', 'gif', 'png', 'jpeg'); ,但是检测文件名后缀的时候使用 upload 类中的exts变量:$upload['exts']来检测,很明显白名单数组赋值错了,导致 exts 变量为空,故绕过了后缀名的 check。
这是开发者在开发时疏漏导致的白名单检测缺陷。

1.3 绕过方法二:修改输入名称
1.3.1 追问:绕过方式将文件输入名称改为editormd-image-file1,如name="editormd-image-file1"; filename="test.php"(绕过了黑名单的检测,同时结合白名单的缺陷,以实现目的)
1.3.2 原理因为他只检测输入名为editormd-image-file文件的文件名是否包含 php,修改对应的输入名即可。举一反三:这也解释了为什么其他场景的文件上传中可以通过修改这个字段名称的方式绕过了文件上传限制

strstr(strtolower($_FILES['editormd-image-file']['name']), ".php")
1.3.3 缺陷虽然在文件夹中看到文件成功上传,但是返回的文件路径中没有文件名
查看源码后发现:是因为在返回的文件名中又一次使用到了editormd-image-file,并通过这个名来获取文件保存路径,由于我们更改了输入名,并不存在editormd-image-file的键值,故返回了文件名为空。


1.3.4 进一步绕过以获取文件名虽然没有返回文件名,但是文件是上传成功的,那么如何知道上传后的文件名?
跟踪源码,发现文件命名调用了uniqid()函数,而该函数的定义是基于以微秒计的当前时间,生成一个唯一的 ID,那么在极短的时间内发送两个包,那么文件名应该是相近的。
方法一:第一个正常上传返回路径,第二个修改输入名不返回路径,依据时间递增遍历即可找到,但是爆破不知上限有些盲目。
方法二:php 文件在两个包之间,对应文件名也在两个包时间之间。如下图,需要爆破5位数

1.3.5 更进一步以缩短爆破时间通过查看 thinkphp 文档后发现,其支持多文件上传。
http://document.thinkphp.cn/manual_3_2.html#upload
测试后多文件上传后,发现文件处理时间间隔更近,命名更相似,3 位数,对应爆破次数不超过 4000 次,效率更高。

1.4 修复方案
修复白名单赋值导致的缺陷即可:将$upload->allowExts= array('jpg', 'gif', 'png', 'jpeg');对应代码更改为$upload->exts= array('jpg', 'gif', 'png', 'jpeg');
https://github.com/star7th/showd ... 207cb85c565372093dd

1.5 其他失败尝试
1.5.1 直接上传 php?原因:最早跟踪<>的漏洞成因,发现是白名单后缀检测失效导致的,既然白名单失效,为什么不考虑直接上传 php
过程:测试失败,然后下断点跟踪代码。
收获:发现了其还有一层黑名单验证机制

1.5.2 strlower 绕过?Unicode 字符?原因:最近打 CISCN2021 的一道 upload 题目,遇到在strtolower进行黑名单匹配时可通过 unicode 绕过
过程:仔细查看后发现我记错了,CTF 中题目是mb_strtolower,而此处是strtolower
收获:对strtolower和mb_strtolower理解更深
1.5.3 .htaccess 可以吗?背景:黑名单只限制 php,phtml 可以上传,但无法被解析,那么可以上传.htaccess使之支持解析吗?
过程:测试,发现上传的.htaccess会被重命名为 “60d150f6ee711.htaccess”
结局:不再发散,点到为止
02
有趣的溯源
2.1 漏洞起源于 CTF1. plzmyy 师傅最早通报这个漏洞给 showdoc 官方在 2020 年的八月
2. plzmyy 师傅提到了最早出现在 RoarCTF2019 的赛事中的 simple_upload 题目 Ethan 师傅的 WP
https://www.fuzzer.xyz/2019/10/14/RoarCTF2019
3. Ethan 师傅发现<>绕过方法是 fuzz 出来这个非预期解,而查看绝大部分 WP 以及疑似官方的 WP 中介绍的是利用 uniqid() 的函数可爆破的弱点。
https://github.com/berTrAM888/RoarCTF-Writeup-some-Source-Code/tree/master/Web/simple_upload/writeup
4. Ethan 师傅文章中提到疑似 0day,引起了我的兴趣,我想分析到底是哪个 day?
2.2 有0day?1. 检索相关关键词,没有发现 0day,而且到现在都没有爆出来,离谱!更加吸引了我的兴趣
2. 搜索引擎限定搜索时间为 19 年 10 月 RoarCTF 开赛之前,发现一个 14 的《ThinkPHP文件上传实例教程》一个 15 年的《ThinkPHP文件上传的实例代码》网页介绍了如何使用 ThinkPHP 文件上传 ,其使用的方法就是$upload->allowExts错误的写法,而 ThinkPHP 官方 3.2 版本的说明文档中则使用的是正确的$upload->exts写法
《ThinkPHP文件上传实例教程》:https://www.jb51.net/article/54209.htm
《ThinkPHP文件上传的实例代码》:http://www.splaybow.com/post/thinkphp-file-upload-sample.html
3. 那么为什么 ThinkPHP 官方是对的,而民间大家用的是这种错误的写法?会不会是官方早期版本教学用的就是这个错误的写法
2.3 官方自相矛盾?1. 怀疑是 ThinkPHP3.2 以下版本的官方文档自身用了错误的写法,而民间只是历史沿用这一用法,搜了 ThinkPHP3.1 的说明文档,果不其然用的就是$upload->allowExts的错误方式
http://www.thinkphp.cn/info/194.html
2. 那么 TP3.2 官方为什么在文档中更正了这种错误的写法?是因为当时爆出了漏洞吗?检索后发现并没有这类漏洞
3. 继续追踪,查看 3.1 和 3.2 官方源码发现:是在TP从3.1到3.2版本升级的时候,由于编写文件上传类的负责人换了一个liu21st→zuojiazi,使得 ① 类名UploadFile()→Upload() ② 白名单后缀数组名allowExts →ext ③ 文件名,文件路径等发生变更。
4. 仔细对比 3.1 和 3.2 的文档,发现虽然源码发生改变,对应文档也发生了改变,本质上并不存在漏洞。
5. 也就说 ①3.1 版本的源码配合 3.1 的文档,$upload = new UploadFile()搭配$upload->allowExts不会产生漏洞;② 3.2版本的源码配合 3.2 的文档,$upload = new \Think\Upload()搭配$upload->exts也不会发生漏洞。③ 但是 3.2 的版本使用3.1的文档,$upload = new \Think\Upload()搭配$upload->allowExts就会产生漏洞,而showdoc的漏洞成因很大几率来源于此。
6. 再次百度、Google 相关教程网页,发现民间教程也都和官方文档一样,一一对应(刚刚只关注键名没关住类名了)。
7. 也就说官方教程不存在问题,网上教程也不存在问题,那么只能是个人了。

2.4 个人问题?1. 再往前追:2016 年 8 月 showdocV1.0.0 版本就存在这种错误的写法,但是 14 年 TP3.2 就出现了
https://github.com/star7th/showdoc/blob/v1.0.0/Application/Home/Controller/PageController.class.php
2. 由于作品发布前后间隔两年,首先怀疑作者是基于一个通用模板或 CMS 改的,但是检索了半天开源框架并没有。
3. 其次只能怀疑是面向百度编程的通病:① 百度内容 ② 发现官网文档 ③ 发现 demo,但是运行失败 ③ 魔改一番,手动将类名由 uploadfile 改为 upload ⑤ 程序运行成功 ⑥ 不影响正常业务 ⑦ 大功告成!
4. 我按照这个步骤复现之后发现很类似,最后和作者取得联系,和怀疑的基本差不多,
作者回复如下:
那块代码应该是从某个网页上拷贝下来的。可能是 thinkphp 官网。这种语法我是不会花精力去记住的,因为它跟框架强相关。我是多个框架使用者,我用某个框架的时候,我才会去搜索它的语法。所以我一开始其实并不知道 exts 和 allowexts 的区别。

2.5 时间线2013年6月7日:ThinkPHP3.1.3 发布,使用UploadFile()->allowExts
2014年2月3日 :ThinkPHP3.2 发布,使用Upload()->exts
2016年8月7日 :showdocV1.0.0 发布 ,使用UploadFile()->exts
2019年10月12日:RoarCTF 开赛,并于14日 Ethan 师傅 Fuzz 出了这个点,但未深究
2020年8月12日 :plzmyy 师傅根据 Ethan 师傅的 WP 发现 showDoc 的漏洞
2.6 小结溯源到最后,可以说 showdoc 是个特例,是由于开发者的一时疏忽导致的。诚然在这个里面个人开发者占很大的问题,但也希望给厂商企业敲响一个警钟,在版本升级时不仅要注意文档和代码的对应性,也需要在更改类的字段名时候慎重考虑。
对于 Ethan 师傅也有点可惜,他觉得是个 0day,但未深究跟踪一下代码,也许他就真的发现了一个 0day。
对于CTFer来说,也需要多看WP,也许能收获到不同的东西。
对于面向百度编程的我,也需要警惕不要做一个盲目 CV 的代码首席移动工程师。
03
总结
正如 Zwell 说的“不断问自己问题”的学习方法,记录下问题解决它,虽然是一次简单漏洞复现 EXP 编写和代码审计,但是随着不停的问自己问题,好奇心吸引着我,到最后不知不觉我发现我的总结上升到了一个新的高度,共勉!
Go 编写 PoC,再到 API 优化 PoC,最终实现反弹 shell,详见上篇


回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

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

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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