FormatStrings - Opty-Forks/SSof GitHub Wiki
Format Strings Vulnerability Lab
The goal of this session is to analyse and exploit format-strings vulnerabilities.
For this lab we propose a series of simple format string exercises that range from reading values that are stored in the local stack (00_local_read.c) to changing the return address of a function (08_retlibc.c).
Format String Vulnerabilities exist whenever a printf(str) is improperly used and the format string str is controlled by the adversary. This allows reading arbitrary memory content, write to arbitrary memory content, and hijack of the execution control flow.
To interact with the server you can adapt the following snippet of code: netcat_template.py
Setup
- Notice that these files do not need to be compiled with
-fno-stack-protectornor-z execstackbut you need to disable the ASLR for all of them (this is global for the OS)
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
- The information on how each file was compiled is at the header of each source file.
- Do not know how to use GDB? A quick GDB-101 with a list of basic GDB commands can be found in the sidebar.
Task 1 - Reading Arbitrary Values from Memory
0-Simple Local Read
Let us start with exercise 00_local_read.c. The goal of this exercise is to read the variable secret_value. How can you do it?
- Recall how you can read the entire stack with
printf(in the presence of a format string vulnerability). - Where is
secret_valuelocated?
This challenge is running at nc mustard.stt.rnl.tecnico.ulisboa.pt 10090.
Tips
- Whenever in the presence of a format string vulnerability, the first thing we do is to try to understand the stack. The idea is to ask for several
%xfor which we did not push the corresponding arguments to the stack. For example, sendAAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x. What will this do?- This will print the string
AAAAfollowed by 20 other hex values (one for each%x) that correspond to the content of the 20 registers of the stack immediately after the format string of theprintf.- We used
%08xto print the values in hex and padded with0's to the left to use 8 characters. - We used
.to separate the printed registers. - Both are just to make the output more readable.
- We used
- This will print the string
- Since you know that the string
secret_valuewas loaded into the stack, it means that the address wheresecret_valueis located is in the stack and so, using%sinstead of%xallows one to read the value pointed by such address. - Be careful when using
%son an address that isNULL.- What can go wrong?
1-Short Local Read
Now we have a much smaller buffer to write into. Can we still do it?
Tips
- Recall that you can use positional arguments
%N$twithprintf(whereNis a number andtis a format specifier). This allows printing theN-th register of the stack immediately after the format string without the need to print the previousN-1. - Where is
secret_valuelocated?
This challenge is running at nc mustard.stt.rnl.tecnico.ulisboa.pt 10091.
Task 2 - Writing Values to Memory
2-Write to Memory
Function printf also allows you to write on variables using the format string %n.
- Can you change the value of
targetto a value other than0? - Notice that target is not in the stack.
This challenge is running at nc mustard.stt.rnl.tecnico.ulisboa.pt 10092.
Tips
- Like
%sallows one to read the value pointed by an address in memory,%nallows one to write a value to an address in memory.- What does
%nwrites to that address? It writes the number of characters printed so far.
- What does
- The first thing to do again is to inspect the stack by sending an arbitrary number of
%x. - Do you see
41414141? What does it mean?41414141is theAAAAthat we have introduced at the beginning of our string. This means that the 7th register on the stack is the beginning ofbufferand is controlled by us. - How can you write into
target?-
Do you know its address? Remember that
targetis a global variable and you have the binary that is running on the server.from pwn import * elf = ELF(PROG_NAME) target_address = elf.symbols['target'] -
What happens if instead of
AAAAyou write the address oftargetat the beginning of our string? -
And if your 7th
%is a%ninstead of%08x?
-
3-Write Specific Value
And can you change the value of target to a specific value?
This challenge is running at nc mustard.stt.rnl.tecnico.ulisboa.pt 10093.
Tips
- Recall that you can always print as many characters as you want (up to a reasonable amount). Either explicitly, eg
AAAAAAAAAAAAAAAAAAAA, or using padding%20x.
4-Write Specific Byte
And can you write a specific value in target? One whose most significant byte is not 0?
This challenge is running at nc mustard.stt.rnl.tecnico.ulisboa.pt 10094.
Tips
- Do not forget that you can apply modifiers to format strings, eg
%hhn, to write a single byte. - Also remember that the most significant byte of memory address
0x08041010is byte0x08041013.
5-Write Big Numbers
Well, but you cannot do it if you have to write a very big number. Or can you?
This challenge is running at nc mustard.stt.rnl.tecnico.ulisboa.pt 10095.
Tips
- Do not forget the modifiers
hh(1 byte) andh(2 bytes).
6-Write Given Number
Enough! Now I tell you what to write. Can you do it?
This challenge is running at nc mustard.stt.rnl.tecnico.ulisboa.pt 10096.
Task 3 - Call Functions
7-Calling Functions
Let's see if you can now call functions. Can you call function win?
- Notice that function
exitis being called... - You might need to use
objdump -R binto know the address ofexit@GOT.- Have you haver heard of GOT?
Alternatively
from pwn import *
elf = ELF(PROG_NAME)
win_address = elf.symbols['win']
exit_address = elf.got['exit']
This challenge is running at nc mustard.stt.rnl.tecnico.ulisboa.pt 10097.
8-Call Function Again
I will not call win for you anymore. Can you do it by yourself?
This challenge is running at nc mustard.stt.rnl.tecnico.ulisboa.pt 10098.
Summary
The steps to perform a Format String Attack are thus the following:
- Send
AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08xto read the content of the 20 registers of the stack immediately after the format string of theprintf. - Is the value in the stack? Yes, and suppose that it is the 5th register below the format string.
- is it an integer? Then send
%08x.%08x.%08x.%08x.%d(or alternatively%5$d). - is it a string (so you have a pointer to it in the 5th position)? Then send
%08x.%08x.%08x.%08x.%s(or alternatively%5$s). - is it a pointer to an address where you want to write? Then send
%08x.%08x.%08x.%08x.%n(or alternatively%5$n). This will write 36 in that address (the alternative solution writes0).
- is it an integer? Then send
- Is the value in the stack? NO, but suppose that
41414141appears in the 10th position after the format string. In this case you just have to add the address to the stack.- If the address you want to read is
0x08042010, then send\x10\x20\x04\x08.%10$sto read the content of this address. - If the address you want to write is
0x08042010, then send\x10\x20\x04\x08.%10$nto write5into this address (one for each of the bytes, plus one for the dot.).
- If the address you want to read is
- How do you write arbitrary values? Use
%Nxto adjust the number of characters that are printed. In this caseN.- Be careful that the number of printed characters does not reset after a
%n. - The way to write
8and then4is to print252extra characters as(8+252) % 256 = 4. - Also, you might want to use
%hhnto write a single byte and not damaging the bytes to the left.
- Be careful that the number of printed characters does not reset after a