首页
社区
课程
招聘
[原创]HTB Nimbus渗透测试靶机 Writeup
发表于: 23小时前 194

[原创]HTB Nimbus渗透测试靶机 Writeup

23小时前
194

本WP旨在讲解HTB上的Nimbus靶机。这个题考察了包括aws、ssrf、容器逃逸等诸多知识点,天天打ctf的我基本没接触过aws,只能一边查一边学,兜兜转转的写了几百行笔记还不知道覆盖的全不全。

我们的靶机ip是10.129.245.218,访问一下会跳转到nimbus.htb,在hosts文件里先添加10.129.245.218 nimbus.htb

然后先启用nmap对目标做一个快速全端口扫描,得到以下结果:

用全扫是因为之前老是扫top1000然后漏端口

扫描只有22与80端口。访问07eK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3&6A6L8h3u0#2M7#2)9J5k6h3S2@1j5R3`.`.后发现页面里一共有三个端口可以访问(右下角的/docs是404)

点击下方的/api/v1/health,其返回了一段json文本

这里暴露了其下的一个子域名aws.nimbus.htb(后续爆破子域名也只发现了这个),同样添加到hosts里访问,发现响应为403

响应显示我携带的token非法,而且格式与官方aws相同,说明其本地应该实现了一套 AWS 服务模拟器且访问aws.nimbus.htb存在鉴权。转回nimbus.htb先看看有没有什么让我们能够访问本地aws服务的方法。

先尝试使用dirsearch扫描目标网站尝试获取一些隐藏的路由,不过扫描完发现扫出来的路由与页面提供的路由没有区别,因此转向业务探查。

点击Sign In,其说明Sign-in temporarily unavailable并给出了跳转到/jobs的超链接,因此猜测重心要放在提交jobs的路由上。

这两个页面可以看到,服务器期望接收一个yaml文件,可以是从url里获取,也可以是直接发送完整的yaml文本。在提交YAML代码的文本框下方还有一行小字Parsed with safe_load. No code execution at submission time.

safe_load配合YAML,很容易让人联想到python库里的yaml.safe_load(),因此这里的信息可以猜测该web服务器使用了python架构,本地模拟的aws也应该是用python跑的LocalStack

python的yaml库有两种加载yaml代码的函数:yaml.load()yaml.safe_load(),其中,前者可以通过标签实例化任何python对象,后者虽然也存在过风险,但是一般不会导致RCE。

本题目里虽然前端说自己是safe_load,但是也可以拿用于load的payload来试一试:

解析失败,看来/job/preview路由确实使用的是安全的safe_load。随便提交一段合法文本看看回显:

这里给出了一些未来可能会用到的信息:

基于第三条,我有理由猜测:只有前端(或者叫代理端)使用了safe_load来避免实例化对象,但是前端审查过了,后端为图方便很可能在解析YAML时使用了不安全的yaml.load来解析YAML代码。

因此,现在就是要想想怎么绕过前端向后端的aws服务的sqs的队列里送入一个会实例化恶意对象的YAML代码。

既然现在的目标是往aws服务送东西,那无论如何得先有个aws的凭证,也就是AKSK,如果是临时凭证还需要token。

首先我们要先知道:对于所有的aws服务,其创建的虚拟主机,内部都会固定一个ip:169.254.169.254,这个ip上绑定的服务就是IMDS (Instance Metadata Service,实例元数据服务)

这个服务让运行在这个虚拟机内部的程序或脚本,不需要任何密码或配置文件,就能直接查询自己当前的运行状态和配置信息,尤其是可以无认证访问到自己的临时凭证与token

我们先在本地开一个python服务,放一个a.yaml文件,里面随便写点内容,然后让服务器访问它。

可以看到确实能访问我们的页面,而且会显示访问到的原始内容,很适合作为一个ssrf来对内网进行探测。但是下面有行小字:URL must point to a .yaml file. Internal addresses and metadata endpoints are blocked.,它声明本地地址被禁用,同时URL似乎必须要指向一个.yaml文件。不过刚才的访问测试告诉我们它似乎并没有校验访问到的文件格式是不是yaml,换句话说,它很可能是做的字符串校验而没有真正做访问校验。

直接用其访问a34K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5J5y4#2)9J5k6e0m8Q4x3X3f1H3i4K6u0W2x3g2)9J5c8X3q4Q4x3X3g2&6j5h3#2D94b1K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5H3i4K6u0W2x3e0m8Q4x3X3f1I4y4W2)9J5k6e0j5I4i4K6y4m8z5o6l9H3x3l9`.`.各返回以下报错:

127.0.0.1可以看到其因为本地地址被拦截了

可以看到因为字符串后缀被拦截了

那么怎么绕过呢?针对这种字面量的匹配很好绕过。对于像本地地址这种匹配,一般改写成10进制或者8进制的地址形式就可以进行绕过,一般的url解析器也会正常解析地址。而像.yaml这种后缀校验匹配也可以选择把它以GET参数形式?a=a.yaml写在路由末尾就行,只要参数不存在就不会影响我们正常进行访问。

