Author: Matthieu Bonetti
Riusksk (quange: http://riusksk.blogbus.com)
The recent (CVE-2010-4344) vulnerability has been widely exploited, allowing attackers to remotely launch attacks to systems that use Exim as a message transmission proxy and have full control over the system. This vulnerability is easy to understand, but it is quite difficult to compile a stable exploit. The originally announced stability exploit was written by jduck1337, and other relatively unstable code can also be found online. Here, we will share some technical details about the vulnerability exploitation process to help you compile more stable exploit programs.
<! -- [If! SupportLists] --> 1. <! -- [Endif] --> Vulnerability Analysis
This vulnerability is caused by the "string_vformat ()" function. When it processes maliciously constructed email messages and headers, the buffer overflow occurs due to the lack of effective filtering. Exim uses a variety of custom functions to process strings, see source code in the exim-4.69/src/string. c file. The working principle of the "string_vformat ()" function is as follows:
-Pointer "p" to buffer
-Pointer "last" to buffer + buflen-1
1066 BOOL
1067 string_vformat (uschar * buffer, int buflen, char * format, va_list ap)
1068 {
1069 enum {L_NORMAL, L_SHORT, L_LONG, L_LONGLONG, L_LONGDOUBLE };
1070
1071 BOOL yield = TRUE;
1072 int width, precision;
1073 char * fp = format;/* Deliberately not unsigned */
1074 uschar * p = buffer;
1075 uschar * last = buffer + buflen-1;
1076
1077 string_datestamp_offset =-1;/* Datestamp not inserted */
Then, set the variables "width" and "precision" to-1:
1102 item_start = fp;
1103 width = precision =-1;
1104
1105 if (strchr ("-+ #0", * (++ fp ))! = NULL)
If the parameter "format" is % s, it performs the following steps:
-First, the buffer length is calculated using "strlen ()", but not the buflen value.
-If both width and precision are false, Set width and precision to the buffer length calculated by "strlen ()".
1238 case S:/* Forces * lower * case */
1239 s = va_arg (ap, char *);
1240
1241 INSERT_STRING:/* Come to from % D abve */
1242 if (s = NULL) s = null;
1243 slen = Ustrlen (s );
1244
1245/* If the width is specified, check that there is a precision
1246 set; if not, set it to the width to prevent overruns of long
1247 strings .*/
1248
1249 if (width> = 0)
[?]
1256
1257 else if (precision> = 0)
[?]
1263
1264 else width = precision = slen;
1265
1266/* Check string space, and add the string to the buffer if OK.
If "p" is greater than "last-width", the two variables width and precision are changed.
1267 not OK, add part of the string (debugging uses this to show
1268 much as possible ).*/
1269
1270 if (p> = last-width)
1271 {
1272 yield = FALSE;
1273 width = precision = last-p-1;
1274}
Precision and width are then called by the "sprint ()" function as parameters, and the buffer is also filled with data until NULL bytes or the specified limit values of width and precision are reached.
1275 sprintf (CS p, "% *. * s", width, precision, s );
1276 if (fp [-1] = S)
1277 while (* p) {* p = tolower (* p); p ++ ;}
1278 else
1279 while (* p) p ++;
To help readers fully understand the working principle of the "string_vformat ()" function, the following parameters are used as an example:
-String_vformat (buffer, 3, "% c % s", ap)
Ap is a variable va_list parameter that contains a single character and long string "looooooooooooong buffer.
First, p and last are set:
-P points to the buffer at the 0x08CCC965 address
-Last point to 0x08CCC965 + 3-1 = 0x08CCC967
Then, start processing "% c", p points to the address plus the size of a char and a space:
P = 0x08CCC965 + 2 = 0x08CCC967
Then, the width and precision are set to-1. Then process "% s ":
-Set slen to 21 (using strlen ()).
-If width and precision are false, set them to 21.
Last minus the new width value and less than p:
0x08CCC967-21 = 0x08ccc952
0x08CCC967> 0x8ccc952
The width and precision variables are changed:
Width = precision = last-p-1
Width = precision = 0x08CCC967-0x08CCC967-1
Width = precision =-1
When the "sprintf ()" function is called, it copies long strings with the width and precision values of 0xFFFFFFFF as its parameters, although the size of the bytes allowed to be copied is limited to 3, the long string content in the entire va_list can be copied, which leads to heap overflow. This vulnerability is also analyzed from the disassembly code. First, p and last are initialized:
. Text: 080A99E9 mov edx, [ebp + buffer] // p
. Text: 080A99EC mov eax, [ebp + buflen]
. Text: 080A99EF mov ecx, [ebp + format]
. Text: 080A99F2 mov string_datestamp_offset, 0 FFFFFFFFh
. Text: 080A99FC mov edi, edx
. Text: 080A99FE lea eax, [edx + eax-1]
. Text: 080A9A02 mov [ebp + last], eax // last
. Text: 080A9A05 sub eax, 1
. Text: 080A9A08 mov [ebp + src], ecx
. Text: 080A9A0B mov [ebp + yield], 1
. Text: 080A9A12 mov [ebp + last_minus_one], eax // last-1, used later
. Text: 080A9A15 jmp short loc_80A9A2F
When processing % s, it uses "strlen ()" to calculate the length of the string in va_list:
. Text: 080A9DE8 loc_80A9DE8:
. Text: 080A9DE8 mov [esp], ebx // long string
. Text: 080A9DEB call _ strlen
. Text: 080A9DF0 mov edx, [ebp + width]
. Text: 080A9DF3 test edx, edx
. Text: 080A9DF5 js loc_80A9F30
[?]
. Text: 080A9F30 loc_80A9F30:
. Text: 080A9F30 mov edx, [ebp + precision]
. Text: 080A9F33 test edx, edx
. Text: 080A9F35 js loc_80AA044
. Text: 080A9F3B mov ecx, [ebp + precision]
. Text: 080A9F3E cmp ecx, eax
. Text: 080A9F40 mov [ebp + width], ecx
. Text: 080A9F43 jle loc_80A9E06
. Text: 080A9F49 mov [ebp + width], eax
. Text: 080A9F4C jmp loc_80A9E06
Compare last-width and p, and change the values of width and precision:
. Text: 080A9E06 loc_80A9E06:
. Text: 080A9E06 mov eax, [ebp + last]
. Text: 080A9E09 sub eax, [ebp + width]
. Text: 080A9E0C cmp edi, eax
. Text: 080A9E0E jb short loc_80A9E22
. Text: 080A9E10 mov eax, [ebp + last_minus_one]
. Text: 080A9E13 mov [ebp + yield], 0
. Text: 080A9E1A sub eax, edi
. Text: 080A9E1C mov [ebp + precision], eax
. Text: 080A9E1F mov [ebp + width], eax
The function sprintf () is called using the new width and precision values as its parameters:
. Text: 080A9E22 loc_80A9E22:
. Text: 08