From 0020641ca3c93576e589b229724dea4a0af7b572 Mon Sep 17 00:00:00 2001 From: cytopia Date: Thu, 14 May 2020 13:25:36 +0200 Subject: [PATCH 1/4] Add feature: Add IP_TOS (`-T`/`--tos`) and show sock info (`--info`) --- CHANGELOG.md | 7 ++ README.md | 12 ++- bin/pwncat | 221 ++++++++++++++++++++++++++++++++++++++++++++++-- docs/index.html | 8 ++ setup.cfg | 2 +- setup.py | 2 +- 6 files changed, 242 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3917b9bc..733f52fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ ## Unreleased +## Release 0.0.18-alpha + +### Added +- Feature: IP ToS selection (`-T`/`--tos`) +- Feature: Print socket options (`--info`) for socket, IPv4, IPv6 and/or TCP + + ## Release 0.0.17-alpha ### Fixed diff --git a/README.md b/README.md index c26d53c7..611c7a2e 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ -> [1] mypy type coverage (fully typed: 93.94%)
+> [1] mypy type coverage (fully typed: 94.11%)
> [2] Windows builds are currently only failing, because they are simply stuck on GitHub actions. @@ -251,7 +251,7 @@ pwncat -R 10.0.0.1:4444 everythingcli.org 3306 -u |---------------------|--------|---------|-----| | Scripting engine | Python | :x: | Lua | | Self-injecting | ✔ | :x: | :x: | -| IP ToS | :x: | ✔ | :x: | +| IP ToS | ✔ | ✔ | :x: | | IPv4 | ✔ | ✔ | ✔ | | IPv6 | ✔ | ✔ | ✔ | | Unix domain sockets | :x: | ✔ | ✔ | @@ -408,9 +408,17 @@ optional arguments: CR on MacOS). -n, --nodns Do not resolve DNS. -u, --udp Use UDP for the connection instead of TCP. + -T str, --tos str Specifies IP Type of Service (ToS) for the connection. + Valid values are the tokens 'mincost', 'lowcost', + 'reliability', 'throughput' or 'lowdelay'. -v, --verbose Be verbose and print info to stderr. Use -v, -vv, -vvv or -vvvv for more verbosity. The server performance will decrease drastically if you use more than three times. + --info type Show additional info about sockets, ip4/6 or tcp opts + applied to the current socket connection. Valid + parameter are 'sock', 'ipv4', 'ipv6', 'tcp' or 'all'. + Note, you must at least be in INFO verbose mode in order + to see them (-vv). -c str, --color str Colored log output. Specify 'always', 'never' or 'auto'. In 'auto' mode, color is displayed as long as the output goes to a terminal. If it is piped into a file, color diff --git a/bin/pwncat b/bin/pwncat index 39e22714..77cf3d11 100755 --- a/bin/pwncat +++ b/bin/pwncat @@ -91,7 +91,7 @@ if os.name != "nt": APPNAME = "pwncat" APPREPO = "https://github.com/cytopia/pwncat" -VERSION = "0.0.17-alpha" +VERSION = "0.0.18-alpha" # Default timeout for timeout-based sys.stdin and socket.recv TIMEOUT_READ_STDIN = 0.1 @@ -284,23 +284,38 @@ class DsSock(object): """`bool`: Determines if we use TCP or UDP.""" return self.__udp + @property + def ip_tos(self): + # type: () -> Optional[str] + """`str`: Determines what IP_TOS (Type of Service) value to set for the socket.""" + return self.__ip_tos + + @property + def info(self): + # type: () -> str + """`str`: Determines what info to display about the socket connection.""" + return self.__info + # -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv6, udp): - # type: (int, int, Optional[float], bool, bool, bool) -> None + def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv6, udp, ip_tos, info): + # type: (int, int, Optional[float], bool, bool, bool, Optional[str], str) -> None assert type(bufsize) is int, type(bufsize) assert type(backlog) is int, type(backlog) assert type(recv_timeout) is float, type(recv_timeout) assert type(nodns) is bool, type(nodns) assert type(ipv6) is bool, type(ipv6) assert type(udp) is bool, type(udp) + assert type(info) is str, type(info) self.__bufsize = bufsize self.__backlog = backlog self.__recv_timeout = recv_timeout self.__nodns = nodns self.__ipv6 = ipv6 self.__udp = udp + self.__ip_tos = ip_tos + self.__info = info # ------------------------------------------------------------------------------------------------- @@ -321,11 +336,24 @@ class DsIONetworkSock(DsSock): # -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, recv_timeout_retry, nodns, ipv6, udp): - # type: (int, int, Optional[float], int, bool, bool, bool) -> None + def __init__( + self, + bufsize, # type: int + backlog, # type: int + recv_timeout, # type: Optional[float] + recv_timeout_retry, # type: int + nodns, # type: bool + ipv6, # type: bool + udp, # type: bool + ip_tos, # type: Optional[str] + info, # type: str + ): + # type: (...) -> None assert type(recv_timeout_retry) is int, type(recv_timeout_retry) self.__recv_timeout_retry = recv_timeout_retry - super(DsIONetworkSock, self).__init__(bufsize, backlog, recv_timeout, nodns, ipv6, udp) + super(DsIONetworkSock, self).__init__( + bufsize, backlog, recv_timeout, nodns, ipv6, udp, ip_tos, info + ) # ------------------------------------------------------------------------------------------------- @@ -753,6 +781,17 @@ class Sock(object): __sock = None # type: socket.socket __conn = None # type: socket.socket + # For Internet Protocol v4 the value consists of an integer, the least significant 8 bits of + # which represent the value of the TOS octet in IP packets sent by the socket. + # RFC 1349 defines the TOS values as follows: + IP_TOS = { + "mincost": 0x02, + "lowcost": 0x02, + "reliability": 0x04, + "throughput": 0x08, + "lowdelay": 0x10, + } + # -------------------------------------------------------------------------- # Constructor / Destructor # -------------------------------------------------------------------------- @@ -982,6 +1021,7 @@ class Sock(object): # (For TCP, this is done in __connect() self.__remote_addr = addr self.__remote_port = port + self.__print_socket_opts(self.__conn, "conn-sock") return True # [TCP 4/4] connect @@ -991,6 +1031,7 @@ class Sock(object): self.__close("conn", self.__conn) return False + self.__print_socket_opts(self.__conn, "conn-sock") return True def run_server(self, addr, port): @@ -1030,6 +1071,7 @@ class Sock(object): self.__close("sock", sock) return False self.__log.info("Listening on %s (family %d/TCP, port %d)", addr, family, port) + self.__print_socket_opts(sock, "bind-sock") # Accept try: conn = self.__accept(sock) @@ -1041,6 +1083,7 @@ class Sock(object): # [4/4] Set current receive timeout for socket to make it non-blocking self.__settimeout(self.__conn) + self.__print_socket_opts(self.__conn, "conn-sock") return True def re_accept_client(self): @@ -1084,6 +1127,129 @@ class Sock(object): # -------------------------------------------------------------------------- # Private Functions (general) # -------------------------------------------------------------------------- + def __print_socket_opts(self, sock, log_prefix="Socket"): + # type: (socket.socket, str) -> None + """Debug logs configured socket options.""" + # https://hg.python.org/cpython/file/3.5/Modules/socketmodule.c + options = { + "Sock": [ + "SO_DEBUG", + "SO_ACCEPTCONN", + "SO_REUSEADDR", + "SO_EXCLUSIVEADDRUSE", + "SO_KEEPALIVE", + "SO_DONTROUTE", + "SO_BROADCAST", + "SO_USELOOPBACK", + "SO_LINGER", + "SO_OOBINLINE", + "SO_REUSEPORT", + "SO_SNDBUF", + "SO_RCVBUF", + "SO_SNDLOWAT", + "SO_RCVLOWAT", + "SO_SNDTIMEO", + "SO_RCVTIMEO", + "SO_ERROR", + "SO_TYPE", + "SO_SETFIB", + "SO_PASSCRED", + "SO_PEERCRED", + "LOCAL_PEERCRED", + "SO_BINDTODEVICE", + "SO_PRIORITY", + "SO_MARK", + ], + "IPv4": [ + "IP_OPTIONS", + "IP_HDRINCL", + "IP_TOS", + "IP_TTL", + "IP_RECVOPTS", + "IP_RECVRETOPTS", + "IP_RECVDSTADDR", + "IP_RETOPTS", + "IP_MULTICAST_IF", + "IP_MULTICAST_TTL", + "IP_MULTICAST_LOOP", + "IP_ADD_MEMBERSHIP", + "IP_DROP_MEMBERSHIP", + "IP_DEFAULT_MULTICAST_TTL", + "IP_DEFAULT_MULTICAST_LOOP", + "IP_MAX_MEMBERSHIPS", + "IP_TRANSPARENT", + ], + "IPv6": [ + "IPV6_JOIN_GROUP", + "IPV6_LEAVE_GROUP", + "IPV6_MULTICAST_HOPS", + "IPV6_MULTICAST_IF", + "IPV6_MULTICAST_LOOP", + "IPV6_UNICAST_HOPS", + "IPV6_V6ONLY", + "IPV6_CHECKSUM", + "IPV6_DONTFRAG", + "IPV6_DSTOPTS", + "IPV6_HOPLIMIT", + "IPV6_HOPOPTS", + "IPV6_NEXTHOP", + "IPV6_PATHMTU", + "IPV6_PKTINFO", + "IPV6_RECVDSTOPTS", + "IPV6_RECVHOPLIMIT", + "IPV6_RECVHOPOPTS", + "IPV6_RECVPKTINFO", + "IPV6_RECVRTHDR", + "IPV6_RECVTCLASS", + "IPV6_RTHDR", + "IPV6_RTHDRDSTOPTS", + "IPV6_RTHDR_TYPE_0", + "IPV6_RECVPATHMTU", + "IPV6_TCLASS", + "IPV6_USE_MIN_MTU", + ], + "TCP": [ + "TCP_NODELAY", + "TCP_MAXSEG", + "TCP_CORK", + "TCP_KEEPIDLE", + "TCP_KEEPINTVL", + "TCP_KEEPCNT", + "TCP_SYNCNT", + "TCP_LINGER2", + "TCP_DEFER_ACCEPT", + "TCP_WINDOW_CLAMP", + "TCP_INFO", + "TCP_QUICKACK", + "TCP_FASTOPEN", + ], + } + for proto, optnames in options.items(): + if self.__options.info == "all" or proto.lower() == self.__options.info: + for optname in optnames: + if proto.lower() == "sock": + level = socket.SOL_SOCKET + elif proto.lower() == "ipv4": + level = socket.IPPROTO_IP + elif proto.lower() == "ipv6": + level = socket.IPPROTO_IPV6 + elif proto.lower() == "tcp": + level = socket.IPPROTO_TCP + try: + self.__log.info( + "[%s] %s: %s: %s", + log_prefix, + proto, + optname, + sock.getsockopt( + level, eval("socket." + optname) # pylint: disable=eval-used + ), + ) + except AttributeError: + pass + except (OSError, socket.error): + pass + def __create_socket(self): # type: () -> socket.socket """Create TCP or UDP socket. @@ -1110,6 +1276,11 @@ class Sock(object): # Get around the "[Errno 98] Address already in use" error, if the socket is still in wait # we instruct it to reuse the address anyway. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if self.__options.ip_tos is not None: + self.__log.info("Setting IP_TOS to: %d", self.IP_TOS[self.__options.ip_tos]) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, self.IP_TOS[self.__options.ip_tos]) + return sock def __settimeout(self, sock): @@ -2545,6 +2716,20 @@ def _args_check_lf(value): raise argparse.ArgumentTypeError("'%s' is an invalid choice" % value) +def _args_check_tos(value): + # type: (str) -> str + if value not in ["mincost", "lowcost", "reliability", "throughput", "lowdelay"]: + raise argparse.ArgumentTypeError("%s is an invalid tos definition" % value) + return value + + +def _args_check_info(value): + # type: (str) -> str + if value not in ["sock", "ipv4", "ipv6", "tcp", "all", ""]: + raise argparse.ArgumentTypeError("%s is an invalid info definition" % value) + return value + + def _args_check_color(value): # type: (str) -> str if value not in ["auto", "always", "never"]: @@ -2886,6 +3071,17 @@ CR on MacOS).""", default=False, help="Use UDP for the connection instead of TCP.", ) + optional.add_argument( + "-T", + "--tos", + metavar="str", + default=None, + type=_args_check_tos, + help="""Specifies IP Type of Service (ToS) for the connection. +Valid values are the tokens 'mincost', 'lowcost', +'reliability', 'throughput' or 'lowdelay'. +""", + ) optional.add_argument( "-v", "--verbose", @@ -2894,6 +3090,17 @@ CR on MacOS).""", help="""Be verbose and print info to stderr. Use -v, -vv, -vvv or -vvvv for more verbosity. The server performance will decrease drastically if you use more than three times.""", + ) + optional.add_argument( + "--info", + metavar="type", + default="", + type=_args_check_info, + help="""Show additional info about sockets, ip4/6 or tcp opts +applied to the current socket connection. Valid +parameter are 'sock', 'ipv4', 'ipv6', 'tcp' or 'all'. +Note, you must at least be in INFO verbose mode in order +to see them (-vv).""", ) optional.add_argument( "-c", @@ -3248,6 +3455,8 @@ def main(): args.nodns, args.ipv6, args.udp, + args.tos, + args.info, ) srv_opts = DsIONetworkSrv(args.keep_open, rebind, args.rebind_wait, args.rebind_robin) cli_opts = DsIONetworkCli(reconn, args.reconn_wait, args.reconn_robin) diff --git a/docs/index.html b/docs/index.html index 034287ff..3e6dff38 100644 --- a/docs/index.html +++ b/docs/index.html @@ -185,9 +185,17 @@

