diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c01d774..0bfb3ca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ ## Unreleased +## Release 0.1.2 + +### Fixes +- Fixes: [#109](https://github.com/cytopia/pwncat/issues/109) when pasting in term I donot get full line echo + + ## Release 0.1.1 ### Fixes diff --git a/Makefile b/Makefile index a4e8e916..a3404fa3 100644 --- a/Makefile +++ b/Makefile @@ -151,6 +151,7 @@ _code-pycodestyle: echo "# Check pycodestyle: $${V}"; \ echo "# -------------------------------------------------------------------- #" @# + docker pull cytopia/pycodestyle docker run --rm $$(tty -s && echo "-it" || echo) -v $(PWD):/data --entrypoint= cytopia/pycodestyle sh -c ' \ mkdir -p /tmp \ && cp $(BINPATH)$(BINNAME) /tmp/$(BINNAME).py \ @@ -163,6 +164,7 @@ _code-pydocstyle: echo "# Check pydocstyle: $${V}"; \ echo "# -------------------------------------------------------------------- #" @# + docker pull cytopia/pydocstyle docker run --rm $$(tty -s && echo "-it" || echo) -v $(PWD):/data --entrypoint= cytopia/pydocstyle sh -c ' \ mkdir -p /tmp \ && cp $(BINPATH)$(BINNAME) /tmp/$(BINNAME).py \ @@ -175,6 +177,7 @@ _code-pylint: echo "# Check pylint: $${V}"; \ echo "# -------------------------------------------------------------------- #" @# + docker pull cytopia/pylint docker run --rm $$(tty -s && echo "-it" || echo) -v $(PWD):/data --entrypoint= cytopia/pylint sh -c ' \ mkdir -p /tmp \ && cp $(BINPATH)$(BINNAME) /tmp/$(BINNAME).py \ @@ -187,6 +190,7 @@ _code-black: echo "# Check Python Black: $${V}"; \ echo "# -------------------------------------------------------------------- #" @# + docker pull cytopia/black docker run --rm $$(tty -s && echo "-it" || echo) -v ${PWD}:/data --entrypoint= cytopia/black sh -c ' \ mkdir -p /tmp \ && cp $(BINPATH)$(BINNAME) /tmp/$(BINNAME).py \ @@ -199,6 +203,7 @@ _code-mypy: echo "# Check Mypy: $${V}"; \ echo "# -------------------------------------------------------------------- #" @# + docker pull cytopia/mypy docker run --rm $$(tty -s && echo "-it" || echo) -v ${PWD}:/data --entrypoint= cytopia/mypy sh -c ' \ mkdir -p /tmp \ && cp $(BINPATH)$(BINNAME) /tmp/$(BINNAME).py \ @@ -499,6 +504,7 @@ docs: _docs-version_readme .PHONY: _docs-man _docs-man: $(BINPATH)$(BINNAME) + docker pull python:3-alpine docker run --rm $$(tty -s && echo "-it" || echo) -v $(PWD):/data -w /data -e UID=$(UID) -e GID=${GID} python:3-alpine sh -c ' \ apk add help2man \ && help2man -n $(BINNAME) --no-info --source=https://github.com/cytopia/pwncat -s 1 -o $(MANPATH)$(BINNAME).1 $(BINPATH)$(BINNAME) \ @@ -520,6 +526,7 @@ _docs-man: $(BINPATH)$(BINNAME) .PHONY: _docs-api _docs-api: @# Generate pdoc API page + docker pull python:3-alpine docker run --rm $$(tty -s && echo "-it" || echo) -v $(PWD):/data -w /data -e UID=$(UID) -e GID=${GID} python:3-alpine sh -c ' \ pip install pdoc3 \ && mkdir -p /tmp \ @@ -531,6 +538,8 @@ _docs-api: .PHONY: _docs-mypy_type_coverage _docs-mypy_type_coverage: @# Generate mypy code coverage page + docker pull cytopia/mypy + docker pull python:3-alpine docker run --rm $$(tty -s && echo "-it" || echo) -v ${PWD}:/data -w /data -e UID=$(UID) -e GID=${GID} --entrypoint= cytopia/mypy sh -c ' \ mypy --config-file setup.cfg --html-report tmp $(BINPATH)$(BINNAME) \ && cp -f tmp/mypy-html.css docs/css/mypy.css \ diff --git a/README.md b/README.md index 01b90f8b..8419ad9d 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ -> [1] mypy type coverage (fully typed: 93.96%)
+> [1] mypy type coverage (fully typed: 94.00%)
> [2] Failing builds do not indicate broken functionality. Integration tests run for multiple hours and break sporadically for various different reasons (network timeouts, unknown cancellations of GitHub Actions, etc): #735, #841
> @@ -166,7 +166,7 @@ tool that works on older and newer machines (hence Python 2+3 compat). Most impo ## :tada: Install -Current version is: **0.1.1** +Current version is: **0.1.2** #### Generic diff --git a/bin/pwncat b/bin/pwncat index 908a92a0..c2198635 100755 --- a/bin/pwncat +++ b/bin/pwncat @@ -128,7 +128,7 @@ if os.environ.get("MYPY_CHECK", False): APPNAME = "pwncat" APPREPO = "https://github.com/cytopia/pwncat" -VERSION = "0.1.1" +VERSION = "0.1.2" # Default timeout for timeout-based sys.stdin and socket.recv TIMEOUT_READ_STDIN = 0.05 @@ -3525,7 +3525,10 @@ class IOStdinStdout(IO): # [2/3] (Linux/Mac) Raw mode if self.__stdin_israw(): - self.__set_input_timeout() + # Issue #109 + # when pasting in term I donot get full line echo + # To mitigate this, I'm disabling the select.select call on sys.stdin + # self.__set_input_timeout() if self.__py3: return sys.stdin.buffer.read(1) return sys.stdin.read(1) # type: ignore diff --git a/docs/pwncat.api.html b/docs/pwncat.api.html index 6ba54f28..ff2ad048 100644 --- a/docs/pwncat.api.html +++ b/docs/pwncat.api.html @@ -3,7 +3,7 @@ - + pwncat API documentation @@ -157,7 +157,7 @@

