Tags: aes-ctr crypto 

Rating:

This write up is written in Korean. If anyone who wanna see the English version, I'll consider that but I think given figures and codes are enough to understand :P

2017 SHA - [CRYPTO] Stack Overflow

Key words

  • AES-CTR
  • Python Lambda as counter

Solution

문제에 다음과 같은 파일을 제공해 줍니다.

import os, sys
from Crypto.Cipher import AES

fn = sys.argv[1]
data = open(fn,'rb').read()

# Secure CTR mode encryption using random key and random IV, taken from
# http://stackoverflow.com/questions/3154998/pycrypto-problem-using-aesctr
secret = os.urandom(16)
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=lambda: secret)

encrypted = crypto.encrypt(data)
open(fn+'.enc','wb').write(encrypted)

문제의 목적은 flag.pdf.enc를 복원하는 것 입니다.

먼저, CTR 모드의 암복호 과정은 다음과 같습니다.

IV와 Secret이 랜덤하지만 항상 고정적이기 때문에 결국엔 다음과 같이 복호화를 시도 해볼 수 있습니다

e(k, iv) ^ pt = ct
ct ^ e(k, iv) = pt

여기서 하려고 하는 것은 e(k, iv)값을 ctxor연산을 한 뒤, 결과가 pt처럼 잘 나오는지 보려고 합니다.

e(k, iv)값이 항상 고정적이기 때문에 이 값을 추측해봅니다.

블록 단위가 16바이트 이기 때문에 단순히 추측하기는 어렵고 대상이 PDF라는 점을 이용하여 첫 16바이트의 암호문을 이용하여 복호를 시도해봅니다.

먼저 PDF 구조를 보면 처음에 고정적인 바이트 값이 있습니다.

%  P  D  F  -  1  .
25 50 44 46 2D 31 2E

pt = "\x25\x50\x44\x46\x2d\x31\x2e\x00\x00\x00\x00\x00\x00\x00\x00\x00"

먼저 예측할 수 있는 부분은 제외한 나머지는 \x00으로 채웁니다.

그리고 암호화된 flag.pdf.enc의 첫 16바이트를 가져오면 다음과 같습니다.

D6 EA B9 75 30 19 6A 9B 05 2A EF 97 EE 2C 00 DB

여기서 우리는 ctpt를 구했기 때문에 이 두개를 xor하면 나오는 값은 e(k, iv)가 됩니다.

지금은 처음 7바이트만 했기 때문에 뒤의 9바이트는 정상적으로 복호화가 되지 않지만 PDF의 구조 특성을 이용하여 처음 7바이트를 복구 한뒤 그 이후의 바이트는 구조를 보면서 추측할 수 있습니다.

먼저 7바이트를 이용하여 flag.pdf.enc를 복구 하면 다음과 같습니다.

%PDF-1..........
j.<<./PRGoB..._B
R./Type..IPTQL..
.>>.end\B`;...O. <--
bj.<<./gYzT..p..
es./Kid@.Q....O0
 ]./Cou]T*.*..e.
ndobj.3..*^BZ*S^
./Type .pkVE:.?.
rent 2 ..X;.bE..
urces <.*%ioRJ..

4번째 줄에 보면 end로 끝나는 글자가 보이는데 이는 endobj로 예측할 수 있습니다. 이를 이용하여 다시 변환다음 다음과 같습니다.

%PDF-1.3 .......
j.<<./PageB..._B
R./Type /CPTQL..
.>>.endobj;...O.
bj.<<./TypT..p..
es./Kids [....O0
 ]./Count .*..e.
ndobj.3 0 ^BZ*S^
./Type /PaVE:.?.
rent 2 0 R;.bE..

이런식으로 계속 맞추다 보면 전체를 다 맞출 수 있고 정상적으로 flag.pdf를 얻을 수 있습니다.

Solution Code

def sxor(a, b):
    ret = ""
    for i in range(0, len(a)):
        ret += chr( ord(a[i]) ^ ord(b[i]))
    return ret

#            %  P   D   F   -   1   .   X   .
predict = "\x25\x50\x44\x46\x2d\x31\x2e\x00\x00\x00\x00\x00\x00\x00\x00\x00"

# p1  = 5c4260, obj = 6f626a : p1 xor obj = 33200a
predict = "\x25\x50\x44\x46\x2d\x31\x2e\x33\x20\x0a\x00\x00\x00\x00\x00\x00"

# p2 = 4200, obj = 7320
predict = "\x25\x50\x44\x46\x2d\x31\x2e\x33\x20\x0a\x31\x20\x00\x00\x00\x00"

# p3 = 5a2a, obj = 6a0a
predict = "\x25\x50\x44\x46\x2d\x31\x2e\x33\x20\x0a\x31\x20\x30\x20\x00\x00"

# p3 = 0e05, obj = 6167
predict = "\x25\x50\x44\x46\x2d\x31\x2e\x33\x20\x0a\x31\x20\x30\x20\x6f\x62"

enc     = "\xD6\xEA\xB9\x75\x30\x19\x6A\x9B\x05\x2A\xEF\x97\xEE\x2C\x00\xDB"
#print sxor("\x0e\x05", "\x61\x67").encode("hex")

key    = sxor(predict, enc)

data = open("flag.pdf.enc", "rb").read()
i = 0
res = ""
while i < len(data)-16:
    res += sxor(data[i*16:i*16+16], key)
    i += 1

fd = open("flag.pdf", "wb")
fd.write(res)
fd.close()

Result

Original writeup (https://github.com/ktb88/ctf/tree/master/crypto/0010_2017_SHA_StackOverflow).