網鼎杯2020-玄武組-writeup(safe_box)

 

好久沒玩ctf了,除了簽到題一個小時纔看到有人拿了一血還是有點意外,打了個醬油就解了道safe_box的題。

題目代碼:

#!/usr/bin/python
# encoding: utf-8
import random
import sys
import os
from hashlib import sha256,sha512
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
from secret import flag

def my_print(message):
    sys.stdout.write('{0}\n'.format(message))
    sys.stdout.flush()
    sys.stderr.flush()

def read_str():
    return sys.stdin.readline().strip()

def read_int():
    return int(sys.stdin.readline().strip())

def proof_of_work():
    s = os.urandom(10)
    digest = sha256(s).hexdigest()
    print("sha256(XXX + {0}) == {1}".format(s[3:].encode('hex'),digest))
    my_print("Give me XXX in hex: ")
    x = read_str()
    if len(x) != 6 or x != s[:3].encode('hex'):
        print("Wrong!")
        return False
    return True

def PoW():
    if not proof_of_work():
        exit(-1)

class Cipher:
    def __init__(self, q, p, d):
        self.p = p
        self.q = q
        self.e = [bytes_to_long(os.urandom(10)) for _ in range(self.p-1)]
        self.d = d

    def pad(self, msg, l):
        return msg+(l-(len(msg)%l))*chr(l-(len(msg)%l))
    
    def unpad(self,msg):
        return msg.replace(msg[-1]*ord(msg[-1]),'')

    def enc(self, p):
        c = []
        for i in range(self.q):
            b = bytes_to_long(p)
            a = i+1
            for j in self.e:
                b += j*a
                a *= i+1
            c.append({'a':i+1, 'b':b})
        return c

    def encrypt(self,p):
        c = {}
        p = self.pad(p, self.d)
        for i in range(len(p)/self.d):
            c[i] = self.enc(p[i*self.d:(i+1)*self.d])
        return c

    def dec(self, x, key):
        s = 0
        for a, b in enumerate(key):
            r1 = 1
            r2 = 1
            for c, d in enumerate(key):
                if a == c:
                    continue
                r1 *= (x - d['a'])
                r2 *= (b['a'] - d['a'])
            s = (s + b['b'] * int(r1/r2))
        return s

    def decrypt(self, c):
        p = ''
        for i in c.keys():
            p += long_to_bytes(self.dec(0,c[i]))
        return self.unpad(p)


def print_key(x):
    for i in range(len(c)):
        my_print('part:{}'.format(i))
        for j in range(x):
            my_print(c[i][j])


def open_box():
    key = {}
    my_print('hash:{}'.format(sha512(pri).hexdigest()))
    my_print('Input your key:')
    try:
        for i in range(len(c)):
            my_print('How many?')
            n = read_int()
            k = []
            for j in range(n):
                my_print('a:')
                a = read_int()
                my_print('b:')
                b = read_int()
                k.append({'a':a, 'b':b})
            key[i] = k
        my_print(C.decrypt(key))
    except:
        my_print('Wrong!')
        exit(-1)


def print_flag():
    my_print('flag:{}'.format(flag_enc))


if __name__ == '__main__':
    PoW()
    rsa_key = RSA.generate(1024)
    pri = rsa_key.exportKey().decode('ascii')
    e = rsa_key.e
    n = rsa_key.n
    flag_enc = pow(bytes_to_long(flag),e,n)
    C = Cipher(40,30,40)
    c = {}
    c = C.encrypt(pri)

    my_print('====================================',)
    my_print('             Safe Box               ',)
    my_print('====================================',)
    my_print('1. Print key                        ',)
    my_print('2. Open the box                     ',)
    my_print('3. Print flag                       ',)
    my_print('4. Exit                             ',)
    my_print('====================================',)

    try:
        while True:
            my_print('Your choice:')
            choice = read_int()
            if choice == 1:
                print_key(20)
            elif choice == 2:
                open_box()
                continue
            elif choice == 3:
                print_flag()
                continue
            elif choice == 4:
                my_print('Bye~')
                exit(0)
            else:
                my_print('Invalid!')
                exit(-1)
    except:
        exit(-1)

代碼看上去並不多,感覺沒啥難度,不過還是浪費了不少時間。

解題步驟:

1.先爆破Pow()函數,直接利用返回的信息裏的sha256值就可以了

