Writeup: DEF CON 2013 - Thyself

Information

  • Category: reverse

Writeup

usage: ./client 50.16.112.8
http://assets-2013.legitbs.net/liabilities/client

Files

1
2
3
4
5
6
$ file client
client: ELF 32-bit LSB executable,
Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.24,
BuildID[sha1]=4f168c9f9ca5caf20d1382419df3490e22ee910b,
not stripped

Analysis

Following the usage, the client connects to a server:

1
2
3
4
Welcome to the key server.
? for help.
key-server% ?
ls, cat, id, whoami, pwd, aeslite_encrypt

After an unsuccessful must-try command injection, we sniffed some packets only to discover that everything is encrypted!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
00000000 20 00 00 00 ...
00000004 5e e2 91 fd 35 a6 08 5a 8c 9c 41 be 3d 31 67 6e ^...5..Z ..A.=1gn
00000014 cc 62 ad aa eb 3b 92 d7 55 df fd 09 83 c1 6b 34 .b...;.. U.....k4
00000024 10 00 00 00 f6 61 f6 98 62 0d 95 8d 87 ca a5 19 .....a.. b.......
00000034 cf c2 10 f7 10 00 00 00 4c 8d 46 1e 00 e5 52 94 ........ L.F...R.
00000044 d6 89 27 a9 6e 1b 39 16 10 04 00 00 01 16 06 dc ..'.n.9\. .......
00000054 93 c3 ae 74 b8 ef bf f0 43 29 48 68 14 2e 1d 41 ...t.... C)Hh...A
00000064 ff fe 0b 5a 0e 2c 6f b5 66 01 d1 4e 01 b6 59 3e ...Z.,o. f..N..Y>
00000074 14 70 c2 fc 0a 2c d4 f5 7e 53 d9 50 a3 e4 b3 69 .p...,.. ~S.P...i
00000084 ef 55 69 5c 0c 74 a4 e7 eb fb 39 30 0d a5 8b 86 .Ui\.t.. ..90....
00000094 7d 88 96 31 b9 b0 d5 52 e3 02 c8 c1 0d a5 8b 86 }..1...R ........
000000A4 7d 88 96 31 b9 b0 d5 52 e3 02 c8 c1 0d a5 8b 86 }..1...R ........
000000B4 7d 88 96 31 b9 b0 d5 52 e3 02 c8 c1 0d a5 8b 86 }..1...R ........
000000C4 7d 88 96 31 b9 b0 d5 52 e3 02 c8 c1 0d a5 8b 86 }..1...R ........
...
...

Back to the console, the ls command shows that there are two different key-files, a key-server and a key (none of them readable with the cat command).

The aeslite_encrypt command encrypts a given 16-byte message.
We thought that the communication was encrypted with the aeslite algorithm using the key key as pre-shared-key and the challenge goal is to obtain the other key key-server.

1
2
3
4
5
key-server% ls
key-server
key
key-server% aeslite_encrypt AAAAAAAAAAAAAAAA
Here you go: SECRET31b43860ea4bfed82a04e38ae7770c5c

After these speculations, we analyzed the assembly code: the client communicate with the server using the aeslite algorithm and a preshared key: thisisnotthekey!.
The aeslite encryption algorithm is the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
804ae07: 8b 45 08 mov 0x8(%ebp),%eax
804ae0a: 89 04 24 mov %eax,(%esp)
804b217: e8 d8 f9 ff ff call 804abf4 <build_state_array_from_string>
804b21c: 8d 45 e8 lea -0x18(%ebp),%eax
804b21f: 89 04 24 mov %eax,(%esp)
804b222: e8 e2 eb ff ff call 8049e09 <AddRoundKey>
804b227: 8d 45 e8 lea -0x18(%ebp),%eax
804b22a: 89 04 24 mov %eax,(%esp)
804b22d: e8 66 e2 ff ff call 8049498 <SubBytes>
804b232: 8d 45 e8 lea -0x18(%ebp),%eax
804b235: 89 04 24 mov %eax,(%esp)
804b238: e8 1c de ff ff call 8049059 <ShiftRows>
804b23d: 8b 45 10 mov 0x10(%ebp),%eax
804b240: 89 44 24 04 mov %eax,0x4(%esp)
804b244: 8d 45 e8 lea -0x18(%ebp),%eax
804b247: 89 04 24 mov %eax,(%esp)
804b24a: e8 6d d8 ff ff call 8048abc <MixColumns>
804b24f: 8b 45 0c mov 0xc(%ebp),%eax
804b252: 89 44 24 04 mov %eax,0x4(%esp)
804b256: 8d 45 e8 lea -0x18(%ebp),%eax
804b259: 89 04 24 mov %eax,(%esp)
804b25c: e8 87 fa ff ff call 804ace8 <build_string_from_state_array>

