PWN学习之栈溢出
PWN学习之栈溢出
前言
我记得我在最开始学编程的时候,经常会听到老师说输入
的时候要注意大小,不要超过数组大小否则会造成缓冲区溢出
导致程序崩溃
的。
当时就觉得溢出就溢出咯,崩溃就崩溃咯,难不成还能导致电脑被攻击吗?就偏偏不控制输入长度。
写bug
先让我们来写个bug体验一下,下面这段程序要求用户输入字符串并且把数据给buffer数组,如果超过12长度的字符串就会造成缓冲区溢出!
bug.cpp源码
#include <stdio.h>
void bug()
{
char buffer[12]={"1"};
scanf("%s",buffer);
}
int main()
{
__asm //加nop是为了方便在OD里面定位到
{
nop;
nop;
nop;
}
bug();
__asm //可以忽略不用理会
{
nop;
nop;
nop;
}
return 0;
}
目标环境:
- Windows 7旗舰版(64位)
- VC++ 6.0
- Notepad++
用cl来编译源码并且生成带.asm
的汇编源码文件,编译命令是cl.exe -FAS ./bug.cpp
进入到lab01
目录编译源码后,生成了.exe 和 .asm文件。
然后我们运行bug.exe测试下输入超过12长度的字符串看看程序反应会怎么样,程序不出意外的崩溃掉了。
OD动态调试bug.exe
OK我们用OD进行调试来看看堆栈中实际的情况。
我这里用的是x32dbg
,载入后找到3条nop指令就可以定位到调用bug
函数附近的汇编代码了。
在0x0040103A
位置下断点。
接着F9让程序运行到这里,此时我们注意观察堆栈,接着马上要按F7了。
按F7让程序进入bug函数的内部
,并且这时候仔细看堆栈。
我们会发现进入函数内部后首先是栈顶发生了变化,之前的栈顶为0x0018FF3C
,现在的栈顶为0x0018FF38
,栈顶减少了4字节,说明发生了push操作,push xx
等于esp-4
也就是0x0018FF38
的地址,并且此时把内容送入esp(栈顶)
地址。
可以发现送入esp地址的内容是调用bug函数的下一句代码的地址,也就是当F7 进入call bug
函数的时候,其实是先执行了push eip
的操作,因为此时eip=下一句代码地址,然后再jmp bug函数
处执行代码。
OD调试观察溢出
好了接下来我们继续F8单步执行,当F8后此时注意ebp被压入了堆栈,估计这个问题会有很多新手做ctf pwn的时候被坑,他们计算出溢出大小后直接+4进行了此地址的覆盖,以为覆盖到了ret地址,其实ret地址在下面,导致拿不到flag。
当F8执行完sub esp,0xC
汇编指令后,栈顶位置减了12字节,用来存放buffer数组的数据。
之后我们一路执行到call
函数这里的时候,发现又有两个数据入栈了,这是调用scanf
函数术后的参数,我们可以不用管它,然后我们在F8步过call后,程序要求我们输入字符串,我们输入AAAAAAAAAAAABBBBCCCC
12个A代表填满buffer数组,BBBB代表溢出覆盖ebp寄存器,CCCC代表溢出覆盖ret地址。
-----------------------------------溢出前--------------------------------
0018FF20 00407034 bug.00407034 |参数1:"%s"
0018FF24 0018FF28 L"1" |参数2:buffer数组地址
0018FF28 00000031 ;buffer数组起始位置
0018FF2C 00000000 ;buffer
0018FF30 00000000 ;buffer
0018FF34 0018FF48 ;ebp寄存器
0018FF38 0040103F ;call bug函数的下一句汇编指令地址 |返回到 bug.00401046 自 bug.00401000
-----------------------------------溢出后----------------------------------
0018FF20 00407034 bug.00407034 |参数1:"%s"
0018FF24 0018FF28 "AAAAAAAAAAAABBBBCCCC" |参数2:buffer数组地址
0018FF28 41414141 ;buffer数组起始位置
0018FF2C 41414141 ;buffer
0018FF30 41414141 ;buffer
0018FF34 42424242 ;ebp寄存器
0018FF38 43434343 ;call bug函数的下一句汇编指令地址 |返回到 bug.00401046 自 bug.00401000
栈溢出攻击之突破密码验证
这里其实主要就是利用栈溢出覆盖掉局部变量的值,让其改变流程。
#include <stdio.h>
#define PASSWORD "1234567"
int verify_password(char *password)
{
int authenticated;
char buffer[8];
authenticated = strcmp(password,PASSWORD);
_asm nop;
strcpy(buffer,password);//缓冲区溢出!!!!
return authenticated;
}
int main()
{
int valid_flag = 0;
char password[1024];
while(1)
{
printf("Please input password:");
scanf("%s",password);
valid_flag = verify_password(password);
if(valid_flag)
{
printf("密码错误\n\n");
}else
{
printf("恭喜!密码输入正确!\n");
break;
}
}
}
只有当密码是1234567
的时候才会停止循环,并且输出密码正确。
用溢出方式来破解密码程序,首先strcmp
函数的返回值是当s1<s2时,返回为负数;当s1=s2时,返回值= 0;当s1>s2时,返回正数。所以我们想办法让authenticated
变量等于0,这样条件才能为假,才能进入恭喜!密码输入正确!
的分支。
所以我们只要输入8位数字,这时候\0 结尾符刚好能覆盖到 authenticated
变量使其等于0,从而改变程序判断。
可以看到je会走到密码正确的分支。
x64位栈溢出
源码还是采用bug.cpp的然后用VisualStudio编译成x64的。
载入IDA来看一下他的汇编代码,可以发现32位寄存器都变成了64位寄存器。
还有x64和x86的还有个区别就是函数调用栈的区别,在x64中只有fastcall
函数调用约定,定义如下。
参数1、参数2、参数3、参数4分别保存在 RCX、RDX、R8D、R9D ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 RAX 中。
#C语言代码
int fastcall_sum = fastcall_add(1, 2, 3, 4, 5, 6, 7);
#----------------------汇编代码--------------------------------
00007FF6577A366E mov dword ptr [rsp+30h],7 ;超过参数4保存在栈中
00007FF6577A3676 mov dword ptr [rsp+28h],6 ;超过参数4保存在栈中
00007FF6577A367E mov dword ptr [rsp+20h],5 ;超过参数4保存在栈中
00007FF6577A3686 mov r9d,4 ;参数4
00007FF6577A368C mov r8d,3 ;参数3
00007FF6577A3692 mov edx,2 ;参数2
00007FF6577A3697 mov ecx,1 ;参数1
#调用fastcall_add函数
00007FF6577A369C call fastcall_add (07FF6577A11C2h)
00007FF6577A36A1 mov dword ptr [fastcall_sum],eax # 返回值
#--------------------------------------------------------------
#---------------------fastcall_add函数--------------------------
int __fastcall fastcall_add(int a, int b, int c, int d, int e, int f, int g)
{
00007FF6D22D1790 mov dword ptr [rsp+20h],r9d ;参数4给临时变量
00007FF6D22D1795 mov dword ptr [rsp+18h],r8d ;参数3给临时变量
00007FF6D22D179A mov dword ptr [rsp+10h],edx ;参数2给临时变量
00007FF6D22D179E mov dword ptr [rsp+8],ecx ;参数1给临时变量
00007FF6D22D17A2 push rbp ;rbp入栈 ,和x86一样待会会用到
00007FF6D22D17A3 push rdi ;rdi入栈 , 不清楚
00007FF6D22D17A4 sub rsp,0E8h ;将栈顶向下拉232个字节
00007FF6D22D17AB mov rbp,rsp ;把rsp栈顶给了rbp 方便寻址定位
00007FF6D22D17AE mov rdi,rsp ;把rsp栈顶也给了rdi
00007FF6D22D17B1 mov ecx,3Ah ;循环变量58
00007FF6D22D17B6 mov eax,0CCCCCCCCh ;烫烫烫烫
00007FF6D22D17BB rep stos dword ptr [rdi] ;循环58此
00007FF6D22D17BD mov ecx,dword ptr [rsp+108h]
int sum = a+b+c+d+e+f+g;
00007FF6D22D17C4 mov eax,dword ptr [b]
00007FF6D22D17CA mov ecx,dword ptr [a]
00007FF6D22D17D0 add ecx,eax ;a+b
00007FF6D22D17D2 mov eax,ecx
00007FF6D22D17D4 add eax,dword ptr [c];+c
00007FF6D22D17DA add eax,dword ptr [d];+d
00007FF6D22D17E0 add eax,dword ptr [e];+e
00007FF6D22D17E6 add eax,dword ptr [f];+f
00007FF6D22D17EC add eax,dword ptr [g];+g
return sun;
00007FF6D22D17F2 mov dword ptr [sum],eax;存放总和
}
00007FF6D22D17F8 lea rsp,[rbp+0E8h]
00007FF6D22D17FF pop rdi
00007FF6D22D1800 pop rbp
00007FF6D22D1801 ret # 没做栈平衡
我们在汇编代码中找到调用bug函数的地方,因为我们bug.cpp中bug函数并没有参数,所以不需要关心这些参数调用方式。
然后注意看bug函数的这个地方,我们发现栈顶被拉低了96个字节,也就是12*8其中在这里一个char占用了8位,然后12个元素x8就是96个字节了。然后我们需要输入超过8x4=32
个字符才能溢出。
在x64dbg中进行实验,看看真实效果。
PWN学习之栈溢出的更多相关文章
- PWN学习之整数溢出
目录 PWN学习之整数溢出 整数溢出 溢出和回绕 漏洞多发函数 整数溢出例子 PWN学习之整数溢出 整数溢出 如果一个整数用来计算一些敏感数值,如缓冲区大小或数值索引,就会产生潜在的危险.通常情况下, ...
- PWN学习之格式化字符串漏洞
目录 PWN学习之格式化字符串漏洞 格式化输出函数 格式化字符串漏洞 漏洞利用 使程序崩溃 栈数据泄露 任意地址内存泄漏 栈数据覆盖 任意地址内存覆盖 PWN学习之格式化字符串漏洞 格式化输出函数 可 ...
- [二进制漏洞]PWN学习之格式化字符串漏洞 Linux篇
目录 [二进制漏洞]PWN学习之格式化字符串漏洞 Linux篇 格式化输出函数 printf函数族功能介绍 printf参数 type(类型) flags(标志) number(宽度) precisi ...
- pwn学习(1)
0x00 简介 入职之后,公司发布任务主搞pwn和re方向,re之前还有一定的了解,pwn我可真是个弟弟,百度了一番找到了蒸米大佬的帖子,现在开始学习. 0x01 保护方式 NX (DEP):堆栈不可 ...
- pwn学习之一
刚刚开始学习pwn,记录一下自己学习的过程. 今天完成了第一道pwn题目的解答,做的题目是2017年TSCTF的bad egg,通过这道题学习到了一种getshell的方法:通过在大小不够存储shel ...
- pwn入门之栈溢出练习
本文原创作者:W1ngs,本文属i春秋原创奖励计划,未经许可禁止转载!前言:最近在入门pwn的栈溢出,做了一下jarvisoj里的一些ctf pwn题,感觉质量都很不错,难度循序渐进,把自己做题的思路 ...
- CTF必备技能丨Linux Pwn入门教程——栈溢出基础
这是一套Linux Pwn入门教程系列,作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的一些题目和文章整理出一份相对完整的Linux Pwn教程. 课程回顾>>Linux ...
- pwn学习日记Day5 基础知识积累
知识杂项 int mprotect(const void *start, size_t len, int prot); mprotect()函数把自start开始的.长度为len的内存区的保护属性修改 ...
- Arm pwn学习
本文首发于“合天智汇”公众号 作者:s0xzOrln 声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关! 刚刚开始学习ARM pwn,下面如有错 ...
随机推荐
- 【OI】竖式问题分析与解答
题目:找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合.输入数字集合(相邻数字之间没有空格),输出所有竖式.每个竖式前应有编号,之后应有一个空行 ...
- 【死磕NIO】— 阻塞、非阻塞、同步、异步,傻傻分不清楚
万事从最基本的开始. 要想完全掌握 NIO,并不是掌握上面文章([死磕NIO]- NIO基础详解)中的三大组件就可以了,我们还需要掌握一些基本概念,如什么是 IO,5 种IO模型的区别,什么是阻塞&a ...
- Dede后台广告管理模块增加图片上传功能插件
用户问题:网站广告后台管理非常方便,但是织梦后台的广告管理模块,发布广告时图片没有上传选项,只能用URL地址,很不方便,那么织梦帮就教大家一个方法实现广告图片后台直接上传,非常方便.先给大家看下修改后 ...
- linux中如何查看文件上下文
grep -C 10 keyword catalina.out filename https://blog.csdn.net/weixin_34791683/article/details/11660 ...
- 配置 放上传文件的目录 apache(httpd)
1. 确认服务器 开放8088端口 https://www.apachefriends.org/download.html 下载XAMPP for Windows,安装 2. 修改apache主配置文 ...
- ES增删改查
了解了一下python对es 7.5的操作,记录下,不难: #!/usr/bin/env python # -*- coding: UTF-8 -*- from settings import Con ...
- django class类即视图类添加装饰器的几种方法
根据别人发布整理,个人爱好收集(原文:https://blog.csdn.net/mydistance/article/details/83958655 ) 第一种:定义函数装饰器,在函数,类中使用函 ...
- P6295-有标号 DAG 计数【多项式求逆,多项式ln】
正题 题目链接:https://www.luogu.com.cn/problem/P6295 题目大意 求所有\(n\)个点的弱联通\(DAG\)数量. \(1\leq n\leq 10^5\) 解题 ...
- iOS Swift逻辑运算符
运算符 运算符分类 从操作数角度看:运算符包括一元.二元.三元.这里的一二三指操作数的数量,操作数指的是被操作的数值. 从运算符位置看:运算符分为前缀.中缀.后缀.例如:!b, b + c, c! 赋 ...
- redis 5.0.12 install
redis 5.0.12 install ## check directory ls -l /XXXXXXX ##create dir mkdir -p /XXXXXXX/dataredis mkdi ...