xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
添加文章 English Version

Cisco IOS进程调试


创建时间:2003-07-31
文章属性:转载
文章提交:freedemon (freedemon_at_citiz.net)

Ciscox notes (Anthony C. Zboralski Gaius)

Research is being done on a useless Cisco 1600 with 4 megs of flash
running IOS 11.1.

Recently after writting my first cisco warez (tunnelx), I told myself hey
we need to find a way to inject arbitrary code, poke and peek at the memory
on a cisco, hide interfaces, route-maps, access-lists.

Let's look around:

scep#show proc
CPU utilization for five seconds: 10%/4%; one minute: 14%; five minutes: 14%
PID QTy       PC Runtime (ms)    Invoked   uSecs    Stacks TTY Process
   1 M*         0         1248        107   11663 2204/4000   1 Virtual Exec    
   2 Lst  802DF16        34668        313  110760 1760/2000   0 Check heaps    
   3 Cwe  801D5DE            0          1       0 1736/2000   0 Pool Manager    
   4 Mst  8058B20            0          2       0 1708/2000   0 Timers          
   5 Lwe  80BFD4A           24         46     521 1448/2000   0 ARP Input      
   6 Mwe  81F78F0            4          1    4000 1744/2000   0 SERIAL A'detect
   7 Lwe  80D935A            4          1    4000 1656/2000   0 Probe Input    
   8 Mwe  80D8CD6            0          1       0 1744/2000   0 RARP Input      
   9 Hwe  80CA966           80         89     898 3116/4000   0 IP Input        
  10 Mwe  80F41BA           16        322      49 1348/2000   0 TCP Timer      
  11 Lwe  80F5EB8            8          3    2666 3244/4000   0 TCP Protocols  
  12 Mwe  813785E           80        177     451 1588/2000   0 CDP Protocol    
  13 Mwe  80D5770            0          1       0 1620/2000   0 BOOTP Server    
  14 Mwe  81112C0         1356       1522     890 1592/2000   0 IP Background  
  15 Lsi  8121298            0         25       0 1792/2000   0 IP Cache Ager  
  16 Cwe  80237BE            0          1       0 1748/2000   0 Critical Bkgnd  
  17 Mwe  802365A           12          5    2400 1476/2000   0 Net Background  
  18 Lwe  804E82E           16          4    4000 1192/2000   0 Logger          
  19 Msp  80456DE           80       1493      53 1728/2000   0 TTY Background  
  20 Msp  802345C           20       1494      13 1800/2000   0 Per-Second Jobs
  21 Msp  80233F2           68       1494      45 1488/2000   0 Net Periodic    
  22 Hwe  80234DC            4          1    4000 1724/2000   0 Net Input      
  23 Msp  8023482          772         25   30880 1800/2000   0 Per-minute Jobs
  24 Lwe  8109834            4          2    2000 3620/4000   0 IP SNMP        
  25 Mwe  815CE08            0          1       0 1712/2000   0 SNMP Traps      
  26 ME   811805A            0         26       0 1892/2000   0 IP-RT Background
  27 ME   803B0F8           32         11    2909 2760/4000   2 Virtual Exec    

now you can even dump the memory with 'show memory'. Good but there isn't
a write memory command, too bad. Maybe not...

I started looking for undocumented and hidden commands and found quite a bunch
of them.

Among all the stupid hidden command, the best candidate for taking full control
of the cisco is 'gdb'.

The IOS gdb command offers three subcommands:

gdb
  debug   PID
  examine PID
  kernel

the kernel subcommand works only on the console.
However 'examine' and 'debug' works perfectly; the debug subcommand is a bit
tricky to use though.

scep#gdb debug 27
||||

oops..

Ok grab a copy of gdb-4.18 and try to compile a version for your
cisco.
mkdir m68k-cisco
../configure --target m68k-cisco
make

if you have a mips based cisco, just s/m68k/mips64/ the above 4 lines.

now type make install and you should have a m68-cisco-gdb binary in your path.

fire# m68k-cisco-gdb
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu --target=m68k-cisco".
(cisco-68k-gdb)

my cisco 1600 is connected to /dev/ttyS0,
scep>en                                                                        
Password:                                                                      
scep#gdb debug 18                                                              
                                                                                
scep#

As you can see it bails out if you hit return. while examine works it seems.

scep#gdb examine 18                                                            
||||

now the console seems locked.
go back to our gdb-4.18 source tree and check out gdb/remote.c which
contains a nice documentation of the gdb remote communication protocol.
added.

