mrctf2020-pwn题
mrctf2020_easy_equation
知识点:
格式符 | 作用 |
---|---|
%x |
打印栈上的4字节数据(十六进制) |
%s |
打印栈上指针指向的字符串 |
%n |
写已打印字符数到对应指针指向的地址 |
%p |
打印指针值(地址) |
%d |
打印十进制整数 |
%c |
输出一个字符 |
格式化字符串漏洞修改内存:
通过控制输出字符数来间接修改内存:
利用%n
系列占位符的 “输出计数写入” 特性,通过控制输出的字符总数,间接将目标值写入指定内存地址。
这种 “间接性” 是由格式化函数的设计决定的,也是漏洞利用的核心技巧 —— 攻击者需要精确计算输出字符数,才能让%n
写入预期的值。
(不是直接修改数值)
%c
:输出一个字符。%n
:将当前已输出的字符总数写入指定地址。%k$hn
:将字符总数写入第k
个参数指向的地址(hn
表示写入 2 字节)。
准备
64位,开了 NX
分析
main函数
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
开头有一个输入,存在格式化字符串漏洞
下面有一个 if
判断,判断的条件是 11*judge²+17*judge⁴-13*judge³-7*judge
是否等于 198
等于时,有直接的连接点
思路:
这里当 judge=2
时,满足 if
判断
所以我们要利用格式化字符串修改 judge
参数的值为 2
先获得偏移(写入地址)
参数位置为7,若要输入完整的地址,还需在填充一个偏移,写在第 8 个参数上
在获取 judge
参数的地址
judge
参数的地址为 0x60105C
最后构造 payload
,这里给几个都可以的 payload
1 | judge=0x60105C |
参数说明(以第一个为例):
首先填充一个偏移, b'a'
写在第 7 个参数位置上
然后格式化字符串修改内存是通过写入字符数,来控制目标值的写入
考虑前面已经填充的一个字节,这里我们只用再写入一个字节,就可以达到目标值 2
写入一个字节的方式直接的 a
或者 %1c
%9$n
是取栈第 9 个参数当地址,把当前输出字符总数( 2 )写到这个地址
这里: b'a'
第 7 个参数,b'%1c%9$na'
第 8 个参数,p64(judge)
第 9 个参数%9$n
后面的 a
是填充,64位程序一个地址 8 字节,填充使得 b'%1c%9$na'
这里有8字节,p64(judge)
完整的地址在第 9 个参数
脚本
1 | from pwn import * |
mrctf2020_shellcode_revenge
准备
64 位,开了 PIE
保护
分析
main函数
反编译的时候发现问题,反编译不了
根据提示修改 124D 的汇编
(改为 nop
自认为看函数影响不大)
然后反编译
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
有 read
函数,读取输入最大 1024(0x400) 个字节到 buf
, buf
大小为 1032,不存在缓冲区溢出
下面用 for
循环限制输入为大小写字母
思路
这题没开 NX
保护且输入可用空间够大,所以可以直接写入 shellcode
但这里限制了输入为大小写字母,所以用可见字符的 shellcode
1 | io.recvuntil(b'Show me your magic!') |
注意:发送的时候不能加换行,会加上 \n
符号,影响输入的 shellcode
脚本
1 | from pwn import * |