diff --git a/CHANGELOG.md b/CHANGELOG.md index 1264ec12..867ed127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ ## Unreleased +## Release 0.0.20-alpha + +### Added +- Feature: Be able to specify source address and port for clients: #66 + + ## Release 0.0.19-alpha ### Added diff --git a/README.md b/README.md index a9e7bb03..1e147565 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ -> [1] mypy type coverage (fully typed: 94.37%)
+> [1] mypy type coverage (fully typed: 94.30%)
> [2] Windows builds are currently only failing, because they are simply stuck on GitHub actions. @@ -257,6 +257,7 @@ pwncat -R 10.0.0.1:4444 everythingcli.org 3306 -u | IPv4 | ✔ | ✔ | ✔ | | IPv6 | ✔ | ✔ | ✔ | | Unix domain sockets | :x: | ✔ | ✔ | +| Socket source bind | ✔ | ✔ | ✔ | | TCP | ✔ | ✔ | ✔ | | UDP | ✔ | ✔ | ✔ | | SCTP | :x: | :x: | ✔ | @@ -414,6 +415,8 @@ optional arguments: -T str, --tos str Specifies IP Type of Service (ToS) for the connection. Valid values are the tokens 'mincost', 'lowcost', 'reliability', 'throughput' or 'lowdelay'. + --source-addr addr Specify the source IP address of the interface for connect mode. + --source-port port Specify the source port for connect mode. -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. diff --git a/bin/pwncat b/bin/pwncat index 2f9dd699..3c002167 100755 --- a/bin/pwncat +++ b/bin/pwncat @@ -112,7 +112,7 @@ if os.environ.get("MYPY_CHECK", False): APPNAME = "pwncat" APPREPO = "https://github.com/cytopia/pwncat" -VERSION = "0.0.19-alpha" +VERSION = "0.0.20-alpha" # Default timeout for timeout-based sys.stdin and socket.recv TIMEOUT_READ_STDIN = 0.1 @@ -305,6 +305,18 @@ class DsSock(object): """`bool`: Only use IPv6 instead of both, IPv4 and IPv6.""" return self.__ipv6 + @property + def src_addr(self): + # type: () -> Optional[str] + """`bool`: Custom source address for connect mode.""" + return self.__src_addr + + @property + def src_port(self): + # type: () -> Optional[int] + """`bool`: Custom source port for connect mode.""" + return self.__src_port + @property def udp(self): # type: () -> bool @@ -326,14 +338,29 @@ class DsSock(object): # -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv4, ipv6, udp, ip_tos, info): - # type: (int, int, Optional[float], bool, bool, bool, bool, Optional[str], str) -> None + def __init__( + self, + bufsize, # type: int + backlog, # type: int + recv_timeout, # type: Optional[float] + nodns, # type: bool + ipv4, # type: bool + ipv6, # type: bool + src_addr, # type: Optional[str] + src_port, # type: Optional[int] + udp, # type: bool + ip_tos, # type: Optional[str] + info, # type: str + ): + # type: (...) -> 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(ipv4) is bool, type(ipv4) assert type(ipv6) is bool, type(ipv6) + assert type(src_addr) is str or src_addr is None, type(src_addr) + assert type(src_port) is int or src_port is None, type(src_port) assert type(udp) is bool, type(udp) assert type(info) is str, type(info) self.__bufsize = bufsize @@ -342,6 +369,8 @@ class DsSock(object): self.__nodns = nodns self.__ipv4 = ipv4 self.__ipv6 = ipv6 + self.__src_addr = src_addr + self.__src_port = src_port self.__udp = udp self.__ip_tos = ip_tos self.__info = info @@ -374,6 +403,8 @@ class DsIONetworkSock(DsSock): nodns, # type: bool ipv4, # type: bool ipv6, # type: bool + src_addr, # type: Optional[str] + src_port, # type: Optional[bool] udp, # type: bool ip_tos, # type: Optional[str] info, # type: str @@ -382,7 +413,7 @@ class DsIONetworkSock(DsSock): 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, ipv4, ipv6, udp, ip_tos, info + bufsize, backlog, recv_timeout, nodns, ipv4, ipv6, src_addr, src_port, udp, ip_tos, info ) @@ -1083,7 +1114,7 @@ class Sock(object): "Client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # A different UDP client connects @@ -1092,7 +1123,7 @@ class Sock(object): "New client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # Set currently active UDP connection socket @@ -1144,21 +1175,26 @@ class Sock(object): # [2/4] Resolve address remove = [] + errors = [] for family in conns: try: conns[family]["remote_host"] = host conns[family]["remote_addr"] = self.__gethostbyname(host, family) conns[family]["remote_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Connect remove = [] + errors = [] for family in conns: try: self.__connect( @@ -1169,13 +1205,15 @@ class Sock(object): # On successful connect, we can abandon/remove all other sockets remove = [key for key in conns if key != family] break - except (OSError, socket.error): - # self.__log.error(error) + except (OSError, socket.error) as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [4/4] Store connections and set active connection @@ -1221,28 +1259,38 @@ class Sock(object): # [2/4] Resolve local address remove = [] + errors = [] for family in conns: try: conns[family]["local_addr"] = self.__gethostbyname(host, family) conns[family]["local_host"] = host conns[family]["local_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Bind socket remove = [] + errors = [] for family in conns: - if not self.__bind(conns[family]["sock"], conns[family]["local_addr"], port): + try: + self.__bind(conns[family]["sock"], conns[family]["local_addr"], port) + except socket.error as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [UDP 4/4] There is no listen or accept for UDP @@ -1508,12 +1556,12 @@ class Sock(object): if self.__options.nodns: flags = socket.AI_NUMERICHOST + + self.__log.debug("Resolving hostname: %s", host) try: - self.__log.debug("Resolving hostname: %s", host) infos = socket.getaddrinfo(host, port, family, socktype, proto, flags) addr = str(infos[0][4][0]) except (AttributeError, socket.gaierror) as error: - self.__log.error("Resolve Error: %s", error) raise socket.gaierror(error) # type: ignore self.__log.debug("Resolved hostname: %s", addr) return addr @@ -1593,7 +1641,7 @@ class Sock(object): # Private Functions (server) # -------------------------------------------------------------------------- def __bind(self, sock, addr, port): - # type: (socket.socket, str, int) -> bool + # type: (socket.socket, str, int) -> None """Bind the socket to an address. Args: @@ -1601,16 +1649,26 @@ class Sock(object): addr (str): The numerical IP address to bind to. port (int): The port to bind to. - Returns: - bool: Returns `True` on success and `False` on Failure. + Raises: + socket.error if socket cannot be bound. """ + sock_family_name = self.get_af_name(sock.family) + sock_type_name = self.get_st_name(sock.type) + self.__log.debug( + "Binding (family %d/%s, %s) socket to %s:%d", + int(sock.family), + sock_family_name, + sock_type_name, + addr, + port, + ) try: - self.__log.debug("Binding socket to %s:%d", addr, port) sock.bind((addr, port)) - return True - except (OverflowError, OSError, socket.error) as error: - self.__log.error("Binding socket to %s:%d failed: %s", addr, port, error) - return False + except (OverflowError, OSError, socket.gaierror, socket.error) as error: + msg = "Binding (family {}/{}, {}) socket to {}:{} failed: {}".format( + sock.family, sock_family_name, sock_type_name, addr, port, error + ) + raise socket.error(msg) def __listen(self, sock): # type: (socket.socket) -> bool @@ -1659,7 +1717,7 @@ class Sock(object): "Client connected from %s:%d (family %d/%s, TCP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) return conn, (addr[0], addr[1]) @@ -1677,15 +1735,22 @@ class Sock(object): port (int): Port of server to connect to. Raises: - socker.error: If client cannot connect to remote peer. + socker.error: If client cannot connect to remote peer or custom bind did not succeed. """ sock_family_name = self.get_af_name(sock.family) sock_type_name = self.get_st_name(sock.type) + # Bind to a custom addr/port + if self.__options.src_addr is not None and self.__options.src_port is not None: + try: + self.__bind(sock, self.__options.src_addr, self.__options.src_port) + except socket.error as error: + raise socket.error(error) + self.__log.debug( "Connecting to %s:%d (family %d/%s, %s)", addr, port, - sock.family, + int(sock.family), sock_family_name, sock_type_name, ) @@ -1712,11 +1777,15 @@ class Sock(object): ) raise socket.error(msg) + local = sock.getsockname() + self.__log.debug( + "Connected from %s:%d", local[0], local[1], + ) self.__log.info( "Connected to %s:%d (family %d/%s, %s)", addr, port, - sock.family, + int(sock.family), sock_family_name, sock_type_name, ) @@ -3196,6 +3265,23 @@ def _args_check_mutually_exclusive(parser, args): ) sys.exit(1) + # [OPTIONS] -s/-p + if not connect_mode and (args.source_port or args.source_addr): + print( + "%s: error: --source-addr and --source-port can only be used in connect mode." + % (APPNAME), + file=sys.stderr, + ) + sys.exit(1) + + # [OPTIONS] -s/-p + if (args.source_port and not args.source_addr) or (not args.source_port and args.source_addr): + print( + "%s: error: --source-addr and --source-port are both required." % (APPNAME), + file=sys.stderr, + ) + sys.exit(1) + # [ADVANCED] --http if args.http and (args.https or args.udp or args.zero): parser.print_usage() @@ -3420,6 +3506,22 @@ Valid values are the tokens 'mincost', 'lowcost', 'reliability', 'throughput' or 'lowdelay'. """, ) + optional.add_argument( + "--source-addr", + metavar="addr", + dest="source_addr", + type=str, + default=None, + help="Specify the source IP address of the interface for connect mode.", + ) + optional.add_argument( + "--source-port", + metavar="port", + dest="source_port", + type=_args_check_port, + default=None, + help="Specify the source port for connect mode.", + ) optional.add_argument( "-v", "--verbose", @@ -3793,6 +3895,8 @@ def main(): args.nodns, args.ipv4, args.ipv6, + args.source_addr, + args.source_port, args.udp, args.tos, args.info, diff --git a/docs/index.html b/docs/index.html index 159445fc..ffacedfc 100644 --- a/docs/index.html +++ b/docs/index.html @@ -309,6 +309,8 @@

