MBRLocker
Introduction
En ce moment je m'amuse à reverse quelques vx. Mais aujourd'hui c'est jour de fête, je suis tombé sur mon premier MBRLocker :
Reverse de la bête
Le problème que j'avais c'était comment dump mon mbr alors que j'étais infécté, revenir au snapshot précédent, attendre que le malware écrive le nouveau mbr et en 1 ligne de python dump le nouveau mbr :
file("vuln_mbr", "wb").write(file(r"\\.\PhysicalDrive0","rb").read(0x200))
Mais j'ai préféré opté pour une autre solution, convertir le disque de ma vm et allé récupérer le mbr à la main :
> vboxmanage clonehd --variant static analyse.vdi temp.vdi
> head -c 348 temp.vdi | hexdump -C
00000000 3c 3c 3c 20 4f 72 61 63 6c 65 20 56 4d 20 56 69 |<<< Oracle VM Vi|
00000010 72 74 75 61 6c 42 6f 78 20 44 69 73 6b 20 49 6d |rtualBox Disk Im|
00000020 61 67 65 20 3e 3e 3e 0a 00 00 00 00 00 00 00 00 |age >>>.........|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 7f 10 da be 01 00 01 00 90 01 00 00 02 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000150 00 00 00 00 00 10 00 00 00 b0 00 00 |............|
0000015c
On récupère l'offset de notre disque dans le fichier : 0x0000b000.
On go chercher le mbr :
> echo $((0x0000b000))
45056
dd if=./temp.vdi of=./vuln_mbr bs=512 count=1 skip=$((45056/512))
On regarde que c'est bien ca :
> file vuln_mbr
mbr_vuln.bin: x86 boot sector; partition 1: ID=0x7, active, starthead 1, startsector 63, 20948697 sectors, code offset 0x80
Bon bah reste plus qu'à reverse le truc, moins de 512 octets de code ca devrait allé vite :
> objdump -D -b binary -mi386 -Maddr16,data16 mbr_vuln
Je vais pas tout vous balancer tout le disas mais juste des lignes inéressantes :
88: b4 00 mov ah,0x0
8a: 57 push di
8b: cd 16 int 0x16
8d: 5f pop di
8e: eb 37 jmp 0xc7
...
c7: 3c 0d cmp al,0xd
c9: 75 55 jne 0x120
cb: be 8c 7d mov si,0x7d8c
ce: bf 00 6c mov di,0x6c00
d1: b5 00 mov ch,0x0
d3: ea 72 7d 00 00 jmp 0x0:0x7d72
Ici on fait appel à l'interrupt 0x16 avec ah = 0 afin de lire un caractère, si ce caractère est égale à 0xd (Carriage Return)
alors on part en 0x172 ( 0x7d72 - 0x7C00 ), le bootloader est chargé en 0x7C00 par le BIOS.
Surement la routine de vérifiction de notre code :
172: 8a 0e 8b 7d mov cl,BYTE PTR ds:0x7d8b
176: ac lods al,BYTE PTR ds:[si]
177: 8a 15 mov dl,BYTE PTR [di]
179: 80 f2 ed xor dl,0xed
17c: 47 inc di
17d: 3a c2 cmp al,dl
17f: 0f 85 58 ff jne 0xdb
183: 49 dec cx
184: 75 f0 jne 0x176
186: ea 36 7d 00 00 jmp 0x0:0x7d36
On peut voir clairement que dans %cl, il y aura la taille du code attendu, %si lui pointe vers le serial auquel on va être comparé, %di pointe sur ce qu'on a rentré, donc tous nos carac vont être xored avec 0xED.
Il suffit d'aller voir ce qui traine en 0x18B ( 0x7d8B - 0x7C00 ) :
> hexdump -s 0x18B mbr_vuln
0000018c 07 a0 ad dc d9 d8 bf b9 00 00 00 00 00 00 00 00
>>> chr(0xa0 ^ 0xED)
'M'
>>> chr(0xad ^ 0xED)
'@'
>>> chr(0xdc ^ 0xED)
'1'
>>> chr(0xd9 ^ 0xED)
'4'
>>> chr(0xd8 ^ 0xED)
'5'
>>> chr(0xbf ^ 0xED)
'R'
>>> chr(0xb9 ^ 0xED)
'T'
Le code serait donc : "M@145RT", on teste et ca boot :]
Bonus
Je trouvais leur message, un peu triste, alors j'ai voulu pour le fun coder un petit bootloader qui déssine une bière, mais qui ne fait rien au final, car 512 octets pour pouvoir caller un dession c'est hard, du coup j'ai 2 stage, le stage 1 qui init la gdt, passer en mode protected, on load le printer de la biere en memoire (0x1000), et on finira par l'exec, voici le résultat :
Le code :