pwncat

CR on MacOS). -n, --nodns Do not resolve DNS. -u, --udp Use UDP for the connection instead of TCP. + -T str, --tos str Specifies IP Type of Service (ToS) for the connection. + Valid values are the tokens 'mincost', 'lowcost', + 'reliability', 'throughput' or 'lowdelay'. -v, --verbose Be verbose and print info to stderr. Use -v, -vv, -vvv or -vvvv for more verbosity. The server performance will decrease drastically if you use more than three times. + --info type Show additional info about sockets, ip4/6 or tcp opts + applied to the current socket connection. Valid + parameter are 'sock', 'ipv4', 'ipv6', 'tcp' or 'all'. + Note, you must at least be in INFO verbose mode in order + to see them (-vv). -c str, --color str Colored log output. Specify 'always', 'never' or 'auto'. In 'auto' mode, color is displayed as long as the output goes to a terminal. If it is piped into a file, color diff --git a/setup.cfg b/setup.cfg index 51138aee..3f367933 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ max-line-length = 100 disable = useless-object-inheritance, bad-continuation, unidiomatic-typecheck max-branches = 17 max-statements = 72 -max-args = 9 +max-args = 10 max-attributes = 9 max-locals = 30 max-module-lines = 5000 diff --git a/setup.py b/setup.py index aee1dc20..f687d1f8 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="pwncat", - version="0.0.17-alpha", + version="0.0.18-alpha", description="Netcat on steroids with Firewall, IDS/IPS evasion, bind and reverse shell and port forwarding magic - and its fully scriptable with Python (PSE).", license="MIT", long_description=long_description, From 1054bb1520bf63960d028c30ca60638917384913 Mon Sep 17 00:00:00 2001 From: cytopia Date: Thu, 14 May 2020 19:22:25 +0200 Subject: [PATCH 2/4] Documentation: example socket information output --- README.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/README.md b/README.md index 611c7a2e..8a16aeb8 100644 --- a/README.md +++ b/README.md @@ -734,6 +734,8 @@ pwncat -l 4445 > **Note:** Ensure you have a reverse shell that keeps coming back to you. This way you can always change your logging settings without loosing the shell. +#### Log level and redirection + If you feel like, you can start a listener in full TRACE logging mode to figure out what's going on or simply to troubleshoot. Log message are colored depending on their severity. Colors are automatically turned off, if stderr is not a pty, e.g.: if piping those to a file. You can also manually disable colored logging for terminal outputs via the `--color` switch. @@ -780,6 +782,69 @@ tail -fn50 comm.txt 2020-05-11 08:40:57,927 DEBUG [STDIN] 834:send(): Sent 15 bytes to 127.0.0.1:46744 (0 bytes remaining) 2020-05-11 08:40:57,928 TRACE [STDIN] 1852:producer(): Reading command output ``` + +#### Socket information + +Another useful feature is to display currently configured socket and network settings. +Use the `--info` switch with either `socket`, `ipv4`, `ipv6`, `tcp` or `all` to display all +available settings. + +**Note:** In order to view those settings, you must at least be at `INFO` log level (`-vv`). + +An example output in IPv4/TCP mode without any custom settings is shown below: +``` +INFO: [bind-sock] Sock: SO_DEBUG: 0 +INFO: [bind-sock] Sock: SO_ACCEPTCONN: 1 +INFO: [bind-sock] Sock: SO_REUSEADDR: 1 +INFO: [bind-sock] Sock: SO_KEEPALIVE: 0 +INFO: [bind-sock] Sock: SO_DONTROUTE: 0 +INFO: [bind-sock] Sock: SO_BROADCAST: 0 +INFO: [bind-sock] Sock: SO_LINGER: 0 +INFO: [bind-sock] Sock: SO_OOBINLINE: 0 +INFO: [bind-sock] Sock: SO_REUSEPORT: 0 +INFO: [bind-sock] Sock: SO_SNDBUF: 16384 +INFO: [bind-sock] Sock: SO_RCVBUF: 131072 +INFO: [bind-sock] Sock: SO_SNDLOWAT: 1 +INFO: [bind-sock] Sock: SO_RCVLOWAT: 1 +INFO: [bind-sock] Sock: SO_SNDTIMEO: 0 +INFO: [bind-sock] Sock: SO_RCVTIMEO: 0 +INFO: [bind-sock] Sock: SO_ERROR: 0 +INFO: [bind-sock] Sock: SO_TYPE: 1 +INFO: [bind-sock] Sock: SO_PASSCRED: 0 +INFO: [bind-sock] Sock: SO_PEERCRED: 0 +INFO: [bind-sock] Sock: SO_BINDTODEVICE: 0 +INFO: [bind-sock] Sock: SO_PRIORITY: 0 +INFO: [bind-sock] Sock: SO_MARK: 0 +INFO: [bind-sock] IPv4: IP_OPTIONS: 0 +INFO: [bind-sock] IPv4: IP_HDRINCL: 0 +INFO: [bind-sock] IPv4: IP_TOS: 0 +INFO: [bind-sock] IPv4: IP_TTL: 64 +INFO: [bind-sock] IPv4: IP_RECVOPTS: 0 +INFO: [bind-sock] IPv4: IP_RECVRETOPTS: 0 +INFO: [bind-sock] IPv4: IP_RETOPTS: 0 +INFO: [bind-sock] IPv4: IP_MULTICAST_IF: 0 +INFO: [bind-sock] IPv4: IP_MULTICAST_TTL: 1 +INFO: [bind-sock] IPv4: IP_MULTICAST_LOOP: 1 +INFO: [bind-sock] IPv4: IP_DEFAULT_MULTICAST_TTL: 0 +INFO: [bind-sock] IPv4: IP_DEFAULT_MULTICAST_LOOP: 0 +INFO: [bind-sock] IPv4: IP_MAX_MEMBERSHIPS: 0 +INFO: [bind-sock] IPv4: IP_TRANSPARENT: 0 +INFO: [bind-sock] TCP: TCP_NODELAY: 0 +INFO: [bind-sock] TCP: TCP_MAXSEG: 536 +INFO: [bind-sock] TCP: TCP_CORK: 0 +INFO: [bind-sock] TCP: TCP_KEEPIDLE: 7200 +INFO: [bind-sock] TCP: TCP_KEEPINTVL: 75 +INFO: [bind-sock] TCP: TCP_KEEPCNT: 9 +INFO: [bind-sock] TCP: TCP_SYNCNT: 6 +INFO: [bind-sock] TCP: TCP_LINGER2: 60 +INFO: [bind-sock] TCP: TCP_DEFER_ACCEPT: 0 +INFO: [bind-sock] TCP: TCP_WINDOW_CLAMP: 0 +INFO: [bind-sock] TCP: TCP_INFO: 10 +INFO: [bind-sock] TCP: TCP_QUICKACK: 1 +INFO: [bind-sock] TCP: TCP_FASTOPEN: 0 +``` + + From 1fe13da18ffd6473fc8ea09dfaeec4e3209526e9 Mon Sep 17 00:00:00 2001 From: cytopia Date: Thu, 14 May 2020 19:24:28 +0200 Subject: [PATCH 3/4] Update documentation --- docs/pwncat.api.html | 531 ++++++++++++++++++++++++++++++++++++++++-- docs/pwncat.man.html | 19 ++ docs/pwncat.type.html | 476 +++++++++++++++++++++++++++++++++++-- man/pwncat.1 | 12 + 4 files changed, 1002 insertions(+), 36 deletions(-) diff --git a/docs/pwncat.api.html b/docs/pwncat.api.html index 0d8d4634..8375c3a2 100644 --- a/docs/pwncat.api.html +++ b/docs/pwncat.api.html @@ -118,7 +118,7 @@

