网鼎杯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))

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