Solution de prae_gitlab pour Not so FAT

intro forensics disque

3 décembre 2024

Analyse préliminaire

Si on analyse le fichier not-so-fat.dd, on voit rapidement que c’est une partition DOS/FAT, avec un boot-sector de 512 octets (le boot-sector allant de l’offset 0x0 à 0x1f0 et se terminant avec la boot signature 0x55AA).

Je ne vais pas partir sur un montage du filesystem via le fichier dump, l’auteur indique avoir supprimé le fichier flag, nous ne trouverons rien par ce biais.

Vu la taille du fichier, nous pouvons nous permettre d’analyser le fichier directement en brut avec hexdump :

$ hexdump -C not-so-fat.dd
00000000  eb 3c 90 6d 6b 66 73 2e  66 61 74 00 02 04 04 00  |.<.mkfs.fat.....|
00000010  02 00 02 00 80 f8 20 00  20 00 40 00 00 00 00 00  |...... . .@.....|
00000020  00 00 00 00 80 00 29 04  4c e8 3b 4e 4f 20 4e 41  |......).L.;NO NA|
00000030  4d 45 20 20 20 20 46 41  54 31 36 20 20 20 0e 1f  |ME    FAT16   ..|
00000040  be 5b 7c ac 22 c0 74 0b  56 b4 0e bb 07 00 cd 10  |.[|.".t.V.......|
00000050  5e eb f0 32 e4 cd 16 cd  19 eb fe 54 68 69 73 20  |^..2.......This |
00000060  69 73 20 6e 6f 74 20 61  20 62 6f 6f 74 61 62 6c  |is not a bootabl|
00000070  65 20 64 69 73 6b 2e 20  20 50 6c 65 61 73 65 20  |e disk.  Please |
00000080  69 6e 73 65 72 74 20 61  20 62 6f 6f 74 61 62 6c  |insert a bootabl|
00000090  65 20 66 6c 6f 70 70 79  20 61 6e 64 0d 0a 70 72  |e floppy and..pr|
000000a0  65 73 73 20 61 6e 79 20  6b 65 79 20 74 6f 20 74  |ess any key to t|
000000b0  72 79 20 61 67 61 69 6e  20 2e 2e 2e 20 0d 0a 00  |ry again ... ...|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000800  f8 ff ff ff 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000810  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00004800  f8 ff ff ff 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00004810  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00008800  e5 7a 00 69 00 45 00 75  00 59 00 0f 00 d6 72 00  |.z.i.E.u.Y....r.|
00008810  4a 00 57 00 00 00 ff ff  ff ff 00 00 ff ff ff ff  |J.W.............|
00008820  e5 49 45 55 59 52 4a 57  20 20 20 20 00 00 39 39  |.IEUYRJW    ..99|
00008830  aa 4e aa 4e 00 00 39 39  aa 4e 00 00 00 00 00 00  |.N.N..99.N......|
00008840  e5 66 00 6c 00 61 00 67  00 2e 00 0f 00 96 7a 00  |.f.l.a.g......z.|
00008850  69 00 70 00 00 00 ff ff  ff ff 00 00 ff ff ff ff  |i.p.............|
00008860  e5 4c 41 47 20 20 20 20  5a 49 50 20 00 00 39 39  |.LAG    ZIP ..99|
00008870  aa 4e aa 4e 00 00 39 39  aa 4e 03 00 f1 00 00 00  |.N.N..99.N......|
00008880  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0000d000  50 4b 03 04 0a 00 09 00  00 00 b7 4e a4 4e db 07  |PK.........N.N..|
0000d010  7e 00 3b 00 00 00 2f 00  00 00 08 00 1c 00 66 6c  |~.;.../.......fl|
0000d020  61 67 2e 74 78 74 55 54  09 00 03 09 45 cd 5c 09  |ag.txtUT....E.\.|
0000d030  45 cd 5c 75 78 0b 00 01  04 e8 03 00 00 04 e8 03  |E.\ux...........|
0000d040  00 00 39 9a cb 6a f2 25  39 a7 f1 1c 77 5c 72 b0  |..9..j.%9...w\r.|
0000d050  bc c5 72 1b 39 af 9b 4f  a1 15 45 f8 43 92 db d1  |..r.9..O..E.C...|
0000d060  18 2b a6 0f b5 9a 79 cd  9b 06 97 4b a9 e2 b1 db  |.+....y....K....|
0000d070  aa c5 2c 7d f4 a1 8f b9  57 c8 ff 7e 92 50 4b 07  |..,}....W..~.PK.|
0000d080  08 db 07 7e 00 3b 00 00  00 2f 00 00 00 50 4b 01  |...~.;.../...PK.|
0000d090  02 1e 03 0a 00 09 00 00  00 b7 4e a4 4e db 07 7e  |..........N.N..~|
0000d0a0  00 3b 00 00 00 2f 00 00  00 08 00 18 00 00 00 00  |.;.../..........|
0000d0b0  00 01 00 00 00 b4 81 00  00 00 00 66 6c 61 67 2e  |...........flag.|
0000d0c0  74 78 74 55 54 05 00 03  09 45 cd 5c 75 78 0b 00  |txtUT....E.\ux..|
0000d0d0  01 04 e8 03 00 00 04 e8  03 00 00 50 4b 05 06 00  |...........PK...|
0000d0e0  00 00 00 01 00 01 00 4e  00 00 00 8d 00 00 00 00  |.......N........|
0000d0f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
01000000

