Transcript Bughunting

Killing the myth of Cisco IOS
rootkits: DIK
(Da Ios rootKit)
Sebastian 'topo' Muñiz
EuSecWest
London, March 2008
Speaker info
Name: Sebastian 'topo' Muñiz
Work: Sr. Exploit Writer at CORE
Likes: Reverse Engineering & Vuln Research
Contact: [email protected]
Agenda

Introduction

IOS characteristics and internal structure

IOS file format structure

IOS analysis phase

Binary patching technique

Rootkit source code advantages and limitations

Setting image ready

Countermeasures
What is a rootkits?

A program that seizes control of the entire OS by hiding files,
processes, network connections and everything else.

Allows unauthorized users to act as with super user privileges (in
IOS this means level 15, like root in Linux).
• Stealth: hides attacker's presence indefinitely.
• This is achieved by intercepting OS low level functions and
manipulating internal structures.
Rootkits along history
• Existed on other OS's like Windows, Unix and Linux distros for
several years.
• Natural evolution of backdoors.
• Present in AV & virii race to avoid one to locate each other.
• More common now a days, specially in malware.
Rootkits on IOS

Are IOS rootkits new? No

Did it begin years ago? Yes, before stolen IOS source code.

Any publicly known IOS rootkit? No, until today.

Can a generic rootkit be created for multiple IOS version? Yes

Does it matter if they have different arch (MIPS or PowerPC)? No

Do I need to know a specific assembly code? No, just plain C :)
Consequences
• Image your company network (ISP, bank, .gov or whoever you
are) compromised on strategic points.
• Network traffic concentrators owned by the attacker.
• Attackers able to see and manipulate all network traffic.
• Consequences in one word: Disastrous.
Introduction to IOS architecture

Monolithic architecture which runs as a single image.

All processes have access to each others memory.

No memory protection between processes.

Uses 'run to completion' priority scheduling.

FIFO (First In First Out) processes queue.

This model reduces local security and system stability.

Completely different to modern OS's.
IOS image file format

Modern Cisco devices use ELF (Extensible File Format).

It's a standard file format on Linux.

File contains CODE segments (instructions) and DATA segments
(strings).

Lot's of information available about it on the net.

Why ELF as IOS format? Implementations available, relocatable, etc.

Values on IOS ELF headers are not equal to the standards.

ELF infection methods (binary modification) were well known by
virus writers for years.
Initial setup on memory

Bootloader performs POST and invokes IOS image.

Downloaded image is not really the image that runs IOS.

Image contains a self decompressing (SFX) header code that
unpacks the fully functional IOS.

A simple ZIP utility can decompress it.

It's compressed because it contains lots of strings that occupy
precious memory.

c2600-i-mz.123-24 occupies 7.4MB ->18.6 MB Decomp (2.51%)
ELF structure as IOS image
+------------------------------+
|
ELF header
|
+------------------------------+
|
SFX code
|
+------------------------------+
|
Magic (0xFEEDFACE)
|
+------------------------------+
|
Compressed image length
|
+------------------------------+
|
Compressed image checksum |
+------------------------------+
| Uncompressed image checksum |
+------------------------------+
|
Uncompressed image length |
+------------------------------+
|
Compressed image
|
+------------------------------+
Self decompressing code
--+
|
|
|
| Magic
| Structure
|
|
|
|
--+
Compressed IOS image
ELF structure as IOS image (cont.)

SFX code uses Magic structure along the entire decompression
process.

Magic values are employed to validate decompression result.

Magic structure length are expressed in words (4 bytes)
Ex. Length of 1024 words = 4096 (1024 * 4) bytes

Magic length values help to verify available memory and size of
buffer to checksum.

Image is checksummed before and after decompression.
ELF structure as IOS image (cont.)

Simple (unsecure) checksum algorithm:
int nwords = compressed_size / sizeof(ulong);
unsigned long sum = 0; // contains the checksum result
unsigned long val
= 0; // temporary value
unsigned char* bufp = (uchar*) ptrData; // pointer to
// data to verify
while (nwords--) { // Read every 4 bytes
val = *bufp++;
sum += val;
if (sum < val) // There was a carry
sum++;
}
Obtain IOS image file

If decompression was successful, resulting (uncompressed) will
be running on device memory.

This image will contain our rootkit.

