Agent Log

June 17th, 2014

We are given an encrypted file (agent_log.enc), it’s corresponding plaintext (agent_log.txt), and the encryption tool source code (crypt.c). We have to find the key.

encrypt and decrypt functions:

int decrypt(const unsigned char *key, const char *infile, const char *outfile)
{
	unsigned char buf[16];
	int result = 0, read_count = 0;
	FILE *fi = NULL, *fo = NULL;

	fail_null(fi, fopen, infile, "rb");
	fail_null(fo, fopen, outfile, "wb");

	while ((read_count = fread(buf, 16, 1, fi)) == 1) {
		permute(buf, iP2);
		substitute(buf, iS2);
		xor(buf, key);
		permute(buf, iP1);
		substitute(buf, iS1);
		if (fwrite(buf, 16, 1, fo) != 1)
			fail_not_zero(ferror, fo);
	}

	fail_not_zero(ferror, fi);

done:
	if (fi) {
		fclose(fi);
		fi = NULL;
	}
	if (fo) {
		fclose(fo);
		fo = NULL;
	}

	return result;
}

int encrypt(const unsigned char *key, const char *infile, const char *outfile)
{
	unsigned char buf[16];
	int result = 0, read_count = 0;
	FILE *fi = NULL, *fo = NULL;

	fail_null(fi, fopen, infile, "rb");
	fail_null(fo, fopen, outfile, "wb");

	while ((read_count = fread(buf, 16, 1, fi)) == 1) {
		substitute(buf, S1);
		permute(buf, P1);
		xor(buf, key);
		substitute(buf, S2);
		permute(buf, P2);
		if (fwrite(buf, 16, 1, fo) != 1)
			fail_not_zero(ferror, fo);
	}

	fail_not_zero(ferror, fi);

done:
	if (fi) {
		fclose(fi);
		fi = NULL;
	}
	if (fo) {
		fclose(fo);
		fo = NULL;
	}

	return result;
}

The encrypt and decrypt functions process the data in 16 byte blocks. There are 2 permutation functions, 2 substitution functions and an inverse for each function. We introduce some new notation below:

P_1(x) denotes permute(x, P1);
P_1'(x) denotes permute(x, iP1);
P_2(x) denotes permute(x, P2);
P_2'(x) denotes permute(x, iP2);
S_1(x) denotes substitute(x, S1);
S_1'(x) denotes substitute(x, iS1);
S_2(x) denotes substitute(x, S2);
S_2'(x) denotes substitute(x, iS2);
K' denotes the key
E(x, K) denotes encryption of a single block (16 bytes)
D(x, K) denotes decryption of a single block (16 bytes)
\oplus denotes the XOR operation

With our new notation we then have:

E(x, K) = P_2(S_2(K \oplus P_1(S_1(x)))) \newline  D(x, K) = P_1'(S_1'(K \oplus S_2'(P_2'(x))))

Where the inverse functions work as follows:

P_1'(P_1(x)) = x \newline  P_2'(P_2(x)) = x \newline  S_1'(S_1(x)) = x \newline  S_2'(S_2(x)) = x

If we start decrypting ‘agent_log.enc’ we get:

S_2'(P_2'( ciphertext )) = \newline  S_2'(P_2'( P_2(S_2(K \oplus P_1(S_1(plaintext)))) ))

Simplifying we get:

intermediate\_decrypt = K \oplus P_1(S_1(plaintext))

We just applied two inverse functions to the ciphertext! In other words we undid the last 2 operations of the encryption.

If we start encrypting ‘agent_log.txt’ we get:

intermediate\_encrypt = P_1(S_1(plaintext))

The most important observation here is that none of the steps in the intermediate_encrypt and intermediate_decrypt require knowledge of the key.

If we XOR intermediate_encrypt and intermediate_decrypt we get:

P_1(S_1(plaintext)) \oplus (K \oplus P_1(S_1(plaintext))) = K

The first and last term cancel out, thus leaving us with the key.

We can actually do it in practice with very slight modifications to crypt.c, as below:

