# randomwareSECCON 2016 quals ## description300 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)  Challenge files is huge, please download it first. Password will release after 60min.  password: h9nn4c2955kik9qti9xphuxti
## Solution### BootFirst 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 bootloaderthat 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)  
### ExtractI 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 usingbinwalk to extract the files. First interesting thing was `23D70D`, which is anELF. 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 squashfsdirectories, but there wasn't anything all that interesting in there (exceptthat firefox-esr is installed). I ended up doing `grep SECCON . -r` which hasexactly one result: 512CE00, a tar. Extracting that gives `opt/` and the veryinteresting `home/` directory.
### Reversein `home/tc/` there is an ELF `getflag` and an encrypted`h1dd3n_s3cr3t_f14g.jpg`. I resisted the temptation to run `getflag` on my hostand instead dropped it into IDA Pro. It's small and not stripped with only 5non-library functions, and only 3 functions that really matter: `main`,`recdir`, and `encrypt`.
#### main![https://puu.sh/sLgRp.png](https://puu.sh/sLgRp.png)
1. Downloads flag from a local server2. 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`
#### recdirWalks the filesystem from `/home/` and calls `encrypt` on files that have anextension in the whitelist which is pdf, xml, bin, txt, or various image andoffice types.
#### encryptEncrypts 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 justhave to find some plaintext. We know the header of a JPEG, so we can get thefirst few bytes of the key, but that's not enough.
#### mime-typesFortunately, there are also hidden files in the home directory. Searching forwhitelisted file extensions gives some mime-type XML files, and `blocklist.xml`and `revocations.txt` in a Firefox profile. The mime-type files were all < 1024bytes, 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 matchingsize, 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 toview 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.xmlAfter some research I discovered that `blocklist.xml` is updated once a day.The one in the challenge was last modified Nov 28, and mine was last modifiedDec 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 requesthistorical versions of the file. I spent a fair amount of time on that beforegiving up and going the more effort way: since XML has a standard format, youcan guess a few characters after what's decrypted, use that to get a few morebytes of the key, and repeat for any index multiple of 1024 in the file.
#### ExampleWe have a plaintext mime-type file and a ciphertext mime-type file which we xorto get the first 254 bytes of the key. xoring the partial key with the start ofthe encrypted `blocklist.xml` gives:```xml
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1456184414000">  <emItems>      <emItem  blockID="i58" id="[email protected]">                        <versionRange  minVersion="0" maxVersion=```We assume that the value of `maxVersion` is a string (to match `minVersion` andbecause it's that way in the newest version of the file), so we now have 255plaintext bytes which we can xor with the ciphertext and get 255 bytes of thekey. Next, we shift to index 1024 in the ciphertext (since the xor cipher has ablock 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 knowwhat 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 andfrom appending a hidden newline to the end of the file.
#### CodeI scripted as much as possible, but it still requires manual intervention oneach iteration.```python#!/usr/bin/python2
def xor(xs, ys):	return "".join([chr(ord(x) ^ ord(y)) for x, y in zip(xs, ys)])
def fread(path):	with open(path) as f:		return f.read()
def fwrite(path, data):	with open(path, "w") as f:		return f.write(data)
gooduserxml = fread("user-extension-xhtml.xml")baduserxml = fread("home/tc/.local/share/mime/packages/user-extension-xhtml.xml")badflag = fread("home/tc/h1dd3n_s3cr3t_f14g.jpg")badblocklist = fread("home/tc/.mozilla/firefox/wir5mrmb.default/blocklist.xml")# If working key is there, use it, else calculate first 254 bytestry:	key = fread("key")except IOError:	key = xor(gooduserxml, baduserxml)
i = 0while len(key) != 1024:	fwrite("partialblocklist.xml", xor(badblocklist[i:i+len(key)], key))	# Append to partialblocklist.xml, and then press enter	raw_input("cur key len: " + str(len(key)))
	partialblocklist = fread("partialblocklist.xml")	key = xor(partialblocklist, badblocklist[i:i+len(partialblocklist)])[:1024]	fwrite("key", key)	i = i + 1024 if i < len(badblocklist) - 2048 else 0
fwrite("flag.jpg", xor(badflag, key))```
### FlagPicture of some meat with text `SECCON{This is Virtual FAT too}`