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。

  • 相关阅读:
    oracle EXP导出一张表时使用query参数指定where条件
    Centos7-安装Apache2.4+PHP5.6
    VMWare 14 Workstation Pro 下载与安装
    Mysql的视图、存储过程、函数、索引全解析
    Eclipse Java注释模板设置详解
    Tomcat 80端口 配置及域名访问步骤
    LeetCode——merge-k-sorted-lists
    LeetCode——rotate-list
    LeetCode——jump-game-ii
    LeetCode——jump-game
  • 原文地址:https://www.cnblogs.com/2014asm/p/10098005.html
Copyright © 2011-2022 走看看