$ diff --unified crypt_old.c crypt.c
--- crypt_old.c 2014-06-17 04:41:36.000000000 +0000
+++ crypt.c     2014-06-17 04:44:58.000000000 +0000
@@ -69,6 +69,8 @@
        while ((read_count = fread(buf, 16, 1, fi)) == 1) {
                permute(buf, iP2);
                substitute(buf, iS2);
+               /* dump intermediate_decrypt bytes for first block */
+               fwrite(buf, 16, 1, stdout); exit(1);
                xor(buf, key);
                permute(buf, iP1);
                substitute(buf, iS1);
@@ -103,6 +105,8 @@
        while ((read_count = fread(buf, 16, 1, fi)) == 1) {
                substitute(buf, S1);
                permute(buf, P1);
+               /* dump intermediate_encrypt bytes for first block */
+               fwrite(buf, 16, 1, stdout); exit(1);
                xor(buf, key);
                substitute(buf, S2);
                permute(buf, P2);

Then we run it:

$ gcc crypt.c -w -o crypt
$ ./crypt -e dummykey agent_log.txt /dev/null > intermediate_encrypt
$ ./crypt -d dummykey agent_log.enc /dev/null > intermediate_decrypt
$ python
Python 2.7.3 (default, Sep 26 2013, 20:08:41)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> xor = lambda a, b: ''.join(chr(ord(x) ^ ord(y)) for x, y in zip(a, b))
>>> xor(open('intermediate_encrypt').read(), open('intermediate_decrypt').read())
'ShallWePlayAGame'

Shoutouts to Lopi, PenguinsHateFlags.

~ X-N2O

DEFCON 21 CTF Quals `linked` writeup

June 17th, 2013

linked was a shellcoding challenge worth 3 points at DEFCON 21 CTF Quals

http://assets.shallweplayaga.me/linked.txt
Running at linked.shallweplayaga.me:22222 OR linked2.shallweplayaga.me:22222

linked.txt:

 typedef struct _llist {
   struct _llist *next;
   uint32_t tag;
   char data[100];
 llist;

and:

register char *answer;
char *(*func)();
llist *head;
...
func = (char *(*)(llist *))userBuf;
answer = (char *)(*func)(head);
send_string(answer);
exit(0);

Write me shellcode that traverses the randomly generated linked list, looking for a node with a tag 0x41414100, and returns a pointer to the data associated with that tag, such that the call to send_string will output the answer.

Tests showed that linked was running on x86 – and by tests I mean “\xeb\xfe” :P. After trying to write and optimize shellcode for doing the job, I thought of an easier attack vector. Instead of writing the shellcode to find the node with the given tag, I thought of scanning memory in order to find the key directly (since all keys start with “The key is:”), much like an egg hunter. The shellcode turned out to be a whole lot shorter than the limit (a whole 2 bytes :).

Code:

bits 32

pop edx
pop eax
mov ebx, 'The '

_loop:
    inc eax
    cmp [eax], ebx
    jnz _loop

jmp edx

; yep, i can afford nops
nop
nop

See it in action:

x-n2o@istari:~$ nasm linked.asm -o linked
x-n2o@istari:~$ nc linked.shallweplayaga.me 22222 < linked
List built.  Send me your shellcode.  Max size: 16
The key is: Who says ESP isn't general purpose!?!?

~ X-N2O

‘Positive’ – BaltCTF 2013

May 14th, 2013

‘Positive’ (ppc task) was worth 300 points at BaltCTF 2013. I decided to solve it using the Z3 SAT solver. ‘数独’, the sudoku task, was solved similarly.

Read the rest of this entry »

DEFCON 20 CTF Quals B400 writeup

June 5th, 2012

This weekend I had the best time playing the DEFCON 20 CTF Quals with Sapheads. My favorite challenges were B400 (for which this writeup is about) and PP300, where t1g3r and I had to come up with some shellcode designed to be resistant to sorting. We did good overall and ended up 33rd.
Read the rest of this entry »

Huffman encoder in 8086 ASM

August 24th, 2010

While working on my 8086 emulator, I figured I might write something nice in 8086 assembly as well. This is what I came up with. Read the rest of this entry »

Clever tricks against antiviruses

April 19th, 2010

I bet you have come across some software you’ve made which you didn’t want the AV to pick up. This article explains how to import from DLLs without having to call GetProcAddress, and also how to encrypt your data section. Anti-viruses rely heavily on their heuristics, if all other (signature) scans fail. The patterns they search for in your executable, are the functions being imported, and the order they are being called. Read the rest of this entry »

AES Explained

November 22nd, 2009

Hello people,
It’s been a while since I have last posted an article. I decided to write an article about the Advanced Encryption Standard. I will explain certain concepts regarding AES and how it basically works. I will provide step by step C code, to make it even easier to understand. You can find the full source code at the end of this article. Actually many websites around the net provide source code for AES. This one is supposed to be easy to understand ;) Read the rest of this entry »

Multithreading

March 19th, 2009

Making an application multithreaded means having several threads,
several functions running at the same time. This may look simple,
and not complicated, but there are certain ‘problems’ that could appear.
The most important factor of multithreading is synchronization!
Read the rest of this entry »