Created
March 16, 2018 22:28
-
-
Save stevebeattie/0eb190004e10ba0926ad8782f89676ad to your computer and use it in GitHub Desktop.
paramiko fix for CVE-2018-7750 backport to paramiko 1.10
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From e9dfd854bdaf8af15d7834f7502a0451d217bb8c Mon Sep 17 00:00:00 2001 | |
From: Jeff Forcier <jeff@bitprophet.org> | |
Date: Mon, 12 Mar 2018 15:34:06 -0700 | |
Subject: [PATCH] Fixes CVE-2018-7750 / #1175 | |
At least, insofar as the new tests pass...! | |
[Ubuntu note: backported patch to 1.10, including Message.get_text() | |
-> Message->get_string() conversion, cMSG_REQUEST_FAILURE -> | |
chr(MSG_REQUEST_FAILURE), and cMSG_CHANNEL_OPEN_FAILURE -> | |
chr(MSG_CHANNEL_OPEN_FAILURE). --sbeattie] | |
--- | |
paramiko/common.py | 1 + | |
paramiko/transport.py | 44 +++++++++++++++++++++++++++++++++++++++++++- | |
2 files changed, 44 insertions(+), 1 deletion(-) | |
Index: b/paramiko/common.py | |
=================================================================== | |
--- a/paramiko/common.py | |
+++ b/paramiko/common.py | |
@@ -27,6 +27,7 @@ MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILU | |
MSG_USERAUTH_BANNER = range(50, 54) | |
MSG_USERAUTH_PK_OK = 60 | |
MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE = range(60, 62) | |
+HIGHEST_USERAUTH_MESSAGE_ID = 79 | |
MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE = range(80, 83) | |
MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \ | |
MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, \ | |
Index: b/paramiko/transport.py | |
=================================================================== | |
--- a/paramiko/transport.py | |
+++ b/paramiko/transport.py | |
@@ -1525,6 +1525,44 @@ class Transport (threading.Thread): | |
finally: | |
self.lock.release() | |
+ def _ensure_authed(self, ptype, message): | |
+ """ | |
+ Checks message type against current auth state. | |
+ | |
+ If server mode, and auth has not succeeded, and the message is of a | |
+ post-auth type (channel open or global request) an appropriate error | |
+ response Message is crafted and returned to caller for sending. | |
+ | |
+ Otherwise (client mode, authed, or pre-auth message) returns None. | |
+ """ | |
+ if ( | |
+ not self.server_mode | |
+ or ptype <= HIGHEST_USERAUTH_MESSAGE_ID | |
+ or self.is_authenticated() | |
+ ): | |
+ return None | |
+ # WELP. We must be dealing with someone trying to do non-auth things | |
+ # without being authed. Tell them off, based on message class. | |
+ reply = Message() | |
+ # Global requests have no details, just failure. | |
+ if ptype == MSG_GLOBAL_REQUEST: | |
+ reply.add_byte(chr(MSG_REQUEST_FAILURE)) | |
+ # Channel opens let us reject w/ a specific type + message. | |
+ elif ptype == MSG_CHANNEL_OPEN: | |
+ kind = message.get_string() | |
+ chanid = message.get_int() | |
+ reply.add_byte(chr(MSG_CHANNEL_OPEN_FAILURE)) | |
+ reply.add_int(chanid) | |
+ reply.add_int(OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED) | |
+ reply.add_string('') | |
+ reply.add_string('en') | |
+ # NOTE: Post-open channel messages do not need checking; the above will | |
+ # reject attemps to open channels, meaning that even if a malicious | |
+ # user tries to send a MSG_CHANNEL_REQUEST, it will simply fall under | |
+ # the logic that handles unknown channel IDs (as the channel list will | |
+ # be empty.) | |
+ return reply | |
+ | |
def run(self): | |
# (use the exposed "run" method, because if we specify a thread target | |
# of a private method, threading.Thread will keep a reference to it | |
@@ -1582,7 +1620,11 @@ class Transport (threading.Thread): | |
continue | |
if ptype in self._handler_table: | |
- self._handler_table[ptype](self, m) | |
+ error_msg = self._ensure_authed(ptype, m) | |
+ if error_msg: | |
+ self._send_message(error_msg) | |
+ else: | |
+ self._handler_table[ptype](self, m) | |
elif ptype in self._channel_handler_table: | |
chanid = m.get_int() | |
chan = self._channels.get(chanid) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment