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
문제에 다음과 같은 파일을 제공해 줍니다.
```python
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 모드의 암복호 과정은 다음과 같습니다.
![](https://github.com/ktb88/ctf/blob/master/crypto/0010_2017_SHA_StackOverflow/mode_ctr.png?raw=true)
IV와 Secret이 랜덤하지만 항상 고정적이기 때문에 결국엔 다음과 같이 복호화를 시도 해볼 수 있습니다
```
e(k, iv) ^ pt = ct
ct ^ e(k, iv) = pt
```
여기서 하려고 하는 것은 `e(k, iv)`값을 `ct`와 `xor`연산을 한 뒤, 결과가 `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
```
여기서 우리는 `ct`와 `pt`를 구했기 때문에 이 두개를 `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./[email protected]
]./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
```python
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
![](https://github.com/ktb88/ctf/blob/master/crypto/0010_2017_SHA_StackOverflow/flag.png?raw=true)