Memory Forensics Analysis of Cisco IOS XR 32 Bits Routers
Total Page:16
File Type:pdf, Size:1020Kb
Memory forensics analysis of Cisco IOS XR 32 bits routers Abstract Attackers are constantly developing new techniques to bypass current security measures, and they are researching new targets to attack. The "Shadow Brokers" case illustrates that advanced attackers now target network devices such as routers. These core devices handle large amounts of data and are generally not audited by the security team, nor protected by specific security solutions, making them very interesting targets for attackers. This paper explains Cisco IOS XR routers internal, relevant to forensics analysis. As no forensics tool exists on this platform, we develop a tool for dumping the memory of the routers and another one, amnesic-sherpa, to analyze the memory dumps. Also, we demonstrate that by using an automated process based on these tools, we can determine if a router was compromised. Keywords: Forensics, QNX, Cisco, Routers forensics, Memory forensics, Routers, Firmware analysis 1. Foreword help debug, ...). This process creates child processes (in user space) and manage the system memory. QNX can There are a lot of publications on Cisco routers us- thus via proc-nto load the services that the system needs. ing the standard version of IOS (Lindner, 2008). There These services use a message passing system (QNX, 2010c) are, however, very few technical papers and research work to communicate together. They are either provided by de- about the security and forensics analysis of Cisco router fault, or developed and distributed in a specific image of using IOS XR (CISCO, 2005). Furthermore these devices the system. are at the core of many networks today. Because of this, When they are created, the services register a path or they are an interesting target for attackers and that's why a succession of paths that are the entry points to com- we choose to study these routers. IOS XR is based on municate with them. An application that wants to use the QNX (BlackBerry, 2010) operating system and there these services starts by opening one of these paths. To are also few publications about its security, and how to this end, the application sends the path of the service to perform forensics on this platform. the resource manager (QNX, 2010e) which resolve it and returns an identifier that is used to send and receive mes- 2. QNX sages from the service. For example, the file system is managed by a service QNX is an operating system based on a microkernel that loads a driver to directly communicate with the hard architecture. The first release was made in 1982 and since drive on one side, and, on the other, exposes a tree of files then, the system has been used in many embedded sys- to an application. In the same way, the network stack is tems, such as routers, infotainment systems or telematics a user space services that, on one side, load a driver for in cars (QNX CAR). the network card and, on the other side, creates a virtual Blackberry, the company that maintains QNX today, mount point that can be used to communicate with the makes available to its customers the sources of the oper- stack via the message system. ating system to let them generate an image of the system To ease the development of third-party applications, optimized for their hardware and containing only the ser- QNX conforms to the POSIX (QNX, 2010d) standard. vices and applications they have selected or developed. Different libraries (libc, libsocket) are also provided to ab- stract the message system and present function signatures 2.1. Architecture to developers as they are specified by the POSIX stan- At system startup, a specific code, the IPL (QNX, dards. 2010a) (Initial Program Loader), runs a startup code to initialize the hardware, and load the system image in RAM. This allows configuring the system before launching the 2.2. Examples of communication via the message system microkernel and the first services which are processes in On Linux, the libc hide the use of system calls (syscall) user space. The microkernel proc-nto (QNX, 2010b) is which allows the communication between the kernel and also the process manager of the operating system and ex- the user space (Listing 1). ists in several variants: proc-nto, proc-nto-inst, proc-nto- smp (multiprocessor management, instrumented version to Listing 1: Linux libc syscall wrapper. Preprint submitted to Elsevier September 16, 2019 edi 0x8047a28 134511144 fd = open("file"); eip 0xb033d95f 0xb033d95f <MsgSendv+39> //Internally the libc do : fd = syscall(open_syscall_number, "file"); write(fd, "abcd", 4); int MsgSend(int coid, const iov_t* siov, int sparts, cont iov_t* riov, rparts) //ret = syscall(write_syscall_number, fd, "abcd", 4); close (fd ); //ret = close(close_syscall_number, fd); Listing 4, shows that once the message is sent by the Similarly on QNX the libc hide the system of connections kernel, the return address is 0xb8222c98, the fd value is and messages between services (Listing 2). 0x03, the IOV (Input/Output Vector) buffer points to 0x08047930 and it contains 3 IOV. There is no IOV re- Listing 2: QNX libc message wrapper. turned by the function, so no return buffer is allocated //For "fd = open("myfile");" QNX libc internally do : (the return buffer can be the same as the send buffer). fd = ConnectAttach(PATHMGR_COID, "myfile", 1, 0, 1); //ConnectAttach is a syscall Listing 4: Disassembly of MsgSendv arguments pointer sent_msg.type = IO_CONNECT sent_msg.data = "myfile" (gdb) x/10x \$ecx sent_msg.path_len = strlen("myfile"); retaddr fd IOVpointer numberofIOV MsgSend(fd, sent_msg, sent_msg_size, reply_msg, reply_msg.size); 0x80478fc: 0xb8222c98 0x00000003 0x08047930 0x00000003 //MsgSend is a syscall //reply_msg.pid; is the id of the process managing the current path ret IOV ptr number of ret iov 0x804790c: 0x00000000 0x00000000 0xb035b0c0 0x08047948 ConnectDetach(fd); 0x804791c: 0xb82248e1 0x08047930 //we connect to the service and we ask for a fd for this path //reply->pid is the pid of the process that handles the hard disks fd = ConnectAttach(reply->nd, reply->pid, reply->chid, 0, 0); In Listing 5, we see that 0x08047948 is the address and MsgSend(fd, sent_msg, sent_msg_size, reply_msg, reply_msg_size) 0x14 is the size of the message in the OIV structure. return (0); Listing 5: Disassembly of the IOV buffer //write(fd, "abcd", 4) sent_msg_buffer.type = IO_WRITE (gdb) x/10x 0x08047930 sent_msg_buffer.nbytes = 4 msgaddr msgsize sent_msg.buffer.data = "abcd" 0x8047930: 0x08047948 0x00000014 0x00000000 0x00000000 MsgSend(fd, sent_msg_buffer, sent_msg_buffer_size, ret_msg_buffer, 0x8047940: 0x40114000 0x0000a000 0x00140102 0x0000a000 sizeof(ret_msg_buffer)); 0x8047950: 0x00000003 0x00000000 //close(fd) sent_msg.type = IO_CLOSE sent_msg.size = sizeof(sent_msg); If we follow the message address, we then find the param- ret = MsgSend(fd, sent_msg_buffer, sizeof(sent_msg), 0, 0); ConnectDetach(fd); eter of the message (Listing 6). //ConnectDetach is a syscall Listing 6: Disassembly of a message 2.3. Message analysis under a debug session (gdb) x/10x 0x08047948 type|c_len nbytes xtype zero To communicate with a service, you need to know its 0x8047948: 0x00140102 0x0000a000 0x00000003 0x00000000 specific message format. The service sources, and thus These values can be translated into the C code in Listing 7. the message format, are, however, not always public. We must therefore reverse engineer the format to understand Listing 7: Construction of a QNX Message in C it. After that, we can communicate with the services with- msg.i.type = _IO_WRITE; msg.i.combine_len = sizeof msg.i; out using the libc or any other libraries. msg.i.xtype = _IO_XTYPE_NONE; msg.i.nbytes = 4096; In Listing 3, we can see how an exchange between an msg.i.zero = 0; SETIOV(iov + 0, &msg.i, sizeof msg.i); application and a service looks in assembly. We see that SETIOV(iov + 1, buff, buff); the code will test the value 0x400 to find out if the system return MsgSendv(fd, iov, 2, 0, 0); requires the call to the sysenter function or an interrupt (0x28) to go into kernel mode. The arguments passed to With this information we can now write a program that MsgSendv, via the stack, are therefore in the ecx register. sends messages without using the libc. This is helpful if we can't, or don't want to, link to the libc. Listing 3: Disassembly of the MsgSendv function Dump of assembler code for function MsgSendv: 0xb033d938 <MsgSendv+0>: mov $0xb,%eax 3. Memory dump 0xb033d93d <MsgSendv+5>: call 0xb033d942 <MsgSendv+10> 0xb033d942 <MsgSendv+10>: pop %edx 0xb033d943 <MsgSendv+11>: mov %edx,%ecx 0xb033d945 <MsgSendv+13>: add $0x3eb92,%ecx Nowadays, most attacks do not leave any trace on the 0xb033d94b <MsgSendv+19>: add $0x1f,%edx 0xb033d951 <MsgSendv+25>: testl $0x400,0x2120(%ecx) disk and are only executed in memory to be stealthier. 0xb033d95b <MsgSendv+35>: je 0xb033d970 <MsgSendv+56> 0xb033d95d <MsgSendv+37>: mov %esp,%ecx Detecting these attacks require to analyze the contents 0xb033d95f <MsgSendv+39>: sysenter 0xb033d961 <MsgSendv+41>: ret of the system memory and extract information from the 0xb033d962 <MsgSendv+42>: add $0x3eb73,%edx 0xb033d968 <MsgSendv+48>: mov 0xcb0(%edx),%edx different data structures. As there is currently no tool to 0xb033d96e <MsgSendv+54>: jmp *%edx 0xb033d970 <MsgSendv+56>: int $0x28 make a copy of the memory of QNX system for the purpose 0xb033d972 <MsgSendv+58>: ret 0xb033d973 <MsgSendv+59>: jmp 0xb033d962 <MsgSendv+42> of forensic analysis, we develop one. (gdb) info reg eax 0xb 11 ecx 0x80478fc 134510844 edx 0xb033d961 -1338779295 ebx 0xb8229d44 -1205691068 esp 0x80478fc 0x80478fc ebp 0x8047968 0x8047968 esi 0xa000 40960 2 3.1. Development of a specific tool • page size To copy the content of the memory of a system such as • processor info Linux or Windows, the memory dumping tools must run in kernel mode (Hale Ligh et al., 2014). But, under QNX, • address space an application in user mode can access all the physical memory.