lunes, octubre 09, 2006

Get local root by infection

I presented at the Barcelona FIST conference a new way to local-hack linux box by infecting ELF executables.

It's possible do an elf-infection to a writable binary, and wait that r00t or a priviledged user executes it, is a simple idea but a complex implementation.

Here is my presentation:
http://www.fistconference.org/data/presentaciones/infR3.pdf

And here is my implementation:
http://www.milw0rm.com/author/300
http://www.badchecksum.com/code/pentest/infR3.s

Here is a demo:http://www.youterm.com/?view=Player&video=hack/infector
or
echo hack/infector/exit | nc youterm.com 9999

miércoles, octubre 04, 2006

Elf entry calculation in c

e_entry points to the virtual address where will be _start at runtime.
In order to calculate the relative virtual address of the entry point from the beginning of the file image, we should look for the code segment and use this formula:

(elf).e_entry - (code).p_vaddr + (code).p_offset

p_offset is the distance from the begining of the file to the code segment.
p_addr is the virtual address of the code segment.

The diference e_entry - p_vaddr can be drawed like this:


(at runtime)
+--- code segment ---- <- p_vaddr
|
|
|
|<---- e_entry
|
We already know the distance inside the code segment where is the entry point (usually at the begining of .text section) If now we sum the offset where code segment starts from the beginning, we will have the offset from the beginning of the file where is exactly the entry point.

void getentry (struct map *elf) {
 int ph;
 int s; //text section Index text
 elf->text.s = elf->s;

 for (s=0; se->e_shnum; s++) {

  if (strcmp(".text",(char *)((unsigned long)elf->e + (unsigned    long)elf->strtab->sh_offset + (unsigned long)elf->text.s->sh_name))    == 0)
   break;

  elf->text.s++;
 }

 if (elf->e->e_shnum == s) {
  printf(".text section can not be found, bad elf\n");
  final();
 }

//elf->p Is the first record, elf->text.p This ptr will be travelling throw the memory since arrive to the text segment
elf->text.p = elf->p;

 for (ph=elf->e->e_phnum; ph>0; ph--) {

  if (elf->text.p->p_type == PT_LOAD && elf->text.p->p_flags == 5) {
   elf->text.size = elf->text.p->p_memsz;
   elf->text.entry.rel = (unsigned long)((unsigned long)elf->e->e_entry -
(unsigned long)elf->text.p->p_vaddr +
(unsigned long)elf->text.p->p_offset);
   elf->text.entry.abs = elf->text.entry.rel + (unsigned long)elf->e;


/*
We have 4 entry points
* elf->e->e_entry (VA of the entry at runtime)
* elf->text.entry.rel (RVA of the entry from the beginning of .text)
* elf->text.entry.abs (VA of the entry from the beginning of the file)
*/

   return;
  }
  elf->text.p++;
 }

 printf("There is no entry point\n");
 final();
}

// EOF

Elf entry calculation in asm

There are two ways to access to the elf fields, directly knowing the offset of the field needed or filling a small structure and then access to the structure field.



main:
...
end:
e_ident:
.long 0
.long 0
.long 0
.long 0
e_type:
.int 0
e_machine:
.int 0
e_version:
.long 0
e_entry:

(structure in at&t format)

ELF struct
  e_ident      dd 4 dup(?)
  e_type       dw ?
  e_machine dw ?
  e_version  dw ?
  e_entry     dd ?
ELF ends

(structure in intel format)

Using a structure is the easy way it only needs a open() and read() syscalls.
But wen some file-image accesses are nedded, is not the best way to make some reads. Is better to map the file and work with pointers.


store_init:
movl $end_vir, %ecx
subl $start_vir, %ecx
movl %ecx,-16(%ebp) # -16 -> size of virus + 5

leal -500(%ebp), %edi # edi -> -500
movl 0x18(%eax), %esi # esi -> RVA e_entry
movl 0x2c(%eax), %ecx # Numero de PH's (e_phnum) (back-count)

first_ph:
movl 0x1c(%eax), %edx # edx -> RVA e_phoff
addl %eax, %edx # edx -> VA e_phoff

seek_ph:
cmpl %esi, 0x08(%edx) # if e_entry > p_vaddr => next ProgramHeader
jna destiny

next_ph:
addl 0x2a(%edx), %edx
loop seek_ph

destiny: ######### THE MAIN KEY ##########
subl 0x08(%edx), %esi # esi -> RVA e_entry-p_vaddr
addl 0x04(%edx), %esi # esi -> RVA e_entry-p_vaddr+p_offset
addl %eax, %esi # esi -> VA e_entry-p_vaddr+p_offset
movl %esi, %edx

#EOF

Elf infection adding new section

The No cON Name 2006 security congress celebrated at Palma de Mallorca, i have presented a possible solution to code vulnerabilities like buffer overflows.

Here is the link: http://www.noconname.org/congreso2006.php

I prensented some current solutions like pax, W^X, address layout randomizations, etc and show their main limitation: doesnt solve the real problem, when overflow happens, there are many traps to prevent the execution of code but the overflow happened yet and with time, attackers will redirect execution and will inject a payload somewhere.

I have shown the real problem: at post-compilation there are no variable-sizes, only exists pointers to the beginning of variables but not the end either the size.

Is it possible to calculate the limits of almost all variables, by reading dinamyc memory calls, and analyzing the use of the stack pointers.

I proposed the idea of a binary regenerator, that study the pointer bounds, and correct the instructions that want to violate this boundaries. In order to repair the code i show some virus infection, cold-patching and hot patchin techiniques.

Instead of replacing code by inline-patching, i suggested to make a call a .regen section and there is the code sanitizeed, in order to recover the bad-code if needed.


Why current protections are dificulting the explotation instead of solve the problem?
May the compilers store the variable sizes at de elf and PE executables?

I think this will solve most of security problems.

[spanish]
En el congreso 2006 de seguridad informática celebrado en palma de mallorca (noconname.org) he presentado una solución a los problemas de desbordamientos de pila, basandose en localizar límites y parchear el código vulnerable.

El parcheo o infección de código lo realizan los virus informáticos, podemos aplicar muchas partes de ellos en la seguridad.

De los diversos tipos de infección que hay (overlay, crecimiento de .text, seccion nueva, etc ..) hablaré de una en concreta que para este caso es más eficaz.

El objetivo es poder corregir el código binario y dejar el código corregido en otra seccion (.patch)

Infección mediante creación de sección:
(en este procedimiento no se va a agrandar shstrtab para incluir ahi el nombre de sección ya que la sección que contiene nombre de secciones no es mapeada en tiempo de ejecución)

1. Remapear con el tamaño del fichero+parche+1 registro de sección
2. Desplazamiento lógico del offset y virtual de las secciones inferiores a la tabla de secciones
3. Desplazamiento físico de las secciones inferiores a la tabla de secciones
4. Añadir nueva sección semejante a .text con flags progbits, alocatable y ejecutable. Y aumentar e_shnum
5. Agrandamiento lógico de segmento de texto en el tamaño del parche
6. Desplazamiento lógico de los segmentos inferiores al de texto (offset y virtual)
7. Desplazamiento lógico de las secciones inferiores al antiguo final del segmento de texto (offset y virtual)
8. Desplazar físicamente lo que haga por debajo del inicio de .patch (final del antiguo segmento texto) para que quede espacio para .patch
9. Actualizar e_shoff ya que se ha desplazado físicamente la tabla de secciones (está abajo)
10. Guardar la nueva versión corregida de las funciones vulnerables en .patch y reapuntar sus calls a esta sección.





EOF