B站pwn教程条记-9

[复制链接]
发表于 2025-9-16 20:43:25 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
前言:可以去一些开源镜像站下载libc老的乌班图镜像,由于堆题的libc可能比较老,没有新的一些掩护措施和机制。
格式化字符串弊端

归根结底,可以读写恣意地点内存。
泄漏栈数据/恣意地点数据

重要题目就是printf不知道本身有没有参数,只会根据格式化字符个数在栈上往上读取,就算不是本身的参数也读取。
如果%s过多,没有充足参数环境下,步伐就会崩溃


%x用来输出对应数据的16进制(不加0x的16进制)%p是把栈数据当作指针来输出,和%x相比前面有0x,以是一样寻常也常用%p泄漏。如下图,2者差不多。%p太多了,有可能给之前某个函数的canary也泄漏了。

如果printf("%p","hello"),现实上入栈的是hello对应的地点。C语言通报字符串就是通报它的地点的,函数参数通报就是云云,不可能直接把数据压栈的。
这也是C语言用指针不安全的一点,比如字符串截断弊端。将对应地点的字符串的\x00删除或篡改,puts函数如许的输出函数在输出对应地点内容的时间,就会不碰到\x00不克制,不停输出反面的内容。相应的,共同strlen和read(xxx,str,str_len)还可以篡改字符串数据。
因此不难懂白,%s则是会读取这段地点,实行剖析为字符串。实在如许也有一个利益,比如栈上存放的got表本身的地点,%s读取后,就是got表内存放的数据的地点了,到达泄漏got表的目标。
而%n$d,表示这是第n个参数,而且按照有符号整数打印出来。
那么,格式化字符串本身存放在什么地方呢?如果步伐没有刻意搞bss之类存放,那么应该就是在栈上存放,在参数谁人栈的地方存放其地点,见下图

因此,我们可以在栈上利用格式化字符串这一特性,在栈上写入敏感数据的地点,而后利用格式化字符串读取,见下图

由于给内里的地点超长了(x86一个栈是4字节),因此%6$s会在下一个栈的地区存放。
这里必要留意,0x00402004会被打印出,具体表明请看下图。


篡改栈数据/恣意地点数据

必要用%n,他也是和%s一样剖析,但不同的是它是向地点写入,写入前面格式化字符打印成功的个数。但是如果我们要用这个写入很大的数据,必须在前方打印充足长的字节吗?
可以用格式化字符串控制,比如%20d,意思是宽度是20的一个d,如许就算打印出来了一个变量,他也会认为前方已经打印出20个字符了。同时%n也支持%4$n如许的语法。
由于%n不是直接修改栈上的值,想要修改栈的值还得是通过%p泄漏EBP之类的值,再利用%n来操纵。
这里要留意,%n默认是4字节整数写入。%hn是2字节,%hhn是一个字节(h代表half)
例题解说

fmtstr1

有canary,估计无法栈溢出。看源码估计也是格式化字符串。由于第10行的printf函数的格式化参数我们是完全可控的。

老师的思绪是先调试一下看看。我们得看看x的地点(由于是%n)对于printf来说是第几个参数才行,ida看不出来这,必须得动调。
易错点:printf的第一个参数是格式化字符串,格式化字符串的参数是%n,%s之类。也就是printf的第二个参数才是格式化字符串的第一个参数。以此类推,重要是格式化字符串也是一个参数,他的地点同样入栈,千万不要搞肴杂。
同时,格式化字符串中的%n$的n指的是相对于格式化字符串的参数的意思,也就是参数是第N个printf的参数减去1。
更加具体的先容,下面是步伐执行流停在了call printf。这里可以看出esp指向的AAAA就是压栈的格式化字符串地点,这个参数现实上是储存在了eax那一行的。从上往下数刚好是第12个(针对于printf而言)而格式化字符串内里肯定是11$.

exp可以证明这一点:

goodluck

这是个64位步伐。
步伐竟然让我们本身输入flag,他进行比对。这肯定是不行的。format完全是我们控制,以是这也是格式化字符串弊端的题目。我估计要想办法泄漏fp的地点把。现实上v10生存着flag字符串,应该是泄漏v10(刚好他也在栈上)
ms的用法:
在 C 语言里,scanf函数的%ms格式说明符是一个拓展功能,重要用于动态分配内存来存储输入的字符串。下面为你具体先容它的功能和用法:
重要功能

  • 动态内存分配:%ms会依据输入字符串的现实长度,主动分配充足的内存空间,如许就无需提前指定缓冲区的大小。
  • 主动字符串终止:和%s一样,%ms会在读取到空缺字符(比方空格、制表符、换行符)时克制,并会主动在字符串末尾添加\0作为竣事符。
  • 返回分配的指针:成功读取输入后,%ms会把分配的内存地点存储到对应的指针变量中。
关键要点

  • 必要指针参数:调用scanf时,传给%ms的参数必须是一个char**范例的指针,也就是一个指向字符指针的指针。
  • 内存管理:使用完分配的内存后,要记得用free()函数开释内存,防止出现内存走漏。
  • 输入截断:%ms会不停读取,直到碰到空缺字符大概文件竣事符(EOF),这一点和%s是雷同的。

这就故意思了,由于泄漏恣意地点数据肯定要包管格式化字符串在栈上呀 。但是无关紧要,我们只必要泄漏栈上的v10。
留意64位printf的参数。起首第一个参数格式化字符串在rdi寄存器,如今我们知道前6个参数肯定在寄存器,第六个才入栈,显着printf也会按照如许的顺序来读取。
可以直接正常执行步伐输入%7$p,看看步伐泄漏出来的第七个参数的地点。固然如许一眼不肯定能看出来,可以在输入89多对比看看。由于一样寻常具有栈地点随机化,但是栈上某些部门的值却是特别的,可以多打印几个显着看出来。
一句话就是由于传参方式不一样,不能直接通过在栈上数参数来找偏移了,必须试出来按正常执行的第七个参数究竟在什么地方。
实在这道题颠末我的观察也可以直接从x64入栈方式来找偏移,第七个参数按理来说就是靠近addr的谁人栈生存的。根据此数出来的偏移也刚好符合题意,实战中二者连合使用吧。

堆引入

想要办理第三题,必须有堆的底子,接下来就认识一下堆。现实的弊端栈多于堆,但是CTF中堆的题目数量是大于栈的。
shared这块就是mmap段。


一样寻常linux就是glibc。GNU协会搞linux的软件环境,以是这linux许多地方都有g的身影。

堆管理器是用户态代码,以是必须通过体系调用和内存这方面硬件创建接洽。
在Linux进程的假造内存结构中,数据段包罗已初始化和未初始化的全局及静态变量,其大小在步伐加载时就已确定,运行过程中无法改变。而堆位于数据段之后,是动态内存分配的地区,`brk`体系调用并非直接扩展数据段,而是通过调解堆的竣事地点(`break`指针)来实现堆空间的动态扩展与紧缩。当步伐调用`malloc`哀求内存,若现有堆空间不足,`malloc`就可能通过`brk`体系调用增长`break`指针的值,使堆向高地点方向延伸;当开释大量内存时,`break`指针可能减小,但内核通常不会立即将内存归还体系,而是生存以供后续分配 。--豆包
mmap故意思了,map就是映射,也就是在物理内存开辟一块空间,映射到相应假造内存。

主线程两种方法都可以(申请内存空间相对大就用mmap,否则是brk),子线程只能用mmap。开释内存则是free


具体相识堆,且听下回分解
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表