Assuming that the AddRoundKey, SubBytes, ShiftRows and MixColumns work like in AES, aeslite provides a faster and unsafe version.
In fact, knowing both the plaintext and the ciphertext, the following operation returns the key:

1
InverseSubBytes(InverseShiftRows(InverseMixColumns(chipertext))) ^ plaintext = key

First of all we obtained the pre-shared key from the client:

1
2
3
4
5
$ readelf -a client | grep key
67: 0804e680 17 OBJECT GLOBAL DEFAULT 24 key
$ objdump -s client | grep 804e680 -A1
804e680 74686973 69736e6f 74746865 6b657921 thisisnotthekey!
804e690 00000000 ....

Thanks to Wikipedia
(
AES,
Sbox,
MixColumn
), we implemented the aeslite operations in python, testing it with the sniffed packets, which are encrypted with the key thisisnotthekey!.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def exploit(plaintext, chipertext):
tmp = prepareData(chipertext)
tmp = doInverseMixColumn(tmp)
tmp = doInverseShift(tmp)
tmp = doInverseSub(tmp)
key = getKey(tmp, prepareData(plaintext))

return key

def getKey(data, res):
key = []
for d,r in zip(data, res):
key.append(d ^ r)
return key

plaintext = "Here you go: SECRET31b43860ea4bfed82a04e38ae7770c5c"[:16]
chipertext = "0bdbf8cb5da0425542629d95ac4dd6e0".decode('hex')
print exploit(plaintext, chipertext)
# thisisnotthekey!

After the test, we used the script to recover the key used by the aeslite_encrypt command.

1
2
3
4
5
plaintext = "AAAAAAAAAAAAAAAA"
chipertext = "SECRET31b43860ea4bfed82a04e38ae7770c5c"[-32:].decode('hex')
print exploit(plaintext, chipertext)

# DamnIhatecrypto!

The key is DamnIhatecrypto!, but it’s not the challenge solution.