Le second gros bloc (0x8800 - 0x8880) semble être la table des matières, on nous indique un fichier flag.zip.

Juste après, si on a l’oeil, on voit un header qu’on connait bien, notre header ZIP, aka “0x50 0x4B” (PK) à l’offset 0xd000 :

$ hexdump -s $((16#D000)) -C not-so-fat.dd
0000d000  50 4b 03 04 0a 00 09 00  00 00 b7 4e a4 4e db 07  |PK.........N.N..|
0000d010  7e 00 3b 00 00 00 2f 00  00 00 08 00 1c 00 66 6c  |~.;.../.......fl|
0000d020  61 67 2e 74 78 74 55 54  09 00 03 09 45 cd 5c 09  |ag.txtUT....E.\.|
0000d030  45 cd 5c 75 78 0b 00 01  04 e8 03 00 00 04 e8 03  |E.\ux...........|
0000d040  00 00 39 9a cb 6a f2 25  39 a7 f1 1c 77 5c 72 b0  |..9..j.%9...w\r.|
0000d050  bc c5 72 1b 39 af 9b 4f  a1 15 45 f8 43 92 db d1  |..r.9..O..E.C...|
0000d060  18 2b a6 0f b5 9a 79 cd  9b 06 97 4b a9 e2 b1 db  |.+....y....K....|
0000d070  aa c5 2c 7d f4 a1 8f b9  57 c8 ff 7e 92 50 4b 07  |..,}....W..~.PK.|
0000d080  08 db 07 7e 00 3b 00 00  00 2f 00 00 00 50 4b 01  |...~.;.../...PK.|
0000d090  02 1e 03 0a 00 09 00 00  00 b7 4e a4 4e db 07 7e  |..........N.N..~|
0000d0a0  00 3b 00 00 00 2f 00 00  00 08 00 18 00 00 00 00  |.;.../..........|
0000d0b0  00 01 00 00 00 b4 81 00  00 00 00 66 6c 61 67 2e  |...........flag.|
0000d0c0  74 78 74 55 54 05 00 03  09 45 cd 5c 75 78 0b 00  |txtUT....E.\ux..|
0000d0d0  01 04 e8 03 00 00 04 e8  03 00 00 50 4b 05 06 00  |...........PK...|
0000d0e0  00 00 00 01 00 01 00 4e  00 00 00 8d 00 00 00 00  |.......N........|
0000d0f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
0000d100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

Nous allons extraire ce petit fichier perdu et seul au milieu de cette forêt de données. Pour cela, on va dégainer notre petit dd et aller directement à la bonne position et récupérer ce qu’il nous faut :

echo "position start = " $((16#D000))
echo "position end   = " $((16#d100))
position start =  53248
position end   =  53504

Notre fichier se trouve à l’offset 53248 et a une taille relativement petite, nous allons l’extraire avec notre ami dd :

$ dd if=not-so-fat.dd skip=$((53248/512)) count=1 | zcat
1+0 records in
1+0 records out
512 bytes copied, 6,6436e-05 s, 7,7 MB/s

gzip: stdin: encrypted file -- use unzip

Tiens ! notre petit fichier se trouve être chiffré ? Nous allons analyser tout ceci plus en profondeur :

$ dd if=not-so-fat.dd skip=$((53248/512)) count=1 > flag.zip

$ zipinfo flag.zip
Archive:  flag.zip
Zip file size: 512 bytes, number of entries: 1
-rw-rw-r--  3.0 unx       47 TX stor 19-May-04 09:53 flag.txt
1 file, 47 bytes uncompressed, 47 bytes compressed:  0.0%

$ zipdetails flag.zip | head  -n6
0000 LOCAL HEADER #1       04034B50
0004 Extract Zip Spec      0A '1.0'
0005 Extract OS            00 'MS-DOS'
0006 General Purpose Flag  0009
     [Bit  0]              1 'Encryption'

Effectivement, notre petit fichier flag.zip a été chiffré !

Sortons alors le bazooka du zip : fcrackzip, avec, comme input en plus, un dictionnaire de mots de passe communs, utile pour le brute force :

$ fcrackzip -u -D -p wordlists.txt flag.zip
PASSWORD FOUND!!!!: pw == password

Utilisons-le pour ouvrir le coffre fort :

$ unzip flag.zip
Archive:  flag.zip
[flag.zip] flag.txt password: password
 extracting: flag.txt

$ cat flag.txt
ECSC{eefea8cda693390c7ce0f6da6e388089dd615379}

Et voilà !

La méthode à-la-Python

Si nous voulions trouver automatiquement le fichier ZIP dans le dumpfile, nous pourrions coder un petit finder dans le filesystem (ou dans tout autre dump) :

#!/usr/bin/env python3

import sys

with open(sys.argv[1], "rb") as file:
    while (buffer := file.read(4)):
        # PK header
        if buffer == b"\x50\x4b\x03\x04":
            # back to the future
            file.seek(file.tell() - 4)
            # read only 256 bytes
            # need to read header for real file size, but don't care :)
            content = file.read(256)
            # write output
            with open(sys.argv[2], "wb") as out:
                print(f"write {sys.argv[2]}")
                out.write(content)
            break # bye

Utilisation de notre petit finder :

$ ./zipfind.py not-so-fat.dd /tmp/output.zip
write /tmp/output.zip

$ zipinfo /tmp/output.zip
Archive:  /tmp/output.zip
Zip file size: 256 bytes, number of entries: 1
-rw-rw-r--  3.0 unx       47 TX stor 19-May-04 09:53 flag.txt
1 file, 47 bytes uncompressed, 47 bytes compressed:  0.0%

Si nous n’avions pas de cracker de zip sous la main, autant en coder un nous même :)

#!/usr/bin/env python3

import sys
import zipfile

with zipfile.ZipFile(sys.argv[1]) as handler:
    with open(sys.argv[2]) as wordlists:
        for wordlist in wordlists.readlines():
            password = wordlist.strip()
            for entry in handler.infolist():
                print(f"opening {entry.filename} with password {password}")
                try:
                    with handler.open(entry.filename, "r", bytes(password, "utf8")) as f:
                        print(f.read().decode('utf8').strip())
                except:
                    pass
                else:
                    sys.exit(0)

Et pour l’utiliser, il suffit de donner le fichier zip et une base de mots de passe :

$ ./zipdec.py flag.zip wordlists.txt
opening flag.txt with password 123456
opening flag.txt with password admin
opening flag.txt with password 12345678
opening flag.txt with password 123456789
opening flag.txt with password 1234
opening flag.txt with password 12345
opening flag.txt with password password
ECSC{eefea8cda693390c7ce0f6da6e388089dd615379}