Usage

-T str, --tos str Specifies IP Type of Service (ToS) for the connection. Valid values are the tokens 'mincost', 'lowcost', 'reliability', 'throughput' or 'lowdelay'. + --source-addr addr Specify the source IP address of the interface for connect mode. + --source-port port Specify the source port for connect mode. -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. diff --git a/docs/pwncat.api.html b/docs/pwncat.api.html index ecf952f6..42eec857 100644 --- a/docs/pwncat.api.html +++ b/docs/pwncat.api.html @@ -139,7 +139,7 @@

Module pwncat

APPNAME = "pwncat" APPREPO = "https://github.com/cytopia/pwncat" -VERSION = "0.0.19-alpha" +VERSION = "0.0.20-alpha" # Default timeout for timeout-based sys.stdin and socket.recv TIMEOUT_READ_STDIN = 0.1 @@ -332,6 +332,18 @@

Module pwncat

"""`bool`: Only use IPv6 instead of both, IPv4 and IPv6.""" return self.__ipv6 + @property + def src_addr(self): + # type: () -> Optional[str] + """`bool`: Custom source address for connect mode.""" + return self.__src_addr + + @property + def src_port(self): + # type: () -> Optional[int] + """`bool`: Custom source port for connect mode.""" + return self.__src_port + @property def udp(self): # type: () -> bool @@ -353,14 +365,29 @@

