반응형

 대회 기간 동안 단 한 문제 푼 나를 생각하며

 

분석

 

 이 문제는 32bit win binary와 암호화된 png 파일을 제공합니다.

 

 

 exe 파일은 UPX Packing 되어 있어 IDA로 바로 분석하기 어려웠지만 UPX Unpacking이 가능했기에 unpacking후 ida로 분석하였고, 동적 분석은 x64dbg를 사용하였다.

 

 프로그램의 첫 번째 실행 루틴입니다.

File 이름을 입력받고 89번째 라인에서 File 내용을 읽어드립니다. 그리고 rand함수를 사용해 121크기의 sbox를 생성합니다.

 

 첫 번째 핵심 루틴입니다.

파일 각 byte를 bit로 바꾼 뒤 121 bit 크기만큼 채워진다면 위에서 생성한 Sbox를 통해 위치를 바꿉니다.

 그리고 83번째 라인에서 121bit에 대한 HASH 연산을 하고 121~128사이를 채워 넣습니다.

 

 그리고 임의의 자리 바꾸기를 하고, 해당 내용을 저장합니다.

 그리고 연산 후 남은 bit 부분은 0을 체워서 저장해 둡니다.

 두 번째 핵심 연산은 16byte 랜덤 값을 생성하는 것으로 시작합니다.

 그 후 첫 번째 연산에서 만든 값을 랜덤 값과 XOR 하고, sub 401660에서 변환을 한 뒤 랜덤 값을 변환된 값으로 바꿔 줍니다.

 

 sub 404EE0 함수에서는 암호화될 파일 앞 16byte를 가져와서 sub 401660에서 사용될 a2의 값을 생성합니다.

여기서 byte_4232A0은 AES Sbox이고, 파일은 PNG 파일이므로 앞 16byte에 무엇이 들어갈지 알 수 있습니다.

 

 변환 함수에서 다른 코드는 역연산에 큰 문제가 없지만 위 코드에서는 문제가 있어 보입니다.

4 byte 브포는 할 수 없으므로, 연산을 조금 수정해 보면

 

 1byte 브포 공식으로 바꿀 수 있습니다.

 

 마지막은 8byte 랜덤으로 MT19937_64 랜덤을 생성하고 16byte마다 1bit를 xor 하는 연산이 있습니다.

해당 연산 때문에 역연산에서 브포를 해야 하는 부분이 생깁니다.

 

 이 연산이 끝나면 .enc파일을 생성하고 암호화된 내용과 연산 2에서 나온 랜덤 값을 저장하고 끝납니다.

 

역연산

 

 일단 우리는 연산 1과 2는 쉽게 해결할 수 있음을 알고 있습니다.

다만 문제는 연산 3에서 브포를 해야 한다는 것인데 이는 DFS 구조를 사용하면 쉽게 할 수 있습니다.

0x10씩 끊어서 연산하면서 연산 1에서 나온 HASH 값을 토대로 찾아가면 될 줄 알았으나... 연산 2의 존재로 인해 잘못된 값이 생성되기 시작했습니다.

 

 하지만 여기서 우리는 PNG파일에서도 CRC32가 존재함을 알고 있기 때문에 역연산 하면서 연산 내용인 PNG 파일을 분석하면서 복호화된 내용이 올바른 값인지 확인하면 됩니다. 

 

 그래서 풀이 코드는 아래와 같습니다.

 

import copy
from struct import unpack
import string
import zlib 
import pickle

OUTPUT_str = ['' for i in range(0xF000)]
STR_OK =  [b'IHDR',b'IDAT',b'IEND',b'gAMA',b'pHYs',b'sRGB',b'sPLT']
OUTPUT = open("test.png","wb")
class THE_PNG_STATER():
    def __init__(self):
        self.STACK = [(0,0,0,0) for i in range(0xF000)]
        self.IDX = 0
        self.LEN = 0
        self.next_in = 0
        self.data = b""
        self.bit_str=""
        
        self.crh_stat = 0
        self.crh_start = 0
        self.crh_len = 0
    
    def crc32(self,data):
        crc = zlib.crc32(data[:-4])
        
        if crc != unpack(">I",data[-4:])[0]:
            return False
        return True

    def check_data(self,I,data_bit):
        if self.next_in == I:
            tmp = self.bit_str + data_bit
            self.STACK[I] = (self.IDX,self.crh_stat,self.crh_start,self.crh_len)
        else:

            alive = (121 * I) // 8
            lb = (121 * I) % 8
            if alive >= len(self.data):
                print("!!!!!!!!!!!!!!!!!!!")
                ta = []
                OUT = ""
                for a in range(I):
                    OUT += OUTPUT_str[a]
                    while len(OUT) >= 8:
                        ta.append(int(OUT[0:8],2))
                        OUT = OUT[8:]
                self.data = bytes(ta)
                self.bit_str = OUT
            else:
                t = self.data[alive]
                if lb == 0:
                    self.bit_str = ""
                else:
                    self.bit_str = f"{t:08b}"[:lb]
            tmp = self.bit_str + data_bit
            self.data = self.data[:alive]
            (self.IDX,self.crh_stat,self.crh_start,self.crh_len) = self.STACK[I]
        datas = []
        while len(tmp) >= 8:
            datas.append(int(tmp[0:8],2))
            tmp = tmp[8:]
        bit_str = tmp

        data = self.data + bytes(datas)
        datalen = (121 * (I+1)) // 8
        idx = self.IDX
        crh_stat = self.crh_stat
        crh_len = self.crh_len
        crh_start = self.crh_start
        
        while idx < datalen:
            if idx == 0:
                if data[0:8] != b"\x89PNG\x0d\x0a\x1a\x0a":
                    return False
                idx += 8
            if crh_stat == 0:
                if idx + 4 >= datalen:
                    break
                crh_len = unpack(">I",data[idx:idx+4])[0]
                if crh_len > 0x7000:
                    print(hex(crh_len))
                    return False
                crh_stat = 1
                idx += 4

            elif crh_stat == 1:
                if idx + 4 >= datalen:
                    break
                t = data[idx:idx+4]
                print(t)
                if t not in STR_OK:
                    return False
                crh_stat = 2
                crh_start = idx
                idx += 4
                
            elif crh_stat == 2:
                if idx - crh_start == crh_len + 8:
                    if self.crc32(data[crh_start:idx]) == False:
                        return False
                    OUTPUT.seek(0);
                    OUTPUT.write(data)
                    crh_stat = 0
                else:
                    idx += 1
                
        # it successed
        self.next_in = I+1
        self.crh_start= crh_start
        self.crh_stat = crh_stat
        self.crh_len = crh_len
        self.IDX = idx
        self.data = data
        self.bit_str = bit_str
        return True

