From effbe78416bb3a566abe4ee90bfaee3c9f0edbbe Mon Sep 17 00:00:00 2001 From: Caleb Stewart Date: Sun, 26 Dec 2021 02:41:02 -0500 Subject: [PATCH 1/2] Replaced paramiko with paramiko-ng --- poetry.lock | 101 ++++++++++++++------------------------- pwncat/facts/__init__.py | 41 +++++++--------- pyproject.toml | 2 +- 3 files changed, 54 insertions(+), 90 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7dace6e..39eda32 100644 --- a/poetry.lock +++ b/poetry.lock @@ -120,7 +120,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "cryptography" -version = "3.4.7" +version = "36.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false @@ -131,11 +131,11 @@ cffi = ">=1.12" [package.extras] docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] -docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] -sdist = ["setuptools-rust (>=0.11.4)"] +sdist = ["setuptools_rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] [[package]] name = "docutils" @@ -261,23 +261,20 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" pyparsing = ">=2.0.2" [[package]] -name = "paramiko" -version = "2.7.2" +name = "paramiko-ng" +version = "2.8.8" description = "SSH2 protocol library" category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] -bcrypt = ">=3.1.3" -cryptography = ">=2.5" -pynacl = ">=1.0.1" +bcrypt = ">=3" +cryptography = ">=1.6" [package.extras] -all = ["pyasn1 (>=0.1.7)", "pynacl (>=1.0.1)", "bcrypt (>=3.1.3)", "invoke (>=1.3)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] -ed25519 = ["pynacl (>=1.0.1)", "bcrypt (>=3.1.3)"] -gssapi = ["pyasn1 (>=0.1.7)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] -invoke = ["invoke (>=1.3)"] +ed25519 = ["pynacl"] +gssapi = ["gssapi", "pyasn1"] [[package]] name = "persistent" @@ -365,22 +362,6 @@ category = "main" optional = false python-versions = ">=3.5" -[[package]] -name = "pynacl" -version = "1.4.0" -description = "Python binding to the Networking and Cryptography (NaCl) library" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -cffi = ">=1.4.1" -six = "*" - -[package.extras] -docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] -tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] - [[package]] name = "pyparsing" version = "2.4.7" @@ -787,7 +768,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "9357cb6dd124d9b201849436e710ae4baec36f65104fa505e5e155c60d602e79" +content-hash = "f6ff82986a82dc3982bae00fee3833362028aecba3eecf7e28a07a32db6615a5" [metadata.files] alabaster = [ @@ -931,18 +912,26 @@ commonmark = [ {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] cryptography = [ - {file = "cryptography-3.4.7-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1"}, - {file = "cryptography-3.4.7-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250"}, - {file = "cryptography-3.4.7-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2"}, - {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6"}, - {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959"}, - {file = "cryptography-3.4.7-cp36-abi3-win32.whl", hash = "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d"}, - {file = "cryptography-3.4.7-cp36-abi3-win_amd64.whl", hash = "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca"}, - {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873"}, - {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d"}, - {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177"}, - {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"}, - {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"}, + {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b"}, + {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2"}, + {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f"}, + {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3"}, + {file = "cryptography-36.0.1-cp36-abi3-win32.whl", hash = "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca"}, + {file = "cryptography-36.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf"}, + {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9"}, + {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1"}, + {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316"}, + {file = "cryptography-36.0.1.tar.gz", hash = "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638"}, ] docutils = [ {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, @@ -1052,9 +1041,9 @@ packaging = [ {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] -paramiko = [ - {file = "paramiko-2.7.2-py2.py3-none-any.whl", hash = "sha256:4f3e316fef2ac628b05097a637af35685183111d4bc1b5979bd397c2ab7b5898"}, - {file = "paramiko-2.7.2.tar.gz", hash = "sha256:7f36f4ba2c0d81d219f4595e35f70d56cc94f9ac40a6acdf51d6ca210ce65035"}, +paramiko-ng = [ + {file = "paramiko-ng-2.8.8.tar.gz", hash = "sha256:d11191bda27dea3d4cd080c41912bd286ba3879c49851b6c7fc286c2fd0d4497"}, + {file = "paramiko_ng-2.8.8-py2.py3-none-any.whl", hash = "sha256:21c8ad4d86006862ef32f6a1f68e563d3dd07fef4336b89a61d2f4c465806e8e"}, ] persistent = [ {file = "persistent-4.7.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:ed939f58937f43a67a0f60abb0057e7f31e90d35b6a215159b04248368696930"}, @@ -1169,26 +1158,6 @@ pygments = [ {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"}, {file = "Pygments-2.9.0.tar.gz", hash = "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f"}, ] -pynacl = [ - {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, - {file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"}, - {file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"}, - {file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"}, - {file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"}, - {file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"}, - {file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"}, - {file = "PyNaCl-1.4.0-cp35-abi3-win32.whl", hash = "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634"}, - {file = "PyNaCl-1.4.0-cp35-abi3-win_amd64.whl", hash = "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6"}, - {file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"}, - {file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"}, - {file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"}, - {file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"}, - {file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"}, - {file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"}, - {file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"}, - {file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"}, - {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, -] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, diff --git a/pwncat/facts/__init__.py b/pwncat/facts/__init__.py index 1cf7e9e..ab72034 100644 --- a/pwncat/facts/__init__.py +++ b/pwncat/facts/__init__.py @@ -8,6 +8,7 @@ import time import pathlib import tempfile import subprocess +from io import StringIO from typing import Callable, Optional import rich.markup @@ -356,30 +357,24 @@ class PrivateKey(Implant): else: raise ModuleFailed(f"unknown username for uid={self.uid}") - with tempfile.NamedTemporaryFile("w") as filp: - filp.write(self.content) - filp.flush() + try: + # Connect via SSH + session = manager.create_session( + "linux", + host=target.public_address[0], + user=user.name, + identity=StringIO(self.content + "\n"), + ) + except (ChannelError, PlatformError) as exc: + manager.log( + f"[yellow]warning[/yellow]: {self.source} implant failed; removing implant types." + ) + self.authorized = False + self.types.remove("implant.remote") + self.types.remove("implant.replace") + raise ModuleFailed(str(exc)) from exc - pathlib.Path(filp.name).chmod(0o600) - - try: - # Connect via SSH - session = manager.create_session( - "linux", - host=target.public_address[0], - user=user.name, - identity=filp.name, - ) - except (ChannelError, PlatformError) as exc: - manager.log( - f"[yellow]warning[/yellow]: {self.source} implant failed; removing implant types." - ) - self.authorized = False - self.types.remove("implant.remote") - self.types.remove("implant.replace") - raise ModuleFailed(str(exc)) from exc - - return session + return session class EscalationReplace(Fact): diff --git a/pyproject.toml b/pyproject.toml index 11086c2..63ceaea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,8 +40,8 @@ rich = "^10.4.0" python-rapidjson = "^1.0" ZODB3 = "^3.11.0" zodburi = "^2.5.0" -paramiko = "^2.7.2" Jinja2 = "^3.0.1" +paramiko-ng = "^2.8.8" [tool.poetry.dev-dependencies] isort = "^5.8.0" From f858441ea6c259068eef89f9f4ab9ea162689d08 Mon Sep 17 00:00:00 2001 From: Caleb Stewart Date: Sun, 26 Dec 2021 03:11:19 -0500 Subject: [PATCH 2/2] Moved to paramiko-ng to provide ssh-agent support This also provides support for key types aside from RSA --- CHANGELOG.md | 2 + poetry.lock | 38 +++++++++++++- pwncat/channel/ssh.py | 111 +++++++++++++++++++-------------------- pwncat/facts/__init__.py | 2 - pyproject.toml | 1 + 5 files changed, 95 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe849bc..6a7e5c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and simply didn't have the time to go back and retroactively create one. ### Changed - Fixed parsing of `--ssl` argument in main entrypoint ([#225](https://github.com/calebstewart/pwncat/issues/225)) +- Replaced `paramiko` with `paramiko-ng` +- Utilized Paramiko SSHClient which will also utilize the SSHAgent if available by default and supports key types aside from RSA ([#91](https://github.com/calebstewart/pwncat/issues/91)) ## [0.5.1] - 2021-12-07 Minor bug fixes. Mainly typos from changing the package name. diff --git a/poetry.lock b/poetry.lock index 39eda32..16ec31d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -362,6 +362,22 @@ category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "pynacl" +version = "1.4.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +cffi = ">=1.4.1" +six = "*" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] + [[package]] name = "pyparsing" version = "2.4.7" @@ -768,7 +784,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "f6ff82986a82dc3982bae00fee3833362028aecba3eecf7e28a07a32db6615a5" +content-hash = "23945db2620e31067d3b8810462ebaa6627fde4220ed01d49ba009e89962a725" [metadata.files] alabaster = [ @@ -1158,6 +1174,26 @@ pygments = [ {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"}, {file = "Pygments-2.9.0.tar.gz", hash = "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f"}, ] +pynacl = [ + {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"}, + {file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"}, + {file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"}, + {file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"}, + {file = "PyNaCl-1.4.0-cp35-abi3-win32.whl", hash = "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634"}, + {file = "PyNaCl-1.4.0-cp35-abi3-win_amd64.whl", hash = "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6"}, + {file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"}, + {file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"}, + {file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"}, + {file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"}, + {file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"}, + {file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"}, + {file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"}, + {file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"}, + {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, +] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, diff --git a/pwncat/channel/ssh.py b/pwncat/channel/ssh.py index d6c0a45..dd0f8d1 100644 --- a/pwncat/channel/ssh.py +++ b/pwncat/channel/ssh.py @@ -8,7 +8,7 @@ An optional port argument is also accepted. """ import os import socket -from typing import Optional +from typing import Union, TextIO, Optional import paramiko from prompt_toolkit import prompt @@ -46,65 +46,31 @@ class Ssh(Channel): password = prompt("Password: ", is_password=True) try: - # Connect to the remote host's ssh server - sock = socket.create_connection((host, port)) - except Exception as exc: - raise ChannelError(self, str(exc)) + client = paramiko.client.SSHClient() + client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy) - # Create a paramiko SSH transport layer around the socket - t = paramiko.Transport(sock) - try: - t.start_client() - except paramiko.SSHException: - sock.close() - raise ChannelError(self, "ssh negotiation failed") + client.connect( + hostname=host, + port=port, + username=user, + password=password, + pkey=load_private_key(identity), + allow_agent=True, + look_for_keys=False, + ) - if identity is not None: - try: - # Load the private key for the user - if isinstance(identity, str): - key = paramiko.RSAKey.from_private_key_file( - os.path.expanduser(identity) - ) - else: - key = paramiko.RSAKey.from_private_key(identity) - except paramiko.ssh_exception.SSHException: - password = prompt("RSA Private Key Passphrase: ", is_password=True) - try: - if isinstance(identity, str): - key = paramiko.RSAKey.from_private_key_file(identity, password) - else: - # Seek back to the beginning of the file (the above load read the whole file) - identity.seek(0) - key = paramiko.RSAKey.from_private_key(identity, password) - except paramiko.ssh_exception.SSHException: - raise ChannelError(self, "invalid private key or passphrase") + columns, rows = os.get_terminal_size(0) + shell = client.invoke_shell(width=columns, height=rows) + shell.setblocking(0) - # Attempt authentication - try: - t.auth_publickey(user, key) - except paramiko.ssh_exception.AuthenticationException as exc: - raise ChannelError(self, str(exc)) - else: - try: - t.auth_password(user, password) - except paramiko.ssh_exception.AuthenticationException as exc: - raise ChannelError(self, str(exc)) + self.client = shell + self.address = (host, port) + self._connected = True - if not t.is_authenticated(): - t.close() - sock.close() - raise ChannelError(self, "authentication failed") - - # Open an interactive session - chan = t.open_session() - chan.get_pty() - chan.invoke_shell() - chan.setblocking(0) - - self.client = chan - self.address = (host, port) - self._connected = True + except paramiko.ssh_exception.AuthenticationException as exc: + raise ChannelError(self, f"ssh authentication failed: {str(exc)}") from exc + except (paramiko.ssh_exception.SSHException, socket.error) as exc: + raise ChannelError(self, f"ssh connection failed: {str(exc)}") from exc @property def connected(self): @@ -153,3 +119,36 @@ class Ssh(Channel): pass return data + + +def load_private_key(identity: Union[str, TextIO], passphrase: str = None): + """Load a private key and return the appropriate PKey object""" + + if identity is None: + return None + + try: + if isinstance(identity, str): + return paramiko.pkey.load_private_key_file( + os.path.expanduser(identity), password=passphrase + ) + + identity.seek(0) + return paramiko.pkey.load_private_key(identity.read(), password=passphrase) + except paramiko.PasswordRequiredException: + # Bad passphrase + if passphrase is not None: + raise + + try: + # No passphrase, prompt for one + passphrase = prompt("Private Key Passphrase: ", is_password=True) + except KeyboardInterrupt: + passphrase = None + + # No passphrase given, re-raise + if passphrase is None: + raise + + # Try again with the given passphrase + return load_private_key(identity, passphrase=passphrase) diff --git a/pwncat/facts/__init__.py b/pwncat/facts/__init__.py index ab72034..b9b5e4e 100644 --- a/pwncat/facts/__init__.py +++ b/pwncat/facts/__init__.py @@ -5,8 +5,6 @@ should not generally need to use these types except as reference when interacting with data returned by an enumeration module. """ import time -import pathlib -import tempfile import subprocess from io import StringIO from typing import Callable, Optional diff --git a/pyproject.toml b/pyproject.toml index 63ceaea..e3c79f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ ZODB3 = "^3.11.0" zodburi = "^2.5.0" Jinja2 = "^3.0.1" paramiko-ng = "^2.8.8" +PyNaCl = "^1.4.0" [tool.poetry.dev-dependencies] isort = "^5.8.0"