Module pwncat

# -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv4, ipv6, udp, ip_tos, info): - # type: (int, int, Optional[float], bool, bool, bool, bool, Optional[str], str) -> None + def __init__( + self, + bufsize, # type: int + backlog, # type: int + recv_timeout, # type: Optional[float] + nodns, # type: bool + ipv4, # type: bool + ipv6, # type: bool + src_addr, # type: Optional[str] + src_port, # type: Optional[int] + udp, # type: bool + ip_tos, # type: Optional[str] + info, # type: str + ): + # type: (...) -> 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(ipv4) is bool, type(ipv4) assert type(ipv6) is bool, type(ipv6) + assert type(src_addr) is str or src_addr is None, type(src_addr) + assert type(src_port) is int or src_port is None, type(src_port) assert type(udp) is bool, type(udp) assert type(info) is str, type(info) self.__bufsize = bufsize @@ -369,6 +396,8 @@

Module pwncat

self.__nodns = nodns self.__ipv4 = ipv4 self.__ipv6 = ipv6 + self.__src_addr = src_addr + self.__src_port = src_port self.__udp = udp self.__ip_tos = ip_tos self.__info = info @@ -401,6 +430,8 @@

Module pwncat

nodns, # type: bool ipv4, # type: bool ipv6, # type: bool + src_addr, # type: Optional[str] + src_port, # type: Optional[bool] udp, # type: bool ip_tos, # type: Optional[str] info, # type: str @@ -409,7 +440,7 @@

Module pwncat

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, ipv4, ipv6, udp, ip_tos, info + bufsize, backlog, recv_timeout, nodns, ipv4, ipv6, src_addr, src_port, udp, ip_tos, info ) @@ -1110,7 +1141,7 @@

Module pwncat

"Client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # A different UDP client connects @@ -1119,7 +1150,7 @@

Module pwncat

"New client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # Set currently active UDP connection socket @@ -1171,21 +1202,26 @@

Module pwncat

# [2/4] Resolve address remove = [] + errors = [] for family in conns: try: conns[family]["remote_host"] = host conns[family]["remote_addr"] = self.__gethostbyname(host, family) conns[family]["remote_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Connect remove = [] + errors = [] for family in conns: try: self.__connect( @@ -1196,13 +1232,15 @@

Module pwncat

# On successful connect, we can abandon/remove all other sockets remove = [key for key in conns if key != family] break - except (OSError, socket.error): - # self.__log.error(error) + except (OSError, socket.error) as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [4/4] Store connections and set active connection @@ -1248,28 +1286,38 @@

Module pwncat

# [2/4] Resolve local address remove = [] + errors = [] for family in conns: try: conns[family]["local_addr"] = self.__gethostbyname(host, family) conns[family]["local_host"] = host conns[family]["local_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Bind socket remove = [] + errors = [] for family in conns: - if not self.__bind(conns[family]["sock"], conns[family]["local_addr"], port): + try: + self.__bind(conns[family]["sock"], conns[family]["local_addr"], port) + except socket.error as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [UDP 4/4] There is no listen or accept for UDP @@ -1535,12 +1583,12 @@

Module pwncat

if self.__options.nodns: flags = socket.AI_NUMERICHOST + + self.__log.debug("Resolving hostname: %s", host) try: - self.__log.debug("Resolving hostname: %s", host) infos = socket.getaddrinfo(host, port, family, socktype, proto, flags) addr = str(infos[0][4][0]) except (AttributeError, socket.gaierror) as error: - self.__log.error("Resolve Error: %s", error) raise socket.gaierror(error) # type: ignore self.__log.debug("Resolved hostname: %s", addr) return addr @@ -1620,7 +1668,7 @@

Module pwncat

# Private Functions (server) # -------------------------------------------------------------------------- def __bind(self, sock, addr, port): - # type: (socket.socket, str, int) -> bool + # type: (socket.socket, str, int) -> None """Bind the socket to an address. Args: @@ -1628,16 +1676,26 @@

Module pwncat