class microsoft_rand_prng:
    def __init__(self): 
        self._seed = 0 

    def srand(self, seed): 
        self._seed = seed 

    def rand(self): 
        n = self._seed * 0x343fd + 0x269ec3 
        self._seed = n & 0xffffffff 
        return (n>>16) & 0x7fff 

libc = microsoft_rand_prng()
libc.srand(1);

BIT_SBOX = [i for i in range(121)]

C = 0
while True:
    T1 = libc.rand()
    T2 = libc.rand()
    if (T1* T2)&0xffffffff <= C:
        break
    T1 = libc.rand() % 121
    T2 = libc.rand() % 121
    tmp = BIT_SBOX[T1]
    BIT_SBOX[T1] = BIT_SBOX[T2]
    BIT_SBOX[T2] = tmp
    C+=1

def HASH_BIT(STR):
    HASH = 0xff
    for i in range(121):
        HASH = ~((int(STR[i]) & 1) - 1) & 0x89 ^ (HASH >> 1)
        HASH &= 0xff
    return ~HASH & 0x7F

s_box = (
    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
    0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
    0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
    0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
)
Sbox_inv = (
    0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
    0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
    0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
    0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
    0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
    0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
    0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
    0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
    0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
    0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
    0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
    0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
    0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
    0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
    0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
)

# WE KNOW THAT
FIRST_IN = [0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52]

ENC_TABLE = [0 for i in range(16 * 11)]
AB = [0,1,2,4,8,16,32,64,128,0x1b,0x36]
for i in range(16):
    ENC_TABLE[i] = FIRST_IN[i]
for i in range(0x28):
    v5 = ENC_TABLE[i*4 + 12]
    v6 = ENC_TABLE[i*4 + 13]
    v7 = ENC_TABLE[i*4 + 14]
    v8 = ENC_TABLE[i*4 + 15]
    if i % 4 == 0:
        v9 = s_box[v6]
        v6 = s_box[v7]
        v7 = s_box[v8]
        v8 = s_box[v5]
        v5 = v9 ^ AB[(i >> 2) + 1]
    ENC_TABLE[4*i+16] = v5 ^ ENC_TABLE[4*i + 0]
    ENC_TABLE[4*i+17] = v6 ^ ENC_TABLE[4*i + 1]
    ENC_TABLE[4*i+18] = v7 ^ ENC_TABLE[4*i + 2]
    ENC_TABLE[4*i+19] = v8 ^ ENC_TABLE[4*i + 3]


def FIND_INV_XORS(K):
    eT = K[0]^K[1]^K[2]^K[3]
    eCxD = K[0] ^ K[2] ^ ((27 * ((eT >> 7))) ^ (2 * (eT)))&0xff
    eAxB = K[1] ^ K[3] ^ ((27 * ((eT >> 7))) ^ (2 * (eT)))&0xff 
    #eBxC = K[0] ^ K[1] ^ ((27 * (eCxD >> 7) ^ (2 * eCxD)))&0xff
    eAxD = K[2] ^ K[3] ^ ((27 * (eCxD >> 7) ^ (2 * eCxD)))&0xff
    #eBxD = K[1] ^ K[2] ^ ((27 * (eAxB >> 7) ^ (2 * eAxB)))&0xff
    eAxC = K[0] ^ K[3] ^ ((27 * (eAxB >> 7) ^ (2 * eAxB)))&0xff
    for i in range(256):
        A = i
        B = eAxB ^ A
        C = eAxC ^ A
        D = eAxD ^ A
        T = A ^ B ^ C ^ D;
        if K[0] == (T ^ (27 * ((B ^ C) >> 7)) ^ (2 * (B ^ C)) ^ C)&0xff and K[1] == (T ^ (27 * ((D ^ B) >> 7)) ^ (2 * (D ^ B)) ^ B)&0xff:
            if K[2] == (T ^ (27 * ((A ^ D) >> 7)) ^ (2 * (A ^ D)) ^ D)&0xff and K[3] == (T ^ (27 * ((A ^ C) >> 7)) ^ (2 * (A ^ C)) ^ A)&0xff:
                return (A,B,C,D)
    raise BaseException

MAP = {}
def REVERSE_XORS(ENC):
    if bytes(ENC) in MAP:
        return copy.deepcopy(MAP[bytes(ENC)])
    S = [0 for i in range(16)]
    T = [0 for i in range(16)]
    S[0] = ENC[0] ^ ENC_TABLE[160] 
    S[5] = ENC[1] ^ ENC_TABLE[161] 
    S[10] = ENC[2] ^ ENC_TABLE[162] 
    S[15] = ENC[3] ^ ENC_TABLE[163] 
    S[4] = ENC[4] ^ ENC_TABLE[164] 
    S[9] = ENC[5] ^ ENC_TABLE[165] 
    S[14] = ENC[6] ^ ENC_TABLE[166] 
    S[3] = ENC[7] ^ ENC_TABLE[167] 
    S[8] = ENC[8] ^ ENC_TABLE[168] 
    S[13] = ENC[9] ^ ENC_TABLE[169] 
    S[2] = ENC[10] ^ ENC_TABLE[170]
    S[7] = ENC[11] ^ ENC_TABLE[171]
    S[12] = ENC[12] ^ ENC_TABLE[172]
    S[1] = ENC[13] ^ ENC_TABLE[173]
    S[6] = ENC[14] ^ ENC_TABLE[174]
    S[11] = ENC[15] ^ ENC_TABLE[175]
    C = 10
    while True:
        for i in range(16):
            S[i] = Sbox_inv[S[i]]
        C -= 1
        for i in range(16):
            S[i] ^= ENC_TABLE[16*C+i]

        if C == 0:
            break
        
        (T[11],T[1],T[12],T[6]) = FIND_INV_XORS((S[12],S[13],S[14],S[15]))
        (T[7],T[13],T[8],T[2]) = FIND_INV_XORS((S[8],S[9],S[10],S[11]))
        (T[3],T[9],T[4],T[14]) = FIND_INV_XORS((S[4],S[5],S[6],S[7]))
        (T[15],T[5],T[0],T[10]) = FIND_INV_XORS((S[0],S[1],S[2],S[3]))
        for i in range(16):
            S[i] = T[i]
    MAP[bytes(ENC)] = copy.deepcopy(S)
    return S

