漏洞利用

可了解

堆喷洒技术(Heap Soray)【对没用ASLR的管用】

在shellcode前面加上大量的滑板指令(slide code),组成非常长的注入代码段,向系统申请大量内存,并反复用这个注入代码段去填充;

ASLR

空间地址分布随机化,将系统关键地址随机化,使攻击者无法获得需要跳转的精确地址

微软从操作系统加载时的地址变化和可执行程序编译时的编译器选项两方面进行实现和完善

  • 系统加载地址变化:当程序将执行文件加载到内存时,操作系统通过将内核模块提供的ASLR功能在原映像基址基础上加一个随机数作为新的映像基址;
  • 编译器选项(DYNAMICBASE):使用该选项后编译后的程序每次运行时其内部的栈等结构地址都被随机化。

G S

对栈的缓冲区溢出防范效果好

VC++编译器中提供/GS编译选项,使用时编译器针对函数调用和返回时添加保护和检查功能代码,在函数被调用时,在缓冲区和函数返回地址之间加上一个32位随机数security_cookie,在函数返回时调用检查函数检查security_cookie是否发生变化

  • security_cookie的值在进程启动时随机产生,并且它的原始地址因Windows操作系统的ALSR机制是随即存放的,攻击者无法篡改
  • 发生缓冲区溢出攻击时,security_cookie的值会被覆盖,因此若其变化了则可发现产生了溢出

DEP

数据执行保护,启用后DEP机制将堆栈区设置为不可执行标志位,因此在溢出后及时跳转到恶意代码地址,其也不能运行,有效组织了缓冲区溢出攻击执行,Studio中为此提供一个/NXCOMPAT选项

GS绕过

当黑客覆盖掉一个异常处理结构并在Cookie检查前触发异常,这是虽然栈中仍有security_cookie但仍然可成功溢出;

其最重要的缺陷是没有保护异常处理器

ASLR缺陷

通过攻击未开启随机化的模块作为跳板、堆喷洒技术、部分地址返回覆盖法等实现

SEH保护机制缺陷

利用未开启SAFESEH模块作为跳板绕过;利用加载模块之外的地址绕过

需掌握

滑板指令:由大量NOP指令填充组成的指令序列,执行时指针会直接滑过,不做任何操作,类NOP指令如0x0C,0x0D等

Heap spary的优缺点

1)通过使用类NOP指令进行覆盖,对shellcode地址的跳转准确性要求降低,增加了缓冲区溢出攻击成功率

2)导致被攻击进程内存占用过大,计算机无法正常运转,易被察觉

3)Windows系统目前启用DEP功能进行防护

Heap spary的构造过程

  • 在内存中找一个jmp esp指令
  • 编写代码,如上一章所说,从第53字节开始写入jmp esp的地址以覆盖原来的返回地址,之后从第57字节(ebp+8)开始写shellcode,以此使得程序跳转到shellcode去执行(相较于上一章这里前52都是直接用90去填充的)

API函数自搜索(考对汇编的补全)

定位kernel32.dll,解析导出表,定位LoadLibraryA函数,找到函数地址,编写shellcode

  • 定位kernel32.dll(假设edx=0)

    1
    2
    3
    4
    5
    mov ebx,fs:[dex+0x30] ;从线程环境TEB中利用偏移地址找到进程环境块PEB的位置
    mov ecx,[ebx+0x0c] ;从PEB中找出PEB LDR DATA结构体的指针
    mov ecx,[ecx+0x1c] ;接着找到指向模块初始化链表的头指针
    mov ecx,[ecx] ;直接把链表里第二个kernel32.dll拿出来
    Mov ebp,[ecx+0x08] ;加上偏移就拿到了内存中加载用的基地址
  • 定位导出表及函数列表(感觉就算考也是考第一段和第三段,这就大概看看吧)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    mov eax,[ebp+0x3c]     ;PE头
    mov ecx,[ebp+eax+0x78] ;导出表的相对地址
    add ecx,ebp ;导出表绝对地址
    mov ebx,[ecx+0x20] ;函数名称表相对地址
    add ebx,ebp ;函数名称表绝对地址
    ...
    mov ebx,[ecx+0x24] ;函数序号表相对地址
    add ebx,ebp ;函数序号表绝对地址
    ...
    mov ebx,[ecx+0x1c] ;函数地址表相对地址
    add ebx,ebp ;函数地址表绝对地址
  • 搜索目标函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ;用哈希运算缩短字符串,后面通过比较摘要来看是不是需要的API
    hash_loop:
    movzx eax,byte ptr[esi] ;每次取1个字节放进eax
    cmp al,ah ;eax和0做比较,即结束符
    jz compare_hash ;计算完hash就跳转
    ror edx,7 ;循环右移7位
    add edx,eax ;累加
    inc esi ;下一个字节
    jmp hash_loop ;重复
    1
    2
    3
    4
    5
    6
    7
    ;找到名称序号后
    mov ebx,[ecx+0x24] ;函数序号表相对地址
    add ebx,ebp ;绝对地址
    mov di,[ebx+edi*2] ;把名称序号换成地址序号
    mov ebx,[ecx+0x1c] ;函数地址表相对地址
    add ebx,ebp ;绝对地址
    add ebp,[ebx+edi*4] ;函数绝对地址

    SafeSEH

    在编译中加入/SEFESEH选项,运行时系统会检查

    • 异常处理函数是否在合法的SEH链表中
    • 异常处理函数是否不在栈上
    • 异常处理函数是否有效

    否则终止异常处理

    SEHOP

    针对SEH攻击的条件检查:

    • 所有SEH结构都在栈上;
    • 所有异常处理函数都不在栈上
    • 所有SEH结构都是4字节对齐
    • 最后一个异常处理函数是FinalExceptionHandler
    • 最后一个nextseh指针是0xFFFFFFFF

    ROP(用于绕过DEP)

    本质就是通过构造栈布局来控制程序执行流和寄存器值

    返回导向编程,基于代码复用的攻击技术,从已有的库或可执行文件中提取指令片段,构造恶意代码

    (ROP小配件:目的执行指令+RET指令)

    攻击思路:

    调用相关API关闭或绕过DEP保护→实现地址跳转→调用相关API将shellcode写入不受DEP保护的可执行内存

    1
    2
    3
    4
    5
    6
    7
    ;此片段执行后,EDX的值为?
    ESP → 栈元素1:gadget1 = POP EAX; RET (指令地址,CPU先执行这个)
    栈元素2:0x00000001 ;(给gadget1的POP EAX提供数据,把1存入eax)
    栈元素3:gadget2 = ADD EAX, 2; RET(gadget1执行完RET后,要跳转执行的指令地址)
    栈元素4:gadget3 = XCHG EAX, EDX; RET(gadget2执行完RET后,要跳转执行的指令地址)

    ;3,EAX值为3,在栈4做了交换,所以EDX值更新为EAX的值

    第八章 结(期末顺利!)