[VolgaCTF 2017 Quals] - KeyPass 100

題目資訊

Category: Crypto + Reverse
Point: 100
Description:
For reasons unknown an amature cryptographer wrote an application to generate “strong encryption keys”. One of these keys was used to encrypt a tar archive with the flag. They used openssl command line util with -aes-128-cbc. Could you please get the flag? It shouldn’t take much time…
flag.zip.enc
keypass

Hints

$ openssl
OpenSSL> version
OpenSSL 1.1.0e 16 Feb 2017

解法

先是把keypass丟進IDA pro看看,看起來要解析似乎有點麻煩,之後就直接跑keypass看看了。

1
2
3
4
$ ./keypass
Usage:
keypass <pass_phrase>
<pass_phrase> that one secret phrase only you could have come up with

看來程式是會吃一個argument當pass_phrase,之後把pass_phrase轉變成要丟進openssl的passphrase。
稍微測測看幾個pass_phrase:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ./keypass a
cq5$%IN1MLc(>F<Ol
$ ./keypass aa
BWf81zF%SdkxL*jL.
$ ./keypass aaa
cq5$%IN1MLc(>F<Ol
$ ./keypass aaaa
BWf81zF%SdkxL*jL.
$ ./keypass abab
BWf81zF%SdkxL*jL.
$ ./keypass ababa
cq5$%IN1MLc(>F<Ol

歸納一下:

1
2
keypass('a') = keypass('aaa') = keypass('ababa') = cq5$%IN1MLc(>F<Ol
keypass('aa') = keypass('aaaa') = keypass('abab') = BWf81zF%SdkxL*jL.

咦!看起來似乎是把字串內的所有字做了xor運算,再根據運算結果輸出一個passphrase,再來驗證一下:

1
2
3
4
5
6
7
8
9
ord('a') ^ ord('b') = 3
$ ./keypass ab
s+Tq4OY=dfK:9+.(,
$ ./keypass $(python -c "print chr(3)")
s+Tq4OY=dfK:9+.(,
keypass('ab') = keypass(chr(3))

驗證成功!所以只要跑keypass(chr(0)) ~ keypass(chr(255)),就可以窮舉出所有的key了!
先建個passphrase_list出來:

1
2
3
4
#!/bin/bash
for (( i = 0; i < 256; i++ )); do
./keypass $(python -c "print chr(${i})") >> passphrase_list.txt
done

再來寫個python script把每個passphrase都拿去解密看看:

1
2
3
4
5
6
7
8
9
10
11
12
from subprocess import PIPE, Popen
with open('passphrase_list.txt', 'r') as f:
for key in f.readlines():
cmd = 'openssl enc -aes-128-cbc -d -in flag.zip.enc -md sha256 -k \'' + key.strip() + '\''
p = Popen(cmd, stdout=PIPE, stdin=PIPE, stderr=PIPE, shell=True)
output, err = p.communicate()
if 'bad decrypt' not in str(err):
print(key)
with open('flag.zip', 'wb') as flagfile:
flagfile.write(output)
break

找到正確的passphrase是\M)R<.DDe/:;d>JZP,得到flag.zip。下unzip flag.zip,得到flag.txt
Flag: VolgaCTF{L0ve_a11_trust_@_few_d0_not_reinvent_the_wh33l}

大坑

官方後來才給了Hints,說明是用最新版OpenSSL 1.1.0e做加密的,但目前的套件管理工具都是存OpenSSL 1.0.2b版本,要裝要自己拿source來編,結果一怒之下就找了個 online unix terminal (TutorialsPoint) 來用了,載source編譯在local安裝,最後跑解密成功。

一開始嘗試了舊版沒辦法解密,原因出在舊版用passphrase去產生key時的implementation中default是用了md5做hash,而新版的則是用sha256。

詳情可以看這個openssl github的commit:
https://github.com/openssl/openssl/commit/f8547f62c212837dbf44fb7e2755e5774a59a57b

後來在stackoverflow找到了這篇:
Encryption/decryption doesn’t work well between two different openssl versions
再看一下openssl -md的說明:

1
2
-md the next argument is the md to use to create a key
from a passphrase. One of md2, md5, sha or sha1

因此如果要用舊版解密要加上-md sha256,嘗試一下:

1
openssl enc -aes-128-cbc -d -in flag.zip.enc -out flag.zip -k "\M)R<.DDe/:;d>JZP" -md sha256

解密成功!