Rating:

# randomware
SECCON 2016 quals
## description
300 points
My PC suddenly got broken. Could you help me to recover it please?
NOTE: The disk can be virus-infected. DO NOT RUN any programs extracted from the disk outside of sandbox.
[disk.qcow2.zip](http://files.quals.seccon.jp/disk.qcow2.zip)

## Solution
### Boot
First thing I did was boot up the image. I used vmware with
qemu-img convert -f qcow2 -O vmdk disk.qcow2. It's some custom bootloader
that starts with
![https://puu.sh/sLf1F.png](https://puu.sh/sLf1F.png)
and then shows a rainbow:
![https://puu.sh/sLf2c.png](https://puu.sh/sLf2c.png)

### Extract
I converted qcow image to a raw disk image:
qemu-img convert -f qcow2 -O raw disk.qcow2 disk.raw.
Disk tools didn't work since the MBR was overwritten, so I ended up using
binwalk to extract the files. First interesting thing was 23D70D, which is an
ELF. Looking at strings suggest that it's a Linux kernel, specifically:
Linux version 4.2.9-tinycore (tc@box) (gcc version 5.2.0 (GCC) ) #1999 SMP Mon Jan 18 19:42:12 UTC 2016.
There also looks like a dump of the fs, split across multiple squashfs
directories, but there wasn't anything all that interesting in there (except
that firefox-esr is installed). I ended up doing grep SECCON . -r which has
exactly one result: 512CE00, a tar. Extracting that gives opt/ and the very
interesting home/ directory.

### Reverse
in home/tc/ there is an ELF getflag and an encrypted
h1dd3n_s3cr3t_f14g.jpg. I resisted the temptation to run getflag on my host
and instead dropped it into IDA Pro. It's small and not stripped with only 5
non-library functions, and only 3 functions that really matter: main,
recdir, and encrypt.

#### main
![https://puu.sh/sLgRp.png](https://puu.sh/sLgRp.png)

2. Calls get_root_dev (just stat("/home/", root_dev) for some reason)
3. Calls get_key (sets encrypt_key to 1024 bytes from /dev/urandom)
4. Calls recdir("/home/tc")
5. Writes bootloader from memory to /dev/sda

#### recdir
Walks the filesystem from /home/ and calls encrypt on files that have an
extension in the whitelist which is pdf, xml, bin, txt, or various image and
office types.

#### encrypt
Encrypts files with repeated xor key (length 1024). Encryption loop:
![https://puu.sh/sLi7G.png](https://puu.sh/sLi7G.png)

### Decrypt
(Brief description of xor: 1^0==1, 0^1==1, 0^0==0, 1^1==0.
ciphertext = key ^ plaintext but also key = plaintext ^ ciphertext)
So, xor block ciphers are vulnerable to known-plaintext attacks, and we just
have to find some plaintext. We know the header of a JPEG, so we can get the
first few bytes of the key, but that's not enough.

#### mime-types
Fortunately, there are also hidden files in the home directory. Searching for
whitelisted file extensions gives some mime-type XML files, and blocklist.xml
and revocations.txt in a Firefox profile. The mime-type files were all < 1024
bytes, with the longest, .local/share/mime/packages/user-extension-xhtml.xml,
being 254 bytes. I happen to have the same file on my computer with a matching
size, so by xoring the two files I got the first 254 bytes of the key (since
ciphertext ^ plaintext = key). Around 1/4 of the key still isn't enough to
view the image, though, so I moved on to the Firefox files.
xml

<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-extension-xhtml">
<comment>xhtml document</comment>
<glob pattern="*.xhtml"/>
</mime-type>
</mime-info>


#### blocklist.xml
After some research I discovered that blocklist.xml is updated once a day.
Dec 10 with a significantly different size. From about:config I found
https://blocklist.addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/,
but I don't know valid arguments, and it looks like there's no way to request
historical versions of the file. I spent a fair amount of time on that before
giving up and going the more effort way: since XML has a standard format, you
can guess a few characters after what's decrypted, use that to get a few more
bytes of the key, and repeat for any index multiple of 1024 in the file.

#### Example
We have a plaintext mime-type file and a ciphertext mime-type file which we xor
to get the first 254 bytes of the key. xoring the partial key with the start of
the encrypted blocklist.xml gives:
xml

<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion=

We assume that the value of maxVersion is a string (to match minVersion and
because it's that way in the newest version of the file), so we now have 255
plaintext bytes which we can xor with the ciphertext and get 255 bytes of the
key. Next, we shift to index 1024 in the ciphertext (since the xor cipher has a
block size of 1024) and xor 255 bytes with the key to get more plaintext:
xml
<versionRange minVersion="0" maxVersion="*" severity="1">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i105" id="{95ff02bc-ffc6-45f0-a5c8-619b8226a9de}">


(there's a newline and a bunch of spaces at the end there) Sadly, we don't know
what comes next this time, so we move on to index 2048.

We repeat until we get the key to length 1024.

Note: if you're doing this in vim, you'll want these options: :set binary and
:set noendofline. They will keep vim from messing with tabs and spaces and
from appending a hidden newline to the end of the file.

#### Code
I scripted as much as possible, but it still requires manual intervention on
each iteration.
python
#!/usr/bin/python2

def xor(xs, ys):
return "".join([chr(ord(x) ^ ord(y)) for x, y in zip(xs, ys)])

with open(path) as f:

def fwrite(path, data):
with open(path, "w") as f:
return f.write(data)

# If working key is there, use it, else calculate first 254 bytes
try:
except IOError:

i = 0
while len(key) != 1024:
# Append to partialblocklist.xml, and then press enter
raw_input("cur key len: " + str(len(key)))


Picture of some meat with text SECCON{This is Virtual FAT too}