반응형

 

 

CryptoHack – Home

A free, fun platform to learn about cryptography through solving challenges and cracking insecure code. Can you reach the top of the leaderboard?

cryptohack.org

 

이번 문제도 그냥 MITM이다. 

Overview

Alice와 Bob이 어떠한 값들을 주고받는지 살펴보자.

 

우선, nc로 연결해보면,

Intercepted from Alice: {"supported": ["DH1536", "DH1024", "DH512", "DH256", "DH128", "DH64"]}

이렇게 나오고, Bob은

Intercepted from Bob: {'chosen': 'DH1536'}

이러한 값을 Alice에게 전달하는 것을 볼 수 있다.

즉, Alice는 Bob에게 선택지를 보내고, Bob은 그 중 secret 값으로 사용할 숫자의 길이를 보내는 것 같다.

Exploit - Theory

그렇다면, Alice가 보내는 선택지를 공격자의 임의로 변경해서 보내면 되지않을까?

{'supported': ['DH64']}

로 바꿔서 보내보았다. 그랬더니,

Intercepted from Bob: {'chosen': 'DH64'}

로 답이 왔다.

길이가 64밖에 되지 않는다면, DLP를 풀 수 있을 것만 같다.

64 길이의 DLP 문제를 푸는 것은, 간단하게 SageMath를 이용해서 해결할 수 있다고 한다.

그런데, 내가 SageMath는 초보라서, 지피티와 함께 풀었다.

Exploit - Do it

우선, 이 문제에서도 이전 문제에서 사용했던 AES decrypt 코드를 이용한다고 하였다.

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import hashlib

def is_pkcs7_padded(message):
    padding = message[-message[-1]:]
    return all(padding[i] == len(padding) for i in range(0, len(padding)))

def decrypt_flag(shared_secret: int, iv: str, ciphertext: str):
    # Derive AES key from shared secret
    sha1 = hashlib.sha1()
    sha1.update(str(shared_secret).encode('ascii'))
    key = sha1.digest()[:16]
    # Decrypt flag
    ciphertext = bytes.fromhex(ciphertext)
    iv = bytes.fromhex(iv)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext)

    if is_pkcs7_padded(plaintext):
        return unpad(plaintext, 16).decode('ascii')
    else:
        return plaintext.decode('ascii')
from pwn import *
import json

r = remote("socket.cryptohack.org", 13379)

def recv_json():
    ret = json.loads(r.recvuntil('}').decode())
    print(ret)
    return ret

def send_json(msg):
    to_send = json.dumps(msg).encode()
    r.sendline(to_send)

r.recvuntil('Intercepted from Alice: ')
Alice_1 = recv_json()
Alice_1 = {'supported': ['DH64']}

r.recvuntil('Send to Bob: ')
send_json(Alice_1)

r.recvuntil('Intercepted from Bob: ')
Bob_1 = recv_json()

r.recvuntil('Send to Alice: ')
send_json(Bob_1)

r.recvuntil('Intercepted from Alice: ')
Alice_2 = recv_json()
p = int(Alice_2['p'], 16)
g = int(Alice_2['g'], 16)
A = int(Alice_2['A'], 16)

r.recvuntil('Intercepted from Bob: ')
data = recv_json()
B = int(data['B'], 16)

이렇게 해서 Alice와 Bob의 p, g, A, B를 받아볼 수 있다.

하지만, 여기에서 사용된 p의 길이가 64밖에 되지 않기 때문에, 다른 취약점이 아닌, 계산을 통해 문제를 풀 수 있다.

이 아래 이와 같은 코드를 추가해주었다.

# sagemath에서 dlp를 계산하고 돌아옴
a = int(input())

잠시 멈추고, 위의 p, g, A, B를 이용해 a 값을 계산하여 이를 입력하는 과정이다.

$ sage
sage: g = Mod({g}, {p})
sage: A = {A}
sage: a = discrete_log(A, g)
sage: a

이렇게 하면 a값을 얻을 수 있다. (물론 '{}'로 씌워진 곳에는 문자가 아니라 숫자를 입력해야한다.)

이 다음 과정부터는 그냥 Diffie-Hellman 이용해서 계산하고, 보내주면 된다. 간단하다.

shared_secret = pow(B, a, p)

r.recvuntil('Intercepted from Alice: ')
data = recv_json()
iv = data['iv']
encrypted_flag = data['encrypted_flag']

print(decrypt_flag(shared_secret, iv, encrypted_flag))
#crypto{d0wn6r4d35_4r3_d4n63r0u5}

r.close()

 

끝~

반응형

+ Recent posts