addr (str): The numerical IP address to bind to. port (int): The port to bind to. - Returns: - bool: Returns `True` on success and `False` on Failure. + Raises: + socket.error if socket cannot be bound. """ + sock_family_name = self.get_af_name(sock.family) + sock_type_name = self.get_st_name(sock.type) + self.__log.debug( + "Binding (family %d/%s, %s) socket to %s:%d", + int(sock.family), + sock_family_name, + sock_type_name, + addr, + port, + ) try: - self.__log.debug("Binding socket to %s:%d", addr, port) sock.bind((addr, port)) - return True - except (OverflowError, OSError, socket.error) as error: - self.__log.error("Binding socket to %s:%d failed: %s", addr, port, error) - return False + except (OverflowError, OSError, socket.gaierror, socket.error) as error: + msg = "Binding (family {}/{}, {}) socket to {}:{} failed: {}".format( + sock.family, sock_family_name, sock_type_name, addr, port, error + ) + raise socket.error(msg) def __listen(self, sock): # type: (socket.socket) -> bool @@ -1686,7 +1744,7 @@

Module pwncat

"Client connected from %s:%d (family %d/%s, TCP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) return conn, (addr[0], addr[1]) @@ -1704,15 +1762,22 @@

Module pwncat

port (int): Port of server to connect to. Raises: - socker.error: If client cannot connect to remote peer. + socker.error: If client cannot connect to remote peer or custom bind did not succeed. """ sock_family_name = self.get_af_name(sock.family) sock_type_name = self.get_st_name(sock.type) + # Bind to a custom addr/port + if self.__options.src_addr is not None and self.__options.src_port is not None: + try: + self.__bind(sock, self.__options.src_addr, self.__options.src_port) + except socket.error as error: + raise socket.error(error) + self.__log.debug( "Connecting to %s:%d (family %d/%s, %s)", addr, port, - sock.family, + int(sock.family), sock_family_name, sock_type_name, ) @@ -1739,11 +1804,15 @@

Module pwncat

) raise socket.error(msg) + local = sock.getsockname() + self.__log.debug( + "Connected from %s:%d", local[0], local[1], + ) self.__log.info( "Connected to %s:%d (family %d/%s, %s)", addr, port, - sock.family, + int(sock.family), sock_family_name, sock_type_name, ) @@ -3223,6 +3292,23 @@

Module pwncat

) sys.exit(1) + # [OPTIONS] -s/-p + if not connect_mode and (args.source_port or args.source_addr): + print( + "%s: error: --source-addr and --source-port can only be used in connect mode." + % (APPNAME), + file=sys.stderr, + ) + sys.exit(1) + + # [OPTIONS] -s/-p + if (args.source_port and not args.source_addr) or (not args.source_port and args.source_addr): + print( + "%s: error: --source-addr and --source-port are both required." % (APPNAME), + file=sys.stderr, + ) + sys.exit(1) + # [ADVANCED] --http if args.http and (args.https or args.udp or args.zero): parser.print_usage() @@ -3447,6 +3533,22 @@

Module pwncat

'reliability', 'throughput' or 'lowdelay'. """, ) + optional.add_argument( + "--source-addr", + metavar="addr", + dest="source_addr", + type=str, + default=None, + help="Specify the source IP address of the interface for connect mode.", + ) + optional.add_argument( + "--source-port", + metavar="port", + dest="source_port", + type=_args_check_port, + default=None, + help="Specify the source port for connect mode.", + ) optional.add_argument( "-v", "--verbose", @@ -3820,6 +3922,8 @@

Module pwncat

args.nodns, args.ipv4, args.ipv6, + args.source_addr, + args.source_port, args.udp, args.tos, args.info, @@ -4201,6 +4305,22 @@

Functions

'reliability', 'throughput' or 'lowdelay'. """, ) + optional.add_argument( + "--source-addr", + metavar="addr", + dest="source_addr", + type=str, + default=None, + help="Specify the source IP address of the interface for connect mode.", + ) + optional.add_argument( + "--source-port", + metavar="port", + dest="source_port", + type=_args_check_port, + default=None, + help="Specify the source port for connect mode.", + ) optional.add_argument( "-v", "--verbose", @@ -4589,6 +4709,8 @@

Functions

args.nodns, args.ipv4, args.ipv6, + args.source_addr, + args.source_port, args.udp, args.tos, args.info, @@ -5704,7 +5826,7 @@

Instance variables

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

A type-safe data structure for IONetwork socket options.

@@ -5736,6 +5858,8 @@

Instance variables

nodns, # type: bool ipv4, # type: bool ipv6, # type: bool + src_addr, # type: Optional[str] + src_port, # type: Optional[bool] udp, # type: bool ip_tos, # type: Optional[str] info, # type: str @@ -5744,7 +5868,7 @@

Instance variables

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, ipv4, ipv6, udp, ip_tos, info + bufsize, backlog, recv_timeout, nodns, ipv4, ipv6, src_addr, src_port, udp, ip_tos, info )

Ancestors

@@ -5780,6 +5904,8 @@