IOS gdbserver implementation
Don't get too excited, IOS gdbserver supports only a limited subset of those
commands. I'll grab a binary of IOS 12 and check if new commands were added.
I didn't have to test every command by hand.. let's just say I have
reliable sources and I know that in IOS 11.2-8 (hum hum), the following
commands are supported:

  Request        Packet

  read registers    g
  write regs        GXX..XX        Each byte of register data
                    is described by two hex digits.
                    Registers are in the internal order
                    for GDB, and the bytes in a register
                    are in the same order the machine uses.
  read mem        mAA..AA,LLLL    AA..AA is address, LLLL is length.
  write mem        MAA..AA,LLLL:XX..XX
                    AA..AA is address,
                    LLLL is number of bytes,
                    XX..XX is data
  continue        cAA.AA        AA..AA is address to resume
                    IF AA..AA is omitted
                    resume at same address.
  step            sAA..AA        AA..AA is address to resume
                    If AA..AA is omitted,
                    resume at same address.

  kill request        k
  last signal        ?        Reply the current reason for stopping.
                    This is the same reply as is generated
                    for step or cont : SAA where AA is the
                    signal number.
  toggle debug        d        toggle debug flag (see 386 & 68k stubs)

All other commands will be ignored... too bad 'search' isn't implemented.

The protocol is simple, quoting remote.c comments:

A debug packet whose contents are <data> is encapsulated for transmission
in the form.
$ <data> # CSUM1 CSUM2

<data> must be ASCII alphanumeric and cannot include characters
'$' or '#'.  If <data> starts with two characters followed by
':', then the existing stubs interpret this as a sequence number.

CSUM1 and CSUM2 are ascii hex representation of an 8-bit
checksum of <data>, the most significant nibble is sent first.
the hex digits 0-9,a-f are used.

Before trying to make gdb work i wrote a little program that computed the right
checksum:

#include <stdio.h>
unsigned char const hexchars[] = "0123456789abcdef";
char tohexchar (unsigned char c)
{
  c &= 0x0f;
  return(hexchars[c]);
}

int main(int argc, char **argv)
{
  unsigned char checksum;
  int count;
  char *command;
  char ch;
  if (argc <= 1)
    exit(1);
  printf("gdb protocol command: ");
  command = argv[1];
  putchar ('$');
  checksum = count = 0;
  while ((ch = command[count]))
  {
    putchar(ch);
    checksum += ch;
    count++;
  }
  putchar('#');
  putchar(tohexchar(checksum >> 4));
  putchar(tohexchar(checksum));
  putchar('\n');
}

./gdbproto g
gdb protocol command: $g#67

now paste that on the |||| prompt and you get register output:
scep#gdb exa 18                                                            
||||+$0*1b20*3320c0490*1920c02440*1080726ce0*e7#14

let's read 4 bytes of mem at 0x804e82e
./gdbproto m804e82e,4
gdb protocol command: $m804e82e,4#9d

Trying to read big chunk of mem really tilts and gdb defaults to 4 bytes at
a time. You can still dump memory with show memory, which is more efficient.
and for patching a bit, efficiency is not an issue. one nop here,
one jump here. If you're using minicom, make use ctrl-a W to enable
line wrap.

scep#gdb exa 18                                                                
                                                                                
||||+$001461ff#f8

Woo it works!
Let's try to read in the exec range:
$mff,4#99
  $04040004#8c

Cisco people have a good sense of humour:

$m0,5#fe
  $badadd0104#15

it is not a bad address, it is 0xba 0xda 0xdd.


type $c#63 to continue and go back to a sane world.

I have was having trouble with gdb..

If you have trouble with the serial connection, you can use the
command `set remotedebug'.  This makes GDB report on all packets sent
back and forth across the serial line to the remote machine.  The
packet-debugging information is printed on the GDB standard output
stream.  `set remotedebug off' turns it off, and `show remotedebug'
shows you its current state.

Says the info documentation for gdb and gives extra hints on the remote gdb
protocol.

Now it works over the serial, DO NOT use the option -b or set baud in gdb,
this is really error prone. Just do the following:

(cisco-68k-gdb) target remote /dev/ttyS0
Remote debugging using /dev/ttyS0
warning: Remote reply is of odd length: 0200471c00e4f640ffffffffffffffffffff70000000000000000000006100000000000000000000d200204262008071aaa020bb6bc020bb9d4020c52c5020d21cb020d2120020d211c0000a00808071aae00000000000000000000000000000000000000000000000000000000000000000000000007
0x120020d2 in ?? ()
(cisco-68k-gdb)

scep#show region
Region Manager:

      Start         End     Size(b)  Class  Media  Name
