PDFId : quand le python met à nu du pdf…

Je rebondis sur un article que j'ai pu lire de Didier Stevens qui mettait en lumière la capacité pour un fichier pdf d'héberger du code et de lancer, dès l'ouverture du document, un exécutable, parfois sans que l'utilisateur n'en soit conscient. PDFId PdfId est un petit script python qui va décortiquer le contenu d'un fichier pdf. Le but étant de pouvoir contrôler qu'aucune action ne sera exécutée à l'ouverture de celui-ci. Bien entendu, sans avoir à le lancer... Son utilisation est très triviale et binaire : analyse du fichier et désarmement. Petite démonstration...

Pratique

Rapatrions l'outil.
$ wget http://www.didierstevens.com/files/software/pdfid_v0_0_11.zip
$ unzip pdfid_v0_0_11.zip
Nous obtenons un script python, pdfid.py. Jouons un peu pour voir. Pour pouvoir le tester, nous avons récupéré un pdf qui lance automatiquement une application calculette. Vous pourrez le télécharger ici.
$ python pdfid.py calc.pdf
PDFiD 0.0.11 calc.pdf
PDF Header: %PDF-1.1
obj                    5
endobj                 5
stream                 1
endstream              0
xref                   1
trailer                1
startxref              1
/Page                  1
/Encrypt               0
/ObjStm                0
/JS                    0
/JavaScript            0
/AA                    0
/OpenAction            1
/AcroForm              0
/JBIG2Decode           0
/RichMedia             0
/Launch                3
/Colors > 2^24         0
Le pdf contient 3 instances dans /Launch. Mais encore...
$ strings calc.pdf
%PDF-1.1
1 0 obj
/OpenAction <<
/F <<
/DOS (C:\\\\WINDOWS\\\\system32\\\\calc.exe)
/Unix (/usr/bin/xcalc)
/Mac (/Applications/Calculator.app)
>>
/S /Launch
/Pages 2 0 R
/Type /Catalog
endobj
2 0 obj
/Kids [ 3 0 R ]
/Count 1
/Type /Pages
endobj
3 0 obj
/Resources <<
/Font <<
/F1 5 0 R
>>
/MediaBox [ 0 0 795 842 ]
/Parent 2 0 R
/Contents 4 0 R
/Type /Page
endobj
4 0 obj
/Length 1260
>>stream
/F1 30 Tf 350 750 Td 20 TL
1 Tr (calc.pdf) Tj
/F1 15 Tf 233 690 Td 20 TL
0 Tr (This page is empty but it should start calc :-D) Tj
/F1 15 Tf 233 670 Td 20 TL
(Dont be afraid of the pop-ups, just click them...) Tj
/F1 14 Tf 75 620 Td 20 TL
2 Tr (Comments:) Tj
/F1 12 Tf 75 600 Td 20 TL
0 Tr (Windows:) Tj (  - Foxit: runs calc.exe at the document opening without any user confirmation message \(!\)      ) ' (  - Acrobat Reader *:) ' (      1. popup proposing to open "calc.exe" \(warning\)) ' (      2.  starts  "calc.exe") ' () ' (Mac:) ' (  - Preview does not support PDF keyword /Launch) ' (  - Acrobat Reader 8.1.2: starts Calculator.app) ' () ' (Linux:) ' (  ! Assumes xcalc is in /usr/bin/xcalc) ' (  - poppler: does not support PDF keyword /Launch) ' (  - Acrobat Reader 7: ) ' (      1. popup telling it can not open "xcalc" \(dumb reasons\)) ' (      2. popup proposing to open "xcalc" \(warning\)) ' (      3. starts  "xcalc") ' (  - Acrobat Reader 8.1.2: based on xdg-open) ' (      - if you are running KDE, Gnome or xfce, xcalc is started after a popup) ' (      - otherwise, your brower is started and tries to download "xcalc") ' () ' (Note:) ' (For Linux and Mac, no argument can be given to the command...) '
ETendstream
endobj
5 0 obj
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Type /Font
endobj
xref
0000000000 65535 f
0000000010 00000 n
0000000234 00000 n
0000000303 00000 n
0000000457 00000 n
0000001774 00000 n
trailer
/Size 6
/Root 1 0 R
/ID [ (bc38735adadf7620b13216ff40de2b26) (bc38735adadf7620b13216ff40de2b26) ]
startxref
1866
%%EOF
Comme je vous l'ai dis en introduction, pdfid.py a une fonction qui permet de désarmer un pdf. Pour cela, il suffit de mentionner la bonne option. Et comme tout bonne outil qui se respecte, il affiche la liste des options via un --help.
$ ./pdfid.py --help
Usage: pdfid.py [options] [pdf-file]
Tool to test a PDF file

Options:
--version     show program's version number and exit
-h, --help    show this help message and exit
-s, --scan    scan the given directory
-a, --all     display all the names
-e, --extra   display extra data, like dates
-f, --force   force the scan of the file, even without proper %PDF header
-d, --disarm  disable JavaScript and auto launch

