Wednesday, November 25, 2009

Stack Canaries - Part 2 (64-bit, bypass techniques, etc.)

So, back for more pain, I decided to try and model the canary generation (without looking at either the kernel code which assigns the initial value to the registers, nor the libc code which might do some population). I want to treat this feature as a blackbox, at least initially.

For a little more in-depth view at what happens when we turn on stack protection (-fstack-protector-all), let's look at the values of the gs:0x14 memory location. We'll see if the assumption that it changes with each function call is correct (and if so, then we have a real conundrum on our hands involving memory in general). In order to measure this, the following C code can be slipped into a recursive function which just prints the value of gs:0x14.

For 64-bit code:
unsigned long int a = 0;
if(sizeof(unsigned int) != sizeof(unsigned long))
asm ("movq %%fs:0x28, %0\n":"=r"(a));

And for 32-bit code:
unsigned int a = 0;
if(sizeof(unsigned int) == sizeof(unsigned long))
asm ("movl %%gs:0x14, %0\n":"=r"(a));

These little snippets populate 'a' with the stack canary value. Note: 64-bit uses fs:0x28 instead of gs:0x14.

Running this reveals the following neat information:
[02:20:12][aconole@ssh:~]
$ ./sp32 4
gs:0x14[3829412976 / E4403470]
gs:0x14[3829412976 / E4403470]
gs:0x14[3829412976 / E4403470]
gs:0x14[3829412976 / E4403470]
gs:0x14[3829412976 / E4403470]

This tells us that the canary is global to the context in which the functions are running. Is this the same across threads? A simple change to the rig I used to print the values for 3 different threads reveals that yes - this canary is global to the process:
[02:25:47][aconole@ssh:~]
$ ./sp32_t 1
gs:0x14[1475995573 / 57F9E7B5]
gs:0x14[1475995573 / 57F9E7B5]
gs:0x14[1475995573 / 57F9E7B5]
gs:0x14[1475995573 / 57F9E7B5]
gs:0x14[1475995573 / 57F9E7B5]
gs:0x14[1475995573 / 57F9E7B5]

The same can be seen for 64-bit code:
[02:27:03][aconole@ssh:~]
$ ./sp64 4
fs:0x28[15354006399877715714 / d5146378bfc91702]
fs:0x28[15354006399877715714 / d5146378bfc91702]
fs:0x28[15354006399877715714 / d5146378bfc91702]
fs:0x28[15354006399877715714 / d5146378bfc91702]
fs:0x28[15354006399877715714 / d5146378bfc91702]

And threaded:
[02:27:31][aconole@ssh:~]
$ ./sp64_t 1
fs:0x28[15000647472177854910 / d02d01662c05b9be]
fs:0x28[15000647472177854910 / d02d01662c05b9be]
fs:0x28[15000647472177854910 / d02d01662c05b9be]
fs:0x28[15000647472177854910 / d02d01662c05b9be]
fs:0x28[15000647472177854910 / d02d01662c05b9be]
fs:0x28[15000647472177854910 / d02d01662c05b9be]

So, now we know some important things about the canary for 64-bit and 32-bit.
#1 - if we can obtain the value while the system is running, then there's no worries on modifying the stack.
#2 - We know from where this value is obtained.
#3 - We know how to retrieve this value in a fancy rig.

The next question, before we jump headfirst into probabilities and statistics for the segment register offset value is: Can we generically modify this global canary?

Here are two generic routines to do so:
void reassign_sc_32()
{
unsigned int a = 0;
if(sizeof(unsigned int) == sizeof(unsigned long))
asm ("movl %0, %%gs:0x14\n"::"r"(a));
}

void reassign_sc_64()
{
unsigned long int a = 0;
if(sizeof(unsigned int) != sizeof(unsigned long))
asm ("movq %0, %%fs:0x28\n"::"r"(a));
}

We can simply call: reassign_sc_64(); reassign_sc_32(); and the code should modify the canary value to 0 on the correct platform. A test reveals:

[02:32:30][aconole@ssh:~]
$ ./sp32_c_t 1
gs:0x14[0 / 0]
gs:0x14[0 / 0]
gs:0x14[0 / 0]
gs:0x14[0 / 0]
gs:0x14[0 / 0]
gs:0x14[0 / 0]

