注:本文相关漏洞均已报告 SRC 并完成修复
前言
近几年工作转型,已经很久没挖过漏洞,突然想检验一下自己当前是否还有挖洞能力。因此本次以挖到某个大型厂商一个中危级别以上漏洞作为目标,最终挖到了一个低危 (存储 xss ) + 一个高危 (任意文件读取) ,完成了本次目标。同时将整个过程较完整的思路记录下来,供大家参考。
前期思路
该企业有着多年的自身安全建设及 SRC 运营,如果以渗透测试的思路拿机器权限绝非易事(渗透测试思路请参考此前发到乌云 drops 的一篇文章 渗透中寻找突破口的那些事),且常见的反射 xss,sql 注入等问题估计早已被内部扫描器、自身及外部安全人员挖的差不多,我就以逻辑类漏洞作为主要挖掘方向。
寻找目标
既然要寻找逻辑类漏洞,首先要寻找功能比较复杂的站点,通过搜索引擎搜索
site:xxx.com
发现这么一个站点
通过域名 git.xxx.com 可初步判断为是一个类似 gitlab 的代码托管平台,此类平台一般会涉及到用户管理、文件管理、项目管理等功能,可测试的种类多样,因此将目标定在这个系统上,深入测试每一个功能,直到挖到中危以上漏洞完成目标。
测试过程
登录系统后可看到左侧功能菜单如下
可以看到功能还是比较丰富的,需要注意的一点是测试全程开启抓包工具,我这里使用的是 fiddler,目的是可以抓取一些接口地址进行测试,以及方便改包重放。 我们顺着提示,先初始化 git 账号,设置用户名时随便输入了 test,提示用户名已存在,这里一定是调用了接口查询后端数据库或缓存,返回 fiddler 看到如下请求
此时我们可以做一个基本的 sql 注入测试,此处属于字符型,一般我会直接在后面加单引号,因为在数据库查询中,如果引号带入的话会触发语法错误而造成页面的异常响应,我们通过页面响应是否异常即可初步判断是否存在 sql 注入,结果如下
通过文案提示可发现后端对用户名格式做了判断,这里的返回结果,除了sql 注入外,xss 需要的引号,尖括号也无法带入,因此用户名的 xss 测试也省了,不过这也是符合预期的,如果这样就挖到漏洞,我估计一定是他们自身的扫描器挂了。后面对于大多数的接口测试思路基本如此,不做赘述。 来到"项目"功能,先创建项目,做如下填写
这里简单说明下,前面的 aaaaa、bbbbb 主要是方便对返回结果搜索来定位过滤情况,不同的功能用不同字母主要是为区分不同功能点的过滤情况,">用来判断目标系统对敏感字符的过滤情况,标签作为大部分白名单标签可观察目标系统是否支持白名单标签 提交后出现如下提示
这里一般分为前端判断与后端判断两种情况
- 前端判断的话我们可以通过改包重放来绕过限制
- 后端判断可以忽略对相关功能点测试
我这里看到了对应的请求包,所以可判断本处为后端判断,所以只进行描述字段的相关测试,提交请求后返回结果如下
这里我发现一个比较可喜的现象,那就是虽然过滤了尖括号,但是 img 标签被正常解析,说明此处支持白名单,在接下来我又分别做了一些测试,结果如下
|测试payload|返回结果|说明情况|
|-|-|-|
|<img src=1 onerror1=alert(1)>
|<img src="HRay2/aaaa/1">
|对onerror属性过滤|
|<img src=1 aaa=1>
|<img src="HRay2/aaaa/1">
|对属性的过滤非黑名单而是白名单|
|<img/src=1/onerror=alert(1)>
|<img/src=1/onerror=alert(1)>
|以 / 分隔属性会直接转义尖括号|
|<bbbbbb>
|空|非白名单标签过滤为空|
|<bb<bbb>bbbb>
|<bbbbbb>
|执行对非白名单标签过滤后会转义尖括号|
|<img src=javascript:xxxxx>
|<img>
|白名单属性内依然有过滤|
|<img src=javas<aaaa>cript:xxxxx>
|<img src=javascript:xxxxx>
|执行对非白名单标签过滤后转义尖括号动作无视黑白标签|
|<img src=jAvascript:xxxxx>
|<img>
|过滤无视大小写|
|<img src=aaaa:xxxxx>
|<img>
|非基于黑名单的过滤|
这里的输出点我们需要依赖尖括号,且目前看来只能使用白名单标签 + 白名单属性,暂时无法利用,所以我们继续看其他功能点。
经过一些类似测试后,我们来到了项目组功能,在项目组的描述文件功能处又遇到了与刚才相同的过滤,不过这个时候我比刚才多做了一个尝试
目的是想深入看一下白名单属性内的过滤情况,结果如下
我们输入的内容被完整的输入,说明白名单属性内,对尖括号是没有做过滤的。 在 html中 ,有些标签是可以优先闭合的,比如下面这段代码
<html>
<title>
<a href="1</title><script>alert(1)</script>"></a>
</html>
这段代码在实际解析中会把<a href="1
作为<title>
标签中的部分,从而完成无双引号的闭合。
本处无法在上面插入有效的title标签,理论上不可利用,但是当我直接测试如下 payload 时
奇迹出现了
经过后端的逻辑处理后插入的 script 标签竟然直接跳出了 a 标签,到了这里大家可能以为把 script 标签内容改为 alert(1) 就解除了,实时并非如此,我发现 script 标签内的符号都会被 url 编码处理,比如
我们通过插入
aaaaaa<a href="1<title><script>aa`~@#$%^&*()-+=_[]{}\| /?</script>">
来测试符号的 url 编码情况,结果如下
开始想通过<script src=xxx.js>
的方法实现,且该站点本身就提供写文件的功能,可以天然绕过同源限制,不过被浏览器拦截
查了一下,大概是因为这里写入的 js 以 raw 格式查看返回的是 text 格式,与 script 标签内要求的 javascript 类型不符导致,查找无 url 编码符号的 payload
最后找到这么一个
aaaaaa<a href="1<title><iframe/src=data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=>">
成功完成弹窗
不过后来这个被评为低危,因为 poc 是在 data:text 下执行的,无法获取用户信息,后来我又找到这么一段 poc
aaaaaa<a href="1<title><script>throw/a/,Uncaught=1,g=alert,a=URL+0,onerror=eval,/1/g+a[12]+[1337]+a[13]</script>">
说明:后面读了测试规范,发现官网其实是严禁弹窗测试的,希望大家后面注意,尽量使用 console.log 来做测试
不过这样停止总觉得有点勉强,那就继续再挖一下看看吧。 因为 xss 的价值比较低,所以后面的测试着重去找一些偏逻辑、水平权限或者敏感接口类的问题,以下思路为测试后没成功,但是我觉得对 web 安全测试来说还是需要关注的点。
- 我们随便打开一个页面的时候,查看网页源代码,会发现有这么一段内容
这里面包含一个 id,并且通过这段内容以及观察其他一些参数的格式,估计可能会存在一个参数名为 user_id 的隐藏参数,之前发现过这么一个例子,就是站点本身的一系列操作都是解析 session 内容来获取用户对应身份进行操作,但是当你主动去传递一个类似 user_id 这么一个参数时,程序会去执行传递的 user_id 对应用户的操作,这就导致了水平权限的问题,这种问题的成因有可能是早期程序逻辑就是根据传参的 userid 去执行操作,后期改成 session,但只是去掉了默认的参数传递,并没有完全从后端去掉这部分逻辑。
- 在 cookie 中存在这么一个字段
熟悉的小伙伴可能一眼就能认出这是一段 base64 的密文
这段密文解密后的内容格式为
Username:毫秒级时间戳:32位md5
其中 md5 没有解密成功,不多做猜测,前面讲到程序一般从 session 获取用户身份信息执行对应操作,当然也可能是从 cookie 中某个字段去取值,所以可尝试将 username 部分替换为其他用户信息(这里替换为自己小号用户名),替换后使用 base64 加密并替换对应字段内容,同时也可以尝试将用户名后面加单引号,用来测试一些隐藏的 sql 注入
- 测试过程中抓到这么一个接口
https://git.xxx.com/search/autocomplete/users/1xxxx.json
看起来可以获取一些基本信息,中间的 users 这里,有可能会存在一些类似的隐藏接口可以获取一些敏感信息,比如把 users 换成 tokens 等
经过一系列失败的测试后,我看到了一个让我眼前一亮的功能,在我们使用客户端提交 commit 的时候,可以选择调用一个 bash 脚本来检查 commit 描述是否符合特定规范,比如必须要使用 --bug=xxx 类似的格式(由于很多图都是提交报告后补截的,这个功能在我报告后很快就下掉了,所以此处只能靠文字描述)。开始想着此处可能要出 rce ,不过这个 bash 脚本是官方写死的,无法更改。最后在查看 fiddler 时被我抓到了一个读脚本内容的接口
本能的尝试读取 /etc/passwd
哈哈,运气不错,抓到了一个文件读取,并且是 root 权限,从 root 的 history 文件里读到了 log 文件路径
到这里足以证明危害,所以马上报告给了官方 src,响应也很快,周末的时候也可以及时完成止损,下掉了对应功能,这个漏洞被官方评定为高危级别,因此本次测试目标达成,至此告一段落。
后记
刚开始工作的时候,有幸跟 0x557 的 la0wang 在一起工作,当时做项目感觉老王总是能用很短的时间挖到可以 getshell 的漏洞,并且很多漏洞并没不复杂,自己也能看懂,就是挖不到,当时感觉是运气不好,随着工作年头的增加逐渐明白那是因为常年的积累,对各种系统、各种程序的逻辑了解的足够深刻,了解哪里容易出现问题才做到的。漏洞本身源于对外部可控因素的过滤或校验不严格,我们需要通过不断积累各种知识来深入理解哪些是我们可控的,可控的因素会带入到哪里,可能经过哪些地方,才能更多的挖掘到别人挖不到的漏洞。人工测试过程中,我们要多去思考程序可能的逻辑是怎样的,而不应该机械化的看到 id=xx 这种就加个单引号,看到输入框就往里加标签,这样人的效率自然拼不过机器。所以本文尽可能比较完整的描述了整个测试的思考流程,希望可以给大家带来参考价值。最后打个小广告,C轮融资的互联网企业,招一枚渗透测试工程师,大家可以把简历发送至hrayha@163.com 感谢大家的阅读。