Skip to content

Commit

Permalink
Add connect_from-ip command
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 committed Aug 20, 2023
1 parent 2fe5cee commit b0ce18a
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 18 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
69 changes: 66 additions & 3 deletions scapy/layers/inet.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""

import time
import ssl
import struct
import re
import random
Expand Down Expand Up @@ -1888,15 +1889,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 +2164,65 @@ 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 tls: (optional) wrap the socket in a TLS context.
: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, timeout=1):
host = str(Net(host))
# poison the next hop
gateway = conf.route.route(host)[2]
if gateway == "0.0.0.0":
# on lan
gateway = host
from scapy.layers.l2 import arpcachepoison
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 b0ce18a

Please sign in to comment.