tables='0123456789abcdef'
def crack(hash,phash):	
	for a in tables:
		for b in tables:
			for c in tables:
				for d in tables:
					for e in tables:
						for f in tables:
							key=a+b+c+d+e+f
							aa=(key+phash).decode('hex')
							en=sha256(aa).hexdigest()					
							if(en==hash):
								print key

 crack('d622fa1b255187ef62a3a7b3f4d8302e8af5e4c9ad4b8022cc35fc460b88aaed','846e0c60f208bd')

2. 輸入爆破得到的值後,就可以進行 Safe Box 的流程了,這裏有3個選項讓你選

   輸入1:返回加密後的private key

   輸入2:可以手動輸入得到的加密Key ,然後得到解密後的key(這裏是個坑)

   輸入3:   返回加密後的flag

一開始一直在選項2裏折騰,發現怎麼都無法解密出還原出原始的private key,仔細一看原來print_key(20)處被寫死,然後又在想會不會有什麼點可以繞過這個限制,事實證明我想多了,只能認真的分析下程序算法了,加解密算法並不複雜,大概原理就是將數據按每40字節分組,然後與一組隨機生成的數組進行40輪簡單加解密。經過分析發現每組數據在第一輪加密後與原始明文long數值的差值是固定的,即隨機數組裏所有元素的和,然後又發現每次明文最後一組的數據是固定的即 :Y-----"""""""""""""""""""""""""""""""""" (轉換成long型就是 744061660490831401720358135957044040624762956183858929566796822738090422707035217211627153793570 )

那麼問題就很簡單了,只要獲取到每個加密數數組的第一輪加密數據,然後減去隨機數組的元素和即可到每組數據到明文了。

from Crypto.Util.number import *
from Crypto.PublicKey import RSA

Y=744061660490831401720358135957044040624762956183858929566796822738090422707035217211627153793570 # Y-----""""""""""""""""""""""""""""""""""

b=744061660490831401720358135957044040624762956183858929566796822738090443044343341555030147467997 # 22:{a:1,b) 

key=b-Y

enArray=(376938888692085382665423970042166795432413643305381575515959305455321957741014775556099620418045,544468557384865678127345888521495313711382128314859750251204026686164722602749584634601451696932,936101863006478342720038665189970172996754294438095819543794318309687441954041410312356961205021,945355955896365723878024971494183079099098972541901990987319507419339577081199196130743996725552,1009927065105467358154476981763299990726808995553632657647913745864178492326783116709365594331925,836241384832791658137916346910573727829004236580891323144317685292381904999801468162685758611444,595280407097020068888759905382405777941512944367468979590546217648279108805708209523967337050866,811862911578721893752222144128069161704293431371813548289344133980059786179414937927256660646891,446003104010318947186274361077732608843996581840608966768396761016666881193309951106081400511499,429193079854919286788434876264198143566911851436927601048419350638341406138476562661649103532338,937014067747737592606609025374139759103571073396876043808772291642335260548903025831635445888559,729208755537554932956043583675437131853651711344675678385026621665056085849104934561648366407166,402107753811158736815110639265848158215095048209693387269254227563798934696564548250919178751495,927727845373399402412610559053654216346976069623456352980299764410620755363258805047478014064640,746115275774873409205189797393521344082179101140418072961666897265918298006377841353214785958414,838257545574874382295194121765901957577655245852630668226460565037927114012623060794922636227621,629433974006044324659221111265384100736178750737700485339308412867233355147145455948368163058927,984891273313147336895952391272313093974330446431216458686712895436107646744733102205255571361025,645150230225498024293187757526128357832649727978165711927749423366581907868767100605472749862954,629728321731935633564795173187536429717504149188177011275213046248401785264348076908848682322412,853445946967267849671129453112618997400335475512430361491441643662072543222492330760705669159718,920655243119519668135791583707354953573453025435735289018915049432726028584966406841033994941440,744061660490831401720358135957044040624762956183858929566796822738090443044343341555030147467997)

pri_key=''
for e in enArray:
	pri_key+=long_to_bytes(e-key)
print pri_key.replace('"','')

enFlag=63691588785660450317214825708635325578264899588028990196984494438817954811895751497618774951869925317964025052448125889561348001616704468223377247262611909655901924855878419507099314895648511160807472080632874648633768843995755598898623455640343918449933193867222158283075059339069188753156113156738195632730
rsa_key=RSA.importKey(pri_key)
n = rsa_key.n
d = rsa_key.d

print long_to_bytes(pow(enFlag,d,n))

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章