Skip to content

Instantly share code, notes, and snippets.

@dmiller-nmap
Last active August 27, 2021 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dmiller-nmap/3b4aa9c0f48d0b4e16e9aa871ffdbbf8 to your computer and use it in GitHub Desktop.
Save dmiller-nmap/3b4aa9c0f48d0b4e16e9aa871ffdbbf8 to your computer and use it in GitHub Desktop.
SSH banner grab NSE
diff --git a/libssh2/include/libssh2.h b/libssh2/include/libssh2.h
index d33df03..f649c50 100644
--- a/libssh2/include/libssh2.h
+++ b/libssh2/include/libssh2.h
@@ -611,6 +611,8 @@ LIBSSH2_API const char *libssh2_session_banner_get(LIBSSH2_SESSION *session);
LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session,
const char *username,
unsigned int username_len);
+LIBSSH2_API char *libssh2_userauth_banner(LIBSSH2_SESSION * session,
+ size_t *banner_len_out);
LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session);
LIBSSH2_API int
diff --git a/libssh2/src/libssh2_priv.h b/libssh2/src/libssh2_priv.h
index 33c5ad3..619b33c 100644
--- a/libssh2/src/libssh2_priv.h
+++ b/libssh2/src/libssh2_priv.h
@@ -702,6 +702,8 @@ struct _LIBSSH2_SESSION
libssh2_nonblocking_states userauth_list_state;
unsigned char *userauth_list_data;
size_t userauth_list_data_len;
+ char *userauth_banner;
+ size_t userauth_banner_len;
packet_requirev_state_t userauth_list_packet_requirev_state;
/* State variables used in libssh2_userauth_password_ex() */
diff --git a/libssh2/src/userauth.c b/libssh2/src/userauth.c
index 949dc1c..268a959 100644
--- a/libssh2/src/userauth.c
+++ b/libssh2/src/userauth.c
@@ -53,6 +53,7 @@
#include "session.h"
#include "userauth.h"
+#define LIBSSH2_USERAUTH_MAX_BANNER 2048
/* libssh2_userauth_list
*
* List authentication methods
@@ -63,8 +64,8 @@
static char *userauth_list(LIBSSH2_SESSION *session, const char *username,
unsigned int username_len)
{
- static const unsigned char reply_codes[3] =
- { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
+ static const unsigned char reply_codes[4] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_BANNER, 0 };
/* packet_type(1) + username_len(4) + service_len(4) +
service(14)"ssh-connection" + method_len(4) = 27 */
unsigned long methods_len;
@@ -117,22 +118,54 @@ static char *userauth_list(LIBSSH2_SESSION *session, const char *username,
session->userauth_list_state = libssh2_NB_state_sent;
}
- if(session->userauth_list_state == libssh2_NB_state_sent) {
- rc = _libssh2_packet_requirev(session, reply_codes,
- &session->userauth_list_data,
- &session->userauth_list_data_len, 0,
- NULL, 0,
- &session->userauth_list_packet_requirev_state);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
- "Would block requesting userauth list");
- return NULL;
- }
- else if(rc || (session->userauth_list_data_len < 1)) {
- _libssh2_error(session, rc, "Failed getting response");
- session->userauth_list_state = libssh2_NB_state_idle;
- return NULL;
- }
+ if (session->userauth_list_state == libssh2_NB_state_sent) {
+ do {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_list_data,
+ &session->userauth_list_data_len, 0,
+ NULL, 0,
+ &session->userauth_list_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ return NULL;
+ } else if (rc || (session->userauth_list_data_len < 1)) {
+ _libssh2_error(session, rc, "Failed getting response");
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+ if (session->userauth_list_data[0] == SSH_MSG_USERAUTH_BANNER) {
+ methods_len = _libssh2_ntohu32(session->userauth_list_data + 1);
+ /* Cap to 512 bytes. */
+ if (methods_len > LIBSSH2_USERAUTH_MAX_BANNER) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Banner length %u exceeds max allowed (%u)",
+ methods_len, LIBSSH2_USERAUTH_MAX_BANNER);
+ methods_len = LIBSSH2_USERAUTH_MAX_BANNER - 1;
+ }
+
+ if (!session->userauth_banner) {
+ session->userauth_banner = LIBSSH2_ALLOC(session, methods_len + 1);
+ }
+ else if (session->userauth_banner_len < methods_len) {
+ session->userauth_banner = LIBSSH2_REALLOC(session, session->userauth_banner, methods_len + 1);
+ }
+ if (!session->userauth_banner) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for userauth_banner");
+ continue;
+ }
+ session->userauth_banner_len = methods_len;
+
+ memmove(session->userauth_banner, session->userauth_list_data + 5, methods_len);
+ session->userauth_banner[methods_len] = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Banner: %s",
+ session->userauth_banner);
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ }
+ else break;
+ } while (1);
if(session->userauth_list_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
/* Wow, who'dve thought... */
@@ -189,6 +222,30 @@ libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user,
return ptr;
}
+/* libssh2_userauth_banner
+ *
+ * Retrieve banner message from server, if available.
+ * If no such message is sent by the server or if no authentication attempt has
+ * been made, this function returns NULL.
+ * libssh2_userauth_list makes a "none" authentication attempt and is
+ * sufficient to collect the pre-auth banner message.
+ *
+ * Banner ought to be UTF-8 encoded, and will be truncated to
+ * LIBSSH2_USERAUTH_MAX_BANNER bytes. Length will be returned in
+ * banner_len_out.
+ */
+LIBSSH2_API char *
+libssh2_userauth_banner(LIBSSH2_SESSION * session,
+ size_t *banner_len_out)
+{
+ char *ptr = NULL;
+ if (session->userauth_banner) {
+ ptr = session->userauth_banner;
+ *banner_len_out = session->userauth_banner_len;
+ }
+ return ptr;
+}
+
/*
* libssh2_userauth_authenticated
*
diff --git a/nse_libssh2.cc b/nse_libssh2.cc
index 983f83d..9ae2c8d 100644
--- a/nse_libssh2.cc
+++ b/nse_libssh2.cc
@@ -357,6 +357,7 @@ static int l_session_open (lua_State *L) {
}
libssh2_session_set_blocking(state->session, 0);
+ libssh2_trace(state->session, 0xffffffff);
if (make_socketpair(state->sp, 1) == -1)
return nseU_safeerror(L, "trying to create socketpair");
@@ -484,6 +485,28 @@ static int l_userauth_list (lua_State *L) {
return userauth_list(L, 0, 0);
}
+static int userauth_banner (lua_State *L, int status, lua_KContext ctx) {
+ const char *auth_banner = NULL;
+ size_t auth_banner_len = 0;
+ struct ssh_userdata *state = NULL;
+
+ state = (struct ssh_userdata *) nseU_checkudata(L, 1, SSH2_UDATA, "ssh2");
+ assert(state->session != NULL);
+
+ if ((auth_banner = libssh2_userauth_banner(state->session, &auth_banner_len)) != NULL) {
+ lua_pushlstring(L, auth_banner, auth_banner_len);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+* Returns pre-auth banner
+*/
+static int l_userauth_banner (lua_State *L) {
+ return userauth_banner(L, 0, 0);
+}
+
static int userauth_publickey (lua_State *L, int status, lua_KContext ctx) {
int rc;
const char *username, *private_key_file, *passphrase, *public_key_file;
@@ -878,6 +901,7 @@ static const struct luaL_Reg libssh2[] = {
{ "session_open", l_session_open },
{ "hostkey_hash", l_hostkey_hash },
{ "set_timeout", l_set_timeout },
+ { "userauth_banner", l_userauth_banner },
{ "userauth_list", l_userauth_list },
{ "userauth_publickey", l_userauth_publickey },
{ "read_publickey", l_read_publickey },
diff --git a/nselib/libssh2-utility.lua b/nselib/libssh2-utility.lua
index bfe7bf0..fc1df10 100644
--- a/nselib/libssh2-utility.lua
+++ b/nselib/libssh2-utility.lua
@@ -31,23 +31,9 @@ end
--
-- @param host A host to connect to.
-- @param port A port to connect to.
--- @return true on success or nil on failure
-function SSHConnection:connect (host, port)
- self.session = libssh2.session_open(host, port.number)
- if self.session then
- return true
- end
-end
-
-
----
--- Sets up a connection with a server. Call performed with pcall.
---
--- @param host A host to connect to.
--- @param port A port to connect to.
-- @return true on success or error message on failure
-- @return error code if error was triggered
-function SSHConnection:connect_pcall (host, port)
+function SSHConnection:connect (host, port)
local status, err
status, self.session, err = pcall(libssh2.session_open, host, port.number)
return status, err
@@ -148,6 +134,23 @@ function SSHConnection:list (username)
end
---
+-- Attempt to retrieve the server's pre-auth banner
+--
+-- Need to attempt auth first (for instance by calling list)
+--
+-- @return The server's banner or false on failure.
+function SSHConnection:banner ()
+ if not self.session then
+ return false
+ end
+ local status, banner = pcall(libssh2.userauth_banner, self.session)
+ if status then
+ return banner
+ end
+ return false
+end
+
+---
-- Attempts to read public key file
--
-- @param publickey An SSH public key file.
diff --git a/scripts/ssh-auth-methods.nse b/scripts/ssh-auth-methods.nse
index dd66213..1e1b675 100644
--- a/scripts/ssh-auth-methods.nse
+++ b/scripts/ssh-auth-methods.nse
@@ -38,6 +38,7 @@ function action (host, port)
local authmethods = helper:list(username)
result["Supported authentication methods"] = authmethods
+ result["Banner"] = helper:banner()
return result
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment