【网络安全】一个堆题inndy_notepad的练习笔记

本文详细分析了32位Linux程序inndy_notepad,探讨了其堆管理机制和安全漏洞。通过分析,揭示了如何利用堆上的UAF(Use-After-Free)漏洞,泄露libc地址并执行任意代码。文章涵盖了堆分配、释放、合并的原理,并给出了exploit执行流程,是学习网络安全和堆漏洞利用的好材料。
摘要由CSDN通过智能技术生成

对于堆的恐惧来自堆复杂的管理机制(unsorted,fastbin,small,large bin看着都头大),相较于栈(压入弹出)来说复杂太多了,再加上使用GDB调试学习堆时,每次堆分配时,调试起来相当的麻烦,所以一直都是理论学习,堆不敢碰不敢尝试。

尝试了一下堆,熟悉了堆的分配机制。

题目分析

基本信息分析

查看文件类型,32位,没有去掉符号( not stripped,很开心,省去了猜函数的“乐趣”)

file notepad notepad: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2,

for GNU/Linux 2.6.32,
BuildID[sha1]=65aa4834fcd253be2490ea1dc24a0c582f0cbb6f, not stripped

查看保护机制,一些直观的映像见下面注释

# checksec notepad
    Arch:     i386-32-little
    RELRO:    Partial RELRO # 可写got
    Stack:    Canary found #如果要栈溢出,需要考虑canary的问题
    NX:       NX enabled #不可以在栈上,bss段上布局shellcode,因为不可执行
    PIE:      No PIE (0x8048000) # 很开心,本程序每次加载的地址都是固定的

拖入IDA,查看字符串(shift+f12),没有system,没有/bin/sh(难受,需要泄露libc地址)。

至此,一些最直观、最简单的分析完毕。我们可以得到以下信息:

本程序是32位程序,每次加载时地址固定,如果存在栈溢出,需要考虑canary check的问题,并且溢出之后不能在数据区(栈、bss段)布局shellcode,因为数据区不可执行,所以需要通过ROP实现我们的意图。同时,程序本身不存在system和/bin/sh,需要通过泄露libc的地址来获取我们需要的libc中的函数(如system)。

功能分析&找茬

好了,下面开始找茬吧

主函数
包含循环,从函数名看是一个菜单显示加功能选择。有四个函数
在这里插入图片描述

menu:
  int __cdecl menu(int a1)
  {
   
    int result; // eax
    int i; // [esp+8h] [ebp-10h]
    int v3; // [esp+Ch] [ebp-Ch]
 
    for ( i = 0; *(4 * i + a1); ++i )
      printf("%c> %s\n", i + 97, *(4 * i + a1));
    printf("::> ");
    v3 = getchar() - 'a';
    freeline();
    if ( v3 < i ) # 没有检查下界???此时一定要标记出来这个函数有问题,不然后面就忘了 --!
      result = v3 + 1;
    else
      result = 0;
    return result;
  }
bash:
  unsigned int bash()
  {
   
    char s; // [esp+Ch] [ebp-8Ch] #128没毛病
    unsigned int v2; // [esp+8Ch] [ebp-Ch]
 
    v2 = __readgsdword(0x14u);
    printf("inndy ~$ ");
    fgets(&s, 128, stdin);
    rstrip(&s); #替换一些特殊字符,没毛病
    printf("bash: %s: command not found\n", &s);
    return __readgsdword(0x14u) ^ v2;
  }
cmd:
  unsigned int cmd()
  {
   
    char s; // [esp+Ch] [ebp-8Ch] #128没毛病
    unsigned int v2; // [esp+8Ch] [ebp-Ch]
 
    v2 = __readgsdword(0x14u);
    puts("Microhard Wind0ws [Version 3.1.3370]");
    puts("(c) 2016 Microhard C0rporat10n. A11 rights throwed away.");
    puts(&byte_8049371);
    printf("C:\\Users\\Inndy>");
    fgets(&s, 128, stdin);
    rstrip(&s);
    printf("'%s' is not recognized as an internal or external command\n", &s);
    return __readgsdword(0x14u) ^ v2;
  }
notepad:
  主要的功能函数,下面分析

menu函数中,我们可以控制menu函数的返回值!看似可疑的两个函数cmd和bash貌似没毛病,往后看。

进入notepad函数:
在这里插入图片描述
包含6个函数:

