Description
Category: Crypto
Source: VolgaCTF 2019 Qualifier
Points: 200
Author: Jisoon Park(js00n.park)
Description:
Pull the flag...if you can.
nc blind.q.2019.volgactf.ru 7070
Write-up
서버 코드와 접속할 수 있는 URL이 주어진다.
class RSA:
def __init__(self, e, d, n):
self.e = e
self.d = d
self.n = n
def sign(self, message):
message = int(message.encode('hex'), 16)
return pow(message, self.d, self.n)
def verify(self, message, signature):
message = int(message.encode('hex'), 16)
verify = pow(signature, self.e, self.n)
return message == verify
"""
Keys
"""
n = 26507591511689883990023896389022361811173033984051016489514421457013639621509962613332324662222154683066173937658495362448733162728817642341239457485221865493926211958117034923747221236176204216845182311004742474549095130306550623190917480615151093941494688906907516349433681015204941620716162038586590895058816430264415335805881575305773073358135217732591500750773744464142282514963376379623449776844046465746330691788777566563856886778143019387464133144867446731438967247646981498812182658347753229511846953659235528803754112114516623201792727787856347729085966824435377279429992530935232902223909659507613583396967
e = 65537
암/복호화 하는 부분은 textbook RSA와 동일하다. 역시나 n과 e가 주어져 있는데, RsaCtfTool을 이용해서 인수분해를 시도해 보았으나 성공하지는 못했다.
while True:
send_message('Enter your command:')
message = read_message().strip()
(sgn, cmd_exp) = message.split(' ', 1)
eprint('Accepting command {0}'.format(cmd_exp))
eprint('Accepting command signature: {0}'.format(sgn))
cmd_l = shlex.split(cmd_exp)
cmd = cmd_l[0]
if cmd == 'ls' or cmd == 'dir':
ret_str = run_cmd(cmd_exp)
send_message(ret_str)
elif cmd == 'cd':
try:
sgn = int(sgn)
if not signature.verify(cmd_exp, sgn):
raise SignatureException('Signature verification check failed')
os.chdir(cmd_l[1])
send_message('')
except Exception as ex:
send_message(str(ex))
elif cmd == 'cat':
try:
sgn = int(sgn)
if not signature.verify(cmd_exp, sgn):
raise SignatureException('Signature verification check failed')
if len(cmd_l) == 1:
raise Exception('Nothing to cat')
ret_str = run_cmd(cmd_exp)
send_message(ret_str)
except Exception as ex:
send_message(str(ex))
elif cmd == 'sign':
try:
send_message('Enter your command to sign:')
message = read_message().strip()
message = message.decode('base64')
cmd_l = shlex.split(message)
sign_cmd = cmd_l[0]
if sign_cmd not in ['cat', 'cd']:
sgn = signature.sign(sign_cmd)
send_message(str(sgn))
else:
send_message('Invalid command')
except Exception as ex:
send_message(str(ex))
이 부분이 핵심이다. 공백으로 구분된 sign과 command를 던져주면, command에 따라 처리하는 루틴이다.
ls와 dir은 서명 검증 없이 수행하고, cd와 cat은 서명 검사 후 실행하며, sign은 서명을 생성해 주는 역할을 하는데 command가 cat이나 cd인 경우에는 서명을 생성해주지 않는다.
일단 서버 테스트를 해보자.
서명 자리에 아무 수나 넣고 ls -l을 실행해 봤더니, server와 동일한 디렉토리에 flag 파일이 있는 것을 확인할 수 있었다. cat flag 명령을 수행하면 flag를 얻을 수 있을 것 같다.
cat flag에 대한 서명을 어떻게 생성할 수 있을까 고민하다가, cat과 cd가 아닌 임의의 데이터에 대해 서명을 생성하는게 가능하다는 사실이 떠올랐다. RSA의 multiplicative property를 이용해보자.
아래와 같이 파라미터를 설정하면,
- m0 = "cat flag"
- m1 = 2
- s1 = sign(m1)
- s01 = sign(m0 * m1)
m0에 대한 서명 s0는 다음과 같이 계산할 수 있을 것이다.
- s1_inv = s1-1 mod n
- s0 = s01 * s1_inv mod n = (m0 * m1)d * s1-1 mod n
= m0d * m1d * m1d * -1 mod n = m0d * m10 mod n = m0d mod n
이렇게 얻은 s0와 m0를 서버로 전송하면 m0가 정상적으로 실행된 결과를 확인할 수 있을 것이다.
s0를 구하는 코드를 작성하여 실행하면 flag를 얻을 수 있다.
Flag : VolgaCTF{B1ind_y0ur_tru3_int3nti0n5}
RSA에 대한 timing attack 방어에 mulitiplicative property를 이용한 blinding을 사용하기 때문에 문제 제목이 Blind가 된것 같다.
'writeups > Crypto' 카테고리의 다른 글
Shadow Cat (0) | 2019.11.26 |
---|---|
LG (0) | 2019.11.26 |
babyrsa (0) | 2019.11.26 |
Easy Pisy (0) | 2019.11.26 |
Count me in (0) | 2019.11.26 |