Module pwncat

APPNAME = "pwncat" APPREPO = "https://github.com/cytopia/pwncat" -VERSION = "0.1.1" +VERSION = "0.1.2" # Default timeout for timeout-based sys.stdin and socket.recv TIMEOUT_READ_STDIN = 0.05 @@ -3554,7 +3554,10 @@

Module pwncat

# [2/3] (Linux/Mac) Raw mode if self.__stdin_israw(): - self.__set_input_timeout() + # Issue #109 + # when pasting in term I donot get full line echo + # To mitigate this, I'm disabling the select.select call on sys.stdin + # self.__set_input_timeout() if self.__py3: return sys.stdin.buffer.read(1) return sys.stdin.read(1) # type: ignore @@ -12672,7 +12675,10 @@

Args

# [2/3] (Linux/Mac) Raw mode if self.__stdin_israw(): - self.__set_input_timeout() + # Issue #109 + # when pasting in term I donot get full line echo + # To mitigate this, I'm disabling the select.select call on sys.stdin + # self.__set_input_timeout() if self.__py3: return sys.stdin.buffer.read(1) return sys.stdin.read(1) # type: ignore @@ -18108,7 +18114,7 @@

T \ No newline at end of file diff --git a/docs/pwncat.type.html b/docs/pwncat.type.html index 89dba086..5e023fdf 100644 --- a/docs/pwncat.type.html +++ b/docs/pwncat.type.html @@ -16,13 +16,13 @@

pwncat - Type Coverage

Total -6.04% imprecise -6361 LOC +6.00% imprecise +6364 LOC bin/pwncat -6.04% imprecise -6361 LOC +6.00% imprecise +6364 LOC @@ -6391,6 +6391,9 @@

pwncat

6359 6360 6361 +6362 +6363 +6364
#!/usr/bin/env python3
 """pwncat."""
@@ -6522,7 +6525,7 @@ 

pwncat