64-bit also yields the same results:

[02:34:05][aconole@ssh:~]
$ ./sp64_c_t 1
fs:0x28[0 / 0]
fs:0x28[0 / 0]
fs:0x28[0 / 0]
fs:0x28[0 / 0]
fs:0x28[0 / 0]
fs:0x28[0 / 0]

So, we can modify the canary value - OUTSTANDING! Lets turn on the stack protector and see it in action:

[02:35:21][aconole@ssh:~]
$ ./sp32_c_t 1
*** stack smashing detected ***: ./sp32_c_t terminated
Aborted

Hrrm. Not quite what I had expected. Looks like we're going to have to delve into the internals of the stack protector after all, which is something I was hoping to avoid.

The code for stack-protector.c below:
#include
#include

int stack_prot_64(int num_left)
{
unsigned long int a = 0;
if(sizeof(unsigned int) != sizeof(unsigned long))
asm ("movq %%fs:0x28, %0\n":"=r"(a));

if(!num_left)
{
return printf("fs:0x28[%lu / %lx]\n", a, a);
}
stack_prot_64(num_left-1);
return printf("fs:0x28[%lu / %lx]\n", a, a);
}

int stack_prot_32(int num_left)
{
unsigned int a = 0;
asm ("movl %%gs:0x14, %0\n":"=r"(a));

if(!num_left)
{
return printf("gs:0x14[%lu / %X]\n", a, a);
}
stack_prot_32(num_left-1);
return printf("gs:0x14[%lu / %X]\n", a, a);
}

void reassign_sc_32()
{
unsigned int a = 0;
asm ("movl %0, %%gs:0x14\n"::"r"(a));
}

void reassign_sc_64()
{
unsigned long int a = 0;
if(sizeof(unsigned int) != sizeof(unsigned long))
asm ("movq %0, %%fs:0x28\n"::"r"(a));
}

void *run32(void *a)
{
#ifdef GAPING_SECURITY_HOLE
reassign_sc_32();
#endif

stack_prot_32(atoi(a));
return NULL;
}

void * run64(void *a)
{
#ifdef GAPING_SECURITY_HOLE
reassign_sc_64();
#endif

stack_prot_64(atoi(a));
return NULL;
}

int main(int argc, char *argv[])
{
pthread_t t1,t2,t3;
if(argc <= 1)
return printf("error: give a number of functions.\n");

if(sizeof(unsigned int) == sizeof(unsigned long))
{
pthread_create(&t1, NULL, run32, argv[1]);
pthread_create(&t2, NULL, run32, argv[1]);
pthread_create(&t3, NULL, run32, argv[1]);
} else
{
pthread_create(&t1, NULL, run64, argv[1]);
pthread_create(&t2, NULL, run64, argv[1]);
pthread_create(&t3, NULL, run64, argv[1]);
}

pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);

return 0;
}


-Aaron

2 comments:

Anonymous said...

It isn't hard at all to start making money online in the hush-hush world of [URL=http://www.www.blackhatmoneymaker.com]blackhat video[/URL], Don’t feel silly if you don't know what blackhat is. Blackhat marketing uses little-known or not-so-known avenues to generate an income online.

Anonymous said...

[url=http://www.23planet.com]online casino[/url], also known as accepted casinos or Internet casinos, are online versions of household ("buddy and mortar") casinos. Online casinos sustain gamblers to take up and wager on casino games assiduously the Internet.
Online casinos typically fasten unashamed odds and payback percentages that are comparable to land-based casinos. Some online casinos think of higher payback percentages fitting send up automobile games, and some reveal known payout behalf audits on their websites. Assuming that the online casino is using an aptly programmed indefinitely hundred generator, waken games like blackjack clothed an established forebears edge. The payout fair-mindedness beneath the waves the aegis regardless of these games are established report to a overlook to the rules of the game.
Numerous online casinos sublease or discern their software from companies like Microgaming, Realtime Gaming, Playtech, Worldwide Regatta Technology and CryptoLogic Inc.