输入fbbK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0l9I4y4K6N6Q4x3X3f1H3x3o6l9H3i4K6u0W2x3o6l9H3x3q4)9J5k6e0l9H3x3o6q4Q4x3V1k6Q4x3@1k6S2i4K6y4p5j5g2)9J5k6i4W2S2L8h3H3`.

可以看到其正常回显,并没有拦截我们的访问。

那么现在ssrf的问题解决了,我们就可以尝试访问169.254.169.254来获取我们需要的AK、SK与token。一般而言,访问304K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5$3z5g2)9J5k6e0t1#2y4q4)9J5k6e0p5$3z5g2)9J5k6e0t1#2y4q4)9J5c8X3I4S2N6r3g2K6N6q4)9J5c8X3#2W2N6r3q4Q4x3X3c8V1j5i4c8S2i4K6u0r3K9h3q4E0i4K6u0r3M7$3g2U0N6i4u0A6N6s2W2Q4x3X3c8U0M7X3g2V1k6h3&6@1K9h3q4D9M7#2)9J5c8R3`.`.我们就可以看到当前用户的角色(同样也是该路径下存在的路由),然后访问该路由即可得到我们需要的凭证。

构造094K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0l9J5y4e0q4Q4x3X3f1H3x3K6M7$3i4K6u0W2x3o6t1#2x3g2)9J5k6e0l9K6y4K6k6Q4x3V1k6D9j5i4c8W2M7%4c8Q4x3V1k6E0k6i4c8S2i4K6u0V1k6r3q4@1j5g2)9J5c8X3W2S2L8g2)9J5c8Y4y4W2j5%4g2J5K9i4c8&6i4K6u0V1j5%4u0W2k6r3g2F1N6r3W2S2L8s2y4Q4x3V1k6Q4x3@1k6S2i4K6y4p5j5g2)9J5k6i4W2S2L8h3H3`.

访问255K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0l9J5y4e0q4Q4x3X3f1H3x3K6M7$3i4K6u0W2x3o6t1#2x3g2)9J5k6e0l9K6y4K6k6Q4x3V1k6D9j5i4c8W2M7%4c8Q4x3V1k6E0k6i4c8S2i4K6u0V1k6r3q4@1j5g2)9J5c8X3W2S2L8g2)9J5c8Y4y4W2j5%4g2J5K9i4c8&6i4K6u0V1j5%4u0W2k6r3g2F1N6r3W2S2L8s2y4Q4x3V1k6F1K9h3#2T1N6i4y4Q4x3X3c8%4k6h3u0Q4x3X3c8J5L8$3I4W2i4K6y4r3j5g2)9K6c8r3q4Q4x3X3g2&6j5h3#2D9

成功窃取到当前用户的aws凭证。在kali里先导入以下环境变量:

然后执行aws sts get-caller-identity --endpoint '1c4K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4%4M7#2)9J5k6h3&6A6L8h3u0#2M7#2)9J5k6h3S2@1j5W2)9J5y4H3`.`.检查凭证是否能正常使用,我们是否能进行访问

可以看到我们能够获取到当前用户的信息,aws凭证有效。下一步就是要探查这个凭证有什么权限。

本来想用Pacu工具对我们拿到的凭证做探查的,结果这个工具没有sqs探测,主要用的都是iam探测,导致我跑了半天没跑出来可用的权限,最后差点以为这个凭证没有任何用处。。。。

这里改用一个轻量化的枚举探测工具enumerate-iam来进行权限探测(github地址:df2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2L8X3c8J5k6i4y4J5K9h3q4F1j5$3S2G2i4K6u0r3k6h3&6#2L8h3g2J5j5i4c8W2i4K6u0V1K9h3q4E0。使用时如果提示错误,记得把这一行代码注释掉:urllib3.disable_warnings(botocore.vendored.requests.packages.urllib3.exceptions.InsecureRequestWarning)

可以看到探测出了sqs.list_queues()函数,能够列出当前队列的url。再结合先前我们的发现:“后端似乎有个workers机器人在不断解析传进队列里的yaml”,我们就可以尝试一下sqs.get-queue-attributes()sqs.send-message()来看看当前队列

先看看队列的url:

返回为:

这个nimbus-jobs与网页上显示的nimbus-jobs对上了。查看当前队列的属性信息:

返回为:

其中ApproximateNumberOfMessagesApproximateNumberOfMessagesNotVisible很特别:他们两个都是0,说明当前队列里的消息都被处理完了,但是我们在前端那里确实发送过一些YAML到队列里,这些信息相互论证,我们就更能确定:有个机器人在消费这些信息,它接收yaml代码,而且有理由怀疑它使用了不安全的yaml.load进行解析。构造sqs.send-message命令向队列里进行投毒:

可以看到成功进行了RCE!接下来把命令换成反弹shell的命令,获取到消费消息的机器人worker的shell。

成功获取到worker环境的shell并拿到user.txt。

现在虽然拿到shell了但是不能过于高兴,因为简单的看一眼就知道:用户名旁边那一串十六进制数就不像个正常的用户环境。而且执行ls -al /.dockerenv发现文件存在,这就更加论证了worker环境是一个容器环境。

容器环境一般工具都很少,这里我们先把一个很好用的工具busybox传上去。这个工具很小,但它集成了 300+ 个最常用 Linux 命令的精简版,包括像wgetcurlmount这些命令它都有,因而可以很好的缓解容器环境工具缺乏的问题。

不过我这里一开始没有注意到这是个容器环境,傻乎乎的往上面传linpeas.sh,结果什么都没跑出来,还耽误了不少时间。(不过传脚本自动探测多舒服,自己打+思考还要烧脑子的token)


冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

收藏
免费 0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回