APPNAME = "pwncat" APPREPO = "https://github.com/cytopia/pwncat" -VERSION = "0.1.1" +VERSION = "0.1.2" # Default timeout for timeout-based sys.stdin and socket.recv TIMEOUT_READ_STDIN = 0.05 @@ -6558,7 +6561,7 @@

pwncat

# ------------------------------------------------------------------------------------------------- # Abstract class with Python 2 + Python 3 support: https://stackoverflow.com/questions/35673474 ABC = ABCMeta("ABC", (object,), {"__slots__": ()}) +Explicit (x2)">ABC = ABCMeta("ABC", (object,), {"__slots__": ()}) # ------------------------------------------------------------------------------------------------- @@ -6589,7 +6592,7 @@

pwncat

# is already initialized, the thread won't create a new object. if cls not in cls._instances: cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs) +Explicit (x5)"> cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] @@ -6773,9 +6776,9 @@

pwncat

): # type: (...) -> None assert type(intvl) is int, type(intvl) +Explicit (x4)"> assert type(intvl) is int, type(intvl) assert type(kwargs) is dict, type(kwargs) +Explicit (x8)"> assert type(kwargs) is dict, type(kwargs) self.__action = action self.__ssig = ssig @@ -6852,11 +6855,11 @@

pwncat

): # type: (...) -> None assert type(repeat) is int, type(repeat) +Explicit (x4)"> assert type(repeat) is int, type(repeat) assert type(pause) is float, type(pause) +Explicit (x4)"> assert type(pause) is float, type(pause) assert type(kwargs) is dict, type(kwargs) +Explicit (x8)"> assert type(kwargs) is dict, type(kwargs) self.__action = action self.__ssig = ssig @@ -6976,23 +6979,23 @@

pwncat

): # type: (...) -> None assert type(bufsize) is int, type(bufsize) +Explicit (x4)"> assert type(bufsize) is int, type(bufsize) assert type(backlog) is int, type(backlog) +Explicit (x4)"> assert type(backlog) is int, type(backlog) assert type(recv_timeout) is float, type(recv_timeout) +Explicit (x4)"> assert type(recv_timeout) is float, type(recv_timeout) assert type(nodns) is bool, type(nodns) +Explicit (x4)"> assert type(nodns) is bool, type(nodns) assert type(ipv4) is bool, type(ipv4) +Explicit (x4)"> assert type(ipv4) is bool, type(ipv4) assert type(ipv6) is bool, type(ipv6) +Explicit (x4)"> assert type(ipv6) is bool, type(ipv6) assert type(src_addr) is str or src_addr is None, type(src_addr) +Explicit (x4)"> 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) +Explicit (x4)"> assert type(src_port) is int or src_port is None, type(src_port) assert type(udp) is bool, type(udp) +Explicit (x4)"> assert type(udp) is bool, type(udp) self.__bufsize = bufsize self.__backlog = backlog self.__recv_timeout = recv_timeout @@ -7045,7 +7048,7 @@

pwncat

): # type: (...) -> None assert type(recv_timeout_retry) is int, type(recv_timeout_retry) +Explicit (x4)"> assert type(recv_timeout_retry) is int, type(recv_timeout_retry) self.__recv_timeout_retry = recv_timeout_retry super(DsIONetworkSock, self).__init__( bufsize, @@ -7083,7 +7086,7 @@

pwncat

def reconn(self, value): # type: (int) -> None assert type(value) is int, type(value) +Explicit (x4)"> assert type(value) is int, type(value) self.__reconn = value @property @@ -7096,7 +7099,7 @@

pwncat

def reconn_wait(self, value): # type: (float) -> None assert type(value) is float, type(value) +Explicit (x4)"> assert type(value) is float, type(value) self.__reconn_wait = value @property @@ -7111,14 +7114,14 @@

pwncat

def __init__(self, reconn, reconn_wait, reconn_robin): # type: (int, float, List[int]) -> None assert type(reconn) is int, type(reconn) +Explicit (x4)"> assert type(reconn) is int, type(reconn) assert type(reconn_wait) is float, type(reconn_wait) +Explicit (x4)"> assert type(reconn_wait) is float, type(reconn_wait) assert type(reconn_robin) is list, type(reconn_robin) +Explicit (x4)"> assert type(reconn_robin) is list, type(reconn_robin) for i in reconn_robin: assert type(i) is int, type(i) +Explicit (x4)"> assert type(i) is int, type(i) self.__reconn = reconn self.__reconn_wait = reconn_wait self.__reconn_robin = reconn_robin @@ -7144,7 +7147,7 @@

