BugkuCTF-PWN题pwn3-read_note超详细讲解

知识点

puts()的特性 , puts()会一直输出某地址的数据,直到遇到 \x00

Canary最低位为\x00(截断符)

\x 和 0x 的区别:
区别不大,都是把数按16进制输出。
1、0x 表示整型数值 (十六进制)
char c = 0x42; 表示的是一个数值(字母B的ASCII码66),可以认为等价于: int c = 0x42;
2、\x42用于字符表达,或者字符串表达
char c = ‘\x42’; 亦等价于: char c = 0x42;
char* s = “\x41\x42”; //表示字符串:AB

程序编译的时候入口并不是main函数,而是start代码段。事实上,start代码段还会调用__libc_start_main来做一些初始化工作,最后调用main函数并在main函数结束后做一些处理。

解题流程

先运行一下看看在这里插入图片描述
一开始会让你输入一个路径,不存在的话就会报错退出,然后打印出文件内容,然后分别输入note的长度和内容,输入内容的长度取决于之前的note长度;如果实际输入的长度不是624,则再输入一遍

查看保护机制
在这里插入图片描述
发现除了RELRO,其他保护机制全开
在这里插入图片描述
在这里插入图片描述

此题难点:

第一要读懂题目找出漏洞
第二是要绕过各类保护机制
第三是exp的编写调试。

第一要读懂题目找出漏洞

首先分析程序,重要部分如下所示。一开始会让你输入一个路径,不存在的话就会报错退出,然后打印出文件内容;以上均可以忽略,没有任何用处。然后分别输入note的长度和内容,输入内容的长度取决于之前的note长度;如果实际输入的长度不是624,则再输入一遍,此时输入内容的长度变为0x270(624)。可以发现,v4的长度为0x258(600),而输入的长度可以任意控制,因此存在栈溢出。
在这里插入图片描述

第二是要绕过各类保护机制

确定了漏洞所在,下一个问题就是如何绕过NX、Canary、PIE和ASLR等保护机制了,下面一个个来说。
1.NX很简单,ROP即可。

2.Canary会很大程度上妨碍栈溢出,但结合本题的环境,输入一次后紧接着一个puts函数将输入内容打印出来,然后还有一次输入的机会,因此可以在第一次输入时,通过覆盖Canary最低位的\x00为其他值(Canary最低位肯定为\x00,而puts()会一直输出直到碰见\x00位置),让puts()泄露出Canary的内容,第二次输入时再将正确的Canary写回去,就可以绕过Canary的保护了。

3.PIE会让程序加载的基地址随机化,但是随机化并不完全,最低三位是不会改变的,可以利用这个特性,通过覆盖最低的两位来有限的修改程序控制流,然后再泄露出程序加载地址。

4.至于ASLR,利用ret2libc的方法,泄露出libc的版本,就可以算出system等函数的地址然后get shell了。

第三是exp的编写调试

通过以上的分析,由于需要泄露三个内容,因此main函数需要执行四次,每次执行都会有两次输入,每次执行的工作分别如下:

第一次执行

需要在填满v4的长度600后,再溢出两个十六进制位(64位计算机一个地址是8个字节),覆盖Canary的最低位,然后将Canary打印出来;再次输入时,将正确的Canary放在原来的位置,然后溢出栈上返回地址的低两位为\x20。为什么是\x20,是因为从ida里可以发现,vul函数最后retn的地址为0xd1f,main函数的起始地址是0xd20,前面的偏移都是相同的,因此可以通过这类方法绕过PIE再跳回main。
在这里插入图片描述
在这里插入图片描述
如图D2E为main的返回地址

第二次执行

在填满600的基础上,需要再多溢出两个地址位数(64位计算机一个地址是8个字节),也就是616。从栈分布可以看出,v4之后是Canary(0x7fffffffde88处),然后再填充一个地址位,就可以输出main+14的真实地址,也就可以得到程序加载的地址。之后使用跟之前同样的方法,再次回到main函数的起始位置。
在这里插入图片描述

第三次执行

要溢出的就是__libc_start_main的真实地址了,作为main函数的返回地址,从上图可以看出,可以从栈上泄露__libc_start_main+240的地址,依次利用LibcSearcher算出libc版本,然后得到system地址和/bin/sh字符串的地址。此时已经具备了get shell的条件,但由于第二次输入的长度所限,因此还要再跳回main函数,再次执行程序。
在这里插入图片描述

第四次执行

将payload拼接好,然后发给程序了。由于是64位,传参需要rdi。通过ROPgadget搜索程序二进制,发现存在pop rdi;ret;的gadget,将其偏移再加上第二步得到的程序加载基地址,就可以得到gadget的真实地址,至此,payload拼接完成,可以拿到shell了。
在这里插入图片描述
exp:

from pwn import *
from LibcSearcher import *
#sh = process('./file/read_note')   #本地调试
sh = remote(
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值