add archiver writeup
Signed-off-by: Julien CLEMENT <julien.clement@epita.fr>
This commit is contained in:
parent
32286c8a62
commit
95852bee94
324
jujure/content/writeups/fcsc_2024/archiver.md
Normal file
324
jujure/content/writeups/fcsc_2024/archiver.md
Normal file
@ -0,0 +1,324 @@
|
||||
---
|
||||
title: "Black box reversing | Archiver @ FCSC 2024"
|
||||
date: "2024-04-13 22:00:00"
|
||||
author: "Juju"
|
||||
tags: ["Reverse", "Writeup", "fcsc"]
|
||||
toc: true
|
||||
---
|
||||
|
||||
# Intro
|
||||
|
||||
This writeup is not a really serious one, if you are reading this
|
||||
as part of the FCSC writeup reviews, my submitted writeups are
|
||||
actually for `svartalfheim` and `megalosaure`. I still thought
|
||||
it would be funny to include this one as it is not that long and
|
||||
I think my solution is kind of unintended.
|
||||
|
||||
Basically we are given a `Windows` stripped binary, compiled
|
||||
from `rust`. The binary is an encrypted archive manager.
|
||||
|
||||
This challenge managed to put every single reverser red flag
|
||||
in a single binary.
|
||||
|
||||
I tried opening up the binary in `binary ninja` or `IDA`, but it
|
||||
was as expected: stripped `rust`.
|
||||
|
||||
So let's close this up, never touch it again and see what we can
|
||||
do without reading the code or debugging.
|
||||
|
||||
{{< image src="/archiver/meme.jpg" style="border-radius: 8px;" >}}
|
||||
|
||||
## Challenge description
|
||||
|
||||
`reverse` | `490 pts` `5 solves` `:star::star:`
|
||||
|
||||
```
|
||||
Notre équipe SIGINT a intercepté un e-mail contenant une pièce jointe
|
||||
result.fcsc. Vu l'extension, il doit s'agir d'une archive au format
|
||||
propriétaire FCSC. Nous avons pu récupérer l'utilitaire dans sa version
|
||||
Windows, archiver.exe.
|
||||
|
||||
Vu le contenu de l'e-mail, ça a l'air assez important. Est-ce qu'on a une
|
||||
chance de savoir ce qu'il y a dedans ?
|
||||
|
||||
Votre prédécesseur, après avoir réussi une analyse similaire, a sombré
|
||||
dans la folie et réside désormais dans un asile psychiatrique. Il
|
||||
murmurait des mots étranges comme "TTD" et parlait sans arrêt de hardware
|
||||
breakpoint.
|
||||
|
||||
Note : La chaîne que vous trouverez est à mettre entre FCSC{} pour avoir
|
||||
le flag.
|
||||
|
||||
```
|
||||
|
||||
## Given files
|
||||
|
||||
[archiver.exe](/archiver/archiver.exe)
|
||||
|
||||
[result.fcsc](/archiver/result.fcsc)
|
||||
|
||||
# Writeup
|
||||
|
||||
## Overview
|
||||
|
||||
With the binary, we are also given a `result.fcsc`, which is
|
||||
an archive encrypted with the given binary.
|
||||
|
||||
This archive contains the flag and we must decrypt it.
|
||||
|
||||
```console
|
||||
$ xxd result.fcsc
|
||||
00000000: 0100 0000 0000 0000 21e2 ae0f b85f de7b ........!...._.{
|
||||
00000010: b246 ed90 194f 601e 041b 3c8a c6e9 37b1 .F...O`...<...7.
|
||||
00000020: 878b d8e0 e796 a098 1800 0000 0000 0000 ................
|
||||
00000030: d44a b96f ea76 8c55 73a0 7266 1a5e b5fd .J.o.v.Us.rf.^..
|
||||
00000040: c3bf cc29 8ed7 e925 1800 0000 0000 0000 ...)...%........
|
||||
00000050: f96d be51 956d 9342 7e3d 1c0d bbef ad7a .m.Q.m.B~=.....z
|
||||
00000060: bc57 7bf0 f36a cb23 .W{..j.#
|
||||
```
|
||||
|
||||
|
||||
## Common fields
|
||||
|
||||
### Sizes
|
||||
|
||||
First thing we can see are 3 `uint64_t` packed in little endian at offsets:
|
||||
|
||||
* `0x0`
|
||||
* `0x28`
|
||||
* `0x48`
|
||||
|
||||
Given how small these numbers are, they are probably representing
|
||||
some sizes.
|
||||
|
||||
If we count manually, we see that the last two `uint64_t` represent the size of the data that immediatly follows them.
|
||||
|
||||
The first one is still unknown but as it is `1`, it probably just
|
||||
is the number of files in the archive.
|
||||
|
||||
### sha256
|
||||
|
||||
Now let's try to create our own archive:
|
||||
|
||||
```console
|
||||
$ echo -n 'FCSC{test_flag}' > flag.txt
|
||||
$ $ ./archiver.exe create password test.db flag.txt
|
||||
$ xxd test.db
|
||||
00000000: 0100 0000 0000 0000 21e2 ae0f b85f de7b ........!...._.{
|
||||
00000010: b246 ed90 194f 601e 041b 3c8a c6e9 37b1 .F...O`...<...7.
|
||||
00000020: 878b d8e0 e796 a098 1800 0000 0000 0000 ................
|
||||
00000030: 8257 3ccc 2608 498d b6b7 5801 740f 2e4e .W<.&.I...X.t..N
|
||||
00000040: 8b36 0169 9273 2c91 1f00 0000 0000 0000 .6.i.s,.........
|
||||
00000050: a278 0ee8 7308 548a 84af 1ad4 6c0c 0844 .x..s.T.....l..D
|
||||
00000060: de13 a830 ea7f 4d37 19ea 7efe 14b5 c5 ...0..M7..~....
|
||||
```
|
||||
|
||||
Two things are already weird:
|
||||
|
||||
* Everything is identical to the given archive until offset `0x30`
|
||||
* This archive is larger than the given one. Either the flag is really small or data is compressed.
|
||||
|
||||
Let's try to archive a really low entropy file to see if the archive is smaller:
|
||||
|
||||
```console
|
||||
$ echo -n 'aaaaaaaaaaaaaaa' > aaaaaaaa
|
||||
$ ./archiver.exe create password low_entropy.db aaaaaaaa
|
||||
$ xxd low_entropy.db
|
||||
00000000: 0100 0000 0000 0000 1f3c e404 15a2 081f .........<......
|
||||
00000010: a3ee e75f c39f ff8e 56c2 2270 d1a9 78a7 ..._....V."p..x.
|
||||
00000020: 249b 592d cebd 20b4 1800 0000 0000 0000 $.Y-.. .........
|
||||
00000030: b2b6 ed4e 38bb 52b8 5cd7 12a7 7df6 261b ...N8.R.\...}.&.
|
||||
00000040: 2bbf f8df 77c4 dfe8 1f00 0000 0000 0000 +...w...........
|
||||
00000050: b2b6 ed4e 38bb 52b8 8876 7671 b114 9799 ...N8.R..vvq....
|
||||
00000060: cb38 24e6 02f9 26f9 25ec a7b8 bdb9 56 .8$...&.%.....V
|
||||
```
|
||||
|
||||
I put exactly the same sizes in the file name and size data.
|
||||
|
||||
The resulting archive is exactly the same size so no compression
|
||||
is performed.
|
||||
|
||||
But wait ! Something changed !
|
||||
|
||||
In our first archive, the firsts `0x30` bytes where identical
|
||||
but now they differ starting at `0x8`, they differ for `0x20` bytes
|
||||
before becoming the same again on the field we identified as a size.
|
||||
|
||||
Well, we changed two things: the filename and the file data.
|
||||
|
||||
It is unlikely that we guessed the file data on our first try.
|
||||
|
||||
But the filename however ? What if the the format stores a hash
|
||||
of the filename on `0x20` bytes at offset `0x8` ?
|
||||
|
||||
```console
|
||||
$ echo -n 'flag.txt' | sha256sum
|
||||
21e2ae0fb85fde7bb246ed90194f601e041b3c8ac6e937b1878bd8e0e796a098 -
|
||||
```
|
||||
|
||||
Bingo ! It matches the bytes we have in our first custom archive
|
||||
and the one given.
|
||||
|
||||
So we know that the filename is `flag.txt` and that the archives
|
||||
stores the `sha256` of the filename at offset `0x8`.
|
||||
|
||||
### Cipher texts
|
||||
|
||||
I wil now try to play with data sizes to identify what are the sizes in the binary refering to.
|
||||
|
||||
Let's create an archive with a filename 1 byte smaller, and data 1 byte larger:
|
||||
|
||||
```console
|
||||
$ echo -n 'aaaaaaaaaaaaaaaa' > aaaaaaa
|
||||
$ ./archiver.exe create password sizes.db aaaaaaa
|
||||
$ xxd sizes.db
|
||||
00000000: 0100 0000 0000 0000 e462 4071 4b5d b3a2 .........b@qK]..
|
||||
00000010: 3eee 6047 9a62 3efb a4d6 33d2 7fe4 f03c >.`G.b>...3....<
|
||||
00000020: 904b 9e21 9a7f be60 1700 0000 0000 0000 .K.!...`........
|
||||
00000030: 8ab9 acd8 126c cc48 064e 9843 2fa1 9492 .....l.H.N.C/...
|
||||
00000040: 8503 ce34 399c 9820 0000 0000 0000 008a ...49.. ........
|
||||
00000050: b9ac d812 6ccc f370 a362 da68 de94 c7d2 ....l..p.b.h....
|
||||
00000060: aa91 eb29 2be2 0aa3 a74f fd99 4dc0 ca ...)+....O..M..
|
||||
```
|
||||
|
||||
Notice that the first `uint64_t` went from `0x18` to `0x17` and
|
||||
the second one from `0x1f` to `0x20`
|
||||
|
||||
Thus the first size and data correspond to the encrypted file name, and the second one to the encrypted data.
|
||||
|
||||
We can also see that encrypted data is always exactly `0x10` bytes
|
||||
larger that the plaintext. So it probably just adds a `0x10` bytes IV in front of it.
|
||||
|
||||
Looking back at the original archive, we can thus see that the
|
||||
the filename is `0x8` bytes large (which matches the `flag.txt`
|
||||
we found) and the data is also `0x8` bytes large, thus confirming
|
||||
the really small flag. (see below for a reminder of the original
|
||||
archive)
|
||||
|
||||
```console
|
||||
$ xxd result.fcsc
|
||||
00000000: 0100 0000 0000 0000 21e2 ae0f b85f de7b ........!...._.{
|
||||
00000010: b246 ed90 194f 601e 041b 3c8a c6e9 37b1 .F...O`...<...7.
|
||||
00000020: 878b d8e0 e796 a098 1800 0000 0000 0000 ................
|
||||
00000030: d44a b96f ea76 8c55 73a0 7266 1a5e b5fd .J.o.v.Us.rf.^..
|
||||
00000040: c3bf cc29 8ed7 e925 1800 0000 0000 0000 ...)...%........
|
||||
00000050: f96d be51 956d 9342 7e3d 1c0d bbef ad7a .m.Q.m.B~=.....z
|
||||
00000060: bc57 7bf0 f36a cb23 .W{..j.#
|
||||
```
|
||||
|
||||
## Figuring out the crypto
|
||||
|
||||
Now I will try to get a file as close as possible as the original
|
||||
flag and see how the resulting archive behaves to small
|
||||
input mutations:
|
||||
|
||||
```console
|
||||
$ echo -n '12345678' > flag.txt
|
||||
$ ./archiver.exe create password 12345678.db flag.txt
|
||||
$ echo -n '22345678' > flag.txt
|
||||
$ ./archiver.exe create password 22345678.db flag.txt
|
||||
$ xxd 12345678.db
|
||||
00000000: 0100 0000 0000 0000 21e2 ae0f b85f de7b ........!...._.{
|
||||
00000010: b246 ed90 194f 601e 041b 3c8a c6e9 37b1 .F...O`...<...7.
|
||||
00000020: 878b d8e0 e796 a098 1800 0000 0000 0000 ................
|
||||
00000030: 8257 3ccc 2608 498d b6b7 5801 740f 2e4e .W<.&.I...X.t..N
|
||||
00000040: 8b36 0169 9273 2c91 1800 0000 0000 0000 .6.i.s,.........
|
||||
00000050: d509 6e9f 3d4a 06c1 9daa bebe f6cb c23b ..n.=J.........;
|
||||
00000060: cf4e 3d32 2b68 09cf .N=2+h..
|
||||
$ xxd 22345678.db
|
||||
00000000: 0100 0000 0000 0000 21e2 ae0f b85f de7b ........!...._.{
|
||||
00000010: b246 ed90 194f 601e 041b 3c8a c6e9 37b1 .F...O`...<...7.
|
||||
00000020: 878b d8e0 e796 a098 1800 0000 0000 0000 ................
|
||||
00000030: 8257 3ccc 2608 498d b6b7 5801 740f 2e4e .W<.&.I...X.t..N
|
||||
00000040: 8b36 0169 9273 2c91 1800 0000 0000 0000 .6.i.s,.........
|
||||
00000050: d609 6e9f 3d4a 06c1 cc6e be60 dd5d 8214 ..n.=J...n.`.]..
|
||||
00000060: 05bb cadc 0bf1 e4b8 ........
|
||||
```
|
||||
|
||||
|
||||
Most part of the two archives are identical, as expected.
|
||||
|
||||
But maybe a little bit too much identical:
|
||||
|
||||
Look at the IV of the file data's cipher text (offset `0x50`)
|
||||
|
||||
The first 8 bytes are almost identical, only the first one
|
||||
has been increased by one.
|
||||
|
||||
Could it be that the IV is generated with the clear text data ?
|
||||
|
||||
Let's try with an other data:
|
||||
|
||||
```console
|
||||
$ echo -n '32345678' > flag.txt
|
||||
$ ./archiver.exe create password 32345678.db flag.txt
|
||||
$ xxd 32345678.db
|
||||
00000000: 0100 0000 0000 0000 21e2 ae0f b85f de7b ........!...._.{
|
||||
00000010: b246 ed90 194f 601e 041b 3c8a c6e9 37b1 .F...O`...<...7.
|
||||
00000020: 878b d8e0 e796 a098 1800 0000 0000 0000 ................
|
||||
00000030: 8257 3ccc 2608 498d b6b7 5801 740f 2e4e .W<.&.I...X.t..N
|
||||
00000040: 8b36 0169 9273 2c91 1800 0000 0000 0000 .6.i.s,.........
|
||||
00000050: d709 6e9f 3d4a 06c1 fcd2 be2a c42f bdf1 ..n.=J.....*./..
|
||||
00000060: 43e8 9879 eb86 bf95 C..y....
|
||||
```
|
||||
|
||||
Again, patching slightly the `n`th byte of the clear text only
|
||||
patched slightly the `n`th byte of the IV.
|
||||
|
||||
I played with some values and noticed that the operation performed
|
||||
is actually a xor:
|
||||
|
||||
```console
|
||||
>>> 0xd7 ^ ord('3')
|
||||
228
|
||||
>>> 0xd6 ^ ord('2')
|
||||
228
|
||||
>>> 0xd5 ^ ord('1')
|
||||
228
|
||||
```
|
||||
|
||||
So the clear text is xored with a key to generate the IV,
|
||||
but I do not know the said key, which seems to be derived
|
||||
from the archive password.
|
||||
|
||||
## Known plaintext
|
||||
|
||||
Or do I ?
|
||||
|
||||
Remember that I know that filename is `flag.txt`, and that I have
|
||||
an associated ciphertext.
|
||||
|
||||
With a little bit of luck, the IV of the filename cipher text
|
||||
is generated with the key:
|
||||
|
||||
```console
|
||||
>>> 0x82 ^ ord('f')
|
||||
228
|
||||
```
|
||||
|
||||
Looks like it does.
|
||||
|
||||
Since I have a known plaintext, example cipher text.
|
||||
|
||||
I can simply, xor the plaintext with the filename IV to recover
|
||||
the xor key.
|
||||
|
||||
Then apply the same key to the file data IV to recover the
|
||||
plain text:
|
||||
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
from pwn import *
|
||||
filename = b'flag.txt'
|
||||
IV = b'\xd4\x4a\xb9\x6f\xea\x76\x8c\x55'
|
||||
c = b'\xf9\x6d\xbe\x51\x95\x6d\x93\x42'
|
||||
key = xor(IV, filename)
|
||||
flag = xor(c, key)
|
||||
print('FCSC{' + flag.decode() + '}')
|
||||
```
|
||||
|
||||
```console
|
||||
$ ./solve.py
|
||||
FCSC{KKfYQogc}
|
||||
```
|
BIN
jujure/static/archiver/archiver.exe
Executable file
BIN
jujure/static/archiver/archiver.exe
Executable file
Binary file not shown.
BIN
jujure/static/archiver/result.fcsc
Normal file
BIN
jujure/static/archiver/result.fcsc
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user