Analyzing the sniffed packets, we saw that the server sent a lot of data at the beginning, compared to the rest of the communication:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
S 00000000 20 00 00 00 ...
S 00000004 5e e2 91 fd 35 a6 08 5a 8c 9c 41 be 3d 31 67 6e ^...5..Z ..A.=1gn
S ...
S 00000434 7d 88 96 31 b9 b0 d5 52 e3 02 c8 c1 0d a5 8b 86 }..1...R ........
S 00000444 7d 88 96 31 b9 b0 d5 52 e3 02 c8 c1 0d a5 8b 86 }..1...R ........
S 00000454 7d 88 96 31 b9 b0 d5 52 e3 02 c8 c1 }..1...R ....
C 00000000 30 00 00 00 0...
C 00000004 b1 c9 4e fd 2c 9e 2f 4b 52 c9 88 6e 30 f1 43 47 ..N.,./K R..n0.CG
C 00000014 27 f4 8f 9c ed 29 c9 8b 68 97 9b c1 06 bd 30 e8 '....).. h.....0.
C 00000024 cf c4 ea 25 7d 88 96 31 b9 b0 d5 52 e3 02 c8 c1 ...%}..1 ...R....
S 00000460 40 00 00 00 @...
S 00000464 0b db f8 cb 5d a0 42 55 42 62 9d 95 ac 4d d6 e0 ....].BU Bb...M..
S 00000474 a2 f6 5a b1 06 d1 18 2f 57 b3 68 19 60 76 7d 09 ..Z..../ W.h.`v}.
S 00000484 b2 37 07 99 b7 90 d9 4b ed af b0 e4 50 17 76 58 .7.....K ....P.vX
S 00000494 c9 c7 e9 20 44 b1 dd 43 27 09 f2 cc f8 10 c1 c8 ... D..C '.......
S 000004A4 10 00 00 00 4c 8d 46 1e 00 e5 52 94 d6 89 27 a9 ....L.F. ..R...'.
S 000004B4 6e 1b 39 16 n.9.
C 00000034 10 00 00 00 ....
C 00000038 e4 5c 72 96 33 c0 4a a3 23 05 fa c8 db db 29 20 .\r.3.J. #.....)
S 000004B8 20 00 00 00 ...
S 000004BC a8 f9 a2 39 bc ea d9 88 1e 3a 43 7b 01 c3 2a f9 ...9.... .:C{..*.
S 000004CC f5 25 d3 fe 82 4b 6b 90 bc fe 8c 31 40 3b 52 ea .%...Kk. ...1@;R.

Why that data wasn’t displayed? The solution lies in the main code.
After receiving a message, the client checks if the message starts with the string SECRET (0x804bc7a).
In such case, it stores the message in 0x804e6e0, without printing it to the standard output.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
80489ae: e8 df 2f 00 00 call 804b992
...
80489d2: 8d 84 24 c4 00 00 00 lea 0xc4(%esp),%eax
80489d9: 89 c2 mov %eax,%edx
80489db: b8 7a bc 04 08 mov $0x804bc7a,%eax
80489e0: b9 06 00 00 00 mov $0x6,%ecx
80489e5: 89 d6 mov %edx,%esi
80489e7: 89 c7 mov %eax,%edi
80489e9: f3 a6 repz cmpsb %es:(%edi),%ds:(%esi)
80489eb: 0f 97 c2 seta %dl
80489ee: 0f 92 c0 setb %al
80489f1: 89 d1 mov %edx,%ecx
80489f3: 28 c1 sub %al,%cl
80489f5: 89 c8 mov %ecx,%eax
80489f7: 0f be c0 movsbl %al,%eax
80489fa: 85 c0 test %eax,%eax
80489fc: 75 46 jne 8048a44 <main+0x2f0>
80489fe: ba e0 e6 04 08 mov $0x804e6e0,%edx
8048a03: b8 00 00 00 00 mov $0x0,%eax
8048a08: b9 01 01 00 00 mov $0x101,%ecx
8048a0d: 89 d7 mov %edx,%edi
8048a0f: f3 ab rep stos %eax,%es:(%edi)
...
8048a3f: e9 2e fe ff ff jmp 8048872 <main+0x11e>
8048a44: b8 81 bc 04 08 mov $0x804bc81,%eax
8048a49: 8d 94 24 c4 00 00 00 lea 0xc4(%esp),%edx
8048a50: 89 54 24 04 mov %edx,0x4(%esp)
8048a54: 89 04 24 mov %eax,(%esp)
8048a57: e8 24 fb ff ff call 8048580 <printf@plt>
...

By debugging or patching the program or decoding the sniffed packet, comes out that the server sends a string to the client (we have encoded the raw byte for the sake of printability :D )

1
SECRETd1e70b1c3496ee591dcb51c64abf9c946411dee67c1722f4479342193cc746759cbc36a69d1b78337b86beae34acb003

Maybe this is encrypted with the key DamnIhatecrypto!.

We used the previous python script to decrypt the message:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def decrypt(chipertext, key):
tmp = prepareData(chipertext)
tmp = doInverseMixColumn(tmp)
tmp = doInverseShift(tmp)
tmp = doInverseSub(tmp)
return addRoundKey(tmp, key)

plaintext = "A"*16
chipertext = "31b43860ea4bfed82a04e38ae7770c5c".decode('hex')
key = exploit(plaintext, chipertext)

SECRETS = ["d1e70b1c3496ee591dcb51c64abf9c94".decode('hex'),
"6411dee67c1722f4479342193cc74675".decode('hex'),
"9cbc36a69d1b78337b86beae34acb003".decode('hex')]
final = ""
for s in SECRETS:
final += ''.join([chr(c) for c in decrypt(s, key)])
print final

The result The key is: Good enough for government work was the challenge solution.

Flag

1
The key is: Good enough for government work