Derbycon 2012 CTF – Crypto Challenge

This was one of my favorite challenges at this year’s CTF. Thanks to the guys who put it on, it was a load of fun and I appreciate all the hard work I know that goes into this sort of thing.

The Challenge

Posted on one of the websites was the file fu2.zip which contained the following files:

zoidberg% unzip fu2.zip -d ./fu2
zoidberg% cd fu2
zoidberg% file ./*
./BouncyCastle.Crypto.dll: PE32 executable for MS Windows (DLL) (console) Intel 80386 32-bit Mono/.Net assembly
./FileEncryptor.exe:       PE32 executable for MS Windows (console) Intel 80386 32-bit Mono/.Net assembly
./plain1_encrypted:        data
./plain2:                  ASCII text, with no line terminators
./plain2_encrypted:        data
zoidberg% ls -al ./plain*
-rw-r--r--  1 mgill  staff  128 Sep 24 13:14 ./plain1_encrypted
-rw-r--r--  1 mgill  staff  112 Sep 17 06:18 ./plain2
-rw-r--r--  1 mgill  staff  128 Sep 24 13:14 ./plain2_encrypted

With the following contents of plain2:

zoidberg% cat plain2
KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57KC57

There are a few clues here if you just go off file names and file sizes, but let’s take the easy route and simply decompile the .NET binary. A quick google search on the md5sum of the BouncyCastle lib tells us it’s unlikely it has been modified so we only want to see what FileEncryptor.exe does. My favorite .net decompiler is dotPeek, and lucky us .net allows for some awesome code generation.

The view from dotPeek

After looking through the code we get a better idea on what is going on. We pass in a password, a plain text file, and a output file. The program then encrypts the plain text file with the password, to produce the output. Here are the interesting functions:

public static void EncryptFile(string password, string inFile, string outFile)
{
    string salt = "s@1tValue";
    string s = "1234567890123456";
    int keySize = 128;
    byte[] plain = Aes.readBytesFromFile(inFile);
    byte[] bytes1 = Encoding.ASCII.GetBytes(s);
    byte[] key = Aes.GenerateKey(password, salt, keySize);
    byte[] bytes2 = Aes.Encrypt(plain, key, bytes1);
    Aes.writeBytesToFile(outFile, bytes2);
}
...
public static byte[] Encrypt(byte[] plain, byte[] key, byte[] iv)
{
    PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher((IBlockCipher)new OfbBlockCipher((IBlockCipher)new AesEngine(), 128));
    ICipherParameters parameters = (ICipherParameters)new ParametersWithIV((ICipherParameters)new KeyParameter(key), iv);
    cipher.Init(true, parameters);
    return Aes.cipherData(cipher, plain);
}

Now we have all the key pieces of information. Our plain2 file, which is 112 bytes of repeating characters (‘KC57’*28) produces plain2_encrypted, at 128 bytes. We have plain1_encrypted, but no plain1. A brute force attack here seems a bit inelegant not to mention being the ballers we are we don’t have time for that shit.

Notice that the password is seeded with a salt and the IV is static. Generally speaking the IV should be a noonce value to stop different types of attacks against block encryptions. Static IV’s are bad. The Encrypt() function above shows that we’re using the OfbBlockCipher or Output-FeedBack. OFB is a stream cipher which generally means the keystream is xor’d with the plaintext value to produce the output. Really what we’re interested in is attacks against OFB, especially when the programmer makes the mistake of using a static IV.

All the stars align. We have a static IV making the keystreams identical for each encryption and a plaintext and encrypted value pair. This means we can decrypt the plain1_encrypted file without ever needing to know the key!  Simply compute the value of the keystream by xoring the plain2 against the encrypted plain2_encrypted, and apply that keystream to the plain1_encrypted. Voila! Here is a bit of python code to get the job done

import itertools

#take in the plain1 encrypted file and convert to ints
p1e = open('plain1_encrypted','r').read()
p1e_ord = map(ord, p1e)

#take in the plain2 encrypted file and convert to ints
p2e = open('plain2_encrypted','r').read()
p2e_ord = map(ord, p2e)

#take in the plain2 file and convert to ints
p2 = open('plain2','r').read()
p2_ord = map(ord, p2)

keystream = []
for x,y in itertools.izip_longest(p2_ord, p2e_ord, fillvalue=0):
	keystream.append(x^y)

final = []
for x,y in zip(p1e_ord, keystream):
	final.append(chr(x^y))

print ''.join(final)

And if you run it…

zoidberg% python decrypt.py      
KC57KC57KC57KC57FLAG=DecryptTheFlagToGetTheKeyLolKC57KC57KC?KC57KC57KC57KC57KC57

Success, the flag has been disclosed! While this is a workable solution, I’m not exactly sure how the bytes are properly padded (instead of using 0 in my case) nor am I sure why there are unprintable characters in the decoded stream. You can see them by directly printing the repr of the finalResult object. But it works and the flag is decoded. If you have any input please throw it my way, it would be nice to know if there is any way to improve on it all.

Thanks!

tl;dr static iv’s are bad and break your crypto

Posted in crypto | Tagged , , | Leave a comment

Install git && gitolite on CentOS 6

Goal

Install git and gitolite on a Centos 6 server using ssh. gitolite is being used here because gitosis is no longer supported.

Steps

Install the git and gitolite packages, add the gitolite user. Generate or use your pub/priv keychain to enable passwordless logins for SSH and add your pub key to the gitolite users authorized keys. Clone the gitolite adminitrative configs and configure for administration.

Guide

Install git

[root@box]# yum install git*

Then, enable EL repo for gitolite, and install gitolite

[root@box]# yum install gitolite
Next, setup the gitolite user and home directory. This must be done in a SELinux safe directory, so use /var/lib/gitolite or have a headache.
[root@box]# usermod -d /var/lib/gitolite gitolite

Generate your pub/private keypair to use as passwordless SSH entry

[user@box]$ ssh-keygen -t rsa -b 2048 -C $(whoami)
 ssh-keygen -t rsa -b 2048 -C $(whoami)
Generating public/private rsa key pair.
Enter file in which to save the key (/home/mgill/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/mgill/.ssh/id_rsa.
Your public key has been saved in /home/mgill/.ssh/id_rsa.pub.
The key fingerprint is:
8d:9d:6b:21:c8:8a:df:e4:ec:e6:f5:87:41:18:2b:41 mgill
The key's randomart image is:
+--[ RSA 2048]----+
|     .E          |
|      . .        |
|       . +       |
|     ...o+..     |
|      o.S.=      |
|   . .   ..o     |
|  . . . . oo     |
|   . =.. o. .    |
|    .+*   ..     |
+-----------------+
[user@box]$
[user@box]$ cd ~/.ssh/
[user@box]$ pwd
/home/user/.ssh
[user@box]$ file id_rsa.pub 
id_rsa.pub: ASCII text, with very long lines

After creating your private key (~/.ssh/id_rsa) and your public key (~/.ssh/id_rsa.pub), get it on your server. Note: don’t move or copy your private key unless you know the risks

[user@box]$ scp ~/.ssh/id_rsa.pub sshUserName@git.box:/tmp/`whoami`.pub

Now, on your server, we will setup gitolite by changing your user and installing the public key we just uploaded. You do not need to modify the gitolite config

[user@box]$ sudo su - gitolite
[sudo] password for user: 
-sh-4.1$ pwd
/var/lib/gitolite
-sh-3.2$ gl-setup /tmp/user.pub

Verify that the key was installed correctly and you’re ready to move forward. By default everything should be good to go on CentOS 6, mileage *may* vary on different distros.

[user@box]$ ssh gitolite@git.box info
hello user, this is gitolite 2.3.1-1.el6 running on git 1.7.1
the gitolite config gives you the following access:
     R   W 	gitolite-admin
    @R_ @W_	testing

Make sure to configure git before you use it

[user@box]$ git config --global user.name "ModifyMyUser"
[user@box]$ git config --global user.email "fakeEmailAddress@no.where"

Now clone your gitolite-admin repo to manage it

[user@box]$ git clone gitolite@git.box:gitolite-admin
loning into 'gitolite-admin'...
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
Receiving objects: 100% (6/6), done.
remote: Total 6 (delta 0), reused 0 (delta 0)

Now read http://sitaramc.github.com/gitolite/admin.html and have fun!

Posted in systems | 2 Comments