Nous allons passer au désarmement du pdf.

Pour cela, il suffit de passer l'option -d sur le fichier à désarmer. On aura alors un joli calc.disarmed.pdf qui sera généré par pdfid.py.
$ python pdfid.py -d calc.pdf
/OpenAction -> /oPENaCTION
/Launch -> /lAUNCH
/Launch -> /lAUNCH
/Launch -> /lAUNCH
PDFiD 0.0.11 calc.pdf
PDF Header: %PDF-1.1
obj                    5
endobj                 5
stream                 1
endstream              0
xref                   1
trailer                1
startxref              1
/Page                  1
/Encrypt               0
/ObjStm                0
/JS                    0
/JavaScript            0
/AA                    0
/OpenAction            1
/AcroForm              0
/JBIG2Decode           0
/RichMedia             0
/Launch                3
/Colors > 2^24         0
Testons maintenant le calc.disarmed.pdf généré par pdfid.py :
$ python pdfid.py calc.disarmed.pdf
PDFiD 0.0.11 calc.disarmed.pdf
PDF Header: %PDF-1.1
obj                    5
endobj                 5
stream                 1
endstream              0
xref                   1
trailer                1
startxref              1
/Page                  1
/Encrypt               0
/ObjStm                0
/JS                    0
/JavaScript            0
/AA                    0
/OpenAction            0
/AcroForm              0
/JBIG2Decode           0
/RichMedia             0
/Launch                0
/Colors > 2^24         0
Plus de Launch. Le calc.disarmed.pdf contient maintenant :
$ strings calc.disarmed.pdf
%PDF-1.1
1 0 obj
/oPENaCTION <<
/F <<
/DOS (C:\\\\WINDOWS\\\\system32\\\\calc.exe)
/Unix (/usr/bin/xcalc)
/Mac (/Applications/Calculator.app)
>>
/S /lAUNCH
/Pages 2 0 R
/Type /Catalog
endobj
2 0 obj
/Kids [ 3 0 R ]
/Count 1
/Type /Pages
endobj
3 0 obj
/Resources <<
/Font <<
/F1 5 0 R
>>
/MediaBox [ 0 0 795 842 ]
/Parent 2 0 R
/Contents 4 0 R
/Type /Page
endobj
4 0 obj
/Length 1260
>>stream
/F1 30 Tf 350 750 Td 20 TL
1 Tr (calc.pdf) Tj
/F1 15 Tf 233 690 Td 20 TL
0 Tr (This page is empty but it should start calc :-D) Tj
/F1 15 Tf 233 670 Td 20 TL
(Dont be afraid of the pop-ups, just click them...) Tj
/F1 14 Tf 75 620 Td 20 TL
2 Tr (Comments:) Tj
/F1 12 Tf 75 600 Td 20 TL
0 Tr (Windows:) Tj (  - Foxit: runs calc.exe at the document opening without any user confirmation message \(!\)      ) ' (  - Acrobat Reader *:) ' (      1. popup proposing to open "calc.exe" \(warning\)) ' (      2.  starts  "calc.exe") ' () ' (Mac:) ' (  - Preview does not support PDF keyword /lAUNCH) ' (  - Acrobat Reader 8.1.2: starts Calculator.app) ' () ' (Linux:) ' (  ! Assumes xcalc is in /usr/bin/xcalc) ' (  - poppler: does not support PDF keyword /lAUNCH) ' (  - Acrobat Reader 7: ) ' (      1. popup telling it can not open "xcalc" \(dumb reasons\)) ' (      2. popup proposing to open "xcalc" \(warning\)) ' (      3. starts  "xcalc") ' (  - Acrobat Reader 8.1.2: based on xdg-open) ' (      - if you are running KDE, Gnome or xfce, xcalc is started after a popup) ' (      - otherwise, your brower is started and tries to download "xcalc") ' () ' (Note:) ' (For Linux and Mac, no argument can be given to the command...) '
ETendstream
endobj
5 0 obj
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Type /Font
endobj
xref
0000000000 65535 f
0000000010 00000 n
0000000234 00000 n
0000000303 00000 n
0000000457 00000 n
0000001774 00000 n
trailer
/Size 6
/Root 1 0 R
/ID [ (bc38735adadf7620b13216ff40de2b26) (bc38735adadf7620b13216ff40de2b26) ]
startxref
1866
%%EOF

Conclusion

Voilà pour cet outil. Simple, concis, il est tout à fait à sa place lorsque vous avez un doute sur un pdf. Court, n'est-ce pas ? Pour une analyse plus poussée, beaucoup d'autres outils de cette trempe existent, que vous pourrez retrouver sur le blog de Didier Stevens. Bonne semaine à tous !
Vus : 1596
Publié par K-Tux : 59