f = open("rewrite.png.enc","rb")
ENC = f.read()
f.close()
SEED = ENC[-16:]
ENC = ENC[:-16]
DEC_1 = []
S = [0 for _ in range(16)]
print(len(ENC)//16)

PNG = THE_PNG_STATER()

ORI_SEED = ['' for _ in range(len(ENC)//16)]
ORI_SEED[0] = SEED
CONT = [[0,0] for _ in range(len(ENC)//16)]
COUT = 0
I = 0


import sys
def my_except_hook(exctype, value, traceback):
    with open("ORI_SEED.pickle","wb") as fw:
        pickle.dump(ORI_SEED, fw)
    with open("CONT.pickle","wb") as fw:
        pickle.dump(CONT, fw)  
    with open("I.pickle","wb") as fw:
        pickle.dump(I, fw)        
    with open("OUTPUT_str.pickle","wb") as fw:
        pickle.dump(OUTPUT_str, fw)   
    with open("PNG.pickle","wb") as fw:
        pickle.dump(PNG, fw)   
    sys.__excepthook__(exctype, value, traceback)
sys.excepthook = my_except_hook

if False:
    with open("ORI_SEED.pickle","rb") as fw:
        ORI_SEED = pickle.load(fw)
    with open("CONT.pickle","rb") as fw:
        CONT = pickle.load(fw)  
    with open("I.pickle","rb") as fw:
        I = pickle.load(fw)        
    with open("OUTPUT_str.pickle","rb") as fw:
        OUTPUT_str = pickle.load(fw)   
    with open("PNG.pickle","rb") as fw:
        PNG = pickle.load(fw)   

while I < (len(ENC)//16):

    print(I)
    CN_s = CONT[I][0]
    CB_s = CONT[I][1]
    tmp = ENC[I*16:I*16+16]
    enc = []
    for i in tmp:
        enc.append(i)

    if I == (len(ENC)//16)-1:

        T = REVERSE_XORS(enc)
        for i in range(16):
            T[i] ^= ORI_SEED[I][i]
        if T[15] != 0:
            CONT[I][0] = 0
            CONT[I][1] = 0
            I -= 1
            continue
        
        print(T)
        OUT = ""
        for i in range(16):
            OUT += f"{T[i]:08b}"[::-1]
        if PNG.check_data(I,copy.deepcopy(OUT)) == False:
            continue

        CONT[I][0] = 0
        CONT[I][1] = 0
        I -= 1
        continue
    F = 0
    for CN in range(CN_s,16):
        for CB in range(CB_s,8):
            enc[15-CN] ^= 1 << (7-CB)
            T = REVERSE_XORS(enc)
            enc[15-CN] ^= 1 << (7-CB)
            for i in range(16):
                T[i] ^= ORI_SEED[I][i]
            TMP3 = ""
            for i in range(16):
                TMP3 += f"{T[i]:08b}"[::-1]
            TMP2 = ['' for _ in range(128)]
            for i in range(121):
                TMP2[i] = TMP3[i+3]
            TMP2[127] = TMP3[0]
            TMP2[122] = TMP3[1]
            TMP2[126] = TMP3[2]
            TMP2[124] = TMP3[124]
            TMP2[125] = TMP3[125]
            TMP2[123] = TMP3[126]
            TMP2[121] = TMP3[127]
            hash = ""
            for i in range(127,120,-1):
                hash += TMP2[i]
            HASH = int(hash,2)
            if HASH_BIT(TMP2) != HASH:
                continue
            
            TMP = ['' for i in range(121)]
            for t in range(121):
                TMP[BIT_SBOX[t]] = TMP2[t]
            OUT = ""
            for t in range(121):
                OUT += TMP[t]
            
            if PNG.check_data(I,copy.deepcopy(OUT)) == False:
                continue

            
            F = 1
            OUTPUT_str[I] = copy.deepcopy(OUT)
            tmp = copy.deepcopy(enc)
            tmp[15-CN] ^= 1 << (7-CB)
            CONT[I][0] = CN
            CONT[I][1] = CB + 1         
            ORI_SEED[I+1] = tmp
            I += 1
            break
        CB_s = 0
        if F == 1:
            break
    if F == 0:
        CONT[I][0] = 0
        CONT[I][1] = 0
        I -= 1

 

 Python으로 코딩하다가 잘못됨을 느꼈지만 C로 포팅하기에는 늦음을 알고 쭉 진행했습니다.

재귀 호출로 DFS를 구현하려다가 스택이 터지는 문제를 보고 while로 억지로 구현하였고, pypy로 실행한 결과 한 1시간 30분 만에 생성되었다.

 

 원래는 아래에 더 있어야 하지만 대충 플레그 나온 곳 까지만 보았다.

 

'CTF' 카테고리의 다른 글

2017 DIMICTF problems & 후기  (0) 2017.07.22
Google CTF 2017 Moon  (0) 2017.06.23
Plaid CTF 2017 BB8  (0) 2017.06.02
SSG 2017 Write Up  (0) 2017.06.02
Plaid CTF 2017 Down the Reversing Hole  (0) 2017.04.24
반응형

MD5: 43978645947da7404f705386a6bfaf6b


.NET으로 만들어 진 Malware입니다.



"JohnJack.Faster.org"라는 리소스에서 Base64 디코딩을 합니다.

key를 UTF8로 인코딩 한 뒤 MD5 hash를 통해 TripleDES ECB의 키로 사용하는 것을 볼 수 있습니다.

이후 디코딩 된 리소스를 실행하는 것 같습니다.




간단히 긁어와서 파일로 출력하게 했고 MZ 헤더가 보입니다.

또 .NET 파일입니다.


TO BE CONTINUE

반응형

주관적인 리버싱 공부 단계

1. 코딩

코딩 능력은 리버싱 뿐만 아니라 여러분이 해킹공부할때 거의 반드시 필요하게 되어 있다.

python 같은 언어는 배우기 쉽고 빠르게 응용할 수 있으므로(생산성이 좋다고 한다) 추천한다. 이미 다른 언어를 잘 사용할 수 있다면 그걸 계속 쓰는 것이 맞다. 리버싱을 공부하게 된다면 C언어 또한 배우는 것을 추천한다.

2. 어셈블리

어셈블리 언어가 어렵다고 하는데 이는 사람마다 다르다(마치 영어처럼). 하지만 리버싱을 공부하는 이상 어셈블리 언어는 반드시 공부해야 하는 것 중 하나이다.

어셈블리 언어는 아키텍쳐 마다 다르다. 하지만 우리가 쓰는 x86,x64를 위주로 공부하는 것이 좋다. 이후 다른 어셈블리 언어를 볼 때 도움이 될 수 있다.

어셈블리 언어는 어려워 보이지만 팁을 주자면 대부분 영어 약칭을 사용하는 경우가 많기에 꾸준히 보고 공부한다면 쉽게 외울 수 있을 것이다.

3. 분석

분석 능력을 키우는 것은 처음에는 너가 만든 프로그램을 직접 어셈블리 디버거로 분석해 보는 것이 좋다. 이후 디버거 정보를 제거한다던지 난이도를 높이면 좋다.

리버싱에서 분석 방법은 숲에서 나무로, 나무에서 숲으로 두 가지가 있을 수 있다. 이것은 너 마음대로 하면 되지만 분석하다 중요한 부분을 놓치면 안된다는 말을 전하고 싶다.

처음부터 IDA PRO 와 같은 프로그램을 사용하면 편하겠지만 가끔 분석이 안되는 것이 있을때도 있다(MIPS, 스택 범위 초과)

그때는 어셈을 가지고 분석해야 하므로 분석 능력을 배우는 것은 좋다.

4. 여러가지 안티 디버깅 공부, packer, 트릭들

몇 프로그램은 여러 안티 디버깅이 존재한다. 나도 이것 전체를 외우지 못했으며 간간히 찾아 보는 일이 있다. 하지만 IsDebuggerPresent와 같은 기본적인 부분은 공부해야 편할 것이다. 그리고 여러 트릭들은 문제를 풀어보며 배우는 것이 좋을 듯 하다

5. 인내 그리고 검색

아마도 제일 중요하지 싶다. 내가 삽질이라고 주위에 말하는 건데 리버싱은 삽질이 많이 필요할 때가 많다. 그래서 포기하지 않고 꾸준이 할 수 있는 인내심이 필요하다.

또한 리버싱을 공부하면 항상 모르는 부분이 나올 것이다. 그런건 검색을 해라 대부분 다른 분들이 미리 알아내서 정리해둔 글이 있을 것이다. 그리고 대부분 글은 영어다.




'Reversing > Etc' 카테고리의 다른 글

APK 정적 분석  (0) 2017.02.17
Angr 설치  (4) 2017.01.20
반응형

본선 문제: https://drive.google.com/open?id=0B-vUealQ_HWMdnl5ZXQ3VFVFVlE(MISC 제외)

예선 문제:  https://drive.google.com/open?id=0B-vUealQ_HWMTTlZU1BrdmNyOTA


예선 문제들 중 400점이 문제가 있었는데, angr로 풀릴줄은 생각하지 않다가, 거하게 뒷통수를 맞아버림...

본선 문제는 300점 빼면 쉽게 냈다고 생각했는데 워낙 못풀길레 오후 문제를 오전에 급조하며 만들었음,

misc문제는 본선때 딱 하나만 냈는데 tesseract-ocr를 쓰면 쉽게 풀 수 있었음(내가 풀이 만들면서 잘 인식되는걸로 조정함)


리버싱 문제를 내면서 ANGR, OCR, Z3 등이 자주 쓰인다는 것을 알려주려고 했었음


'CTF' 카테고리의 다른 글

CodeGate 2022 NDEncryptor  (0) 2022.02.28
Google CTF 2017 Moon  (0) 2017.06.23
Plaid CTF 2017 BB8  (0) 2017.06.02
SSG 2017 Write Up  (0) 2017.06.02
Plaid CTF 2017 Down the Reversing Hole  (0) 2017.04.24
반응형

이름의 의미가 뭔지는 모르겠지만

이번 문제는 Moon 이다.


OpenGL을 사용하고 있었고 다른 팀원들 컴퓨터에서는 작동을 하지 않았기 때문에

나 혼자서 풀었어야 했었다.



We choose to go to the moon. We choose to go to the moon in this decade and do the other things, not because they are easy, but because they are hard, because that challenge is one that we are willing to accept, one we are unwilling to postpone, and one which we intend to win.

그럼 문제를 풀어보자



main 화면을 디스어셈 한 일부분이다.

main 내부가 크고 복잡하였기 때문에 일부분만 분석하였다.



main 부분에서 중요한 부분이다.

150 번째 줄에서 memcmp를 하는 것을 볼 수 있는데

이 부분을 x64 dbg로 참이 되게 바꾸니 Good 이라고 나오는 것을 확인했다.


Buf2 의 값은

30c7ead97107775969be4ba00cf5578f1048ab1375113631dbb6871dbe35162b1c62e982eb6a7512f3274743fb2e55c818912779ef7a34169a838666ff3994bb4d3c6e14ba2d732f14414f2c1cb5d3844935aebbbe3fb206343a004e18a092daba02e3c0969871548ed2c372eb68d1af41152cb3b61f300e3c1a8246108010d282e16df8ae7bff6cb6314d4ad38b5f9779ef23208efe3e1b699700429eae1fa93c036e5dcbe87d32be1ecfac2452ddfdc704a00ea24fbc2161b7824a968e9da1db756712be3e7b3d3420c8f33c37dba42072a941d799ba2eebbf86191cb59aa49a80ebe0b61a79741888cb62341259f62848aad44df2b809383e09437928980f

이었고 sub_401BF0 에서 주 연산이 이루어 진다는 것을 분석하였다.


문제는 분석하면서 qword_4CC528 라는 함수에서 갑자기 입력값에서 시리얼리 갑자기 툭 나온다는 것이었다.

라이브러리 끝까지 분석해 봤는데 syscall 나와서 멘붕했다는...

나중에 xref로 따라가 보니 GetProcAddress로 glClientWaitSync 이라는 함수 주소를 저장하는 걸 보고 냉장고에 머리를 찍었다.



위 사진은 sub_401BF0 내부이다.

여기서 glClientWaitSync를 실행하고 나면 시리얼이 만들어 지고 %.8x를 통해 문자열 화 될 것이다.



glClientWaitSync를 실행하기 전에 수행되는 작업인데 이 부분을 어셈으로 보면



이렇게 나오며 RAX를 hexdump로 아랫부분을 살펴보면



이렇게 입력값이 들어가 있다. 아마도 구조체인거 같은데 분석하기 귀찮고 직관적이어서 PASS 했다.



glClientWaitSync를 실행하게 되면 xmm0으로 넣었던 01 부분이 02가 되고 시리얼 값이 나오게 된다.



이제 glClientWaitSync이게 뭐 하는 함수인지 보자.



딱 보니 thread에서 wait 같이 기다리는 함수인거 같다.

sync을 찾아보니 glFenceSync의 반환값이라는 것을 알 수 있었고

인자 중 0x9117는 SYNC_GPU_COMMANDS_COMPLETE 였다.

아마도 GPU를 쓰는 거 같다. 


COMMANDS 라고 해서 sub_401BF0 함수 내부를 뜯어보던 중



이런 부분이 있길레 dword_4CA0A8를 xref로 찾아 보니



이런 부분이 나왔다.

62번째 줄 윗부분에는 Memory를 가지고 많은 연산을 하고 있었다.



sub_401770 내부이다. Compile 나오고 Source 나오고 난리났다.

뭔가 엄청나게 수상해 보인다.

따라서 디버거로 sub_401770 호출 부분에 BP를 걸고 인자 내부를 보았다.



????????



GLSL 이다.

소스

#version 430
layout(local_size_x=8,local_size_y=8)in;
layout(std430,binding=0) buffer shaderExchangeProtocol{uint state[64];uint hash[64];uint password[32];};
vec3 calc(uint p)
{
	float r=radians(p);
	float c=cos(r);
	float s=sin(r);
	mat3 m=mat3(c,-s,0.0,s,c,0.0,0.0,0.0,1.0);
	vec3 pt=vec3(1024.0,0.0,0.0);
	vec3 res=m*pt;
	res+=vec3(2048.0,2048.0,0.0);
	return res;
}
uint extend(uint e)
{
	uint i;uint r=e^0x5f208c26;
	for (i=15;i<31;i+=3)
	{
		uint f=e<<i;
		r^=f;
	}
	return r;
}
uint hash_alpha(uint p)
{
	vec3 res=calc(p);
	return extend(uint(res[0]));
}
uint hash_beta(uint p)
{
	vec3 res=calc(p);
	return extend(uint(res[1]));
}
void main()
{
	uint idx=gl_GlobalInvocationID.x+gl_GlobalInvocationID.y*8;
	uint final;
	if (state[idx]!=1)
	{
		return;
	}
	if ((idx&1)==0)
	{
		final=hash_alpha(password[idx/2]);
	}
	else
	{
		final=hash_beta(password[idx/2]);
	}
	uint i;
	for (i=0;i<32;i+=6)
	{
		final^=idx<<i;
	}
	uint h=0x5a;
	for (i=0;i<32;i++)
	{
		uint p=password[i];
		uint r=(i*3)&7;
		p=(p<<r)|(p>>(8-r));
		p&=0xff;
		h^=p;
	}
	final^=(h|(h<<8)|(h<<16)|(h<<24));
	hash[idx]=final;
	state[idx]=2;
	memoryBarrierShared();
}

뭔가 많이 복잡해 보이지만 쉽다.

입력한 한 글자마다 hash_alpha, hash_beta를 각각 만들어서 final로 저장하는 부분

충분히 계산 가능한 idx 가지고 연산하는 부분

마지막으로 역연산이 불가능 해 보이는 모든 password가지고 연산하는 부분 

3부분으로 이루어 진다.


첫 번째와 두 번째는 충분히 GLSL 코드를 변경하든지 해서 값을 뽑아올 수 있지만

마지막이 막막해 보이지만 2n 번째 hash와 2n+1 번째 hash 각각 2번째 부분을 해결 한뒤

서로 xor 하게 되면 마지막 부분이 xor의 성질에 따라 사라지고 hash_alpha ^ hash_beta를 한 값과 동일해 지게 된다.


따라서 hash_alpha와 hash_beta 가 출력하는 값을 가져오면 된다.

이부분은 GLSL이 C 기반이기도 하고 쉽기 때문에 PASS


from struct import unpack
f = open("ihash","rb")
hashtxt = f.read()
f.close()
f = open("alpha","rb")
alphatxt = f.read()
f.close()
f = open("beta","rb")
betatxt = f.read()
f.close()

hashs= []
for i in range(64):
    hashs.append(unpack(">I",hashtxt[i*4:(i+1)*4])[0])
alpha=[]
for i in range(0x60):
    alpha.append(unpack("<I",alphatxt[i*4:(i+1)*4])[0])
beta=[]
for i in range(0x60):
    beta.append(unpack("<I",betatxt[i*4:(i+1)*4])[0])
    
for i in range(64):
    fin = 0
    for j in range(0,32,6):
        fin ^= i << j
    hashs[i] ^= fin

where = []

for i in range(0x00,0x60):
    where.append(alpha[i] ^ beta[i])

txt = ""
for i in range(32):
    txt += chr(where.index(hashs[i*2] ^ hashs[i*2+1])+0x20)
print txt


이렇게 하면 답이 CTF{OpenGLMoonMoonG*esT*TheMoon} 이렇게 나왔다.

그러나 인증이 되지 않았고 실제 입력해 보니 답이 아니라고 나왔다.



삽질하던 도중 팀원분이 *를 0 으로 바꾸니 인증이 됬다고 하셨다.

원인을 알아보니 *와 0의 hash_alpha ^ hash_beta 값이 같았기 때문이다.



OpenGL를 강제적으로 배울 수 있는 계기였다.

FLAG: CTF{OpenGLMoonMoonG0esT0TheMoon}

'CTF' 카테고리의 다른 글

CodeGate 2022 NDEncryptor  (0) 2022.02.28
2017 DIMICTF problems & 후기  (0) 2017.07.22
Plaid CTF 2017 BB8  (0) 2017.06.02
SSG 2017 Write Up  (0) 2017.06.02
Plaid CTF 2017 Down the Reversing Hole  (0) 2017.04.24
반응형

이 문서는 https://www.chromium.org/quic/quic-faq 를 번역한 문서입니다. 오역 및 의역이 매우 많을 가능성이 있습니다. :)

QUIC 작동 방법에 관한 문서가 있습니까?

2015 년 4 월, 우리는 QUIC에서 보았던 성능상의 이점에 대한 블로그 게시물을 썼습니다.
게시물: https://blog.chromium.org/2015/04/a-quic-update-on-googles-experimental.html

Chrome을 빌드하지 않고 간이 클라이언트, 서버를 구축 할 수 있습니까?

넵. quic_server 타겟은 Chrome전체를 빌드하지 않고 만들 수 있습니다. playing-with-quic 문서에 따라 독립 실행 형 QUIC 클라이언트 및 서버를 빌드하고 실행할 수 있습니다.

테스트 서버에서 어떻게 Chrome을 목표로 지정합니까?

HTTP 서버가 있다면 다음과 같은 응답 헤더를 보내야합니다.


Alternate-Protocol: quic:<QUIC server port>


그런 다음 평상시처럼 크롬을 실행할 수 있으며, 자동으로 QUIC를 사용하게 됩니다.

간이 QUIC 서버로만 테스트하는 경우 다음과 같이 할 수 있습니다.


% chrome --disable-setuid-sandbox --enable-quic --origin-to-force-quic-on=localhost:6121 http://localhost:6121/


문제 해결에 도움이 필요하면 –v = 1을 사용하여 QUIC 서버를 실행하거나 playing-with-quic 문서를 확인하십시오.

전송 계층이 암호화되는 이유는 무엇입니까?

QUIC(혼잡 관련 정보)에 대한 전송 정보는 전송이 항상 발전할 수 있다는 것을 보장하기 위해 주로 암호화 됩니다. 만약 ACK또는 checksum이 올바른 경우, 우려되는 것은 결국 미들박스는 혼잡정보를 분석하기 시작할 것이고 어떠한 전향적인 변화와도 단절될 것입니다. 이것이 현재 TCP의 문제입니다. 유선 형식은 인터넷 상의 현재 하드웨어의 기대 때문에 협상 옵션들과 실제적으로 사용 불가능한 유연한 기능을 허용합니다.

물론 아랫쪽인 미들 박스에서 세부 정보를 숨기는 것은 엔드 포인트를 제어 할 수 없다면 QUIC를 분석하기 어렵다는 것을 의미합니다. tcpdump 툴은 패킷의 비율과, 패킷의 틈을 시각화 할 수 있지만, 어떤 패킷이 페이로드, 혼잡 정보, 재전송 등을 포함하고 있는지 판단하기 어렵습니다. 클라이언트와 서버측 코드는 사용자 공간 처리 중에 이러한 정보를 쉽게 덤프할 수 있도록 설계되어 있습니다. 이러한 로그는 tcpdumps보다 더 많은 데이터를 포함할 수 있으며 커널 수준의 패킷 추적과 함께 묶어 전체 시스템에서 대기 시간을 더 잘 파악할 수 있습니다.

'Network > QUIC' 카테고리의 다른 글

QUIC overview  (0) 2017.06.03
반응형

이 문서는 https://docs.google.com/document/d/1gY9-YNDNAB1eip-RTPbqphgySwSNSDHLq9D5Bty4FSU/edit 를 번역한 문서입니다. 오역 및 의역이 매우 많을 가능성이 있습니다. :)

QUIC란?

QUIC (Quick UDP Internet Connections) Google에서 개발한 새로운 인터넷 전송 프로토콜 입니다.

QUIC는 사용자들은 거의 또는 전혀 변경하지 않으면서, 최신 웹 애플리케이션에서 발생하는 수많은 전송 계층과 응용 계층 문제를 해결합니다. QUIC는 TCP+TLS+HTTP2와 비슷하지만 UDP위에 구현됩니다. 자체적으로 만들어진 프로토콜인 QUIC를 사용하면 레거시 클라이언트와 미들박스에 의해 방해받는 기존의 프로토콜로는 불가능한 혁신을 가능케 할 수 있습니다.

QUIC over TCP+TLS+HTTP2의 주요 이점들:

연결 설정 대기 시간
혼잡 제어 개선
Head-of-line blocking(HOL blocking)문제 없이 멀티플렉싱
전진 에러 수정(FEC)
연결 마이그레이션

연결 설정

연결 설정에 대해 자세히 알고 싶다면 QUIC Crypto design 문서를 참조하세요. 간단히, 페이로드를 보내기 위해 1~3번의 왕복이 필요한 TCP+TLS와 비교해서 QUIC의 handshake는 자주 왕복을 필요로 하지 않습니다.

처음으로 QUIC 클라이언트가 서버에 접속할때, 클라이언트는 반드시 handshake를 완료하는데 필요한 정보를 얻기 위해 1 왕복 handshake를 수행해야 합니다. 클라이언트가 inchoate(empty) client hello(CHLO)를 보내면, 서버는 소스 주소 토큰과 서버 인증서를 포함하여 클라이언트가 진행하는데 필요한 정보를 rejection(REJ)와 함께 보냅니다. 다음번에 클라이언트가 CHLO를 전송하면, 이전 연결의 캐시 된 자격 증명을 사용하여 암호화된 요청을 즉시 서버에 보낼 수 있습니다.

혼잡 제어

QUIC는 플러그형 혼잡 제어를 가지고 있으며, TCP보다 혼잡제어 알고리즘에 더 많은 정보를 제공합니다. 현재, Google의 QUIC는 TCP Cubic의 재 구현을 사용하고 있으며 대안적인 접근법을 실험하고 있습니다.

많은 정보의 예는 원본과 재전송된 각 패킷이 새로운 시퀸스 번호를 전달한다는 것입니다. 이를 통해 QUIC 전달자는 원본에 대한 ACK과 재전송을 위한 ACK을 구별할 수 있으며 TCP의 재전송 모호 문제를 피할 수 있습니다. 또한 QUIC ACK는 패킷 수신과 전송 확인 사이의 지연을 단조롭게 전달하고 단조롭게 증가하는 시퀀스 번호와 함께 전달합니다. 이를 통해 정확한 왕복 시간을 계산할 수 있습니다.

마지막으로, QUIC의 ACK 프레임은 최대 256 NACK 범위를 지원하므로, QUIC는 TCP(SACK포함)보다 순서 재 지정에 더 탄력적이며, 재 지정 또는 손실이 있을때 더 많은 바이트를 망에 보관 가능합니다. 클라이언트와 서버 모두 각자가 수신 한 패킷을 정확하게 파악할 수 있습니다.

멀티플렉싱

TCP+HTTP2가 갖는 큰 문제중 하나는 head-of-line blocking(HOL blocking) 문제입니다. 애플리케이션은 TCP 연결을 바이트 스트림으로 간주합니다. TCP 패킷이 손실되면, 해당 HTTP2 연결 스트림은 패킷이 재 전송되어 상대방이 수신 할 때까지 계속 진행할 수 없습니다. 이러한 스트림에 대한 데이터가있는 패킷이 도착하여 버퍼에서 대기중인 경우에도 마찬가지입니다.

QUIC는 멀티플렉싱 동작을 위해 처음부터 디자인 되었기 때문에, 개별 스트림에 대한 데이터를 가지고 있는 손실 패킷은 일반적으로 특정 스트림에만 영향을 미칩니다. 각 스트림 프레임은 도착시 해당 스트림으로 전달 할 수 있으므로, 손실이 없는 스트림은 재구성될 수 있고 애플리케이션에서 진행될 수 있습니다.

전진 에러 수정(FEC)

재전송을 기다리지 않고 손실된 패킷을 복구하기 위해, QUIC는 FEC 패킷으로 패킷 그룹을 보완할 수 있습니다. RAID-4와 유사하게 FEC 패킷은 FEC 그룹의 패킷들의 패리티를 포함합니다. 그룹 내의 패킷 중 하나가 손실되면, 그 패킷의 내용은 FCE 패킷과 그룹 내부에 남아있는 패킷들로부터 복구할 수 있습니다. 전송자는 특정 시나리오(예시: 요청의 시작과 끝)를 최적화하기 위해 FEC 패킷을 보낼지 여부를 결정할 수 있습니다.

연결 마이그레이션

QUIC 연결은 클라이언트에서 임의로 생성된 64bit 연결 ID로 식별됩니다. 반대로 TCP 연결은 송신 주소, 송신 포트, 수신 주소, 수신 포트라는 4개의 튜플로 식별됩니다. 이것은 만약 클라이언트의 IP주소가 바뀌거나(예시: Wi-Fi 범위를 벗어나서 데이터 모드로 전환)또는 포트(NAT box가 손실되어 포트를 리바인딩 하는 경우)가 바뀌는 경우 활성화 된 TCP 연결은 더 이상 유효하지 않습니다. QUIC 클라이언트가 IP를 변경하게 되면, 새로운 IP 주소에서 이전 연결 ID를 사용하여 진행중인 요청을 방해받지 않을 수 있습니다.

'Network > QUIC' 카테고리의 다른 글

QUIC FAQ  (0) 2017.06.03
반응형

아직도 생각하면 멘탈 나가는 문제

양자 통신 프로토콜 BB84를 구현한 문제이다.

https://ko.wikipedia.org/wiki/%EC%96%91%EC%9E%90%EC%95%94%ED%98%B8 : 위키백과

우리는 Alice와 Bob이 통신하는 것을 도청과 값을 바꿀 수 있는데, 이 때문에 Man in the Middle Attack이 가능하다.


일단 어떤 절차인지 알아보자

일단 이 프로그램은 편광필터(기저, Basis)를 Y와 Z로 구분했고 비트는 -1, 1이 존재한다.

1. Alice가 600개의 랜덤한 기저와 비트를 생성해서 Bob에게 보낸다. Bob은 Z기저 1을 보내어 ACK 확인을 한다.

2. Bob은 600개를 받는 즉시 기저를 Z에 맞추고, 추측한 600개의 기저를 Alice에게 Z로 추측했다면 -1을 Y로 추측했다면 1을 보내고, Alice는 Z기저로 추측한 기저가 맞다면 1을 틀리다면 -1을 보낸다.

3. Alice와 Bob은 도청이 있는지 확인하기 위해서 짝수번째 비트들을 Z기저를 통해 보낸다. Alice는 Z기저로 1을 보내어 ACK 확인을 한다.

4. 이후 Alice와 Bob은 도청이 없다고 확인한 뒤 홀수번째 비트들로 AES 128bit키를 만든다(-1 -> 0, 1 -> 1)

5. Alice와 Bob은 AES128-ECB 통신을 한다.


해석만 할 줄 알면 쉬운 문제였는데 문제는 통합 1500+a 통신을 하는 것을 감청하고 바꿔야 해서 

한번 시도 하는데 30분이 넘게 걸려서 멘탈이 파쇄당했다. 한번에 비트들을 보냈으면 더 좋지 않았을까 한다.



from pwn import *

r = remote("bb8.chal.pwning.xxx",20811)

def GetQubit():
    ZAq = []
    ZBq = []
    for i in range(599):
        print i
        r.sendlineafter("Bob, do you want to intercept (y/N)?","y")
        r.sendlineafter("(Z/Y)","Z")
        r.recvuntil("measured ")
        K = r.recvuntil("\n")
        ZAq.append(int(K))
        r.sendlineafter("(Y)?","Y")
        r.sendlineafter("(Z/Y)","Z")
        r.sendlineafter("(-1/1)","-1")
        ZBq.append(0)
        r.sendlineafter("(y/N)?","N")

    print "600"
    r.sendlineafter("Bob, do you want to intercept (y/N)?","y")
    r.sendlineafter("(Z/Y)","Z")
    r.recvuntil("measured ")
    K = r.recvuntil("\n")
    ZAq.append(int(K))
    r.sendlineafter("(Y)?","Y")
    r.sendlineafter("(Z/Y)","Z")
    ZBq.append(0)
    r.sendlineafter("(-1/1)","-1")
    return (ZAq,ZBq)

def WatchBases(ZAq,ZBq):
    CAq = []
    CBq = []
    C = 0
    T = 0
    for i in range(600):
        print i
        r.sendlineafter("(y/N)?","y")
        r.sendlineafter("(Z/Y)","Z")
        r.recvuntil("measured ")
        B = r.recvuntil("\n")
        r.sendlineafter("(Y)?","Y")
        r.sendlineafter("(Z/Y)","Z")
        r.sendlineafter("(-1/1)","-1")
        r.sendlineafter("(y/N)?","y")
        r.sendlineafter("(Z/Y)","Z")
        r.recvuntil("measured ")
        K = r.recvuntil("\n")
        if int(K) == 1:
            CAq.append(ZAq[i])
            C+=1
        r.sendlineafter("(Y)?","Y")
        r.sendlineafter("(Z/Y)","Z")
        if T <= 256:
            
            if int(B) == 1 or C == 0:
                r.sendlineafter("(-1/1)","-1")
            else:
                T+=1
                r.sendlineafter("(-1/1)","1")
                CBq.append(ZBq[i])
                C -= 1
        else:
            if C != 0:
                r.sendlineafter("(-1/1)","1")
                CBq.append(ZBq[i])
                C -= 1
            else:
                r.sendlineafter("(-1/1)","-1")
    return (CAq,CBq)

def Check(Cq):
    for i in range(0, len(Cq), 2):
        print i
        r.sendlineafter("(y/N)?", "y")
        r.sendlineafter("(Z/Y)","Z")
        r.sendlineafter("(Y)?","Y")
        r.sendlineafter("(Z/Y)","Z")
        r.sendlineafter("(-1/1)",str(Cq[i]))
        r.sendlineafter("(y/N)?","y")
        r.sendlineafter("(Z/Y)","Z")
        r.recvuntil("measured ")
        K = r.recvuntil("\n")
        if int(K) != 1:
            print "fuck!"
        r.sendlineafter("(Y)?","N")

def main():
    ZAq,ZBq = GetQubit()
    print ZAq
    print ZBq
    CAq,CBq = WatchBases(ZAq,ZBq)
    print CAq
    print CBq
    print len(CAq)
    print len(CBq)
    assert(len(CAq) == len(CBq))
    Check(CAq)

    KeybitsA = []
    KeybitsB = []

    for i in range(len(CAq)):
        if CAq[i] == -1:
            KeybitsA.append(0)
        else:
            KeybitsA.append(1)

    for i in range(len(CBq)):
        if CBq[i] == -1:
            KeybitsB.append(0)
        else:
            KeybitsB.append(1)

    print KeybitsA
    print KeybitsB
    aes_keyA = 0
    aes_keyB = 0
    for i in range(128):
        aes_keyA |= (KeybitsA[2*i+1] << (127-i))
        aes_keyB |= (KeybitsB[2*i+1] << (127-i))

    print "aes key A : " + hex(aes_keyA)
    print "aes key B : " + hex(aes_keyB)

    r.interactive()

if __name__ == "__main__":
    main()


'CTF' 카테고리의 다른 글

2017 DIMICTF problems & 후기  (0) 2017.07.22
Google CTF 2017 Moon  (0) 2017.06.23
SSG 2017 Write Up  (0) 2017.06.02
Plaid CTF 2017 Down the Reversing Hole  (0) 2017.04.24
CodeGate 2017 Event Challenge Keypad  (0) 2017.04.14
반응형

포너블 실력은 어디갔는지 보이지를 않네;;


SSGWriteUp.pdf


'CTF' 카테고리의 다른 글

Google CTF 2017 Moon  (0) 2017.06.23
Plaid CTF 2017 BB8  (0) 2017.06.02
Plaid CTF 2017 Down the Reversing Hole  (0) 2017.04.24
CodeGate 2017 Event Challenge Keypad  (0) 2017.04.14
DoubleS1405 Write UP  (0) 2017.04.01
반응형

Docker

https://docs.docker.com/install/linux/docker-ce/ubuntu/


Docker Compose

sudo curl -L "https://github.com/docker/compose/releases/download/1.10.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose


문제 서버 만들때 매우 좋다

문제 하나 터져서 다른 문제가 터지는 일을 방지할 수 있음


내가 쓰는 거

docker-compose.yml

version: '2.2'


services:

    treefrog:

        build: ./

        volumes: 

            - ./share:/home/treefrog:ro

            - ./xinetd:/etc/xinetd.d/xinetd:ro

            - ./tmp:/tmp:ro

        ports:

            - "10101:10101"

        expose:

            - "10101"

        

networks:

    default:

        external:

            name: treefrog


Dockerfile

FROM ubuntu:16.04

MAINTAINER KSHMK

RUN apt update

RUN apt install xinetd -y

RUN apt install libc6-dev-i386 -y

RUN useradd -m {계정명}

RUN chmod 774 /tmp

RUN chmod -R 774 /var/tmp

RUN chmod -R 774 /dev

RUN chmod -R 774 /run

RUN chmod 1733 /tmp /var/tmp /dev/shm

RUN chown -R root:root /home/{계정명}

CMD ["/usr/sbin/xinetd","-dontfork"] 


xinetd

service treefrog 

{

    disable = no

    type        = UNLISTED

    wait        = no

    server      = /home/treefrog/treefrog

    socket_type = stream

    protocol    = tcp

    user        = treefrog

    port        = 10101

    flags       = REUSE


'Pwnable > Etc' 카테고리의 다른 글

BFF Fuzzer 약간설명&옵션설명  (1) 2016.02.17
SongSari - Basic Bof  (0) 2015.12.01
Pin Tool 간단 설명  (0) 2015.11.21
Layer7 CTF SPILL  (0) 2015.11.02
PlaidCTF-2013 pork  (0) 2015.10.28

+ Recent posts