Today I've been playing with a format string bug on a software for Windows when I came across a special "glitch" in Microsoft's C Runtime library (MSVCR) version 10.
Seeing how the use of %n in a format string could essentially allow you to write any number in an arbitrary memory location, I decided to try it out. My first try involved writing the number 0xBAAD to location 0x41414141 using the format string "AAAA%47785u%n" where 47785 = 0xBAA9 + 4 bytes for the int value = 0xBAAD. Note that the location of the format string buffer is inside the stack just 4 bytes after the single printf argument.
Stack:
However, when feeding the desired format string the application just crashed with the following stack trace:
Call stack of main thread
I decided to debug the code and see where the program crashes. Apparently when the format string function in MSVCR100 comes across %n the _get_printf_count_output function, which checks if %n is supported, is called:
If %n is not supported then the JE branch is taken leading to a section of the code which calls errno and _invalid_parameter_noinfo:
Within _invalid_parameter_noinfo a close-by function named _invalid_parameter is called which checks if the user has registered an "invalid parameter handler" to jump to. If not then watson is invoked.
Within the _invoke_watson function something interesting happens:
The process is terminated...
In order to handle a simple call to printf (or any other format string functions) one would have to register an "invalid parameter handler" using the _set_invalid_parameter_handler function or enable support for %n by using the _set_printf_count_output function.
This "additional" overhead is a trade-off for one bug with another. You might agree that you are safe against format string attacks that modify the data within an arbitrary memory address, but unless you register an "invalid parameter handler" you would certainly be open to denial of service attacks.
Seeing how the use of %n in a format string could essentially allow you to write any number in an arbitrary memory location, I decided to try it out. My first try involved writing the number 0xBAAD to location 0x41414141 using the format string "AAAA%47785u%n" where 47785 = 0xBAA9 + 4 bytes for the int value = 0xBAAD. Note that the location of the format string buffer is inside the stack just 4 bytes after the single printf argument.
Stack:
0018FF30 0018FF38 |format = "AAAA%47785u%n" 0018FF34 00000000 |<%47785u> = 0 0018FF38 41414141
However, when feeding the desired format string the application just crashed with the following stack trace:
Call stack of main thread
Address Stack Procedure / arguments Called from Frame 0018FC04 74E3E830 ntdll.ZwTerminateProcess KERNELBA.74E3E82A 0018FC10 0018FC14 665BA407 kernel32.TerminateProcess MSVCR100.665BA401 0018FC10 0018FC18 FFFFFFFF hProcess = FFFFFFFF 0018FC1C C0000417 ExitCode = C0000417 (-1073740777.) 0018FC24 665BA435 MSVCR100._invoke_watson MSVCR100.665BA430 0018FC3C 0018FC40 665BA442 ? MSVCR100._invalid_parameter MSVCR100.665BA43D 0018FC3C 0018FC58 665AA8B4 MSVCR100._invalid_parameter_noinfo MSVCR100.665AA8AF 0018FEE0 0018FEE4 6659317F MSVCR100.665AA839 MSVCR100.6659317A 0018FEE0 0018FF28 0040103C ? MSVCR100.printf XXXXXXXXXXXX 0018FF24 0018FF2C 0018FF34 format = "AAAA%47785u%n" 0018FF30 00000000 <%47785u> = 0
I decided to debug the code and see where the program crashes. Apparently when the format string function in MSVCR100 comes across %n the _get_printf_count_output function, which checks if %n is supported, is called:
665AAE5F |. E8 1D84FEFF |CALL MSVCR100._get_printf_count_output 665AAE64 |. 85C0 |TEST EAX,EAX 665AAE66 |.^ 0F84 38FAFFFF |JE MSVCR100.665AA8A4
If %n is not supported then the JE branch is taken leading to a section of the code which calls errno and _invalid_parameter_noinfo:
665AA8A4 |> /E8 E568F9FF CALL MSVCR100._errno 665AA8A9 |. |C700 16000000 MOV DWORD PTR DS:[EAX],16 665AA8AF |. |E8 82FB0000 CALL MSVCR100._invalid_parameter_noinfo
Within _invalid_parameter_noinfo a close-by function named _invalid_parameter is called which checks if the user has registered an "invalid parameter handler" to jump to. If not then watson is invoked.
665BA409 >/$ 8BFF /MOV EDI,EDI 665BA40B |. 55 |PUSH EBP 665BA40C |. 8BEC |MOV EBP,ESP 665BA40E |. FF35 E0345E66 |PUSH DWORD PTR DS:[665E34E0] 665BA414 |. FF15 04105366 |CALL DWORD PTR DS:[<&KERNEL32.DecodePointer>] ; ntdll.RtlDecodePointer 665BA41A |. 85C0 |TEST EAX,EAX 665BA41C |. 74 03 |JE SHORT MSVCR100.665BA421 665BA41E |. 5D |POP EBP 665BA41F |. FFE0 |JMP EAX 665BA421 |> FF75 18 |PUSH DWORD PTR SS:[EBP+18] 665BA424 |. FF75 14 |PUSH DWORD PTR SS:[EBP+14] 665BA427 |. FF75 10 |PUSH DWORD PTR SS:[EBP+10] 665BA42A |. FF75 0C |PUSH DWORD PTR SS:[EBP+C] 665BA42D |. FF75 08 |PUSH DWORD PTR SS:[EBP+8] 665BA430 |. E8 AFFFFFFF |CALL MSVCR100._invoke_watson
Within the _invoke_watson function something interesting happens:
665BA3E4 >/$ 8BFF MOV EDI,EDI 665BA3E6 |. 56 PUSH ESI 665BA3E7 |. 6A 01 PUSH 1 665BA3E9 |. BE 170400C0 MOV ESI,C0000417 665BA3EE |. 56 PUSH ESI 665BA3EF |. 6A 02 PUSH 2 665BA3F1 |. E8 BEFEFFFF CALL MSVCR100.665BA2B4 665BA3F6 |. 83C4 0C ADD ESP,0C 665BA3F9 |. 56 PUSH ESI ; /ExitCode 665BA3FA |. FF15 24115366 CALL DWORD PTR DS:[<&KERNEL32.GetCurrentProcess>] ; |[GetCurrentProcess 665BA400 |. 50 PUSH EAX ; |hProcess 665BA401 |. FF15 94125366 CALL DWORD PTR DS:[<&KERNEL32.TerminateProcess>] ; \TerminateProcess 665BA407 |. 5E POP ESI 665BA408 \. C3 RETN
The process is terminated...
In order to handle a simple call to printf (or any other format string functions) one would have to register an "invalid parameter handler" using the _set_invalid_parameter_handler function or enable support for %n by using the _set_printf_count_output function.
This "additional" overhead is a trade-off for one bug with another. You might agree that you are safe against format string attacks that modify the data within an arbitrary memory address, but unless you register an "invalid parameter handler" you would certainly be open to denial of service attacks.