Module pwncat

APPNAME = "pwncat" APPREPO = "https://github.com/cytopia/pwncat" -VERSION = "0.0.17-alpha" +VERSION = "0.0.18-alpha" # Default timeout for timeout-based sys.stdin and socket.recv TIMEOUT_READ_STDIN = 0.1 @@ -311,23 +311,38 @@

Module pwncat

"""`bool`: Determines if we use TCP or UDP.""" return self.__udp + @property + def ip_tos(self): + # type: () -> Optional[str] + """`str`: Determines what IP_TOS (Type of Service) value to set for the socket.""" + return self.__ip_tos + + @property + def info(self): + # type: () -> str + """`str`: Determines what info to display about the socket connection.""" + return self.__info + # -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv6, udp): - # type: (int, int, Optional[float], bool, bool, bool) -> None + def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv6, udp, ip_tos, info): + # type: (int, int, Optional[float], bool, bool, bool, Optional[str], str) -> None assert type(bufsize) is int, type(bufsize) assert type(backlog) is int, type(backlog) assert type(recv_timeout) is float, type(recv_timeout) assert type(nodns) is bool, type(nodns) assert type(ipv6) is bool, type(ipv6) assert type(udp) is bool, type(udp) + assert type(info) is str, type(info) self.__bufsize = bufsize self.__backlog = backlog self.__recv_timeout = recv_timeout self.__nodns = nodns self.__ipv6 = ipv6 self.__udp = udp + self.__ip_tos = ip_tos + self.__info = info # ------------------------------------------------------------------------------------------------- @@ -348,11 +363,24 @@

Module pwncat

# -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, recv_timeout_retry, nodns, ipv6, udp): - # type: (int, int, Optional[float], int, bool, bool, bool) -> None + def __init__( + self, + bufsize, # type: int + backlog, # type: int + recv_timeout, # type: Optional[float] + recv_timeout_retry, # type: int + nodns, # type: bool + ipv6, # type: bool + udp, # type: bool + ip_tos, # type: Optional[str] + info, # type: str + ): + # type: (...) -> None assert type(recv_timeout_retry) is int, type(recv_timeout_retry) self.__recv_timeout_retry = recv_timeout_retry - super(DsIONetworkSock, self).__init__(bufsize, backlog, recv_timeout, nodns, ipv6, udp) + super(DsIONetworkSock, self).__init__( + bufsize, backlog, recv_timeout, nodns, ipv6, udp, ip_tos, info + ) # ------------------------------------------------------------------------------------------------- @@ -780,6 +808,17 @@

Module pwncat

__sock = None # type: socket.socket __conn = None # type: socket.socket + # For Internet Protocol v4 the value consists of an integer, the least significant 8 bits of + # which represent the value of the TOS octet in IP packets sent by the socket. + # RFC 1349 defines the TOS values as follows: + IP_TOS = { + "mincost": 0x02, + "lowcost": 0x02, + "reliability": 0x04, + "throughput": 0x08, + "lowdelay": 0x10, + } + # -------------------------------------------------------------------------- # Constructor / Destructor # -------------------------------------------------------------------------- @@ -1009,6 +1048,7 @@

Module pwncat

# (For TCP, this is done in __connect() self.__remote_addr = addr self.__remote_port = port + self.__print_socket_opts(self.__conn, "conn-sock") return True # [TCP 4/4] connect @@ -1018,6 +1058,7 @@

Module pwncat

self.__close("conn", self.__conn) return False + self.__print_socket_opts(self.__conn, "conn-sock") return True def run_server(self, addr, port): @@ -1057,6 +1098,7 @@

Module pwncat

self.__close("sock", sock) return False self.__log.info("Listening on %s (family %d/TCP, port %d)", addr, family, port) + self.__print_socket_opts(sock, "bind-sock") # Accept try: conn = self.__accept(sock) @@ -1068,6 +1110,7 @@

Module pwncat

# [4/4] Set current receive timeout for socket to make it non-blocking self.__settimeout(self.__conn) + self.__print_socket_opts(self.__conn, "conn-sock") return True def re_accept_client(self): @@ -1111,6 +1154,129 @@

Module pwncat

