[0ctf 2017] integrity - 75p
0ctf quals 2017 : integrity
Category: Crypto Points: 75 Solves: Description:
Just a simple scheme.
nc 202.120.7.217 8221
문제가 괜찮아서 writeup을 써본다.
문제코드는 아래와 같다.
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | #!/usr/bin/python -u from Crypto.Cipher import AES from hashlib import md5 from Crypto import Random from signal import alarm BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-ord(s[-1])] class Scheme: def __init__(self,key): self.key = key def encrypt(self,raw): raw = pad(raw) raw = md5(raw).digest() + raw iv = Random.new().read(BS) cipher = AES.new(self.key,AES.MODE_CBC,iv) return ( iv + cipher.encrypt(raw) ).encode("hex") def decrypt(self,enc): enc = enc.decode("hex") iv = enc[:BS] enc = enc[BS:] cipher = AES.new(self.key,AES.MODE_CBC,iv) blob = cipher.decrypt(enc) checksum = blob[:BS] data = blob[BS:] if md5(data).digest() == checksum: return unpad(data) else: return key = Random.new().read(BS) scheme = Scheme(key) flag = open("flag",'r').readline() alarm(30) print "Welcome to 0CTF encryption service!" while True: print "Please [r]egister or [l]ogin" cmd = raw_input() if not cmd: break if cmd[0]=='r' : name = raw_input().strip() if(len(name) > 32): print "username too long!" break if pad(name) == pad("admin"): print "You cannot use this name!" break else: print "Here is your secret:" print scheme.encrypt(name) elif cmd[0]=='l': data = raw_input().strip() name = scheme.decrypt(data) if name == "admin": print "Welcome admin!" print flag else: print "Welcome %s!" % name else: print "Unknown cmd!" break | cs |
AES로 암호화하는데 CBC모드를 이용한다. CBC모드란?
CBC모드는 랜덤하게 생성된 IV으로 첫 블록을 암호화시킨 암호문을 다음 블록의 암호화에 사용한다.
If the first block has index 1, the mathematical formula for CBC encryption is
while the mathematical formula for CBC decryption is
그런데 여기 문제에서는 secret값의 IV || encrypted
형식이고 encrypted는 md5(pad(username)) || pad(username)
이다.
CBC모드는 IV로 블록을 암호화한 암호문을 다음 블록을 암호화하는데 쓰이므로, IV를 떼어낸 encrypted만 보내면 encrypted의 첫 블록을 IV로 사용하여 나머지 블록들은 모두 복호화가 될 것 이다.
우리가 이 값 md5(pad("admin")).digest() || admin
을 username으로 보내면
IV || enc(real md5) || enc(admin md5) || enc(admin)
. 이렇게 값이 돌아올 것이다.
+---------+------------------------+--------------------+---------------+ | IV | Enc(md5(admin)||admin) | Enc(md5(admin)) | Enc(admin) | +---------+------------------------+--------------------+---------------+
md5가 128bit이고 암호화 블록사이즈를 128bit로 해서 이런 암호문을 얻을 수 있는 것이다.
여기서 우리가 필요한 값은 admin을 복호화시키는 것 뿐이므로 IV와 첫암호화블록 C1을 빼버린 값을 보낸다면
Enc(md5(admin))값이 IV로 들어가서 Enc(admin)을 복호화하여 admin만 남게 될 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | from pwn import * from hashlib import md5 BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-ord(s[-1])] name = md5(pad("admin")).digest() + "admin" r = remote("202.120.7.217", 8221) r.sendlineafter("or [l]ogin\n", "r") r.sendline(name) r.recvuntil("secret:\n") secret = r.recvline().strip() r.sendlineafter("or [l]ogin\n", "l") r.sendline(secret[32:]) r.interactive() | cs |
'Write-up > Crypto' 카테고리의 다른 글
[Byte Bandits CTF 2018]R u Ronald Rivest? (0) | 2018.06.02 |
---|---|
[Plaid CTF 2018] macsh - 125 (0) | 2018.05.07 |
[Byte Bandits CTF 2018] R u Ronald Rivest? 70p (0) | 2018.04.27 |
[UIUCTF 2018] xoracle (250) (0) | 2018.04.18 |
[TyokoWesterns] My Simple Cipher[75] (0) | 2017.09.02 |