menu
    函数负责显示菜单,并且根据输入选择执行功能。前面提到,这个函数可以输出一个负数,但是貌似在这没有什么用!跳过
notepad_new
    见下面
notepad_open
    见下面
notepad_delete
    见下面
notepad_rdonly
    用于分析note struct字段
notepad_keepsec
    用于分析note struct字段

notepad_new

在这里插入图片描述
大致通过注释解释了一下分析过程,后面不再进行详细的分析。这里需要留意的地方是:这里的函数(notepad_show,notepad_destory)指针放在了堆上,如果我们能够溢出覆盖到这两个函数指针,岂不是就可以控制EIP执行我们想要执行的流程了吗?(初步感觉,实际上并不是溢出,只是分析时存在利用的可能性)可以看出通过size控制输入的长度,但并不存在溢出的机会。接着看下面
网络安全学习攻略·资料

notepad_open:

在这里插入图片描述

在这里使用了menu函数。还记得前面我们分析的结构,我们可以控制这个函数的输出吗?控制了这个值后,我们就间接控制了上图menu函数下面的这个函数指针(*(&v3->p_func_show + v0 - 1))(v3);这个函数的参数是这个块的首地址(不受控制)。所以这里我们可以分析得出:

  1. 如果我们能够通过控制v0控制函数指针指向我们想要执行的函数就完成了第一步,例如变成system
  2. 第二步,如果我们能控制v3处的内容就好了,例如变成’/bin/sh’,怎么实现呢?貌似没有什么思路,接着看吧!

notepad_delete:
在这里插入图片描述
这个函数中通过id释放了相应的note,并且清空了相应的指针,堵住了UAF的路。

等等!!UAF,我们不是能够控制一个函数指针吗?参数正好是分配的堆块地址!我们可以控制这个函数指针为free,释放掉当前块,并且没有清空指针的操作!一个野指针就这么诞生了,UAF!

至此,一个邪恶的计划产生了!

一个邪恶的计划

  1. 生成两个大小相同的堆块A和B(这两个堆块相邻哦);对于A,我们填充其内容,使其包含free函数的地址;对于B,我们使用menu的返回值(负数),控制函数指针指向A内容中的free函数地址,这样我们可以控制函数指针指向free(此时参数是B的首地址),这样通过操作B就可以free掉B自己。重要的是,虽然此时B已经被free掉了,但是因为我们还控制着指向B的指针,所以我们还能操控B,这很重要(use
    after free)。
  2. B现在被free掉了,躺在unsortedbin中,但是这有什么用呢?如果我们能让A覆盖到B就好了。可以吗?可以的!!这里用到了堆分配中的一个知识点:当相同大小的堆块释放时,会被放入同一个类型bin上。所以,此时如果我们free掉A,那么他们就同时躺在unsortedbin中了(此时他们会被合并!另一个知识点)。此时,我们使用一个大于堆A,小于A+B的大小,malloc一个块,此时返回的地址就是A的地址(称为A’),但是范围却覆盖到了B。至此我们就能控制B的内容了,比如通过重新分配出来的A’,覆盖B的首地址位置,输入’/bin/sh’。
  3. 但是,现在我们还差个system函数啊?libc的地址还没有获取到呢?另一个堆的知识点(真多,麻木!)linux使用free进行内存释放时,不大于64B的块会先放入fastbin,大于64的块会放入unsortedbin。如果fastbin为空时,unsortedbin中第一个块的fd和bk指针指向自身的main_arena中。而main_arena在libc中,利用这个点,我们可以泄露libc的地址。怎么弄呢?在第一步中,如果我们的B的size大于64(本例中0x60),那么在free时,就会直接被放入unsortedbin,此时fastbin中没有数据,那么B的数据区的前两个DWORD就是fd和bk,指向libc中的main_arena+48(针对本例chunk大小ox60)的位置。而main_arena在libc中是固定偏移的,我们用IDA打开libc,找到malloc_trim函数,如下图高亮位置就是偏移量,本例中是0x1b3780。至此我们可以获得libc的地址,通过偏移,我们可以找到system的地址。
    网络安全学习攻略·资料
    在这里插入图片描述
    终于,我们邪恶艰难的计划有了雏形。

exploit

下面就是执行了

首先,套路

#!/usr/bin/python
#coding:utf-8
from pwn import 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值