It's needed for analysis, so download using the known CLI's copy
command (from flash to ftp or any another destination)

Unzip the compressed (downloaded) image with ZIP.

With the decompressed image in our hands, move to analysis
phase.
Analysis phase

Why?

What tool should we use?

Why IDA (Interactive Disassembler Debugger) ?
Analysis phase (cont.)

IOS image contains lots of debug strings with information about
the internal OS working.

Take advantage of debug strings info to locate interesting
functions for an attacker.

Feed IDA with the uncompressed IOS image (will throw warnings)

Come back several minutes (sometimes hours) later :)
Analysis phase (cont.)

IDA will do a good job, but not enough.

Several functions and string won't be recognized

Part of the IOS image was not correctly analyzed.

On a c2600-i-mz.123-24 IDA detected:
28121 Functions
126379 Strings

An enhanced analysis is needed.
Enhanced image analysis

Let's use IDA-Python (python support for IDA scripting).

Create a script to detect remaining functions and strings.

Remember that MIPS and PowerPC instructions are aligned to a 4
byte boundary.

Fixed instruction size of 4 bytes.
Enhanced image analysis for functions

Iterate on every CODE section (like .text)

Compiler writes one function after another.

Strings are not inlined in CODE sections.

Move on a 4 byte boundary and tell IDA to create functions.

Rely on IDA magic.
Enhanced image analysis for strings

Iterate on every DATA section (like .data)

Strings length not 4 byte modulo are padded with zeros.

DATA segments may include references to other sections.

Move on a 4 byte boundary and analyse the memory content.

Try to resolve if current value is the begin of a string or a pointer
to another location (instruction or string).
Enhanced image analysis for strings (cont.)

TIPS:
Printable chars (between 0x20 and 0x7f) are probably strings.
Also check for Tab (0xD), CR (0xA) and LF ('0xD').
Bytes with values between CODE sections range may be pointers.

What if both happens? Like in a CODE section from 0x61000000
to 0x61700000?

Is 0x61616120 (“AAA ”) a pointer or just the begin of a string
from the AAA subsystem?
Enhanced image analysis results

After enhanced analysis:
46296 Functions (18175 new functions)
143603 Strings (17224 new strings)

IOS image is now successfully analysed and ready to give us info.
Locating low-level IOS functions

Locate functions of interest for the attacker like:
Password checking.
File manipulation.
Logging information.
Packet handling functions.
Access lists manipulation function.
Locating low-level IOS functions (cont.)

Strings are used to diagnose key functions result.

Some of them are usually displayed as output.

Locating their references, we locate those functions.

Iterate through every string to locate the ones of attacker
interest.

For each one, use string XREF property from IDA to know it's
callers (IDA-Python magic again :)
Locating low-level IOS functions (cont.)

What if function does not use strings?

Functions are compiled in the same order as source file.

Other functions (neighbors) next to it will surely do (whether
other IOS function call them or not).

Apply this procedure to every function that we want to fin using
simple IDA-Python functions.
Rootkit code home

IOS contains lot's of debug strings.

Sacrifice one large (probably never used) string to put code.

Also another CODE section could be added (known ELF infection
technique).

String overwrite is the easier to implement.

Remember to change section's permissions on ELF section
header.
Analyze low-level functions

Rootkit contains IOS functions counterpart that do attacker's will.

Recompile rootkit code every time is not an option.

Cross-compile it once (for MIPS and PowerPC).

How to locate compiled counterpart functions offsets?

Use 'objdump' to obtains symbolic info and offsets.

Name of a counterpart functions resolves to it's offset in
compiled code.
Redirect IOS execution flow

Knowing counterpart function offsets means that they are
callable now.

Intercept IOS functions and redirect it's execution flow.

Hook every interesting IOS function with a 'trampoline' code.

Trampoline code is located at function first instruction.

Jump to rootkit code location in memory (sacrificed string).

Trampoline code (like jmp on x86) for:
PowerPC -> unconditional branch instruction (1 inst.)
MIPS
-> unconditional jump instruction (2 inst.)
Redirect IOS execution flow (cont.)

Can I just jump to compiled C code? No, bad boy, you can't.

A 'glue' code is needed to create a bridge between IOS assembly
code and plain C code.

The glue code should work either on MIPS or PowerPC.

