Skip to content

Instantly share code, notes, and snippets.

@73spica
Last active November 15, 2017 17:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 73spica/66a6c7d0cd013c2c9a9699565b3e8aac to your computer and use it in GitHub Desktop.
Save 73spica/66a6c7d0cd013c2c9a9699565b3e8aac to your computer and use it in GitHub Desktop.
HITCON CTF 2017 Secret Server Revenge
# coding:utf-8
from m1z0r3.crypro import sock, read_until, split_n
from itertools import product
import string
from Crypto.Hash import *
from base64 import b64encode, b64decode
remoteip = "52.192.29.52"
remoteport = 9999
#remoteip = "localhost"
#remoteport = 1025
s,f = sock(remoteip, remoteport)
def pad(msg):
pad_length = 16 - len(msg) % 16
return msg + chr(pad_length) * pad_length
def sxor(s1,s2):
return "".join([chr(ord(c1)^ord(c2)) for c1,c2 in zip(s1,s2)])
def proof_of_work():
data = read_until(f).strip()
print data
suffix = data.split(" ")[0].strip("SHA256()").strip("XXXX+")
print "[+] suffix:", suffix
_hash = data.split(" ")[-1]
print "[+] Correct Hash:", _hash
print "[+] BruteForce... "
rep = 4
for x in product(string.ascii_letters+string.digits, repeat=rep):
x = "".join(x)
if SHA256.new(x+suffix).hexdigest() == _hash:
print "[+] Detect! :",x
break
read_until(f, "Give me XXXX:")
s.send(x+"\n")
def get_welcome():
return read_until(f).strip()
def get_cnf(b64_welcome):
s.send(b64_welcome + "\n")
return read_until(f).strip()
def log(a,b=""): print "[+] %s: %s"%(b,a)
def get_md5(b64_welcome, a):
enc_welcome = b64decode(b64_welcome)
ew_blocks = split_n(enc_welcome, 16)[:-1]
crufted_iv = sxor(sxor(ew_blocks[0], pad("Welcome!!")), pad("get-md5"+a))
send_data = crufted_iv + "".join(ew_blocks[1:])
s.send(b64encode(send_data)+"\n")
return read_until(f).strip()
def get_token(b64_welcome):
enc_welcome = b64decode(b64_welcome)
ew_blocks = split_n(enc_welcome, 16)[:-1]
crufted_iv = sxor(sxor(ew_blocks[0], pad("Welcome!!")), pad("get-token"))
send_data = crufted_iv + "".join(ew_blocks[1:])
s.send(b64encode(send_data)+"\n")
return read_until(f).strip()
def check_token(b64_welcome, token):
enc_welcome = b64decode(b64_welcome)
ew_blocks = split_n(enc_welcome, 16)[:-1]
crufted_iv = sxor(sxor(ew_blocks[0], pad("Welcome!!")), pad("check-token"))
send_data = crufted_iv + "".join(ew_blocks[1:])
s.send(b64encode(send_data)+"\n")
read_until(f).strip()
s.send(b64encode(token)+"\n")
return read_until(f).strip()
def search_cand(md5_last_chr,cand_list):
tmp = []
if len(cand_list[0])==56:
for cand in cand_list:
if MD5.new(cand + "\x01").digest()[-1] == md5_last_chr:
tmp.append(cand)
else:
for cand in cand_list:
for j in xrange(256):
if MD5.new(cand + chr(j)).digest()[-1] == md5_last_chr:
tmp.append(cand + chr(j))
return list(tmp)
def main():
count = 0 # Num. of requests
proof_of_work()
b64_welcome = get_welcome()
print "[+] b64_welcome:", b64_welcome
print
#===== Step1. Getting Cipher of "Command not found" (1 requests) =====
print "[+] ==== Step1 ===="
b64_cnf = get_cnf(b64_welcome)
count += 1
print "[+] b64_cnf:", b64_cnf
print
#===== Step2. Getting Token Cipher (1 requests) =====
print "[+] ==== Step2 ===="
b64_token = get_token(b64_welcome)
count += 1
print "[+] b64_token:", b64_token
print
#===== Step3 (57 requests) =====
print "[+] ==== Step3 ===="
et_blocks = split_n(b64decode(b64_token), 16)[:-1]
true_iv = et_blocks[0]
crufted_iv = sxor(sxor(et_blocks[0], "token: "), "get-md5") + et_blocks[0][7:]
unpad_max = 88
token_len = 56
token_md5_list = []
for unpad in reversed(xrange(unpad_max-token_len, unpad_max+1)):
crufted_pad = chr(1 ^ ord(et_blocks[-2][-1]) ^ unpad)
send_data = crufted_iv + "".join(et_blocks[1:]) + "".join(et_blocks[-2][:-1]) + crufted_pad + "".join(et_blocks[-1])
s.send(b64encode(send_data)+"\n")
count += 1
b64_token_md5 = read_until(f).strip()
assert b64_token_md5 != b64_cnf
token_md5_list.append(split_n(b64decode(b64_token_md5),16)[1])
print "[+] len(token_md5_list):", len(token_md5_list)
print "[+] Done.\n"
#===== Step4 (176 requests) =====
print "[+] ==== Step4 ===="
unpad_pattern_dict = {}
for unpad in xrange(32, 208):
crufted_pad = chr(1 ^ ord(et_blocks[-2][-1]) ^ unpad)
enc_welcome = b64decode(b64_welcome)
crufted_iv = sxor(sxor(et_blocks[0], "token: "), "get-md5") + true_iv[7:]
send_data = crufted_iv + "".join(et_blocks[1:]) # 1 + 4 = 5 blocks
send_data += "\x00"*16*11 # 5 + 11 = 16 blocks ( Decryption result: 15 blocks = 240 chrs = "get-md5" + 233 chrs )
send_data += "\x00"*15 + crufted_pad # 16 + 1 = 17 blocks(Decryption result: 16 blocks = 256 chrs = "get-md5" + 249 chrs)
send_data += et_blocks[-1] # 17 + 1 = 18 blocks(Decryption result: 17 blocks = 272 chrs = "get-md5" + 265 chrs)
s.send(b64encode(send_data)+"\n")
count += 1
unpad_pattern = read_until(f).strip()
unpad_pattern_dict[unpad_pattern] = unpad
for unpad in xrange(208, 256): # 255->9, 208->56
idx = 264 - unpad
unpad_pattern_dict[token_md5_list[idx]] = unpad
assert b64_cnf not in unpad_pattern_dict
unpad_pattern_dict[b64_cnf] = 0
print "[+] Done.\n"
#===== Step5 =====
print "[+] ==== Step5 ===="
cand_list = ['']
for i in xrange(56):
crufted_iv = sxor(sxor(et_blocks[0], "token: "), "get-md5") + et_blocks[0][7:]
send_data = crufted_iv + "".join(et_blocks[1:]) # 1 + 4 = 5 blocks
send_data += "\x00"*16*11 # 5 + 11 = 16 blocks(Decryption result: 15 blocks = 240chrs = "get-md5" + 233chrs)
send_data += "\x00"*16 # 16 + 1 = 17 blocks(Decryption result: 16 blocks = 256chrs = "get-md5" + 249chrs)
send_data += token_md5_list[i] # 17 + 1 = 18 blocks(Decryption result: 17 blocks = 272chrs = "get-md5" + 265chrs)
s.send(b64encode(send_data)+"\n")
count += 1
unpad_check = read_until(f).strip()
if unpad_check in unpad_pattern_dict:
unpad = unpad_pattern_dict[unpad_check]
md5_last_chr = chr(unpad ^ 0 ^ ord(true_iv[-1]))
cand_list = search_cand(md5_last_chr, cand_list)
else:
crufted_iv = sxor(sxor(et_blocks[0], "token: "), "get-md5") + et_blocks[0][7:]
send_data = crufted_iv + "".join(et_blocks[1:]) # 1 + 4 = 5 blocks
send_data += "\x00"*16*11 # 5 + 11 = 16 blocks(Decryption result: 15 blocks = 240chrs = "get-md5" + 233chrs)
send_data += "\x00"*15 + "\x7f" # 16 + 1 = 17 blocks(Decryption result: 16 blocks = 256chrs = "get-md5" + 249chrs)
send_data += token_md5_list[i] # 17 + 1 = 18 blocks(Decryption result: 17 blocks = 272chrs = "get-md5" + 265chrs)
s.send(b64encode(send_data)+"\n")
count += 1
unpad_check = read_until(f).strip()
if unpad_check in unpad_pattern_dict:
unpad = unpad_pattern_dict[unpad_check]
md5_last_chr = chr(unpad ^ 127 ^ ord(true_iv[-1]))
cand_list = search_cand(md5_last_chr, cand_list)
else:
print "[+] Something is wrong."
return
print "[+] #%d: The number of candidates is %d."%(i,len(cand_list))
print "[+] Done.\n"
print "==== Step6 ===="
crufted_iv = sxor(sxor(et_blocks[0], "token: "), "get-md5") + et_blocks[0][7:]
send_data = crufted_iv + "".join(et_blocks[1:]) # 1 + 4 = 5 blocks
send_data += "\x00"*16*11 # 5 + 11 = 16 blocks(Decryption result: 15 blocks = 240chrs = "get-md5" + 233chrs)
send_data += "\x00"*15 + "\x7f" # 16 + 1 = 17 blocks(Decryption result: 16 blocks = 256chrs = "get-md5" + 249chrs)
send_data += token_md5_list[56] # 17 + 1 = 18 blocks(Decryption result: 17 blocks = 272chrs = "get-md5" + 265chrs)
s.send(b64encode(send_data)+"\n")
count += 1
unpad_check = read_until(f).strip()
if unpad_check in unpad_pattern_dict:
unpad = unpad_pattern_dict[unpad_check]
md5_last_chr = chr(unpad ^ 127 ^ ord(true_iv[-1]))
cand_list = search_cand(md5_last_chr, cand_list)
else:
crufted_iv = sxor(sxor(et_blocks[0], "token: "), "get-md5") + et_blocks[0][7:]
send_data = crufted_iv + "".join(et_blocks[1:]) # 1 + 4 = 5 blocks
send_data += "\x00"*16*11 # 5 + 11 = 16 blocks(Decryption result: 15 blocks = 240chrs = "get-md5" + 233chrs)
send_data += "\x00"*15 + "\x00" # 16 + 1 = 17 blocks(Decryption result: 16 blocks = 256chrs = "get-md5" + 249chrs)
send_data += token_md5_list[56] # 17 + 1 = 18 blocks(Decryption result: 17 blocks = 272chrs = "get-md5" + 265chrs)
s.send(b64encode(send_data)+"\n")
count += 1
unpad_check = read_until(f).strip()
if unpad_check in unpad_pattern_dict:
unpad = unpad_pattern_dict[unpad_check]
md5_last_chr = chr(unpad ^ 0 ^ ord(true_iv[-1]))
cand_list = search_cand(md5_last_chr, cand_list)
else:
print "[+] Something is wrong."
return
print cand_list
print "[+] All steps is done. The number of candidates is %d."%len(cand_list)
flag = check_token(b64_welcome, cand_list[0])
print "[+] Flag:", flag # hitcon{uNp@d_M3th0D_i5_am4Z1n9!}
print "The number of requests is %s/340."%count
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment