![]() |
|
[Tutorial] ~ Format String Exploitation ~ Understanding The Stack - Printable Version +- Hack Forums (https://hackforums.net) +-- Forum: Hacks, Exploits, and Various Discussions (https://hackforums.net/forumdisplay.php?fid=45) +--- Forum: Whitehat • Blue Team • Defensive Security (https://hackforums.net/forumdisplay.php?fid=448) +---- Forum: Pentesting and Forensics (https://hackforums.net/forumdisplay.php?fid=231) +---- Thread: [Tutorial] ~ Format String Exploitation ~ Understanding The Stack (/showthread.php?tid=4234208) Pages: Pages
1
2
|
[Tutorial] ~ Format String Exploitation ~ Understanding The Stack - Goodies - May 18, 2014 Hello everyone! It's been a while since I have been able to write tutorials. This tutorial will be about format string vulnerabilities. Though they are not quite as common as some buffer overflows or null pointer dereferences, there have been several reliable exploits using format string vulnerabilities. What is a "format string vulnerability"? As any beginner C programmer will be able to tell you, format strings allow you to print variables in the form of strings. Various functions allow you to perform specific actions to or with the strings. For example:
Examples: PHP Code How do format strings work? From here on, I'll be referring to the format string function printf() more so than any other function. Just realize that I am, in fact, talking about the other string functions as well. That being said, I'd like to explain how these format string functions work. After all, since these are all executed in runtime (IE: this is has nothing to do with the pre-processor), there must be something happening during the execution program. When executing a printf() function, the parameters that will be printed out are PUSHed onto the stack. The format string will then be interpreted and POP specific parameters from the stack and read them into stdout file stream (which are interpreted as ASCII strings). For example:
Taking a look: Let's take a look at a file vulnerable to format string vulnerabilities: Compiled with: gcc fmtstr.c -mpreferred-stack-boundary=2 -m32 -ggdb -o fmtstr PHP Code This is all well and good, but what does this mean? This program seems completely innocent? First, it will declare 3 separate strings. Then, it will print whatever the user passes into the program. How is this harmful? Well, since the first parameter in the printf()function is the format string, nothing is stopping the user from supplying his own format escape strings. Say the user supplies something like (command and output): ./fmtstr "This is Goodies!" This is Goodies! This is exactly what we had expected, right? Well, what if the user supplied, say, a format string: ./fmtstr "Goodies says '%s'" Goodies says 'Test string #3 ' This is definitely not the output we expected! What happened? Well, in a format string, passing the %s takes a pointer off of the stack and will read it as an ASCII string until it hits a NULL byte. Since the local variables str1, str2, and str3 are declared on the stack (and in that order, too), they are popped off in reverse order (remember, the stack is First In Last Out) and interpreted as a string. Before we get ahead of ourselves, read this comment that I had originally written inside the file, but in order to keep it small for HF, I decided to separate it :) PHP Code Debugging for a closer look: Before we get ahead of ourselves, let's take a look at how the stack grows. I compiled it with -ggdb so I can more easily debug it, but it's definitely not required! gdb -q ./fmtstr (gdb) Alright, fine. We are in Gnu Debugger :) If you are like me and prefer Intel ASM syntax over AT&T syntax, run: (gdb) set disassembly-flavor intel With that, go ahead and disassemble the main function: (gdb) disassemble main PHP Code From this alone, we can easily see the addresses of the strings. If you so chose, you can see these strings: PHP Code The memory addresses are in reverse order because the stack grows towards lower addresses, but local variables grow towards higher addresses. Jest to test this out, let's extract 3 strings starting from the address 0x405064: (gdb) x/3s 0x405064 PHP Code Okay, great! Before we continue, let's set a breakpoint in the program so we can dump the registers and make alterations as needed. The best place for a breakpoint in this case, would be right at the printf() statement which is at 0x004016e8. After that, let's execute it and it will stop at the printf() function. (gbd) break *0x004016e8 (gdb) run "Testing123" Aaaaannnnddd we hit our break point. Let's look at our base pointer: (gdb) x/8xw $ebp Here is my output: PHP Code Well, we know EBP (That first address: 0x0022ffa0) is the base pointer and points the the beginning of the current stack frame. In our case, we are in the main() stack frame. That being said, we know the following address is the return address of our main function. Since I'm currently confined to a windows machine, my return address is in "__mingw_CRTStartup". In a Unix machine, it is different. Now, let's dump the ESP which points to the top of the stack. (gdb) x/8xw $esp PHP Code If you'd like, go ahead and get the string output of each of those (first 4. We know the first address in the second row is the base pointer). (gdb) x/1s 0x003e2490 0x3e2490: "Testing123" (gdb) x/1s 0x00405084 0x405084 <__register_frame_info+4214916>: "Test string #3\n" (gdb) x/1s 0x00405074 0x405074 <__register_frame_info+4214900>: "Test string #2\n" (gdb) x/1s 0x00405064 0x405064 <__register_frame_info+4214884>: "Test string #1\n" "Testing123" is not actually the string at the address of the parameter that we provided. It is actually the format string itself. If you'd like to change the source code to something like: PHP Code The second through fourth strings are the test strings in, of course, reverse order as they are declared that way (local variables: lower memory addresses first, rise towards higher memory addresses in the stack frame). (gdb) continue Okay, so what EXACTLY is going on here? When the printf statement is called, it will find any arguments needed in the printf format string (%s, %x, %d, etc) and take the next memory address from the stack. In our case, it is interpreted as a string. This is all very neat, but what the hell can I use it for? Just to break a program or read a stored string?? Let's continue. Viewing the Stack Contents Viewing the stack contents is very easy in format string vulnerabilities. We're simply going to pass a %x which will pop the next address off of the stack and print it out. This is all well and good, but let's see it in action. Tutorial written by Goodies from HackForums.net First, let's dump the stack output for, oh, 12 words. This is equivalent to 12 %08x's. If you cleared the breakpoint or exited the program, you will need to add it again. Run the program with (gdb) run "%08x %0x8 %08x %08x %08x %0x8 %08x %08x %08x %0x8 %08x %08x" Now that we hit the breakpoint, before we continue, let's dump the ESP again using: (gdb) x/12xw $esp Dump GDB: PHP Code Program output:Tutorial written by Goodies from HackForums.net PHP Code Upon reading the stack, we can use this information many ways. We can examine it, read it in the debugger, etc... Writing contents to stack This is going to be very difficult for me as I am inside windows and the gdb console with mingw does not support python scripting built in. Much of this section will be proof of concept and/or theoretical, however, I will do my best :) Note: You may want to turn off ASLR when first doing this until you understand what's going on! There is one format string that I have not gone over and that is the %n tool. %n is very interesting because it will WRITE to a memory address as opposed to READ from it. %n - writes the number of bytes printed to the address provided. Here is a practical example: PHP Code Remember our return memory address? If you scroll back up, you'll see that it is 0x00401413. This can be shown as the integer 4,199,443. This is quite large! Let's just say, for arguments sake, that our shellcode is somewhere in our program at the address 0x00401555. This is a totally made up address and may or may not even be valid inside the scope of our program. Anyways, we want to overwrite our return address with 0x00401555 which, in decimal, equates to: 4,199,765. We would need to print out exactly that many characters and pop off that many places in stack. How hard could it be? First, let's find how many until the return address. We know what the return address is, so let's use %x until we find it. (gdb) run "%x %x %x %x %x %x %x" 405084 405074 405064 22ffa0 401413 2 3e24c0 Our return address is the 5th one which means popping 4 off of the stack would be perfect. Let's see exactly how many characters it would be: (gdb) run "%x%x%x%x" 40508440507440506422ffa0 = 24 characters. Since our return address is 4,199,765 in decimal form, we have a long way to go :) If I were in GDB in linux and could use python scripting, I would do something along the lines of: (gdb) run $(python -c 'print "%x%x%x%x" + "A"*(4199765-24)+"%n"') Which would, in theory, cause us to return to 0x00401555! Conclusion Even if you never use a format string vulnerability in an actual system security test, they have been known to be very severe. For example screen, a program in Linux that multiplexes one terminal for several simultaneous processes, had a format sting vulnerability that lead to a local root privilege escalation! Novell had a format string vulnerability for several versions and decided that it was benign enough to ignore. After someone showed just how detrimental it was, they had it fixed and a patch was released within 3 days. The most important information is this thread is the understanding of how the stack grows, how local variables are stored, and how memory works. Format string vulnerabilities are but one of many ways to exploit programs, but the success of all of them are contingent upon your understanding of the stack and memory process! * - M3ga. - May 18, 2014 Good tutorial but you're missing lots of info.. * - Goodies - May 18, 2014 (May 18, 2014 03:46 PM)M3ga. Wrote: Good tutorial but you're missing lots of info.. As much as I appreciate the post, why don't you actually contribute instead of simply trying to get 25 posts so you can PM? I'm happy yo help, but unless you actually contribute, I'm going to completely disregard you and your post. Thanks. * - RopMyCock - May 18, 2014 Really, I don't see this being needed, as Levelup and Divine already did it, but, I guess it's diverse from the usual shit here. Actually, after reading it over a second time, I think you were more detailed and focused on the more important things, which is great. Good tutorial * - mudasir007 - May 18, 2014 Nice tutorial,Keep it up, Good work * - Goodies - May 18, 2014 (May 18, 2014 04:07 PM)RopMyCock Wrote: Really, I don't see this being needed, as Levelup and Divine already did it, but, I guess it's diverse from the usual shit here. Wow. I honestly have never seem his tutorial before. However, like his other tutorials, I was able to find identical source codes online in tutorials he copied. Hmm. * - Sʜᴀᴅᴏᴡ Rᴇᴀʟᴍ - May 19, 2014 (May 18, 2014 07:22 PM)Goodies Wrote: Wow. I honestly have never seem his tutorial before. However, like his other tutorials, I was able to find identical source codes online in tutorials he copied. Hmm. divine or levelup? That reminds me I haven't talked to him in ages * - RopMyCock - May 19, 2014 (May 19, 2014 05:36 AM)Sʜᴀᴅᴏᴡ Rᴇᴀʟᴍ Wrote: divine or levelup? He's talking about Divine m8. Apparently some of his tutorials were copied but I can't say for certain * - Goodies - May 19, 2014 Why did people rate this 1 star? Do you have any idea how long these tutorials take me? * - RopMyCock - May 19, 2014 (May 19, 2014 05:13 PM)Goodies Wrote: Why did people rate this 1 star? Do you have any idea how long these tutorials take me? Tried 5 starring but then remembered reg members can't, which is bullshit. * - DrDinosaur - May 19, 2014 (May 19, 2014 05:13 PM)Goodies Wrote: Why did people rate this 1 star? Do you have any idea how long these tutorials take me? Faggo.ts will always remain faggo.ts. faggot lol * - A32 - May 20, 2014 Thanks bro. Now, is it wrong to say that BOF with this method is highly unpractical? Writing that million bytes to, say, a remote program will take a long time. Unless you can fish some juicy info (say, passwords) from the memory with %x%x%x%x, it ain't worth it. Correct me if I'm wrong :D * - LevelUp - May 20, 2014 Really confused as to why this doesn't include something as basic as direct parameter access? (May 20, 2014 12:14 AM)A32 Wrote: Thanks bro. Now, is it wrong to say that BOF with this method is highly unpractical? Writing that million bytes to, say, a remote program will take a long time. Unless you can fish some juicy info (say, passwords) from the memory with %x%x%x%x, it ain't worth it. It's not useless, eg: Pwn the .dtors section's end address -> -> /bin/sh? * - RopMyCock - May 20, 2014 (May 20, 2014 01:03 PM)LevelUp Wrote: Really confused as to why this doesn't include something as basic as direct parameter access? What's direct parameter access? 🤝 * - LevelUp - May 20, 2014 (May 20, 2014 02:07 PM)RopMyCock Wrote: What's direct parameter access? 🤝 Instead of popping x 8 times with %x%x%x%x%x%x%x%x Read the 8th parameter with %8$x |