zoukankan      html  css  js  c++  java
  • linux漏洞分析入门笔记-栈溢出

    ida7.0

    ubuntu16.04 lts

    0x00:环境配置

    使用IDA远程调试Linux程序步骤如下:

    1. 在进行远程调试之前需要对Linux平台进行一些准备工作。在IDA的安装目录中的dbgsrv文件夹中,选择linux_server或者linux_serverx64复制到需要调试Linux程序所在的目录下。将复制过来的文件赋予执行权限chmod 777 linux_server*。执行该文件./linux_server或者./linux_server64。

    2. 在IDA中选择菜单Debugger-Run-Remote Linux debugger。如图。分别将程序所在位置,程序所在目录,参数(没有可不写),主机IP,主机端口,点击OK。相对路径路径要填写相对

    linux_server或者linux_serverx64的相对路径。

              图1

              图2

            图3

    3. 此时,下关键函数下好断点后,即可进行动态调试,如下图:

            图4

    常用快捷键包括:

    a. 单步步过:F8

    b. 单步步入:F7

    c. 执行到光标位置:F4

    d. 设置断点:F2

    e. 顺序执行:F9

    0x01:漏洞简介

    1.一个简单的linux x64平台栈溢出漏洞,漏洞定位到vuln函数,如下图:

            图5

            图6

            图7

    0x02.漏洞调试

    1.从上面汇编代码可以看出,进入该vuln后 sub rsp-0x40 ,堆栈开辟了0x40字节空间,然后调用gets函数读入数据到edi所指向的空间,edi此时实际上是等于rsp的指向栈顶的位置,gets函数读入数据以换行符号为结束标志,在遇到换行符号前,会读取任意数据到栈里,这样当读入超长字符串后,就会覆盖函数的返回地址,在该函数执行retn时就会可以返回到任意我们指定的地方去执行代码。产生缓冲溢出漏洞,下好断点后开始动态调试。

    2.尝试构造字符去覆盖函数的返回地址,代码如下:

     1 from pwn import *
     2 import pdb
     3 context.log_level = 'debug'
     4 target = process('./test')
     5 elf=ELF('./test')
     6 pdb.set_trace()
     7 #poc
     8 rop='A'*0x40#
     9 rop+='B'*0x8#
    10 rop+=p64(0x400763)#pop rdi ret
    11 rop+=p64(0x7FFFF7B99D57)#/bin/sh
    12 rop+=p64(0x7FFFF7A52390)#System
    13 target.sendline(rop)
    14 target.interactive()

    代码执行后栈此时的情况如下:

            图8

            图9

    后面用8个字符c就可以覆盖返回地址了,函数返回时将会跳转到cccccccc 指向的空间去执行,如图9所示。

    0x03:利用Ret2Lib突破NX保护

    1.用checksec来检查目标文件进发现它开启了NX保护,如图10所示:

            图10

    2.NX就是将非代码段的地址空间设置成不可执行属性,一旦系统从这些地址空间进行取指令时,CPU就是报内存违例异常,结束进程。栈空间也被操作系统设置了不可执行属性,因此我们注入的Shellcode就无法执行了。

    既然注入Shellcode无法执行,进程和动态库的代码段怎么也要执行吧,具有可执行属性,那我们能否利用进程空间现有的代码段给合成想要的功能代码,答案是肯定的。

    在系统函数库(Linux称为libc)有个system函数,它就是通过/bin/sh命令去执行一个用户执行命令或者脚本,我们完全可以利用system函数来实现Shellcode的功能。EIP改写成system函数地址后,在执行system函数时,它需要获取参数。而根据Linux X86 32位函数调用约定,参数是压到栈上的。但是栈空间完全由我们控制了,所以控制system的函数不是一件难事情。

    这种攻击方法称之为ret2libc,即return-to-libc,返回到系统库函数执行的攻击方法。

    但是我们使用的环境是64bit系统,它和32位系统的一个区别就是system函数的参数传递方式。32位系统使用堆栈来传参,在64位系统中使用RDI来传递参数,所以我们不仅需要控制系统栈,还需要控制RDI,这无疑给我们增加了许多难度,但是这并不是做不到的!

    要获得shell需要做如下步骤:

    a. 获取system函数的地址。

    b. 获取“/bin/sh”字符串的地址。

    c. 将RDI中的值,改成“/bin/sh”字符串的地址。

    3.所以Shellcode不能放在栈下来执行,因此我们就需用用到ROP技术来间接执行功能代码。

    0x04:简单ROP构造

    1.由于目标程序有数据执行保护,所以我们往栈中的填充的数据并不能执行。所以在内存中代码最好找到类似“pop rdi, ret”这样的语句,由于我们可以完全控制栈中的数据,所以我们就可以通过pop为rdi赋值,再通过ret指令跳转到我们希望的地方。

    但是很不幸,目标程序并没有到这样的指令,不过我们可以找到其它代替指令,图11所示:

            图11

    pop rdi 的机器码是 5f c3,然而 pop r15 的机器码是 41 5f c3,而且一般pop r15之后一般都是紧跟ret指令。

    所以我们就可以使用pop r15指令的后半部分,即 5f (pop rdi)。

    2.由于系统开户地址空间随机化,我们先临时通过echo 0 > /proc/sys/kernel/randomize_va_space关闭地址随机化功能写死地址进行测试。

    3.最后构造后rop代码如下:

     1 from pwn import *
     2 import pdb
     3 context.log_level = 'debug'
     4 target = process('./test')
     5 elf=ELF('./test')
     6 pdb.set_trace()
     7 #poc
     8 rop='A'*0x40#
     9 rop+='B'*0x8#
    10 rop+=p64(0x400763)#pop rdi ret
    11 rop+=p64(0x7FFFF7B99D57)#/bin/sh address
    12 rop+=p64(0x7FFFF7A52390)#System address
    13 target.sendline(rop)
    14 target.interactive()

    4.运行poc后通过IDA调试看看栈的情况如图12所示:

            图12

    5.执行完后就可以正确获得shell,如图13所示:

            图13

    0x05:通过plt和got绕NX与ascii armoring

    1. 上面这个poc成功执行得利于关闭ASLR,system函数和“/bin/sh”的地址才能固定下来。我们构造poc才方便很多。虽然目标程序编译时默认没有开启ALSR,但程序使用的系统动态链接库会受到ALSR的约束,每次重新启动程序后,libc.so的地址会随机生成。所以我们的poc就会失效,下面我们就来构造一个不受libc.so基地址随机变化影响的poc。

    2.通过return-to-plt来实现绕过libc.so基地址随机化。

    什么是return-to-plt?

    在这种技术中,而不是返回到libc函数(其地址是随机的)攻击者返回到一个函数的PLT(其地址不是随机的、其地址在执行之前已知)。由于'function@PLT'不是随机的,所以攻击者不再需要预测libc的基地址,而是可以简单地返回到“function@PLT”来调用“function”。

    什么是PLT,如何通过调用“function@PLT”来调用“函数”?

    要了解过程链接表(PLT),先让我简要介绍一下共享库!

    与静态库不同,共享库代码段在多个进程之间共享,而其数据段对于每个进程是唯一的。这有助于减少内存和磁盘空间。由于代码段在多个进程之间共享,所以应该只有read和execute权限,因此动态链接器不能重新定位代码段中存在的数据符号或函数地址(因为它没有写权限)。那么动态链接如何在运行时重新定位共享库符号而不修改其代码段?它使用PIC完成!

    什么是PIC?

    位置无关代码(PIC)是为了解决这个问题而开发的 - 它确保共享库代码段在多个进程之间共享,尽管在加载时执行重定位。PIC通过一级间接寻址实现这一点-共享库代码段不包含绝对虚拟地址来代替全局符号和函数引用,而是指向数据段中的特定表。该表是全局符号和函数绝对虚拟地址的占位符。动态链接器作为重定位的一部分来填充此表。因此,只有重定位数据段被修改,代码段保持不变!

    动态链接器以两种不同的方式重新定位PIC中发现的全局符号和函数,如下所述:

    全局偏移表(GOT):

    全局偏移表包含每个全局变量的4字节条目,其中4字节条目包含全局变量的地址。当代码段中的指令引用全局变量时,而不是全局变量的绝对虚拟地址,指令指向GOT中条目。当加载共享库时,GOT条目由动态链接器重新定位。因此,PIC使用该表来重新定位具有单个间接级别的全局符号。

    过程链接表(PLT): 过程链接表包含每个全局函数的存根代码。代码段中的调用指令不直接调用函数('function'),而是调用存根代码(function @ PLT)。这个存根代码在动态链接器的帮助下解析了函数地址并将其复制到GOT(GOT [n])。这次解析仅在函数('function')的第一次调用期间发生,稍后当代码段中的调用指令调用存根代码(function @PLT)时,而不是调用动态链接器来解析函数地址('function')存根代码直接从GOT(GOT [n])获取功能地址并跳转到它。因此,PIC使用这个表来重新定位具有两级间接的功能地址。

            图14

    用ida反编译目标程序后发现其中有printf,gets ,setvbuf,在内存这几个函数的got表地址是固定的。从图14可以看出在执行printf函数前,edi指向的是格式化串,rsi指向的是被打印串的地址。如果控制了rsi那么我们就可以打印任何地址的内容。然后通过当前函数地址(gets) - system = 偏移地址 (两个函数的相对偏移是固定的),得到一个固定的相对偏移地址,得到偏移地址后通过当前地址加上偏移得到system函数的内存地址,然后传入’/bin/sh’,执行system就达到目的。

    3.通过构造ROP获得system函数地址,在目标程序中找到图15代码。

            图15

            图16

    看看printf_got_addr=0x600af0 这个数据里面刚好有个0x0a,这个就是换行符号对应的内存值,因此在读取0xf0后gets就结束读取了,所以后面的就无法正常覆盖了,我们得换一种方法来实现调用printf,就是将printf_got_addr=0x600af0地址拆开,然后在通过 call    qword ptr [r12+rbx*8] 来组合,只要没有0x0a就行,最后执行后如图16所示。执行完后再让它返回到发生漏洞的函数中,再将构造rop来执行system,通过pop edi ret来实现,步骤和第4步相同。

    执行后成功获得shell,如图17、18所示:

            图17

            图18

    0x06:总结

    1. Linux系统中对应用程序漏洞防护有三个:

    SSP(Stack-Smashing Protectot):堆栈防溢出保护,它会在每个函数的栈帧底部添加一个随机字节,每次函数将要返回时,都会这个随机字节进行验证,如果这个随机字节被篡改,则说明该栈帧发生数据溢出,报出异常,程序终止。在编译时可以通过-fno-stack-protector选项取消这项保护。

    NX(Never eXecute):数据执行保护,在64位系统的CPU中增加一位NX位,用来标示数据如果可写就不可执行。在overflow这个程序中我们具有对栈数据写的权限,就没有对栈数据可执行的权限。

    ASLR(Address Space Layout Randomization):地址空间随机化,在每次程序加载运行的时候,堆栈数据的定位都会进行随机化处理。由于每次程序运行时堆栈地址都会发生变化,所以无疑给溢出利用增加了很大的难度。可以通过这个命令

    echo 0 > /proc/sys/kernel/randomize_va_space ,取消ASLR保护,然后方便验证poc。最后通过plt方式过掉ASLR。

  • 相关阅读:
    python--模块与包
    内置函数 的总结
    迭代器 生成器 列表推导式 生成器表达式的一些总结
    函数的有用信息 带参数的装饰器 多个装饰器装饰一个函数
    函数名的应用(第一对象) 闭包 装饰器
    动态参数 名称空间 作用域 作用域链 加载顺序 函数的嵌套 global nonlocal 等的用法总结
    函数的初识 函数的返回值 参数
    文件操作 常用操作方法 文件的修改
    遍历字典的集中方法 集合的作用 以及增删查的方法
    计算机硬件的小知识
  • 原文地址:https://www.cnblogs.com/2014asm/p/10098005.html
Copyright © 2011-2022 走看看