pwncat

# type: (bool) -> None """Change keep_open value.""" assert type(value) is bool, type(value) +Explicit (x4)"> assert type(value) is bool, type(value) self.__keep_open = value @property @@ -7157,7 +7160,7 @@

pwncat

def rebind(self, value): # type: (int) -> None assert type(value) is int, type(value) +Explicit (x4)"> assert type(value) is int, type(value) self.__rebind = value @property @@ -7178,16 +7181,16 @@

pwncat

def __init__(self, keep_open, rebind, rebind_wait, rebind_robin): # type: (bool, int, float, List[int]) -> None assert type(keep_open) is bool, type(keep_open) +Explicit (x4)"> assert type(keep_open) is bool, type(keep_open) assert type(rebind) is int, type(rebind) +Explicit (x4)"> assert type(rebind) is int, type(rebind) assert type(rebind_wait) is float, type(rebind_wait) +Explicit (x4)"> assert type(rebind_wait) is float, type(rebind_wait) assert type(rebind_robin) is list, type(rebind_robin) +Explicit (x4)"> assert type(rebind_robin) is list, type(rebind_robin) for i in rebind_robin: assert type(i) is int, type(i) +Explicit (x4)"> assert type(i) is int, type(i) self.keep_open = keep_open self.__rebind = rebind self.__rebind_wait = rebind_wait @@ -7479,7 +7482,7 @@

pwncat

"""Implementation of rstring which works on bytes or strings.""" # We have a bytes object in Python3 if sys.version_info >= (3, 0) and type(data) is not str: +Explicit (x2)"> if sys.version_info >= (3, 0) and type(data) is not str: # Strip whitespace if search is None: while True: @@ -7789,7 +7792,7 @@

pwncat

"Invalid key for address family: %s (type: %s) (valid: %s)", repr(family), repr(type(family)), +Explicit (x2)"> repr(type(family)), repr(self.__AF_HUMAN), ) return "unknown" @@ -7805,7 +7808,7 @@

pwncat

"Invalid key for address sock_type: %s (type: %s) (valid: %s)", repr(sock_type), repr(type(sock_type)), +Explicit (x2)"> repr(type(sock_type)), repr(self.__ST_HUMAN), ) return "unknown" @@ -8009,8 +8012,7 @@

pwncat

port, ) try: - sock.bind((addr, port)) + sock.bind((addr, port)) 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 @@ -8144,8 +8146,7 @@

pwncat

# Ensure to use connect() protocol independent info = socket.getaddrinfo(addr, port, sock.family, sock.type, sock.proto) - sock.connect(info[0][4]) + sock.connect(info[0][4]) # UDP stateful connect # A UDP client doesn't know if the connect() was successful, so the trick @@ -8153,9 +8154,9 @@

pwncat

# receive or simply a timeout (which means success). if udp_sconnect and int(sock.type) == int(socket.SOCK_DGRAM): assert type(udp_send_payload) is bytes +Explicit (x2)"> assert type(udp_send_payload) is bytes assert type(udp_recv_bufsize) is int +Explicit (x2)"> assert type(udp_recv_bufsize) is int # Some applications like netcat do not like to receive empty # data, as they treat it as an EOF and will quit upon receive, # so we're using a nullbyte character instead. @@ -8413,8 +8414,7 @@

pwncat

# Only UDP server has not made a connect() to the socket, all others # are already connected and need to use send() instead of sendto() if self.__udp_mode_server: - curr = self.__active["conn"].sendto( + curr = self.__active["conn"].sendto( data, (self.__active["remote_addr"], self.__active["remote_port"]) ) send += curr @@ -10121,7 +10121,10 @@

pwncat

