Last active
November 15, 2017 17:10
-
-
Save 73spica/66a6c7d0cd013c2c9a9699565b3e8aac to your computer and use it in GitHub Desktop.
HITCON CTF 2017 Secret Server Revenge
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 | |
#===== Step1. Getting Cipher of "Command not found" (1 requests) ===== | |
print "[+] ==== Step1 ====" | |
b64_cnf = get_cnf(b64_welcome) | |
count += 1 | |
print "[+] b64_cnf:", b64_cnf | |
#===== Step2. Getting Token Cipher (1 requests) ===== | |
print "[+] ==== Step2 ====" | |
b64_token = get_token(b64_welcome) | |
count += 1 | |
print "[+] b64_token:", b64_token | |
#===== 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