攻防世界pwn题:forgot

0x00:查看文件信息

该文件是32位的,canaryPIE保护机制没开。

 

0x01:用IDA进行静态分析

总览:

该函数就是:v5初值为1,对v2输入一串字符。然后执行一个会根据输入的字符串而修改v5的循环语句,最后调用相应的函数。

 

同时,发现文件里面已经含有cat flag的函数:

 

函数snprintf介绍:
printf("cat %s", "./flag")是将cat ./flag输出到屏幕上。
snprintf(s, 0x32, "cat %s", "./flag")是最多将后面的字符串("cat ./flag")输入0x32个到变量s上。

 

       所以,我们要想办法去执行这个cat_flag函数。现在我们已知的漏洞点是scanfv2输出存在溢出。再根据程序的第3行到第8行中变量的声明可了解到v2可以溢出覆盖数组v3、数组s、变量v5甚至是所在栈帧的返回地址。

       一个常规的解法是覆盖v3[0]cat_flag函数的地址值,然后进一步想办法使执行循环后,v5的值为1。这样在最后就会调用v3[--1]指向的函数,即get_flag函数。

v5的初值为1,所以在switch中为case 1。其条件判断为:

因为v5本来就是1,所以我们只要保持不变就好了,即return false。根据短路原理,只要字符串均为A(ascii=65 < 96)就行了。

 

0x02:编写exp

from pwn import *
context(os=linux, arch=i386, log_level=debug)
io
= process("./forgot") #io = gdb.debug("./forgot",b *0x08048A5D) #io = remote("111.200.241.244",58065) get_flag = 0x080486CC payload = bA*0x20 + p32(get_flag) io.sendline("tolele") io.recv() io.sendline(payload) io.recv() io.interactive()

可以成功catflag

 

0x03:回顾再分析

因为v2定义的数组大小是32个元素,所以我们还需要考虑的问题是:

  • 当对v2进行溢出式的赋值后,strlen(v2)会等于多少呢?
  • 如果strlen(v2)的值大于或等于32,程序中对数组v2的访问岂不是越界了么?

 

解决问题:

我们可以对strlen函数的源代码进行分析:

strlen.c source code [glibc/string/strlen.c] - Woboq Code Browser

对该源代码分析的博客:

c语言库函数strlen源码实现_风雨也从容的博客-CSDN博客_c语言strlen源代码

 

简而言之,strlen(const char* str)的返回值就是,从首字节开始从1往上计数,到\x00停止,且不算入\x00

 

(注:以下内容仅是个人想法,请保留质疑!)

       既然这样,那么payload = bA*0x20 + p32(get_flag)岂不是会远远大于32(数组v3中均没有出现\x00字节),这样对数组的访问不会报错吗?

       其实,我们平时遇到的数组访问越界是由于在集成开发环境(vs,vc…)中会进行检测。但实际上源代码中是没有检测机制的,linux中就是直接对动态库进行链接,相当于是直接使用了源代码。所以,在linux上并不会出现报错。

        同样,数组索引的本质实现是基于汇编语言,相当于就是*(v2+i)。在linux中并不会进行越界检测。

 

实验一下:

我们对c2进行字符串输入后,会自动在字符串末尾添加个\x00。即上面实验中c3[14]被赋值了\x00。同时,由于get_flag = 0x080486CC,ascii码值均小于96,所以v5不会被改变。

所以,问题也就差不多解决了。

 

0x04:其他思路的分析

其实在最开始时,我们如果看到了栈溢出和get_flag函数。最先想到的就是直接栈溢出覆盖main函数栈帧的返回地址,结束时直接调用就好啦~

此时,payload = bA*0x78 + bB*0x4 + p32(get_flag)

(动态调试后发现是0x78,并不是ida中的0x74)

执行后发现pwn不通,动态调试一下:

得知,程序开在了0x08048a61这一步。这不卡住才怪,用eax*4来进行偏移,而eax0x44444443(CDDD)

 

ida中继续对eax值的来源进行调查:

       对该处按F5查看反汇编代码,发现((void (*)(void))v3[--v5])(); 这里的。上一步是将esp + 78h的值赋给了eaxesp + 78h对应的变量则是v5。由于我们直接覆盖到返回地址,所以图中也把v5给覆盖了,值还挺大的。早就超了最大空间,所以执行不下去了。

 

0x05:个人唠叨

        以前在做题过程中遇到问题总喜欢逃避,毕竟这是“解决”问题最轻松的一种方式。在做这道题时,我硬逼着自己去思考能够解决问题的方法,以及动手去尝试。一步一步的去解决问题,虽然很慢,但知识学得很实。希望自己能够继续以这种方式面对各种困难。

        最后再反省一下:在第一个思路pwn不通的时候,直接丢个代码和exp问群里的师傅为啥pwn不通?现在想想这样去提问还挺不好的,这将会浪费师傅们很多时间去确定问题所在。所以,以后问问题的话,尽量缩小出问题的范围,或者将问题转换为概念性的提问。

 


 

tolele

2022-06-05


文章标签:

原文连接:https://www.cnblogs.com/tolele/p/16344335.html

相关推荐

Google Play 开发者账户被封 如何改代码快速上架

go-linq 从入门到实战

Go序列化与反序列化

Inochigohan的2022年中总结 || 我走得很慢,但我从不后退

素数算法(Prime Num Algorithm)

爽!用golang 手撸了个贪吃蛇

Google 为造芯再掀“抢人大战”,英特尔 17 年老将加入

Go的错误处理设计

五分钟给你的 gRPC服务 加上 HTTP 接口

【一起学Rust】Rust包管理工具Cargo初步了解

MongoDB数据库简介、安装和基本使用

Go 快速开发 | 05 - Go 中的切片和字典

聊一聊Go语言的反射机制

Go实战 | http请求排队处理的实现

windows10:vscode下go语言的适配

Go语言测试在开发中的最佳实践 | 使用Docker容器进行测试

简单撩一撩go的Interface,为什么要用它呢

快速了解一下go中有哪些基本的数据类型

解读字节开源HTTP框架Hertz | 核心结构与入口的实现

在你的Gin项目中添加jwt功能吧