Redirection must occur without crashing current process state.
Gluing all together

Save the return address

Store the function parameters currently allocated

Allocate stack space for extra parameter needed by rootkit code.

Call the rootkit plain C code.

Decide whether to continue or not to hooked function.

If it continues, restore original parameters, execute instruction at
trampoline address and jump after trampoline

If it goes back to caller, set extra parameter value as hooked
function return value and go the saved return address.
Glueing all together (cont.)
IOS caller
chk_pass_IOS(p)
+-----------------+
+-----------------+
|
|
|
|
| r=chk_pass(p)
|--->| trampoline
|-->
|
|
|
|
| if r == true:
|
| rest of code
|
| login()
|
| ...
|
| else:
|
| return legal_res|
| deny_login()
|
|
|
| ...
|
+-----------------+
+-----------------+
Glueing all together (cont.)
Glue code
chk_pass_DIK(p,i)
+----------------------+
+-----------------+
| store parent RA
| +-->| if p == 'l337': |
| store params p
| |
| i = true
|
| create param i
| | +-| return RET
|
| add stack
| | | | else:
|
| o = chk_pass_DIK(p,i)|-+ |-| return CONT
|
| fix stack
|
| |
|
| if o == CONT:
|<--+ |
|
| exec orig instruct |
+-----------------+
| return params p
|
| cont chk_pass_IOS
|
| else:
|
| r = i
|
| jump to RA
|
+----------------------+
Glueing all together (cont.)
IOS caller
chk_pass_IOS(p)
+-----------------+
+-----------------+
|
|
|
|
| r=chk_pass(p)
|
| trampoline
|
|
|
|
|
| if r == true:
|<-+ | rest of code
|
| login()
| | | ...
|
| else:
| +-| return legal_res|
| deny_login()
| | |
|
| ...
| | +-----------------+
+-----------------+ |
+----<-------<-----------<--
Advantages of this method

A few lines of shellcode called 'trampoline' and
'glue' code filled the gap between a C function
compiled and existing IOS code.

Only one C code is maintained instead of two
assembly codes that perform the same actions on
different architectures (a MIPS code and a PowerPC
code).
Limitation of C code

Strings are put in another a DATA section.

All code together is needed.

Code must be PIC (Position independent Code).

Only relevant code to rootkit is needed.
Workaround the Limitations

Strings are declared with a MACRO like this:
char* pszPassword(void)
// String pointer name
{
jump_to_next_inst
var = ret_addr
// prev inst address
var += after function's end // string begins there
return var
}
asm(".ascii \"my backdoor password\");
asm(".byte 0");
// NULL terminator
Workaround the Limitations

Set 'flags' in C code to mark rootkit code region
#define BOF_DIK_CODE
#define EOF_DIK_CODE

asm(".ascii \"BOF_\"")
asm(".ascii \"EOF_\"")
Find those markers on generated code:
BOF_ = 0x424f465f
EOF_ = 0x454f465f
IOS functions for rootkit usage

Not only intercept low-level functions.

Obtain other IOS functions addresses.

Use them as functions pointers.

Enhance rootkit functionality using them inside our
code.

Raise rootkit stealth level of the rootkit.
IOS functions for rootkit usage (cont.)

Implement functions/structures pointer table.

Call functions as tables indexes.

Pointers to internal structures, too.
Putting it all together

Dump modified IDA sections.

Merge changed bytes with original decompressed IOS
image on disk.

Compress using Zlib / ZIP.

Calculate new checksums.

Merge with compressed IOS image.

Ready to rock n' roll :)
Simple runtime patching

GDB inside every image.

GDB can read/writer memory and processor registers.

Debugging one process can write entire memory.

Entire system compromised by simple process debug.

Use TCL to automate local GDB usage and self-
patching after every boot.

This TCL file will be hidden by the rootkit once
it's running.
Resulting IOS image

Binary patched IOS image.

No new processes created (“show proc” won't help).

Rootkit code execution is triggered by events.

Cannot trust router info if it's compromised.

Only external clean up methods will work.
Countermeasures

Follow Cisco guidelines.

Periodically cheksum images using MD5, SHA1, etc. to
see binary changes!!!

Try CIR (Cisco Incident Response) from Recurity-Labs

Harden network infrastructure.
DEMO