# -------------------------------------------------------------------------- # Private Functions (general) # -------------------------------------------------------------------------- + def __print_socket_opts(self, sock, log_prefix="Socket"): + # type: (socket.socket, str) -> None + """Debug logs configured socket options.""" + # https://hg.python.org/cpython/file/3.5/Modules/socketmodule.c + options = { + "Sock": [ + "SO_DEBUG", + "SO_ACCEPTCONN", + "SO_REUSEADDR", + "SO_EXCLUSIVEADDRUSE", + "SO_KEEPALIVE", + "SO_DONTROUTE", + "SO_BROADCAST", + "SO_USELOOPBACK", + "SO_LINGER", + "SO_OOBINLINE", + "SO_REUSEPORT", + "SO_SNDBUF", + "SO_RCVBUF", + "SO_SNDLOWAT", + "SO_RCVLOWAT", + "SO_SNDTIMEO", + "SO_RCVTIMEO", + "SO_ERROR", + "SO_TYPE", + "SO_SETFIB", + "SO_PASSCRED", + "SO_PEERCRED", + "LOCAL_PEERCRED", + "SO_BINDTODEVICE", + "SO_PRIORITY", + "SO_MARK", + ], + "IPv4": [ + "IP_OPTIONS", + "IP_HDRINCL", + "IP_TOS", + "IP_TTL", + "IP_RECVOPTS", + "IP_RECVRETOPTS", + "IP_RECVDSTADDR", + "IP_RETOPTS", + "IP_MULTICAST_IF", + "IP_MULTICAST_TTL", + "IP_MULTICAST_LOOP", + "IP_ADD_MEMBERSHIP", + "IP_DROP_MEMBERSHIP", + "IP_DEFAULT_MULTICAST_TTL", + "IP_DEFAULT_MULTICAST_LOOP", + "IP_MAX_MEMBERSHIPS", + "IP_TRANSPARENT", + ], + "IPv6": [ + "IPV6_JOIN_GROUP", + "IPV6_LEAVE_GROUP", + "IPV6_MULTICAST_HOPS", + "IPV6_MULTICAST_IF", + "IPV6_MULTICAST_LOOP", + "IPV6_UNICAST_HOPS", + "IPV6_V6ONLY", + "IPV6_CHECKSUM", + "IPV6_DONTFRAG", + "IPV6_DSTOPTS", + "IPV6_HOPLIMIT", + "IPV6_HOPOPTS", + "IPV6_NEXTHOP", + "IPV6_PATHMTU", + "IPV6_PKTINFO", + "IPV6_RECVDSTOPTS", + "IPV6_RECVHOPLIMIT", + "IPV6_RECVHOPOPTS", + "IPV6_RECVPKTINFO", + "IPV6_RECVRTHDR", + "IPV6_RECVTCLASS", + "IPV6_RTHDR", + "IPV6_RTHDRDSTOPTS", + "IPV6_RTHDR_TYPE_0", + "IPV6_RECVPATHMTU", + "IPV6_TCLASS", + "IPV6_USE_MIN_MTU", + ], + "TCP": [ + "TCP_NODELAY", + "TCP_MAXSEG", + "TCP_CORK", + "TCP_KEEPIDLE", + "TCP_KEEPINTVL", + "TCP_KEEPCNT", + "TCP_SYNCNT", + "TCP_LINGER2", + "TCP_DEFER_ACCEPT", + "TCP_WINDOW_CLAMP", + "TCP_INFO", + "TCP_QUICKACK", + "TCP_FASTOPEN", + ], + } + for proto, optnames in options.items(): + if self.__options.info == "all" or proto.lower() == self.__options.info: + for optname in optnames: + if proto.lower() == "sock": + level = socket.SOL_SOCKET + elif proto.lower() == "ipv4": + level = socket.IPPROTO_IP + elif proto.lower() == "ipv6": + level = socket.IPPROTO_IPV6 + elif proto.lower() == "tcp": + level = socket.IPPROTO_TCP + try: + self.__log.info( + "[%s] %s: %s: %s", + log_prefix, + proto, + optname, + sock.getsockopt( + level, eval("socket." + optname) # pylint: disable=eval-used + ), + ) + except AttributeError: + pass + except (OSError, socket.error): + pass + def __create_socket(self): # type: () -> socket.socket """Create TCP or UDP socket. @@ -1137,6 +1303,11 @@

Module pwncat

# Get around the "[Errno 98] Address already in use" error, if the socket is still in wait # we instruct it to reuse the address anyway. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if self.__options.ip_tos is not None: + self.__log.info("Setting IP_TOS to: %d", self.IP_TOS[self.__options.ip_tos]) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, self.IP_TOS[self.__options.ip_tos]) + return sock def __settimeout(self, sock): @@ -1599,6 +1770,12 @@

Module pwncat

continue return + def ping(self, data): + # type: (str) -> None + """Send data to the socket and increment the port afterwards.""" + self.__net.send(data) + self.__increment_port_pointer() + def consumer(self, data): # type: (str) -> None """Send data to a socket.""" @@ -1618,6 +1795,13 @@

Module pwncat

# -------------------------------------------------------------------------- # Private Functions # -------------------------------------------------------------------------- + def __increment_port_pointer(self): + # type: () -> None + """Increments the pointer pointing to the list of ports we have available.""" + self.__pport += 1 + if self.__pport == len(self.__ports): + self.__pport = 0 + def __client_reconnect_to_server(self): # type: () -> bool """Ensure the client re-connects to the remote server, if the remote server hang up. @@ -1649,9 +1833,7 @@

Module pwncat

return False # [4/6] Increment the port numer (if --reconn-robin has multiple) - self.__pport += 1 - if self.__pport == len(self.__ports): - self.__pport = 0 + self.__increment_port_pointer() if self.__cli_opts.reconn > 0: self.log.info( @@ -2572,6 +2754,20 @@

Module pwncat

raise argparse.ArgumentTypeError("'%s' is an invalid choice" % value) +def _args_check_tos(value): + # type: (str) -> str + if value not in ["mincost", "lowcost", "reliability", "throughput", "lowdelay"]: + raise argparse.ArgumentTypeError("%s is an invalid tos definition" % value) + return value + + +def _args_check_info(value): + # type: (str) -> str + if value not in ["sock", "ipv4", "ipv6", "tcp", "all", ""]: + raise argparse.ArgumentTypeError("%s is an invalid info definition" % value) + return value + + def _args_check_color(value): # type: (str) -> str if value not in ["auto", "always", "never"]: @@ -2913,6 +3109,17 @@

Module pwncat

default=False, help="Use UDP for the connection instead of TCP.", ) + optional.add_argument( + "-T", + "--tos", + metavar="str", + default=None, + type=_args_check_tos, + help="""Specifies IP Type of Service (ToS) for the connection. +Valid values are the tokens 'mincost', 'lowcost', +'reliability', 'throughput' or 'lowdelay'. +""", + ) optional.add_argument( "-v", "--verbose", @@ -2921,6 +3128,17 @@

Module pwncat

help="""Be verbose and print info to stderr. Use -v, -vv, -vvv or -vvvv for more verbosity. The server performance will decrease drastically if you use more than three times.""", + ) + optional.add_argument( + "--info", + metavar="type", + default="", + type=_args_check_info, + help="""Show additional info about sockets, ip4/6 or tcp opts +applied to the current socket connection. Valid +parameter are 'sock', 'ipv4', 'ipv6', 'tcp' or 'all'. +Note, you must at least be in INFO verbose mode in order +to see them (-vv).""", ) optional.add_argument( "-c", @@ -3275,6 +3493,8 @@

Module pwncat

args.nodns, args.ipv6, args.udp, + args.tos, + args.info, ) srv_opts = DsIONetworkSrv(args.keep_open, rebind, args.rebind_wait, args.rebind_robin) cli_opts = DsIONetworkCli(reconn, args.reconn_wait, args.reconn_robin) @@ -3460,7 +3680,7 @@

Module pwncat

if type(args.ping_intvl) is int and args.ping_intvl > 0: run.add_timer( "PING", - DsRunnerTimer(net.consumer, ssig, args.ping_intvl, (args.ping_word)), # send data + DsRunnerTimer(net.ping, ssig, args.ping_intvl, (args.ping_word)), # send data ) run.run() @@ -3630,6 +3850,17 @@

Functions

default=False, help="Use UDP for the connection instead of TCP.", ) + optional.add_argument( + "-T", + "--tos", + metavar="str", + default=None, + type=_args_check_tos, + help="""Specifies IP Type of Service (ToS) for the connection. +Valid values are the tokens 'mincost', 'lowcost', +'reliability', 'throughput' or 'lowdelay'. +""", + ) optional.add_argument( "-v", "--verbose", @@ -3638,6 +3869,17 @@

Functions

help="""Be verbose and print info to stderr. Use -v, -vv, -vvv or -vvvv for more verbosity. The server performance will decrease drastically if you use more than three times.""", + ) + optional.add_argument( + "--info", + metavar="type", + default="", + type=_args_check_info, + help="""Show additional info about sockets, ip4/6 or tcp opts +applied to the current socket connection. Valid +parameter are 'sock', 'ipv4', 'ipv6', 'tcp' or 'all'. +Note, you must at least be in INFO verbose mode in order +to see them (-vv).""", ) optional.add_argument( "-c", @@ -4007,6 +4249,8 @@

Functions

args.nodns, args.ipv6, args.udp, + args.tos, + args.info, ) srv_opts = DsIONetworkSrv(args.keep_open, rebind, args.rebind_wait, args.rebind_robin) cli_opts = DsIONetworkCli(reconn, args.reconn_wait, args.reconn_robin) @@ -4192,7 +4436,7 @@

Functions

