Skip to content

Commit

Permalink
Add connect_from_ip command (#4098)
Browse files Browse the repository at this point in the history
connect_from_ip creates a tcp socket that spoofs another IP.
  • Loading branch information
gpotter2 authored Aug 22, 2023
1 parent 2fe5cee commit 62f398c
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 20 deletions.
27 changes: 13 additions & 14 deletions scapy/automaton.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import logging
import os
import random
import socket
import sys
import threading
import time
Expand Down Expand Up @@ -77,7 +78,7 @@ def select_objects(inputs, remain):
[b]
:param inputs: objects to process
:param remain: timeout. If 0, return [].
:param remain: timeout. If 0, poll.
"""
if not WINDOWS:
return select.select(inputs, [], [], remain)[0]
Expand Down Expand Up @@ -901,35 +902,33 @@ def __init__(self,
wr # type: Union[int, ObjectPipe[bytes], None]
):
# type: (...) -> None
if rd is not None and not isinstance(rd, (int, ObjectPipe)):
rd = rd.fileno()
if wr is not None and not isinstance(wr, (int, ObjectPipe)):
wr = wr.fileno()
self.rd = rd
self.wr = wr
if isinstance(self.rd, socket.socket):
self.__selectable_force_select__ = True

def fileno(self):
# type: () -> int
if isinstance(self.rd, ObjectPipe):
return self.rd.fileno()
elif isinstance(self.rd, int):
if isinstance(self.rd, int):
return self.rd
elif self.rd:
return self.rd.fileno()
return 0

def read(self, n=65535):
# type: (int) -> Optional[bytes]
if isinstance(self.rd, ObjectPipe):
return self.rd.recv(n)
elif isinstance(self.rd, int):
if isinstance(self.rd, int):
return os.read(self.rd, n)
elif self.rd:
return self.rd.recv(n)
return None

def write(self, msg):
# type: (bytes) -> int
if isinstance(self.wr, ObjectPipe):
return self.wr.send(msg)
elif isinstance(self.wr, int):
if isinstance(self.wr, int):
return os.write(self.wr, msg)
elif self.wr:
return self.wr.send(msg)
return 0

def recv(self, n=65535):
Expand Down
81 changes: 76 additions & 5 deletions scapy/layers/inet.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@
from scapy.base_classes import Gen, Net
from scapy.data import ETH_P_IP, ETH_P_ALL, DLT_RAW, DLT_RAW_ALT, DLT_IPV4, \
IP_PROTOS, TCP_SERVICES, UDP_SERVICES
from scapy.layers.l2 import Ether, Dot3, getmacbyip, CookedLinux, GRE, SNAP, \
Loopback
from scapy.layers.l2 import (
CookedLinux,
Dot3,
Ether,
GRE,
Loopback,
SNAP,
arpcachepoison,
getmacbyip,
)
from scapy.compat import raw, chb, orb, bytes_encode, Optional
from scapy.config import conf
from scapy.fields import (
Expand Down Expand Up @@ -1888,15 +1896,18 @@ class TCP_client(Automaton):
:param ip: the ip to connect to
:param port:
:param src: (optional) use another source IP
"""

def parse_args(self, ip, port, *args, **kargs):
def parse_args(self, ip, port, srcip=None, **kargs):
from scapy.sessions import TCPSession
self.dst = str(Net(ip))
self.dport = port
self.sport = random.randrange(0, 2**16)
self.l4 = IP(dst=ip) / TCP(sport=self.sport, dport=self.dport, flags=0,
seq=random.randrange(0, 2**32))
self.l4 = IP(dst=ip, src=srcip) / TCP(
sport=self.sport, dport=self.dport,
flags=0, seq=random.randrange(0, 2**32)
)
self.src = self.l4.src
self.sack = self.l4[TCP].ack
self.rel_seq = None
Expand Down Expand Up @@ -2160,6 +2171,66 @@ def fragleak2(target, timeout=0.4, onlyasc=0, count=None):
pass


@conf.commands.register
class connect_from_ip:
"""
Open a TCP socket to a host:port while spoofing another IP.
:param host: the host to connect to
:param port: the port to connect to
:param srcip: the IP to spoof. the cache of the gateway will
be poisonned with this IP.
:param poison: (optional, default True) ARP poison the gateway (or next hop),
so that it answers us.
:param timeout: (optional) the socket timeout.
Example - Connect to 192.168.0.1:80 spoofing 192.168.0.2::
from scapy.layers.http import HTTP, HTTPRequest
client = connect_from_ip("192.168.0.1", 80, "192.168.0.2")
sock = SSLStreamSocket(client.sock, HTTP)
resp = sock.sr1(HTTP() / HTTPRequest(Path="/"))
Example - Connect to 192.168.0.1:443 with TLS wrapping spoofing 192.168.0.2::
import ssl
from scapy.layers.http import HTTP, HTTPRequest
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
client = connect_from_ip("192.168.0.1", 443, "192.168.0.2")
sock = context.wrap_socket(client.sock)
sock = SSLStreamSocket(client.sock, HTTP)
resp = sock.sr1(HTTP() / HTTPRequest(Path="/"))
"""

def __init__(self, host, port, srcip, poison=True, timeout=1):
host = str(Net(host))
# poison the next hop
if poison:
gateway = conf.route.route(host)[2]
if gateway == "0.0.0.0":
# on lan
gateway = host
arpcachepoison(gateway, srcip, count=1, interval=0, verbose=0)
# create a socket pair
self._sock, self.sock = socket.socketpair()
self.sock.settimeout(timeout)
self.client = TCP_client(
host, port,
srcip=srcip,
external_fd={"tcp": self._sock},
)
# start the TCP_client
self.client.runbg()

def close(self):
self.client.stop()
self.client.destroy()
self.sock.close()
self._sock.close()


class ICMPEcho_am(AnsweringMachine):
"""Responds to ICMP Echo-Requests (ping)"""
function_name = "icmpechod"
Expand Down
7 changes: 6 additions & 1 deletion scapy/layers/l2.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,9 @@ def arpcachepoison(
target, # type: Union[str, List[str]]
addresses, # type: Union[str, Tuple[str, str], List[Tuple[str, str]]]
broadcast=False, # type: bool
count=None, # type: Optional[int]
interval=15, # type: int
**kwargs, # type: Any
):
# type: (...) -> None
"""Poison targets' ARP cache
Expand Down Expand Up @@ -815,9 +817,12 @@ def arpcachepoison(
hwsrc=y, hwdst="00:00:00:00:00:00")
for x, y in couple_list
]
if count is not None:
sendp(p, iface_hint=str_target, count=count, inter=interval, **kwargs)
return
try:
while True:
sendp(p, iface_hint=str_target)
sendp(p, iface_hint=str_target, **kwargs)
time.sleep(interval)
except KeyboardInterrupt:
pass
Expand Down
1 change: 1 addition & 0 deletions scapy/supersocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ def send(self, x):

class SimpleSocket(SuperSocket):
desc = "wrapper around a classic socket"
__selectable_force_select__ = True

def __init__(self, sock):
# type: (socket.socket) -> None
Expand Down

0 comments on commit 62f398c

Please sign in to comment.