testbook rsa 문제파일을 받아 열어보니
#!/usr/bin/python3
from Crypto.Util.number import getStrongPrime, bytes_to_long, inverse
class RSA(object):
def __init__(self):
self.p = getStrongPrime(512)
self.q = getStrongPrime(512)
self.N = self.p * self.q
self.e = 0x10001
self.d = inverse(self.e, self.N - self.p - self.q + 1)
def encrypt(self, pt):
return pow(pt, self.e, self.N)
def decrypt(self, ct):
return pow(ct, self.d, self.N)
rsa = RSA()
FLAG = bytes_to_long(open("flag", "rb").read())
FLAG_enc = rsa.encrypt(FLAG)
print("Welcome to dream's RSA server")
while True:
print("[1] Encrypt")
print("[2] Decrypt")
print("[3] Get info")
choice = input()
if choice == "1":
print("Input plaintext (hex): ", end="")
pt = bytes_to_long(bytes.fromhex(input()))
print(rsa.encrypt(pt))
elif choice == "2":
print("Input ciphertext (hex): ", end="") #2번을 누르고 플래그 값이나 암호문보다 값이 작은 N을 입력하면 치트를 쓰지말라고 한다
ct = bytes_to_long(bytes.fromhex(input()))
if ct == FLAG_enc or ct > rsa.N:
print("Do not cheat !")
else:
print(rsa.decrypt(ct))
elif choice == "3": #3번을 누르면 공개키 (E,n)과 암호문을 준다.
print(f"N: {rsa.N}")
print(f"e: {rsa.e}")
print(f"FLAG: {FLAG_enc}")
else:
print("Nope")
이런식으로 운영되는 방식이였다
근데 공개키로 평문을 암호문으로 만들지 않나? 그럼 어떻게 공격하지.. 싶었는데 찾아보니
선택 암호문 공격 (CCA) 를 알아야한다
선택 암호문 공격은 임의로 선택된 암호문과 일치하는 평문으로부터 암호키를 알아내기 위해 시도하는 공격이다.
즉 원하는 암호문을 복호화해주는 경우 사용할 수 있는 공격 방법이다.
RSA에서 쓰이는 경우는 "곱셈에 대한 준동형사상(Homomorphism)의 성질"이라고 한다
곱셈에 대한 준동형사상은 다음 식으로 정리될 수 있으며, 이를 이용하면 다음과 같은 공격이 가능해진다.
암호화된 flag가 있고, 암/복호화 기능이 존재한다고 가정하면
1. 숫자 2를 암호화 한다.
2. 숫자 2를 암호화 한 값 * flag를 암호화 한 값을 곱한다.
3. 결과 값을 숫자 2로 나누면 flag가 된다.
덧붙여, 이 방법을 막기 위해서 고안된 방법은 RSA-OAEP를 제안하였다.
RSA-OAEP는 암호화 단계에서 평문 해시값과 정해진 개수의 '0' 등으로 만들어진 "인증 정보"를 평문의 앞에 추가하고 RSA로 암호화한다.
이후 복호화단계에서 이 "인증 정보"가 보이지 않을 경우 평문을 알고 있는 사람이 만든 암호문이 아니라고 판단하여 복호화를 진행하지 않는다.
실제로 RSA-OAEP에서는 난수를 이용하는 등 암호문이 매번 다른 패턴이 되도록 하여 안전성을 높였다
즉, 이 문제에서는
2^e * flag_enc 를 [2] Decrypt에 넣어 1/2 해주면 된다.
내가 참고한 blog에서는 pwn 툴을 사용했는데 처음 써봐서 베끼는 수준 이였지만..
이 문제를 통해 pwn툴의 이해와 CCA의 개념을 이해했다
from pwn import *
from Crypto.Util.number import *
p = remote('host3.dreamhack.games', 21275) # 서버랑 연결
p.sendlineafter('info\n', '3') #기다렸다가, 3번째 메뉴 선택
p.recvuntil('N: ') # N값 받아오기(개행문자 제거해 정수로 변환한 값을 n에 저장)
n = int(p.recvline()[:-1])
p.recvuntil('e: ') # e값 받아오기 (개행문자 제거해 정수로 변환한 값을 e에 저장)
e = int(p.recvline()[:-1])
p.recvuntil('FLAG: ') # FLAG값 받아오기(개행문자 제거해 정수로 변환한 값을 flag_enc에 저장)
flag_enc = int(p.recvline()[:-1])
exploit_flag = (pow(2,e) * flag_enc) % n # exploit_flag에 2^e * flag_enc % n 값을 저장
p.sendlineafter('info\n', '2') # 2번째 메뉴 선택
p.sendlineafter('hex): ', hex(exploit_flag)[2:])# exploit_flag값을 16진수로 변환해 전송 16진수 변환시 0x가 붙으므로 [2:]로 0x를 제거
verflag = int(p.recvline()[:-1]) # verflag에 받아온 값을 정수로 변환해 저장
flag = verflag // 2 # verflag값을 2로 나눈 몫을 flag에 저장
log.info("REAL FLAG: " + str(long_to_bytes(flag))) # flag값을 바이트로 변환해 출력
참고 / 인용 블로그 출처
DreamHack - Textbook-RSA
문제에 주어진 코드는 다음과 같다. 임의로 p와 q를 생성한 후, 공개키 값으로 65537을 사용하여 flag 값을...
blog.naver.com
'Cryptography' 카테고리의 다른 글
RSA 역원 (0) | 2024.02.17 |
---|---|
RSA Wiener attack code (0) | 2024.02.17 |
RSA 수학적 원리 발표 / 세특 (2) | 2024.01.03 |
Factordb 사용법 (0) | 2024.01.03 |
[Python] Crypto 모듈 다운로드 명령어 (0) | 2024.01.02 |