if type(args.ping_intvl) is int and args.ping_intvl > 0: run.add_timer( "PING", - DsRunnerTimer(net.consumer, ssig, args.ping_intvl, (args.ping_word)), # send data + DsRunnerTimer(net.ping, ssig, args.ping_intvl, (args.ping_word)), # send data ) run.run() @@ -5118,7 +5362,7 @@

Instance variables

class DsIONetworkSock -(bufsize, backlog, recv_timeout, recv_timeout_retry, nodns, ipv6, udp) +(bufsize, backlog, recv_timeout, recv_timeout_retry, nodns, ipv6, udp, ip_tos, info)

A type-safe data structure for IONetwork socket options.

@@ -5141,11 +5385,24 @@

Instance variables

# -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, recv_timeout_retry, nodns, ipv6, udp): - # type: (int, int, Optional[float], int, bool, bool, bool) -> None + def __init__( + self, + bufsize, # type: int + backlog, # type: int + recv_timeout, # type: Optional[float] + recv_timeout_retry, # type: int + nodns, # type: bool + ipv6, # type: bool + udp, # type: bool + ip_tos, # type: Optional[str] + info, # type: str + ): + # type: (...) -> None assert type(recv_timeout_retry) is int, type(recv_timeout_retry) self.__recv_timeout_retry = recv_timeout_retry - super(DsIONetworkSock, self).__init__(bufsize, backlog, recv_timeout, nodns, ipv6, udp) + super(DsIONetworkSock, self).__init__( + bufsize, backlog, recv_timeout, nodns, ipv6, udp, ip_tos, info + )

Ancestors

class DsSock -(bufsize, backlog, recv_timeout, nodns, ipv6, udp) +(bufsize, backlog, recv_timeout, nodns, ipv6, udp, ip_tos, info)

A type-safe data structure for DsSock options.

@@ -5704,23 +5963,38 @@

Instance variables

"""`bool`: Determines if we use TCP or UDP.""" return self.__udp + @property + def ip_tos(self): + # type: () -> Optional[str] + """`str`: Determines what IP_TOS (Type of Service) value to set for the socket.""" + return self.__ip_tos + + @property + def info(self): + # type: () -> str + """`str`: Determines what info to display about the socket connection.""" + return self.__info + # -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv6, udp): - # type: (int, int, Optional[float], bool, bool, bool) -> None + def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv6, udp, ip_tos, info): + # type: (int, int, Optional[float], bool, bool, bool, Optional[str], str) -> None assert type(bufsize) is int, type(bufsize) assert type(backlog) is int, type(backlog) assert type(recv_timeout) is float, type(recv_timeout) assert type(nodns) is bool, type(nodns) assert type(ipv6) is bool, type(ipv6) assert type(udp) is bool, type(udp) + assert type(info) is str, type(info) self.__bufsize = bufsize self.__backlog = backlog self.__recv_timeout = recv_timeout self.__nodns = nodns self.__ipv6 = ipv6 - self.__udp = udp + self.__udp = udp + self.__ip_tos = ip_tos + self.__info = info

Subclasses

    @@ -5756,6 +6030,34 @@

    Instance variables

    return self.__bufsize
+
var info
+
+

str: Determines what info to display about the socket connection.

+
+ +Expand source code + +
@property
+def info(self):
+    # type: () -> str
+    """`str`: Determines what info to display about the socket connection."""
+    return self.__info
+
+
+
var ip_tos
+
+

str: Determines what IP_TOS (Type of Service) value to set for the socket.

+
+ +Expand source code + +
@property
+def ip_tos(self):
+    # type: () -> Optional[str]
+    """`str`: Determines what IP_TOS (Type of Service) value to set for the socket."""
+    return self.__ip_tos
+
+
var ipv6

bool: Determines if we use IPv6 instead of IPv4.

@@ -6458,6 +6760,12 @@

Args

continue return + def ping(self, data): + # type: (str) -> None + """Send data to the socket and increment the port afterwards.""" + self.__net.send(data) + self.__increment_port_pointer() + def consumer(self, data): # type: (str) -> None """Send data to a socket.""" @@ -6477,6 +6785,13 @@

Args

# -------------------------------------------------------------------------- # Private Functions # -------------------------------------------------------------------------- + def __increment_port_pointer(self): + # type: () -> None + """Increments the pointer pointing to the list of ports we have available.""" + self.__pport += 1 + if self.__pport == len(self.__ports): + self.__pport = 0 + def __client_reconnect_to_server(self): # type: () -> bool """Ensure the client re-connects to the remote server, if the remote server hang up. @@ -6508,9 +6823,7 @@

Args

return False # [4/6] Increment the port numer (if --reconn-robin has multiple) - self.__pport += 1 - if self.__pport == len(self.__ports): - self.__pport = 0 + self.__increment_port_pointer() if self.__cli_opts.reconn > 0: self.log.info( @@ -6610,6 +6923,22 @@

Methods

self.ssig.raise_stop()
+
+def ping(self, data) +
+
+

Send data to the socket and increment the port afterwards.

+
+ +Expand source code + +
def ping(self, data):
+    # type: (str) -> None
+    """Send data to the socket and increment the port afterwards."""
+    self.__net.send(data)
+    self.__increment_port_pointer()
+
+
def producer(self)
@@ -7564,6 +7893,17 @@

Args

__sock = None # type: socket.socket __conn = None # type: socket.socket + # For Internet Protocol v4 the value consists of an integer, the least significant 8 bits of + # which represent the value of the TOS octet in IP packets sent by the socket. + # RFC 1349 defines the TOS values as follows: + IP_TOS = { + "mincost": 0x02, + "lowcost": 0x02, + "reliability": 0x04, + "throughput": 0x08, + "lowdelay": 0x10, + } + # -------------------------------------------------------------------------- # Constructor / Destructor # -------------------------------------------------------------------------- @@ -7793,6 +8133,7 @@

Args

# (For TCP, this is done in __connect() self.__remote_addr = addr self.__remote_port = port + self.__print_socket_opts(self.__conn, "conn-sock") return True # [TCP 4/4] connect @@ -7802,6 +8143,7 @@

Args

self.__close("conn", self.__conn) return False + self.__print_socket_opts(self.__conn, "conn-sock") return True def run_server(self, addr, port): @@ -7841,6 +8183,7 @@

Args

self.__close("sock", sock) return False self.__log.info("Listening on %s (family %d/TCP, port %d)", addr, family, port) + self.__print_socket_opts(sock, "bind-sock") # Accept try: conn = self.__accept(sock) @@ -7852,6 +8195,7 @@

Args

# [4/4] Set current receive timeout for socket to make it non-blocking self.__settimeout(self.__conn) + self.__print_socket_opts(self.__conn, "conn-sock") return True def re_accept_client(self): @@ -7895,6 +8239,129 @@

Args

# -------------------------------------------------------------------------- # Private Functions (general) # -------------------------------------------------------------------------- + def __print_socket_opts(self, sock, log_prefix="Socket"): + # type: (socket.socket, str) -> None + """Debug logs configured socket options.""" + # https://hg.python.org/cpython/file/3.5/Modules/socketmodule.c + options = { + "Sock": [ + "SO_DEBUG", + "SO_ACCEPTCONN", + "SO_REUSEADDR", + "SO_EXCLUSIVEADDRUSE", + "SO_KEEPALIVE", + "SO_DONTROUTE", + "SO_BROADCAST", + "SO_USELOOPBACK", + "SO_LINGER", + "SO_OOBINLINE", + "SO_REUSEPORT", + "SO_SNDBUF", + "SO_RCVBUF", + "SO_SNDLOWAT", + "SO_RCVLOWAT", + "SO_SNDTIMEO", + "SO_RCVTIMEO", + "SO_ERROR", + "SO_TYPE", + "SO_SETFIB", + "SO_PASSCRED", + "SO_PEERCRED", + "LOCAL_PEERCRED", + "SO_BINDTODEVICE", + "SO_PRIORITY", + "SO_MARK", + ], + "IPv4": [ + "IP_OPTIONS", + "IP_HDRINCL", + "IP_TOS", + "IP_TTL", + "IP_RECVOPTS", + "IP_RECVRETOPTS", + "IP_RECVDSTADDR", + "IP_RETOPTS", + "IP_MULTICAST_IF", + "IP_MULTICAST_TTL", + "IP_MULTICAST_LOOP", + "IP_ADD_MEMBERSHIP", + "IP_DROP_MEMBERSHIP", + "IP_DEFAULT_MULTICAST_TTL", + "IP_DEFAULT_MULTICAST_LOOP", + "IP_MAX_MEMBERSHIPS", + "IP_TRANSPARENT", + ], + "IPv6": [ + "IPV6_JOIN_GROUP", + "IPV6_LEAVE_GROUP", + "IPV6_MULTICAST_HOPS", + "IPV6_MULTICAST_IF", + "IPV6_MULTICAST_LOOP", + "IPV6_UNICAST_HOPS", + "IPV6_V6ONLY", + "IPV6_CHECKSUM", + "IPV6_DONTFRAG", + "IPV6_DSTOPTS", + "IPV6_HOPLIMIT", + "IPV6_HOPOPTS", + "IPV6_NEXTHOP", + "IPV6_PATHMTU", + "IPV6_PKTINFO", + "IPV6_RECVDSTOPTS", + "IPV6_RECVHOPLIMIT", + "IPV6_RECVHOPOPTS", + "IPV6_RECVPKTINFO", + "IPV6_RECVRTHDR", + "IPV6_RECVTCLASS", + "IPV6_RTHDR", + "IPV6_RTHDRDSTOPTS", + "IPV6_RTHDR_TYPE_0", + "IPV6_RECVPATHMTU", + "IPV6_TCLASS", + "IPV6_USE_MIN_MTU", + ], + "TCP": [ + "TCP_NODELAY", + "TCP_MAXSEG", + "TCP_CORK", + "TCP_KEEPIDLE", + "TCP_KEEPINTVL", + "TCP_KEEPCNT", + "TCP_SYNCNT", + "TCP_LINGER2", + "TCP_DEFER_ACCEPT", + "TCP_WINDOW_CLAMP", + "TCP_INFO", + "TCP_QUICKACK", + "TCP_FASTOPEN", + ], + } + for proto, optnames in options.items(): + if self.__options.info == "all" or proto.lower() == self.__options.info: + for optname in optnames: + if proto.lower() == "sock": + level = socket.SOL_SOCKET + elif proto.lower() == "ipv4": + level = socket.IPPROTO_IP + elif proto.lower() == "ipv6": + level = socket.IPPROTO_IPV6 + elif proto.lower() == "tcp": + level = socket.IPPROTO_TCP + try: + self.__log.info( + "[%s] %s: %s: %s", + log_prefix, + proto, + optname, + sock.getsockopt( + level, eval("socket." + optname) # pylint: disable=eval-used + ), + ) + except AttributeError: + pass + except (OSError, socket.error): + pass + def __create_socket(self): # type: () -> socket.socket """Create TCP or UDP socket. @@ -7921,6 +8388,11 @@

Args

# Get around the "[Errno 98] Address already in use" error, if the socket is still in wait # we instruct it to reuse the address anyway. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if self.__options.ip_tos is not None: + self.__log.info("Setting IP_TOS to: %d", self.IP_TOS[self.__options.ip_tos]) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, self.IP_TOS[self.__options.ip_tos]) + return sock def __settimeout(self, sock): @@ -8051,6 +8523,13 @@

Args

self.__remote_addr = addr self.__remote_port = port +

Class variables

+
+
var IP_TOS
+
+
+
+

Methods

@@ -8335,6 +8814,7 @@

Returns

# (For TCP, this is done in __connect() self.__remote_addr = addr self.__remote_port = port + self.__print_socket_opts(self.__conn, "conn-sock") return True # [TCP 4/4] connect @@ -8344,6 +8824,7 @@

Returns

self.__close("conn", self.__conn) return False + self.__print_socket_opts(self.__conn, "conn-sock") return True
@@ -8405,6 +8886,7 @@

Returns

self.__close("sock", sock) return False self.__log.info("Listening on %s (family %d/TCP, port %d)", addr, family, port) + self.__print_socket_opts(sock, "bind-sock") # Accept try: conn = self.__accept(sock) @@ -8416,6 +8898,7 @@

Returns

# [4/4] Set current receive timeout for socket to make it non-blocking self.__settimeout(self.__conn) + self.__print_socket_opts(self.__conn, "conn-sock") return True @@ -9168,6 +9651,8 @@

DsSock

@@ -9235,6 +9721,7 @@

Runner

  • Sock

      +
    • IP_TOS
    • close_bind_sock
    • close_conn_sock
    • gethostbyname
    • diff --git a/docs/pwncat.man.html b/docs/pwncat.man.html index e7df6c7a..6ae11623 100644 --- a/docs/pwncat.man.html +++ b/docs/pwncat.man.html @@ -178,6 +178,15 @@

      DESCRIPTION

      Use UDP for the connection instead of TCP.

      +

      −T str, +−−tos str

      + +

      Specifies IP Type of Service +(ToS) for the connection. Valid values are the tokens +’mincost’, ’lowcost’, +’reliability’, ’throughput’ or +’lowdelay’.

      +

      −v, −−verbose

      @@ -187,6 +196,16 @@

      DESCRIPTION The server performance will decrease drastically if you use more than three times.

      +

      −−info +type

      + +

      Show additional info about +sockets, ip4/6 or tcp opts applied to the current socket +connection. Valid parameter are ’sock’, +’ipv4’, ’ipv6’, ’tcp’ or +’all’. Note, you must at least be in INFO +verbose mode in order to see them (−vv).

      +

      −c str, −−color str

      diff --git a/docs/pwncat.type.html b/docs/pwncat.type.html index 5ba44754..1aa5863e 100644 --- a/docs/pwncat.type.html +++ b/docs/pwncat.type.html @@ -14,13 +14,13 @@

      Mypy Type Check Coverage Summary

  • - - + + - - + +
    Total6.06% imprecise3450 LOC5.89% imprecise3670 LOC
    bin/pwncat6.06% imprecise3450 LOC5.89% imprecise3670 LOC
    @@ -3485,6 +3485,226 @@

    pwncat

    3448 3449 3450 +3451 +3452 +3453 +3454 +3455 +3456 +3457 +3458 +3459 +3460 +3461 +3462 +3463 +3464 +3465 +3466 +3467 +3468 +3469 +3470 +3471 +3472 +3473 +3474 +3475 +3476 +3477 +3478 +3479 +3480 +3481 +3482 +3483 +3484 +3485 +3486 +3487 +3488 +3489 +3490 +3491 +3492 +3493 +3494 +3495 +3496 +3497 +3498 +3499 +3500 +3501 +3502 +3503 +3504 +3505 +3506 +3507 +3508 +3509 +3510 +3511 +3512 +3513 +3514 +3515 +3516 +3517 +3518 +3519 +3520 +3521 +3522 +3523 +3524 +3525 +3526 +3527 +3528 +3529 +3530 +3531 +3532 +3533 +3534 +3535 +3536 +3537 +3538 +3539 +3540 +3541 +3542 +3543 +3544 +3545 +3546 +3547 +3548 +3549 +3550 +3551 +3552 +3553 +3554 +3555 +3556 +3557 +3558 +3559 +3560 +3561 +3562 +3563 +3564 +3565 +3566 +3567 +3568 +3569 +3570 +3571 +3572 +3573 +3574 +3575 +3576 +3577 +3578 +3579 +3580 +3581 +3582 +3583 +3584 +3585 +3586 +3587 +3588 +3589 +3590 +3591 +3592 +3593 +3594 +3595 +3596 +3597 +3598 +3599 +3600 +3601 +3602 +3603 +3604 +3605 +3606 +3607 +3608 +3609 +3610 +3611 +3612 +3613 +3614 +3615 +3616 +3617 +3618 +3619 +3620 +3621 +3622 +3623 +3624 +3625 +3626 +3627 +3628 +3629 +3630 +3631 +3632 +3633 +3634 +3635 +3636 +3637 +3638 +3639 +3640 +3641 +3642 +3643 +3644 +3645 +3646 +3647 +3648 +3649 +3650 +3651 +3652 +3653 +3654 +3655 +3656 +3657 +3658 +3659 +3660 +3661 +3662 +3663 +3664 +3665 +3666 +3667 +3668 +3669 +3670
    #!/usr/bin/env python3
     """pwncat."""
    @@ -3580,7 +3800,7 @@ 

    pwncat

    APPNAME = "pwncat" APPREPO = "https://github.com/cytopia/pwncat" -VERSION = "0.0.17-alpha" +VERSION = "0.0.18-alpha" # Default timeout for timeout-based sys.stdin and socket.recv TIMEOUT_READ_STDIN = 0.1 @@ -3785,11 +4005,23 @@

    pwncat

    """`bool`: Determines if we use TCP or UDP.""" return self.__udp + @property + def ip_tos(self): + # type: () -> Optional[str] + """`str`: Determines what IP_TOS (Type of Service) value to set for the socket.""" + return self.__ip_tos + + @property + def info(self): + # type: () -> str + """`str`: Determines what info to display about the socket connection.""" + return self.__info + # -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv6, udp): - # type: (int, int, Optional[float], bool, bool, bool) -> None + def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv6, udp, ip_tos, info): + # type: (int, int, Optional[float], bool, bool, bool, Optional[str], str) -> None assert type(bufsize) is int, type(bufsize) assert type(ipv6) is bool, type(ipv6) assert type(udp) is bool, type(udp) + assert type(info) is str, type(info) self.__bufsize = bufsize self.__backlog = backlog self.__recv_timeout = recv_timeout self.__nodns = nodns self.__ipv6 = ipv6 self.__udp = udp + self.__ip_tos = ip_tos + self.__info = info # ------------------------------------------------------------------------------------------------- @@ -3828,12 +4064,25 @@

    pwncat

    # -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, recv_timeout_retry, nodns, ipv6, udp): - # type: (int, int, Optional[float], int, bool, bool, bool) -> None + def __init__( + self, + bufsize, # type: int + backlog, # type: int + recv_timeout, # type: Optional[float] + recv_timeout_retry, # type: int + nodns, # type: bool + ipv6, # type: bool + udp, # type: bool + ip_tos, # type: Optional[str] + info, # type: str + ): + # type: (...) -> None assert type(recv_timeout_retry) is int, type(recv_timeout_retry) self.__recv_timeout_retry = recv_timeout_retry - super(DsIONetworkSock, self).__init__(bufsize, backlog, recv_timeout, nodns, ipv6, udp) + super(DsIONetworkSock, self).__init__( + bufsize, backlog, recv_timeout, nodns, ipv6, udp, ip_tos, info + ) # ------------------------------------------------------------------------------------------------- @@ -4275,6 +4524,17 @@

    pwncat

    __sock = None # type: socket.socket __conn = None # type: socket.socket + # For Internet Protocol v4 the value consists of an integer, the least significant 8 bits of + # which represent the value of the TOS octet in IP packets sent by the socket. + # RFC 1349 defines the TOS values as follows: + IP_TOS = { + "mincost": 0x02, + "lowcost": 0x02, + "reliability": 0x04, + "throughput": 0x08, + "lowdelay": 0x10, + } + # -------------------------------------------------------------------------- # Constructor / Destructor # -------------------------------------------------------------------------- @@ -4525,6 +4785,7 @@

    pwncat

    # (For TCP, this is done in __connect() self.__remote_addr = addr self.__remote_port = port + self.__print_socket_opts(self.__conn, "conn-sock") return True # [TCP 4/4] connect @@ -4534,6 +4795,7 @@

    pwncat

    self.__close("conn", self.__conn) return False + self.__print_socket_opts(self.__conn, "conn-sock") return True def run_server(self, addr, port): @@ -4575,6 +4837,7 @@

    pwncat

    return False self.__log.info("Listening on %s (family %d/TCP, port %d)", addr, family, port) + self.__print_socket_opts(sock, "bind-sock") # Accept try: conn = self.__accept(sock) @@ -4586,6 +4849,7 @@

    pwncat

    # [4/4] Set current receive timeout for socket to make it non-blocking self.__settimeout(self.__conn) + self.__print_socket_opts(self.__conn, "conn-sock") return True def re_accept_client(self): @@ -4629,6 +4893,131 @@

    pwncat

    # -------------------------------------------------------------------------- # Private Functions (general) # -------------------------------------------------------------------------- + def __print_socket_opts(self, sock, log_prefix="Socket"): + # type: (socket.socket, str) -> None + """Debug logs configured socket options.""" + # https://hg.python.org/cpython/file/3.5/Modules/socketmodule.c + options = { + "Sock": [ + "SO_DEBUG", + "SO_ACCEPTCONN", + "SO_REUSEADDR", + "SO_EXCLUSIVEADDRUSE", + "SO_KEEPALIVE", + "SO_DONTROUTE", + "SO_BROADCAST", + "SO_USELOOPBACK", + "SO_LINGER", + "SO_OOBINLINE", + "SO_REUSEPORT", + "SO_SNDBUF", + "SO_RCVBUF", + "SO_SNDLOWAT", + "SO_RCVLOWAT", + "SO_SNDTIMEO", + "SO_RCVTIMEO", + "SO_ERROR", + "SO_TYPE", + "SO_SETFIB", + "SO_PASSCRED", + "SO_PEERCRED", + "LOCAL_PEERCRED", + "SO_BINDTODEVICE", + "SO_PRIORITY", + "SO_MARK", + ], + "IPv4": [ + "IP_OPTIONS", + "IP_HDRINCL", + "IP_TOS", + "IP_TTL", + "IP_RECVOPTS", + "IP_RECVRETOPTS", + "IP_RECVDSTADDR", + "IP_RETOPTS", + "IP_MULTICAST_IF", + "IP_MULTICAST_TTL", + "IP_MULTICAST_LOOP", + "IP_ADD_MEMBERSHIP", + "IP_DROP_MEMBERSHIP", + "IP_DEFAULT_MULTICAST_TTL", + "IP_DEFAULT_MULTICAST_LOOP", + "IP_MAX_MEMBERSHIPS", + "IP_TRANSPARENT", + ], + "IPv6": [ + "IPV6_JOIN_GROUP", + "IPV6_LEAVE_GROUP", + "IPV6_MULTICAST_HOPS", + "IPV6_MULTICAST_IF", + "IPV6_MULTICAST_LOOP", + "IPV6_UNICAST_HOPS", + "IPV6_V6ONLY", + "IPV6_CHECKSUM", + "IPV6_DONTFRAG", + "IPV6_DSTOPTS", + "IPV6_HOPLIMIT", + "IPV6_HOPOPTS", + "IPV6_NEXTHOP", + "IPV6_PATHMTU", + "IPV6_PKTINFO", + "IPV6_RECVDSTOPTS", + "IPV6_RECVHOPLIMIT", + "IPV6_RECVHOPOPTS", + "IPV6_RECVPKTINFO", + "IPV6_RECVRTHDR", + "IPV6_RECVTCLASS", + "IPV6_RTHDR", + "IPV6_RTHDRDSTOPTS", + "IPV6_RTHDR_TYPE_0", + "IPV6_RECVPATHMTU", + "IPV6_TCLASS", + "IPV6_USE_MIN_MTU", + ], + "TCP": [ + "TCP_NODELAY", + "TCP_MAXSEG", + "TCP_CORK", + "TCP_KEEPIDLE", + "TCP_KEEPINTVL", + "TCP_KEEPCNT", + "TCP_SYNCNT", + "TCP_LINGER2", + "TCP_DEFER_ACCEPT", + "TCP_WINDOW_CLAMP", + "TCP_INFO", + "TCP_QUICKACK", + "TCP_FASTOPEN", + ], + } + for proto, optnames in options.items(): + if self.__options.info == "all" or proto.lower() == self.__options.info: + for optname in optnames: + if proto.lower() == "sock": + level = socket.SOL_SOCKET + elif proto.lower() == "ipv4": + level = socket.IPPROTO_IP + elif proto.lower() == "ipv6": + level = socket.IPPROTO_IPV6 + elif proto.lower() == "tcp": + level = socket.IPPROTO_TCP + try: + self.__log.info( + "[%s] %s: %s: %s", + log_prefix, + proto, + optname, + sock.getsockopt( + level, eval("socket." + optname) # pylint: disable=eval-used + ), + ) + except AttributeError: + pass + except (OSError, socket.error): + pass + def __create_socket(self): # type: () -> socket.socket """Create TCP or UDP socket. @@ -4658,6 +5047,12 @@

    pwncat

    # Get around the "[Errno 98] Address already in use" error, if the socket is still in wait # we instruct it to reuse the address anyway. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if self.__options.ip_tos is not None: + self.__log.info("Setting IP_TOS to: %d", self.IP_TOS[self.__options.ip_tos]) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, self.IP_TOS[self.__options.ip_tos]) + return sock def __settimeout(self, sock): @@ -5147,6 +5542,12 @@

    pwncat

    continue return + def ping(self, data): + # type: (str) -> None + """Send data to the socket and increment the port afterwards.""" + self.__net.send(data) + self.__increment_port_pointer() + def consumer(self, data): # type: (str) -> None """Send data to a socket.""" @@ -5167,6 +5568,13 @@

    pwncat

    # -------------------------------------------------------------------------- # Private Functions # -------------------------------------------------------------------------- + def __increment_port_pointer(self): + # type: () -> None + """Increments the pointer pointing to the list of ports we have available.""" + self.__pport += 1 + if self.__pport == len(self.__ports): + self.__pport = 0 + def __client_reconnect_to_server(self): # type: () -> bool """Ensure the client re-connects to the remote server, if the remote server hang up. @@ -5198,9 +5606,7 @@

    pwncat

    return False # [4/6] Increment the port numer (if --reconn-robin has multiple) - self.__pport += 1 - if self.__pport == len(self.__ports): - self.__pport = 0 + self.__increment_port_pointer() if self.__cli_opts.reconn > 0: raise argparse.ArgumentTypeError("'%s' is an invalid choice" % value) +def _args_check_tos(value): + # type: (str) -> str + if value not in ["mincost", "lowcost", "reliability", "throughput", "lowdelay"]: + raise argparse.ArgumentTypeError("%s is an invalid tos definition" % value) + return value + + +def _args_check_info(value): + # type: (str) -> str + if value not in ["sock", "ipv4", "ipv6", "tcp", "all", ""]: + raise argparse.ArgumentTypeError("%s is an invalid info definition" % value) + return value + + def _args_check_color(value): # type: (str) -> str if value not in ["auto", "always", "never"]: @@ -6552,6 +6972,18 @@

    pwncat

    ) optional.add_argument( + "-T", + "--tos", + metavar="str", + default=None, + type=_args_check_tos, + help="""Specifies IP Type of Service (ToS) for the connection. +Valid values are the tokens 'mincost', 'lowcost', +'reliability', 'throughput' or 'lowdelay'. +""", + ) + optional.add_argument( "-v", "--verbose", action="count", @@ -6562,6 +6994,18 @@

    pwncat

    ) optional.add_argument( + "--info", + metavar="type", + default="", + type=_args_check_info, + help="""Show additional info about sockets, ip4/6 or tcp opts +applied to the current socket connection. Valid +parameter are 'sock', 'ipv4', 'ipv6', 'tcp' or 'all'. +Note, you must at least be in INFO verbose mode in order +to see them (-vv).""", + ) + optional.add_argument( "-c", "--color", metavar="str", @@ -6944,6 +7388,10 @@

    pwncat

    Explicit (x1)"> args.ipv6, args.udp, + args.tos, + args.info, ) srv_opts = DsIONetworkSrv(args.keep_open, rebind, args.rebind_wait, args.rebind_robin) @@ -7163,7 +7611,7 @@

    pwncat

    run.add_timer( "PING", DsRunnerTimer(net.consumer, ssig, args.ping_intvl, (args.ping_word)), # send data +Explicit (x6)"> DsRunnerTimer(net.ping, ssig, args.ping_intvl, (args.ping_word)), # send data ) run.run() diff --git a/man/pwncat.1 b/man/pwncat.1 index 39670e69..aeaf8b46 100644 --- a/man/pwncat.1 +++ b/man/pwncat.1 @@ -84,11 +84,23 @@ Do not resolve DNS. \fB\-u\fR, \fB\-\-udp\fR Use UDP for the connection instead of TCP. .TP +\fB\-T\fR str, \fB\-\-tos\fR str +Specifies IP Type of Service (ToS) for the connection. +Valid values are the tokens 'mincost', 'lowcost', +\&'reliability', 'throughput' or 'lowdelay'. +.TP \fB\-v\fR, \fB\-\-verbose\fR Be verbose and print info to stderr. Use \fB\-v\fR, \fB\-vv\fR, \fB\-vvv\fR or \fB\-vvvv\fR for more verbosity. The server performance will decrease drastically if you use more than three times. .TP +\fB\-\-info\fR type +Show additional info about sockets, ip4/6 or tcp opts +applied to the current socket connection. Valid +parameter are 'sock', 'ipv4', 'ipv6', 'tcp' or 'all'. +Note, you must at least be in INFO verbose mode in order +to see them (\fB\-vv\fR). +.TP \fB\-c\fR str, \fB\-\-color\fR str Colored log output. Specify 'always', 'never' or 'auto'. In 'auto' mode, color is displayed as long as the output From d57032a1c93e457e3b635675713c8836ee40f9c7 Mon Sep 17 00:00:00 2001 From: cytopia Date: Thu, 14 May 2020 19:59:38 +0200 Subject: [PATCH 4/4] Update docs --- README.md | 2 +- docs/pwncat.api.html | 55 +++++++------------------------------------ docs/pwncat.type.html | 38 +++++++----------------------- 3 files changed, 17 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 8a16aeb8..c5507e95 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ -> [1] mypy type coverage (fully typed: 94.11%)
    +> [1] mypy type coverage (fully typed: 94.10%)
    > [2] Windows builds are currently only failing, because they are simply stuck on GitHub actions. diff --git a/docs/pwncat.api.html b/docs/pwncat.api.html index 8375c3a2..d60e3f09 100644 --- a/docs/pwncat.api.html +++ b/docs/pwncat.api.html @@ -1770,12 +1770,6 @@

    Module pwncat

    continue return - def ping(self, data): - # type: (str) -> None - """Send data to the socket and increment the port afterwards.""" - self.__net.send(data) - self.__increment_port_pointer() - def consumer(self, data): # type: (str) -> None """Send data to a socket.""" @@ -1795,13 +1789,6 @@

    Module pwncat

    # -------------------------------------------------------------------------- # Private Functions # -------------------------------------------------------------------------- - def __increment_port_pointer(self): - # type: () -> None - """Increments the pointer pointing to the list of ports we have available.""" - self.__pport += 1 - if self.__pport == len(self.__ports): - self.__pport = 0 - def __client_reconnect_to_server(self): # type: () -> bool """Ensure the client re-connects to the remote server, if the remote server hang up. @@ -1833,7 +1820,9 @@

    Module pwncat

    return False # [4/6] Increment the port numer (if --reconn-robin has multiple) - self.__increment_port_pointer() + self.__pport += 1 + if self.__pport == len(self.__ports): + self.__pport = 0 if self.__cli_opts.reconn > 0: self.log.info( @@ -3680,7 +3669,7 @@

    Module pwncat

    if type(args.ping_intvl) is int and args.ping_intvl > 0: run.add_timer( "PING", - DsRunnerTimer(net.ping, ssig, args.ping_intvl, (args.ping_word)), # send data + DsRunnerTimer(net.consumer, ssig, args.ping_intvl, (args.ping_word)), # send data ) run.run() @@ -4436,7 +4425,7 @@

    Functions

    if type(args.ping_intvl) is int and args.ping_intvl > 0: run.add_timer( "PING", - DsRunnerTimer(net.ping, ssig, args.ping_intvl, (args.ping_word)), # send data + DsRunnerTimer(net.consumer, ssig, args.ping_intvl, (args.ping_word)), # send data ) run.run() @@ -6760,12 +6749,6 @@

    Args

    continue return - def ping(self, data): - # type: (str) -> None - """Send data to the socket and increment the port afterwards.""" - self.__net.send(data) - self.__increment_port_pointer() - def consumer(self, data): # type: (str) -> None """Send data to a socket.""" @@ -6785,13 +6768,6 @@

    Args

    # -------------------------------------------------------------------------- # Private Functions # -------------------------------------------------------------------------- - def __increment_port_pointer(self): - # type: () -> None - """Increments the pointer pointing to the list of ports we have available.""" - self.__pport += 1 - if self.__pport == len(self.__ports): - self.__pport = 0 - def __client_reconnect_to_server(self): # type: () -> bool """Ensure the client re-connects to the remote server, if the remote server hang up. @@ -6823,7 +6799,9 @@

    Args

    return False # [4/6] Increment the port numer (if --reconn-robin has multiple) - self.__increment_port_pointer() + self.__pport += 1 + if self.__pport == len(self.__ports): + self.__pport = 0 if self.__cli_opts.reconn > 0: self.log.info( @@ -6923,22 +6901,6 @@

    Methods

    self.ssig.raise_stop() -
    -def ping(self, data) -
    -
    -

    Send data to the socket and increment the port afterwards.

    -
    - -Expand source code - -
    def ping(self, data):
    -    # type: (str) -> None
    -    """Send data to the socket and increment the port afterwards."""
    -    self.__net.send(data)
    -    self.__increment_port_pointer()
    -
    -
    def producer(self)
    @@ -9688,7 +9650,6 @@

    IONetwork
  • consumer
  • interrupt
  • -
  • ping
  • producer
  • diff --git a/docs/pwncat.type.html b/docs/pwncat.type.html index 1aa5863e..a2e7d8cd 100644 --- a/docs/pwncat.type.html +++ b/docs/pwncat.type.html @@ -14,13 +14,13 @@

    Mypy Type Check Coverage Summary

    - - + + - - + +
    Total5.89% imprecise3670 LOC5.90% imprecise3659 LOC
    bin/pwncat5.89% imprecise3670 LOC5.90% imprecise3659 LOC
    @@ -3694,17 +3694,6 @@

    pwncat

    3657 3658 3659 -3660 -3661 -3662 -3663 -3664 -3665 -3666 -3667 -3668 -3669 -3670
    #!/usr/bin/env python3
     """pwncat."""
    @@ -5542,12 +5531,6 @@ 

    pwncat

    continue return - def ping(self, data): - # type: (str) -> None - """Send data to the socket and increment the port afterwards.""" - self.__net.send(data) - self.__increment_port_pointer() - def consumer(self, data): # type: (str) -> None """Send data to a socket.""" @@ -5568,13 +5551,6 @@

    pwncat

    # -------------------------------------------------------------------------- # Private Functions # -------------------------------------------------------------------------- - def __increment_port_pointer(self): - # type: () -> None - """Increments the pointer pointing to the list of ports we have available.""" - self.__pport += 1 - if self.__pport == len(self.__ports): - self.__pport = 0 - def __client_reconnect_to_server(self): # type: () -> bool """Ensure the client re-connects to the remote server, if the remote server hang up. @@ -5606,7 +5582,9 @@

    pwncat

    return False # [4/6] Increment the port numer (if --reconn-robin has multiple) - self.__increment_port_pointer() + self.__pport += 1 + if self.__pport == len(self.__ports): + self.__pport = 0 if self.__cli_opts.reconn > 0: run.add_timer( "PING", DsRunnerTimer(net.ping, ssig, args.ping_intvl, (args.ping_word)), # send data +Explicit (x6)"> DsRunnerTimer(net.consumer, ssig, args.ping_intvl, (args.ping_word)), # send data ) run.run()