Killing the Myth of IOS rootkits: DIK, Da IOS rookit
Download
Report
Transcript Killing the Myth of IOS rootkits: DIK, Da IOS rookit
Killing the myth of Cisco IOS
rootkits: DIK
(Da Ios rootKit)
Sebastian 'topo' Muñiz
EuSecWest
London, May 2008
Speaker info
Name: Sebastian 'topo' Muñiz
Work: Sr. Exploit Writer at CORE
Likes: Reverse Engineering & Vuln Research
Contact: [email protected]
[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 rootkit?
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 low-level OS functions and
manipulating internal structures.
Rootkits along history
• Existed on other OSes 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 versions? Yes
Does it matter if they have different arch (MIPS or PowerPC)? No
Do I need to know assembly code for a specific arch? No, just
plain C :)
Consequences
• Imagine your company’s 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 other’s memory.
No memory protection between processes.
Uses 'run to completion' priority scheduling.
FIFO (First In First Out) process queue.
This model reduces local security and system stability.
Completely different to modern OSes.
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).
Lots 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.5x)
ELF structure as IOS image
ELF header
SFX code
Self
code de-compressing
MAGIC (0xFEEDFACE)
Compressed image length
Compressed image checksum
Magic
Structure
Uncompressed image checksum
Uncompressed image length
Compressed Image
Compressed IOS image
ELF structure as IOS image (cont.)
SFX code uses Magic Structure along the entire decompression
process.
Values in Magic Structure are employed to validate
decompression result.
Length values in Magic Structure are expressed in words (4 bytes)
Ex. Length of 1024 words = 4096 (1024 * 4) bytes
Length values help to verify available memory and size of buffer
to checksum.
Image’s checksum is calculated before and after decompression.
ELF structure as IOS image (cont.)
Simple (insecure) 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, the resulting (uncompressed)
image will end up running on the device’s memory.
This image will contain our rootkit.
It's needed for analysis, so download using the 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 workings of the OS.
Take advantage of debug strings info to locate functions of
interest to 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
Parts of the IOS image were not analyzed correctly.
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 over 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 over every DATA section (like .data)
Strings where (0 != length mod 4) are padded with null bytes.
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 beginning 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 (0x09), CR (0x0a) and LF (0x0d).
Bytes with values within CODE section ranges may be pointers.
What if both happens? Like in a CODE section from 0x61000000 to
0x61700000?
Is 0x61616120 (“AAA ”) a pointer or just the beginning 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 results of key functions.
Some of them are usually displayed as output.
By locating their references, we locate those functions.
Iterate over every string to locate those of interest to an attacker.
For each one, use IDA’s string XREF property to identify it's
callers (IDA-Python magic again :)
Locating low-level IOS functions (cont.)
What if a function does not use any string?
Functions are compiled in the same order as source file.
Other functions (neighbours) next to it will surely do (whether
other IOS function call them or not).
Apply this procedure to every function that we want to find using
simple IDA-Python functions.
A home for the rootkit’s code
IOS contains lots 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 counterparts to IOS functions that do attacker's
will.
Recompiling the rootkit code every time is not an option.
Cross-compile it once (for MIPS and PowerPC).
How to locate offsets of compiled counterpart functions?
Use 'objdump' to obtain symbolic info and offsets.
Name of a counterpart function resolves to it's offset in compiled
code.
Redirect IOS execution flow
Knowing offsets of counterpart functions means that they are
callable now.
Intercept IOS functions and redirect their execution flow.
Hook every interesting IOS function with a trampoline code.
Trampoline code is placed at function’s first instruction.
Jumps to the location of rootkit code 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.
Glueing 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’s plain C code.
Decide whether flow should continue or not to the hooked
function.
If it should restore original parameters, execute instruction at
trampoline address and jump after trampoline
If it should go back to caller, set extra parameter value as the
hooked function’s return value and go to the saved return
address.
Glueing all together
IOS caller
…
r = chk_pass(p)
if (r == true):
login()
else:
deny_login()
chk_pass()
trampoline
…
chk_pass code
…
return (value)
Glue code
add stack
store parent’s RA
store params p
create param i
o = chk_pass_DIK(p)
fix stack
if (o == CONT):
execute orig inst
return params p
cont chk_pass_IOS
else:
r = i
jump to RA
chk_pass_DIK()
if (p == ‘1337’):
i = true
return RET
else:
return CONT
Advantages of this method
A few lines of shellcode called trampoline and glue
code bridge the gap between a compiled C function 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).
Limitations of C code
Strings are put in another 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 able to intercept low-level functions.
Obtain addresses of other IOS functions.
Use them as function pointers.
Enhance rootkit functionality using them inside our
code.
Increase rootkit’s stealthness level.
IOS functions for rootkit usage (cont.)
Implement tables of function/structure pointers.
Call functions as table’s 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/write 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 checksum images using MD5, SHA1, etc.
to see binary changes!!!
Try CIR (Cisco Incident Response) from Recurity-Lab
to detect anomalies.
Harden network infrastructure.
DEMO