Inherited members

  • ipv6
  • nodns
  • recv_timeout
  • +
  • src_addr
  • +
  • src_port
  • udp
  • @@ -6257,7 +6383,7 @@

    Instance variables

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

    A type-safe data structure for DsSock options.

    @@ -6307,6 +6433,18 @@

    Instance variables

    """`bool`: Only use IPv6 instead of both, IPv4 and IPv6.""" return self.__ipv6 + @property + def src_addr(self): + # type: () -> Optional[str] + """`bool`: Custom source address for connect mode.""" + return self.__src_addr + + @property + def src_port(self): + # type: () -> Optional[int] + """`bool`: Custom source port for connect mode.""" + return self.__src_port + @property def udp(self): # type: () -> bool @@ -6328,14 +6466,29 @@

    Instance variables

    # -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv4, ipv6, udp, ip_tos, info): - # type: (int, int, Optional[float], bool, bool, bool, bool, Optional[str], str) -> None + def __init__( + self, + bufsize, # type: int + backlog, # type: int + recv_timeout, # type: Optional[float] + nodns, # type: bool + ipv4, # type: bool + ipv6, # type: bool + src_addr, # type: Optional[str] + src_port, # type: Optional[int] + udp, # type: bool + ip_tos, # type: Optional[str] + info, # type: str + ): + # type: (...) -> 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(ipv4) is bool, type(ipv4) assert type(ipv6) is bool, type(ipv6) + assert type(src_addr) is str or src_addr is None, type(src_addr) + assert type(src_port) is int or src_port is None, type(src_port) assert type(udp) is bool, type(udp) assert type(info) is str, type(info) self.__bufsize = bufsize @@ -6344,6 +6497,8 @@

    Instance variables

    self.__nodns = nodns self.__ipv4 = ipv4 self.__ipv6 = ipv6 + self.__src_addr = src_addr + self.__src_port = src_port self.__udp = udp self.__ip_tos = ip_tos self.__info = info @@ -6466,6 +6621,34 @@

    Instance variables

    return self.__recv_timeout
    +
    var src_addr
    +
    +

    bool: Custom source address for connect mode.

    +
    + +Expand source code + +
    @property
    +def src_addr(self):
    +    # type: () -> Optional[str]
    +    """`bool`: Custom source address for connect mode."""
    +    return self.__src_addr
    +
    +
    +
    var src_port
    +
    +

    bool: Custom source port for connect mode.

    +
    + +Expand source code + +
    @property
    +def src_port(self):
    +    # type: () -> Optional[int]
    +    """`bool`: Custom source port for connect mode."""
    +    return self.__src_port
    +
    +
    var udp

    bool: Determines if we use TCP or UDP.

    @@ -8501,7 +8684,7 @@

    Args

    "Client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # A different UDP client connects @@ -8510,7 +8693,7 @@

    Args

    "New client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # Set currently active UDP connection socket @@ -8562,21 +8745,26 @@

    Args

    # [2/4] Resolve address remove = [] + errors = [] for family in conns: try: conns[family]["remote_host"] = host conns[family]["remote_addr"] = self.__gethostbyname(host, family) conns[family]["remote_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Connect remove = [] + errors = [] for family in conns: try: self.__connect( @@ -8587,13 +8775,15 @@

    Args

    # On successful connect, we can abandon/remove all other sockets remove = [key for key in conns if key != family] break - except (OSError, socket.error): - # self.__log.error(error) + except (OSError, socket.error) as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [4/4] Store connections and set active connection @@ -8639,28 +8829,38 @@

    Args

    # [2/4] Resolve local address remove = [] + errors = [] for family in conns: try: conns[family]["local_addr"] = self.__gethostbyname(host, family) conns[family]["local_host"] = host conns[family]["local_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Bind socket remove = [] + errors = [] for family in conns: - if not self.__bind(conns[family]["sock"], conns[family]["local_addr"], port): + try: + self.__bind(conns[family]["sock"], conns[family]["local_addr"], port) + except socket.error as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [UDP 4/4] There is no listen or accept for UDP @@ -8926,12 +9126,12 @@

    Args

    if self.__options.nodns: flags = socket.AI_NUMERICHOST + + self.__log.debug("Resolving hostname: %s", host) try: - self.__log.debug("Resolving hostname: %s", host) infos = socket.getaddrinfo(host, port, family, socktype, proto, flags) addr = str(infos[0][4][0]) except (AttributeError, socket.gaierror) as error: - self.__log.error("Resolve Error: %s", error) raise socket.gaierror(error) # type: ignore self.__log.debug("Resolved hostname: %s", addr) return addr @@ -9011,7 +9211,7 @@

    Args

    # Private Functions (server) # -------------------------------------------------------------------------- def __bind(self, sock, addr, port): - # type: (socket.socket, str, int) -> bool + # type: (socket.socket, str, int) -> None """Bind the socket to an address. Args: @@ -9019,16 +9219,26 @@

    Args

    addr (str): The numerical IP address to bind to. port (int): The port to bind to. - Returns: - bool: Returns `True` on success and `False` on Failure. + Raises: + socket.error if socket cannot be bound. """ + sock_family_name = self.get_af_name(sock.family) + sock_type_name = self.get_st_name(sock.type) + self.__log.debug( + "Binding (family %d/%s, %s) socket to %s:%d", + int(sock.family), + sock_family_name, + sock_type_name, + addr, + port, + ) try: - self.__log.debug("Binding socket to %s:%d", addr, port) sock.bind((addr, port)) - return True - except (OverflowError, OSError, socket.error) as error: - self.__log.error("Binding socket to %s:%d failed: %s", addr, port, error) - return False + except (OverflowError, OSError, socket.gaierror, socket.error) as error: + msg = "Binding (family {}/{}, {}) socket to {}:{} failed: {}".format( + sock.family, sock_family_name, sock_type_name, addr, port, error + ) + raise socket.error(msg) def __listen(self, sock): # type: (socket.socket) -> bool @@ -9077,7 +9287,7 @@

    Args

    "Client connected from %s:%d (family %d/%s, TCP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) return conn, (addr[0], addr[1]) @@ -9095,15 +9305,22 @@

    Args

    port (int): Port of server to connect to. Raises: - socker.error: If client cannot connect to remote peer. + socker.error: If client cannot connect to remote peer or custom bind did not succeed. """ sock_family_name = self.get_af_name(sock.family) sock_type_name = self.get_st_name(sock.type) + # Bind to a custom addr/port + if self.__options.src_addr is not None and self.__options.src_port is not None: + try: + self.__bind(sock, self.__options.src_addr, self.__options.src_port) + except socket.error as error: + raise socket.error(error) + self.__log.debug( "Connecting to %s:%d (family %d/%s, %s)", addr, port, - sock.family, + int(sock.family), sock_family_name, sock_type_name, ) @@ -9130,11 +9347,15 @@

    Args

    ) raise socket.error(msg) + local = sock.getsockname() + self.__log.debug( + "Connected from %s:%d", local[0], local[1], + ) self.__log.info( "Connected to %s:%d (family %d/%s, %s)", addr, port, - sock.family, + int(sock.family), sock_family_name, sock_type_name, ) @@ -9384,7 +9605,7 @@

    Raises

    "Client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # A different UDP client connects @@ -9393,7 +9614,7 @@

    Raises

    "New client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # Set currently active UDP connection socket @@ -9464,21 +9685,26 @@

    Returns

    # [2/4] Resolve address remove = [] + errors = [] for family in conns: try: conns[family]["remote_host"] = host conns[family]["remote_addr"] = self.__gethostbyname(host, family) conns[family]["remote_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Connect remove = [] + errors = [] for family in conns: try: self.__connect( @@ -9489,13 +9715,15 @@

    Returns

    # On successful connect, we can abandon/remove all other sockets remove = [key for key in conns if key != family] break - except (OSError, socket.error): - # self.__log.error(error) + except (OSError, socket.error) as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [4/4] Store connections and set active connection @@ -9563,28 +9791,38 @@

    Returns

    # [2/4] Resolve local address remove = [] + errors = [] for family in conns: try: conns[family]["local_addr"] = self.__gethostbyname(host, family) conns[family]["local_host"] = host conns[family]["local_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Bind socket remove = [] + errors = [] for family in conns: - if not self.__bind(conns[family]["sock"], conns[family]["local_addr"], port): + try: + self.__bind(conns[family]["sock"], conns[family]["local_addr"], port) + except socket.error as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [UDP 4/4] There is no listen or accept for UDP @@ -10403,6 +10641,8 @@

    DsSock

  • ipv6
  • nodns
  • recv_timeout
  • +
  • src_addr
  • +
  • src_port
  • udp
  • diff --git a/docs/pwncat.man.html b/docs/pwncat.man.html index afb4d452..e8636105 100644 --- a/docs/pwncat.man.html +++ b/docs/pwncat.man.html @@ -200,6 +200,20 @@

    DESCRIPTION ’reliability’, ’throughput’ or ’lowdelay’.

    + +

    −−source−addr +addr

    + +

    Specify the source IP address +of the interface for connect mode.

    + + +

    −−source−port +port

    + +

    Specify the source port for +connect mode.

    +

    −v, −−verbose

    diff --git a/docs/pwncat.type.html b/docs/pwncat.type.html index b60ad95c..1c7cf034 100644 --- a/docs/pwncat.type.html +++ b/docs/pwncat.type.html @@ -14,13 +14,13 @@

    Mypy Type Check Coverage Summary

    - - + + - - + +
    Total5.63% imprecise3999 LOC5.70% imprecise4103 LOC
    bin/pwncat5.63% imprecise3999 LOC5.70% imprecise4103 LOC
    @@ -4034,6 +4034,110 @@

    pwncat

    3997 3998 3999 +4000 +4001 +4002 +4003 +4004 +4005 +4006 +4007 +4008 +4009 +4010 +4011 +4012 +4013 +4014 +4015 +4016 +4017 +4018 +4019 +4020 +4021 +4022 +4023 +4024 +4025 +4026 +4027 +4028 +4029 +4030 +4031 +4032 +4033 +4034 +4035 +4036 +4037 +4038 +4039 +4040 +4041 +4042 +4043 +4044 +4045 +4046 +4047 +4048 +4049 +4050 +4051 +4052 +4053 +4054 +4055 +4056 +4057 +4058 +4059 +4060 +4061 +4062 +4063 +4064 +4065 +4066 +4067 +4068 +4069 +4070 +4071 +4072 +4073 +4074 +4075 +4076 +4077 +4078 +4079 +4080 +4081 +4082 +4083 +4084 +4085 +4086 +4087 +4088 +4089 +4090 +4091 +4092 +4093 +4094 +4095 +4096 +4097 +4098 +4099 +4100 +4101 +4102 +4103
    #!/usr/bin/env python3
     """pwncat."""
    @@ -4150,7 +4254,7 @@ 

    pwncat

    APPNAME = "pwncat" APPREPO = "https://github.com/cytopia/pwncat" -VERSION = "0.0.19-alpha" +VERSION = "0.0.20-alpha" # Default timeout for timeout-based sys.stdin and socket.recv TIMEOUT_READ_STDIN = 0.1 @@ -4356,6 +4460,18 @@

    pwncat

    return self.__ipv6 @property + def src_addr(self): + # type: () -> Optional[str] + """`bool`: Custom source address for connect mode.""" + return self.__src_addr + + @property + def src_port(self): + # type: () -> Optional[int] + """`bool`: Custom source port for connect mode.""" + return self.__src_port + + @property def udp(self): # type: () -> bool """`bool`: Determines if we use TCP or UDP.""" @@ -4376,8 +4492,21 @@

    pwncat

    # -------------------------------------------------------------------------- # Constructor # -------------------------------------------------------------------------- - def __init__(self, bufsize, backlog, recv_timeout, nodns, ipv4, ipv6, udp, ip_tos, info): - # type: (int, int, Optional[float], bool, bool, bool, bool, Optional[str], str) -> None + def __init__( + self, + bufsize, # type: int + backlog, # type: int + recv_timeout, # type: Optional[float] + nodns, # type: bool + ipv4, # type: bool + ipv6, # type: bool + src_addr, # type: Optional[str] + src_port, # type: Optional[int] + udp, # type: bool + ip_tos, # type: Optional[str] + info, # type: str + ): + # type: (...) -> None assert type(bufsize) is int, type(bufsize) assert type(ipv6) is bool, type(ipv6) assert type(src_addr) is str or src_addr is None, type(src_addr) + assert type(src_port) is int or src_port is None, type(src_port) + assert type(udp) is bool, type(udp) assert type(info) is str, type(info) @@ -4400,6 +4533,8 @@

    pwncat

    self.__nodns = nodns self.__ipv4 = ipv4 self.__ipv6 = ipv6 + self.__src_addr = src_addr + self.__src_port = src_port self.__udp = udp self.__ip_tos = ip_tos self.__info = info @@ -4432,6 +4567,8 @@

    pwncat

    nodns, # type: bool ipv4, # type: bool ipv6, # type: bool + src_addr, # type: Optional[str] + src_port, # type: Optional[bool] udp, # type: bool ip_tos, # type: Optional[str] info, # type: str @@ -4441,7 +4578,7 @@

    pwncat

    Explicit (x2)"> 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, ipv4, ipv6, udp, ip_tos, info + bufsize, backlog, recv_timeout, nodns, ipv4, ipv6, src_addr, src_port, udp, ip_tos, info ) @@ -5173,7 +5310,7 @@

    pwncat

    "Client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # A different UDP client connects @@ -5183,7 +5320,7 @@

    pwncat

    "New client connected: %s:%d (family %d/%s, UDP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) # Set currently active UDP connection socket @@ -5237,21 +5374,27 @@

    pwncat

    # [2/4] Resolve address remove = [] + errors = [] for family in conns: try: conns[family]["remote_host"] = host conns[family]["remote_addr"] = self.__gethostbyname(host, family) conns[family]["remote_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Connect remove = [] + errors = [] for family in conns: try: self.__connect( @@ -5262,13 +5405,16 @@

    pwncat

    # On successful connect, we can abandon/remove all other sockets remove = [key for key in conns if key != family] break - except (OSError, socket.error): - # self.__log.error(error) + except (OSError, socket.error) as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["conn"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [4/4] Store connections and set active connection @@ -5314,28 +5460,40 @@

    pwncat

    # [2/4] Resolve local address remove = [] + errors = [] for family in conns: try: conns[family]["local_addr"] = self.__gethostbyname(host, family) conns[family]["local_host"] = host conns[family]["local_port"] = port - except socket.gaierror: + except socket.gaierror as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error("Resolve Error: %s", error) return False # [3/4] Bind socket remove = [] + errors = [] for family in conns: - if not self.__bind(conns[family]["sock"], conns[family]["local_addr"], port): + try: + self.__bind(conns[family]["sock"], conns[family]["local_addr"], port) + except socket.error as err: remove.append(family) + errors.append(str(err)) for family in remove: self.__close(self.get_af_name(family), conns[family]["sock"]) del conns[family] if not conns: + for error in errors: + self.__log.error(error) return False # [UDP 4/4] There is no listen or accept for UDP @@ -5607,16 +5765,15 @@

    pwncat

    if self.__options.nodns: flags = socket.AI_NUMERICHOST - try: + self.__log.debug("Resolving hostname: %s", host) +Explicit (x4)"> self.__log.debug("Resolving hostname: %s", host) + try: infos = socket.getaddrinfo(host, port, family, socktype, proto, flags) addr = str(infos[0][4][0]) except (AttributeError, socket.gaierror) as error: - self.__log.error("Resolve Error: %s", error) raise socket.gaierror(error) # type: ignore self.__log.debug("Resolved hostname: %s", addr) @@ -5706,7 +5863,7 @@

    pwncat

    # Private Functions (server) # -------------------------------------------------------------------------- def __bind(self, sock, addr, port): - # type: (socket.socket, str, int) -> bool + # type: (socket.socket, str, int) -> None """Bind the socket to an address. Args: @@ -5714,19 +5871,28 @@

    pwncat

    addr (str): The numerical IP address to bind to. port (int): The port to bind to. - Returns: - bool: Returns `True` on success and `False` on Failure. + Raises: + socket.error if socket cannot be bound. """ - try: + sock_family_name = self.get_af_name(sock.family) + sock_type_name = self.get_st_name(sock.type) self.__log.debug("Binding socket to %s:%d", addr, port) +Explicit (x4)"> self.__log.debug( + "Binding (family %d/%s, %s) socket to %s:%d", + int(sock.family), + sock_family_name, + sock_type_name, + addr, + port, + ) + try: sock.bind((addr, port)) - return True - except (OverflowError, OSError, socket.error) as error: - self.__log.error("Binding socket to %s:%d failed: %s", addr, port, error) - return False + except (OverflowError, OSError, socket.gaierror, socket.error) as error: + msg = "Binding (family {}/{}, {}) socket to {}:{} failed: {}".format( + sock.family, sock_family_name, sock_type_name, addr, port, error + ) + raise socket.error(msg) def __listen(self, sock): # type: (socket.socket) -> bool @@ -5781,7 +5947,7 @@

    pwncat

    "Client connected from %s:%d (family %d/%s, TCP)", addr[0], addr[1], - conn.family, + int(conn.family), self.get_af_name(conn.family), ) return conn, (addr[0], addr[1]) @@ -5799,16 +5965,23 @@

    pwncat

    port (int): Port of server to connect to. Raises: - socker.error: If client cannot connect to remote peer. + socker.error: If client cannot connect to remote peer or custom bind did not succeed. """ sock_family_name = self.get_af_name(sock.family) sock_type_name = self.get_st_name(sock.type) + # Bind to a custom addr/port + if self.__options.src_addr is not None and self.__options.src_port is not None: + try: + self.__bind(sock, self.__options.src_addr, self.__options.src_port) + except socket.error as error: + raise socket.error(error) + self.__log.debug( "Connecting to %s:%d (family %d/%s, %s)", addr, port, - sock.family, + int(sock.family), sock_family_name, sock_type_name, ) @@ -5836,12 +6009,17 @@

    pwncat

    ) raise socket.error(msg) + local = sock.getsockname() + self.__log.debug( + "Connected from %s:%d", local[0], local[1], + ) self.__log.info( "Connected to %s:%d (family %d/%s, %s)", addr, port, - sock.family, + int(sock.family), sock_family_name, sock_type_name, ) @@ -7396,6 +7574,25 @@

    pwncat

    ) sys.exit(1) + # [OPTIONS] -s/-p + if not connect_mode and (args.source_port or args.source_addr): + print( + "%s: error: --source-addr and --source-port can only be used in connect mode." + % (APPNAME), + file=sys.stderr, + ) + sys.exit(1) + + # [OPTIONS] -s/-p + if (args.source_port and not args.source_addr) or (not args.source_port and args.source_addr): + print( + "%s: error: --source-addr and --source-port are both required." % (APPNAME), + file=sys.stderr, + ) + sys.exit(1) + # [ADVANCED] --http if args.http and (args.https or args.udp or args.zero): @@ -7649,6 +7846,24 @@

    pwncat

    ) optional.add_argument( + "--source-addr", + metavar="addr", + dest="source_addr", + type=str, + default=None, + help="Specify the source IP address of the interface for connect mode.", + ) + optional.add_argument( + "--source-port", + metavar="port", + dest="source_port", + type=_args_check_port, + default=None, + help="Specify the source port for connect mode.", + ) + optional.add_argument( "-v", "--verbose", action="count", @@ -8054,6 +8269,10 @@

    pwncat

    args.ipv6, args.source_addr, + args.source_port, + args.udp, args.tos, diff --git a/man/pwncat.1 b/man/pwncat.1 index dc33d4a4..1a494b53 100644 --- a/man/pwncat.1 +++ b/man/pwncat.1 @@ -92,6 +92,12 @@ Specifies IP Type of Service (ToS) for the connection. Valid values are the tokens 'mincost', 'lowcost', \&'reliability', 'throughput' or 'lowdelay'. .TP +\fB\-\-source\-addr\fR addr +Specify the source IP address of the interface for connect mode. +.TP +\fB\-\-source\-port\fR port +Specify the source port for connect mode. +.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 diff --git a/setup.cfg b/setup.cfg index 43c75416..2e9d0d87 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,8 +28,8 @@ max-line-length = 100 disable = useless-object-inheritance, bad-continuation, unidiomatic-typecheck max-branches = 24 max-statements = 74 -max-args = 11 -max-attributes = 9 +max-args = 13 +max-attributes = 11 max-locals = 30 max-module-lines = 5000 max-bool-expr = 6 diff --git a/setup.py b/setup.py index fafb1037..1b7f1e4e 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="pwncat", - version="0.0.19-alpha", + version="0.0.20-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,