0x02000000  0x0217FFFF     1572864  Local  R/W    main
0x02005000  0x02019A43       84548  IData  R/W    main:data
0x02019A44  0x0203D67B      146488  IBss   R/W    main:bss
0x0203D67C  0x0217FFFF     1321348  Local  R/W    main:heap
0x02180000  0x021FFFFF      524288  Iomem  R/W    iomem
0x0801A1E4  0x082D8813     2876976  IText  R/W    text

scep#show region address 0x804E82E
Address 0x0804E82E is located physically in :

  Name  : text
  Class : IText
  Media : R/W
  Start : 0x0801A1E4
  End   : 0x082D8813
  Size  : 0x002BE630

(cisco-68k-gdb) set *0x2180000=0
Sending packet: $m2180000,4#28...Ack
Packet received: ab1234cd
binary downloading NOT suppported by target
Sending packet: $M2180000,4:00000000#c2...Ack
Packet received: OK

show subsys class
show subsys name
show memory free
show memory processes
show memory failures alloc

        No IOS platform built on the M68k family uses versions of the CPU that
        have full MMU support. Hence there is no hardware support for write
        protecting the text segment of the IOS image. The system does checksum
        the text segment (every 30 seconds) and will crash if the checksum is
        incorrect. However, finding the offending code is hard.
mips
        At init time, the IOS programs the MMU of these processors to provide
        write protection of the text segment. If any code tries to write
        into the code space, the system will crash with a segmentation
        violation.

        However the protection is not completely foolproof, although very
        good. The physical memory in which the code sits is mapped into other
        segments of the address space and is writable through those segments.

206EA78    5124 206D118  206FEA4    1                  80353F6   *Hardware IDB*
                                                                                
206FEA4    2200 206EA78  2070764    1                  803540C   *Software IDB*


show chunk
25 chunks created, 3 chunks destroyed
12 siblings created, 2 siblings trimmed

    Chunk  Flags   Size(b)   Free    Max  Saved(b)  Name
  203D6A0    D          16      0     50      1704  List Elements
  204379C    DS         16      0     50      1704  (sibling)
  2043BE4    DS         16     20     50       584  (sibling)
    Total                      20    150      3992
  203DAE8    D          48     50     50  -   2696  List Headers
  204114C    D          16     50     50  -   1096  messages
  204B714    D          12      0    100      3504  Reg Function
  207B278    DS         12      0    100      3504  (sibling)
  207E99C    DS         12      0    100      3504  (sibling)
  209FB9C    DS         12      0    100      3504  (sibling)
  20A314C    DS         12      0    100      3504  (sibling)
  20A4D5C    DS         12      0    100      3504  (sibling)
  20B5ED4    DS         12      0    100      3504  (sibling)
  20B879C    DS         12      0    100      3504  (sibling)
  20B9064    DS         12     29    100      1996  (sibling)
  206194C               32    200    200  -   7296  IDB SB Chunks
  20635CC               68    200    200  -  14496  IDB List Header Chunks
  2066E6C               24    898    900  -  25168  IDB List Element Chunks
  206D13C               28    200    200  -   6496  IDB Notification Chunks
  20A2178    D          24     49     50  -   1432  IP Addresses
  20A5EE4    D          48     99    100  -   5208  IP RDB Chunk
  20C24F8    D        3980      3      4  -  12012  Parseinfo Blocks
  20C6398    D         836     21     21  -  17736  Parse Nodes
* syntax: capture dump interface <interface> <tftp server> <file name>
  *
   * descr:  This command dumps the captured packets into the specified
    *         tftp server.
    EOLNS   (capture_dump_cmds_eol, capture_dump_commands);
    STRING  (capture_dump_fname, capture_dump_cmds_eol, no_alt,
             OBJ(string,1), "File name into which to dump");
         IPADDR_NAME     (capture_dump_tftp_svr, capture_dump_fname, no_alt,
         OBJ(paddr,1), "Name or address of TFTP server to dump onto");
         INTERFACE       (capture_dump_get_interf, capture_dump_tftp_svr, no_alt,
         OBJ(idb,1), IFTYPE_CAPTURE);
         KEYWORD (capture_dump_interfaces, capture_dump_get_interf, no_alt,
         common_str_interface, "interface in capture dumping mode", PRIV_USER);
         KEYWORD (capture_dump_cmds, capture_dump_interfaces, no_alt,
         "dump", "write captured info to a TFTP server", PRIV_USER);
         KEYWORD (exec_capture_dump_tftp, capture_dump_cmds, no_alt,
         "capture", "commands for the capturing facility", PRIV_ROOT);