# [2/3] (Linux/Mac) Raw mode if self.__stdin_israw(): - self.__set_input_timeout() + # Issue #109 + # when pasting in term I donot get full line echo + # To mitigate this, I'm disabling the select.select call on sys.stdin + # self.__set_input_timeout() if self.__py3: return sys.stdin.buffer.read(1) return sys.stdin.read(1) # type: ignore @@ -13167,13 +13170,13 @@

pwncat

), ) if type(args.ping_intvl) is int and args.ping_intvl > 0: +Explicit (x3)"> if type(args.ping_intvl) is int and args.ping_intvl > 0: payload = StringEncoder.encode(args.ping_word) run.add_timer( "PING-INT", - DsRunnerTimer(net.consumer, ssig, args.ping_intvl, (payload,), {}), # send data + DsRunnerTimer(net.consumer, ssig, args.ping_intvl, (payload,), {}), # send data ) if args.ping_init: diff --git a/man/pwncat.1 b/man/pwncat.1 index ae656d18..105974ff 100644 --- a/man/pwncat.1 +++ b/man/pwncat.1 @@ -1,5 +1,5 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. -.TH PWNCAT: "1" "June 2021" "https://github.com/cytopia/pwncat" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.3. +.TH PWNCAT: "1" "October 2021" "https://github.com/cytopia/pwncat" "User Commands" .SH NAME pwncat: \- pwncat .SH DESCRIPTION diff --git a/setup.cfg b/setup.cfg index 06bfc235..cf22b3b2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,7 +25,7 @@ max-line-length = 100 # useless-object-inheritance: don't lint useless-object-inheritance to stary Python2/3 compatible # bad-continuation: let Python Black take care of this # unidiomatic-typecheck: Need to check if int or bool and this doesnt work with isinstance() -disable = useless-object-inheritance, bad-continuation, unidiomatic-typecheck, super-with-arguments, raise-missing-from, use-a-generator +disable = useless-object-inheritance, bad-continuation, unidiomatic-typecheck, super-with-arguments, raise-missing-from, use-a-generator, consider-using-f-string, consider-using-dict-items, unused-private-member, unspecified-encoding max-branches = 30 max-statements = 125 max-args = 15 diff --git a/setup.py b/setup.py index 022d3f4c..69f5cf3b 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="pwncat", - version="0.1.1", + version="0.1.2", 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, diff --git a/tests/bin/check-usage.sh b/tests/bin/check-usage.sh index c2030087..2f9d4360 100755 --- a/tests/bin/check-usage.sh +++ b/tests/bin/check-usage.sh @@ -20,7 +20,7 @@ validate_readme() { printf "[TEST] Checking README.md ... " # shellcheck disable=SC2002 if diff --ignore-trailing-space \ - <("${bin}" -h) \ + <($(which python2) "${bin}" -h) \ <(cat "${readme}" | grep -E -A 10000 'usage:[[:space:]]' | grep -E -B 10000 '^[[:space:]]+\-V') \ ; then printf "%s\\n" "OK" @@ -35,7 +35,7 @@ _diff_website() { local next_arg="${2}" if ! diff --ignore-trailing-space --ignore-blank-lines \ - <("${bin}" -h | grep "^${curr_arg}" -A 2000 | grep "^${next_arg}" -B 2000 | grep -v "^${next_arg}" ) \ + <($(which python2) "${bin}" -h | grep "^${curr_arg}" -A 2000 | grep "^${next_arg}" -B 2000 | grep -v "^${next_arg}" ) \ <(grep "^${curr_arg}" -A 2000 "${website}" | grep "^${next_arg}" -B 2000 | grep -v "^${next_arg}" | grep -v '') \ ; then printf "%s\\n" "ERROR - usage" @@ -92,7 +92,7 @@ validate_website() { # [6/5] Check misc arguments: if ! diff --ignore-trailing-space --ignore-blank-lines \ - <("${bin}" -h | grep 'misc arguments' -A 2000 | grep '\-\-version' -B 2000) \ + <($(which python2) "${bin}" -h | grep 'misc arguments' -A 2000 | grep '\-\-version' -B 2000) \ <(grep 'misc arguments' -A 2000 "${website}" | grep '\-\-version' -B 2000) \ ; then printf "%s\\n" "ERROR - misc arguments"