diff --git a/Makefile.in b/Makefile.in index 19818e465..43ca82785 100644 --- a/Makefile.in +++ b/Makefile.in @@ -62,9 +62,14 @@ INSTALLNDIFF=@INSTALLNDIFF@ UNINSTALLZENMAP=@UNINSTALLZENMAP@ ifneq (@LIBLUA_LIBS@,) -NSE_SRC=nse_main.cc nse_nsock.cc nse_init.cc nse_fs.cc nse_nmaplib.cc nse_debug.cc nse_pcrelib.cc nse_binlib.cc nse_hash.cc nse_bit.cc -NSE_HDRS=nse_main.h nse_nsock.h nse_init.h nse_fs.h nse_nmaplib.h nse_debug.h nse_macros.h nse_pcrelib.h nse_binlib.h nse_hash.h nse_bit.h -NSE_OBJS=nse_main.o nse_nsock.o nse_init.o nse_fs.o nse_nmaplib.o nse_debug.o nse_pcrelib.o nse_binlib.o nse_hash.o nse_bit.o +NSE_SRC=nse_main.cc nse_nsock.cc nse_init.cc nse_fs.cc nse_nmaplib.cc nse_debug.cc nse_pcrelib.cc nse_binlib.cc nse_bit.cc +NSE_HDRS=nse_main.h nse_nsock.h nse_init.h nse_fs.h nse_nmaplib.h nse_debug.h nse_macros.h nse_pcrelib.h nse_binlib.h nse_bit.h +NSE_OBJS=nse_main.o nse_nsock.o nse_init.o nse_fs.o nse_nmaplib.o nse_debug.o nse_pcrelib.o nse_binlib.o nse_bit.o +ifneq (@OPENSSL_LIBS@,) +NSE_SRC+=nse_openssl.cc +NSE_HDRS+=nse_openssl.h +NSE_OBJS+=nse_openssl.o +endif endif export SRCS = main.cc nmap.cc targets.cc tcpip.cc nmap_error.cc utils.cc idle_scan.cc osscan.cc osscan2.cc output.cc scan_engine.cc timing.cc charpool.cc services.cc protocols.cc nmap_rpc.cc portlist.cc NmapOps.cc TargetGroup.cc Target.cc FingerPrintResults.cc service_scan.cc NmapOutputTable.cc MACLookup.cc nmap_tty.cc nmap_dns.cc traceroute.cc portreasons.cc $(NSE_SRC) @COMPAT_SRCS@ diff --git a/docs/scripting.xml b/docs/scripting.xml index 7486ee620..d471c9e93 100644 --- a/docs/scripting.xml +++ b/docs/scripting.xml @@ -1164,6 +1164,233 @@ if(s) code_to_be_done_on_match end + + openssl NSE module + OpenSSLin NSE + OpenSSL NSE bindings + + + The openssl module provides functions for + dealing with multiprecision integers. The functions reside inside the + openssl namespace. + + + + + + + + Returns the size of bignum in bits. + + + + + + + Returns the size of bignum in bytes. + + + + + + + Sets bit at position in bignum. + + + + + + + Clears bit at position in bignum. + + + + + + + Get bit at position in bignum. + + + + + + + Set sign of bignum. If negative is false the sign becomes positive otherwise it becomes negative. + + + + + + + Check sign of bignum. + + + + + + + Converts binary encoded string into a bignum. + + + + + + + Converts decimal encoded string into a bignum. + + + + + + + Converts hex-encoded string into a bignum. + + + + + + + Converts bignum into a binary encoded string. + + + + + + + Converts bignum into a decimal encoded string. + + + + + + + Converts bignum into a hex-encoded string. + + + + + + + Returns random bignum with bits bit size. + + + + + + + Returns pseudo random bignum with bits bit size. + + + + + + + Returns bignum which is the result of a^p mod m. + + + + + + + Returns a string of bytes length with random data. + + + + + + + Returns a string of bytes length with pseudo random data. + + + + + + + Returns the MD2 digest of message. + + + + + + + Returns the MD4 digest of message. + + + + + + + Returns the MD5 digest of message. + + + + + + + Returns the SHA1 digest of message. + + + + + + + Returns the RIPEMD160 digest of message. + + + + + + + Returns the digest specified by algorithm of message. + + + + + + + + + + + + + + Encrypt data with the encryption algorithm algorithm, key and the initialization vector iv. + + + + + + + Decrypt data with the encryption algorithm algorithm, key and the initialization vector iv. + + + + + + + Returns a table with the names of the supported cipher algorithms. + + + + + + + Returns a table with the names of the supported digest algorithms. + + + + + + + Converts data which must be a 7-byte string into a 8-byte DES key and sets the parity. + + + + + + + IP Operations ipOps NSE module diff --git a/mswin32/nmap.vcproj b/mswin32/nmap.vcproj index e20fb7941..1cee0e841 100644 --- a/mswin32/nmap.vcproj +++ b/mswin32/nmap.vcproj @@ -291,7 +291,7 @@ > -#include -extern "C" { -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" -} -#include "nse_hash.h" - -#include "nbase/nbase_md5.h" -#include "nbase/nbase_sha1.h" - - -static int l_md5(lua_State *L) -{ - size_t len; - const char *str = luaL_checklstring(L, 1, &len); - - MD5_CTX c; - unsigned char digest[MD5_DIGEST_LENGTH]; - luaL_Buffer buf; - char hdigit[3]; - - luaL_buffinit(L,&buf); - - if (!nb_MD5_Init(&c)) { - /* ERROR */ - luaL_error(L, "MD5 init error"); - } - - nb_MD5_Update(&c, str, len); - nb_MD5_Final(digest, &c); - - for (int ii = 0; ii < MD5_DIGEST_LENGTH; ii++) { - sprintf(hdigit, "%02x", digest[ii]); - luaL_addlstring(&buf, hdigit, 2); - } - luaL_pushresult(&buf); - return 1; -} - -static int l_md5bin(lua_State *L) -{ - size_t len; - const char *str = luaL_checklstring(L, 1, &len); - - MD5_CTX c; - unsigned char digest[MD5_DIGEST_LENGTH]; - luaL_Buffer buf; - - luaL_buffinit(L,&buf); - - if (!nb_MD5_Init(&c)) { - /* ERROR */ - luaL_error(L, "MD5 init error"); - } - - nb_MD5_Update(&c, str, len); - nb_MD5_Final(digest, &c); - - lua_pushlstring(L, (char *)digest, MD5_DIGEST_LENGTH); - return 1; -} - -static int l_sha1(lua_State *L) -{ - size_t len; - const char *str = luaL_checklstring(L, 1, &len); - - SHA_CTX c; - unsigned char digest[SHA_DIGEST_LENGTH]; - luaL_Buffer buf; - char hdigit[3]; - - luaL_buffinit(L,&buf); - - if (!nb_SHA1_Init(&c)) { - /* ERROR */ - luaL_error(L, "sha1 init error"); - } - - nb_SHA1_Update(&c, str, len); - nb_SHA1_Final(digest, &c); - - for (int ii = 0; ii < SHA_DIGEST_LENGTH; ii++) { - sprintf(hdigit, "%02x", digest[ii]); - luaL_addlstring(&buf, hdigit, 2); - } - luaL_pushresult(&buf); - return 1; -} - - -static int l_sha1bin(lua_State *L) -{ - size_t len; - const char *str = luaL_checklstring(L, 1, &len); - - SHA_CTX c; - unsigned char digest[SHA_DIGEST_LENGTH]; - luaL_Buffer buf; - - - luaL_buffinit(L,&buf); - - if (!nb_SHA1_Init(&c)) { - /* ERROR */ - luaL_error(L, "sha1 init error"); - } - - nb_SHA1_Update(&c, str, len); - nb_SHA1_Final(digest, &c); - - lua_pushlstring(L, (char *) digest, SHA_DIGEST_LENGTH); - return 1; -} - - -static const luaL_reg hashlib[] = -{ - {"md5", l_md5}, - {"sha1", l_sha1}, - {"md5bin", l_md5bin}, - {"sha1bin", l_sha1bin}, - {NULL, NULL} -}; - -LUALIB_API int luaopen_hashlib (lua_State *L) { - luaL_register(L, NSE_HASHLIBNAME, hashlib); - return 1; -} diff --git a/nse_hash.h b/nse_hash.h deleted file mode 100644 index e9470f493..000000000 --- a/nse_hash.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef NSE_HASHLIB -#define NSE_HASHLIB - -#define NSE_HASHLIBNAME "hash" - -LUALIB_API int luaopen_hashlib (lua_State *L); - -#endif /* NSE_HASHLIB */ diff --git a/nse_init.cc b/nse_init.cc index cfe272752..78c5039f6 100644 --- a/nse_init.cc +++ b/nse_init.cc @@ -8,8 +8,11 @@ #include "nse_pcrelib.h" #include "nse_bit.h" +#ifdef HAVE_OPENSSL +#include "nse_openssl.h" +#endif + #include "nse_binlib.h" -#include "nse_hash.h" #include "nbase.h" @@ -245,8 +248,10 @@ int init_lua (lua_State *L) {NSE_PCRELIBNAME, luaopen_pcrelib}, // pcre library {"nmap", luaopen_nmap}, // nmap bindings {NSE_BINLIBNAME, luaopen_binlib}, - {NSE_HASHLIBNAME, luaopen_hashlib}, {BITLIBNAME, luaopen_bit}, // bit library +#ifdef HAVE_OPENSSL + {OPENSSLLIBNAME, luaopen_openssl}, // openssl bindings +#endif }; luaL_openlibs(L); // opens all standard libraries diff --git a/nse_openssl.cc b/nse_openssl.cc new file mode 100644 index 000000000..6acb3ed06 --- /dev/null +++ b/nse_openssl.cc @@ -0,0 +1,520 @@ +#ifdef WIN32 +#include "nmap_winconfig.h" +#else +#include "nmap_config.h" +#endif + + +/* OpenSSL library for lua + * adapted from lmd5 library (http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/) + * Original code written by Luiz Henrique de Figueiredo + * Adapted for NMap by Thomas Buchanan + * bignum and rand_bytes functions added by Sven Klemm + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nse_openssl.h" + +typedef struct bignum_data { + BIGNUM * bn; +} bignum_data_t; + +static int l_bignum_bin2bn( lua_State *L ) /** bignum_bin2bn( string s ) */ +{ + size_t len; + const unsigned char * s = (unsigned char *) luaL_checklstring( L, 1, &len ); + BIGNUM * num = BN_new(); + BN_bin2bn( s, len, num ); + bignum_data_t * data = (bignum_data_t *) lua_newuserdata( L, sizeof(bignum_data_t)); + luaL_getmetatable( L, "BIGNUM" ); + lua_setmetatable( L, -2 ); + data->bn = num; + return 1; +} + +static int l_bignum_dec2bn( lua_State *L ) /** bignum_dec2bn( string s ) */ +{ + const char * s = luaL_checkstring( L, 1 ); + BIGNUM * num = BN_new(); + BN_dec2bn( &num, s ); + bignum_data_t * data = (bignum_data_t *) lua_newuserdata( L, sizeof(bignum_data_t)); + luaL_getmetatable( L, "BIGNUM" ); + lua_setmetatable( L, -2 ); + data->bn = num; + return 1; +} + +static int l_bignum_hex2bn( lua_State *L ) /** bignum_hex2bn( string s ) */ +{ + const char * s = luaL_checkstring( L, 1 ); + BIGNUM * num = BN_new(); + BN_hex2bn( &num, s ); + bignum_data_t * data = (bignum_data_t *) lua_newuserdata( L, sizeof(bignum_data_t)); + luaL_getmetatable( L, "BIGNUM" ); + lua_setmetatable( L, -2 ); + data->bn = num; + return 1; +} + +static int l_bignum_rand( lua_State *L ) /** bignum_rand( number bits ) */ +{ + size_t bits = luaL_checkint( L, 1 ); + BIGNUM * num = BN_new(); + BN_rand( num, bits, -1, 0 ); + bignum_data_t * data = (bignum_data_t *) lua_newuserdata( L, sizeof(bignum_data_t)); + luaL_getmetatable( L, "BIGNUM" ); + lua_setmetatable( L, -2 ); + data->bn = num; + return 1; +} + +static int l_bignum_pseudo_rand( lua_State *L ) /** bignum_pseudo_rand( number bits ) */ +{ + size_t bits = luaL_checkint( L, 1 ); + BIGNUM * num = BN_new(); + BN_pseudo_rand( num, bits, -1, 0 ); + bignum_data_t * data = (bignum_data_t *) lua_newuserdata( L, sizeof(bignum_data_t)); + luaL_getmetatable( L, "BIGNUM" ); + lua_setmetatable( L, -2 ); + data->bn = num; + return 1; +} + +static int l_bignum_mod_exp( lua_State *L ) /** bignum_mod_exp( BIGNUM a, BIGNUM p, BIGNUM m ) */ +{ + bignum_data_t * a = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + bignum_data_t * p = (bignum_data_t *) luaL_checkudata(L, 2, "BIGNUM"); + bignum_data_t * m = (bignum_data_t *) luaL_checkudata(L, 3, "BIGNUM"); + BIGNUM * result = BN_new(); + BN_CTX * ctx = BN_CTX_new(); + BN_CTX_init( ctx ); + BN_mod_exp( result, a->bn, p->bn, m->bn, ctx ); + BN_CTX_free( ctx ); + bignum_data_t * data = (bignum_data_t *) lua_newuserdata( L, sizeof(bignum_data_t)); + luaL_getmetatable( L, "BIGNUM" ); + lua_setmetatable( L, -2 ); + data->bn = result; + return 1; +} + +static int l_bignum_num_bits( lua_State *L ) /** bignum_num_bits( BIGNUM bn ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + lua_pushnumber( L, BN_num_bits( userdata->bn) ); + return 1; +} + +static int l_bignum_num_bytes( lua_State *L ) /** bignum_num_bytes( BIGNUM bn ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + lua_pushnumber( L, BN_num_bytes( userdata->bn) ); + return 1; +} + +static int l_bignum_set_bit( lua_State *L ) /** bignum_set_bit( BIGNUM bn, number position ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + int position = luaL_checkint( L, 2 ); + BN_set_bit( userdata->bn, position ); + return 0; +} + +static int l_bignum_clear_bit( lua_State *L ) /** bignum_clear_bit( BIGNUM bn, number position ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + int position = luaL_checkint( L, 2 ); + BN_clear_bit( userdata->bn, position ); + return 0; +} + +static int l_bignum_is_bit_set( lua_State *L ) /** bignum_set_bit( BIGNUM bn, number position ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + int position = luaL_checkint( L, 2 ); + lua_pushboolean( L, BN_is_bit_set( userdata->bn, position ) ); + return 1; +} + +static int l_bignum_set_negative( lua_State *L ) /** bignum_set_negative( BIGNUM bn ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + int value = lua_toboolean(L, 2); + BN_set_negative( userdata->bn, value ); + return 0; +} + +static int l_bignum_is_negative( lua_State *L ) /** bignum_is_negative( BIGNUM bn ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + lua_pushboolean( L, BN_is_negative( userdata->bn) ); + return 1; +} + +static int l_bignum_bn2bin( lua_State *L ) /** bignum_bn2bin( BIGNUM bn ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + unsigned char * result = (unsigned char *) malloc( BN_num_bytes( userdata->bn ) ); + if (!result) return luaL_error( L, "Couldn't allocate memory."); + + int len = BN_bn2bin( userdata->bn, result ); + lua_pushlstring( L, (char *) result, len ); + free( result ); + return 1; +} + +static int l_bignum_bn2dec( lua_State *L ) /** bignum_bn2dec( BIGNUM bn ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + char * result = BN_bn2dec( userdata->bn ); + lua_pushstring( L, result ); + OPENSSL_free( result ); + return 1; +} + +static int l_bignum_bn2hex( lua_State *L ) /** bignum_bn2hex( BIGNUM bn ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + char * result = BN_bn2hex( userdata->bn ); + lua_pushstring( L, result ); + OPENSSL_free( result ); + return 1; +} + +static int l_bignum_free( lua_State *L ) /** bignum_free( bignum ) */ +{ + bignum_data_t * userdata = (bignum_data_t *) luaL_checkudata(L, 1, "BIGNUM"); + BN_clear_free( userdata->bn ); + return 0; +} + +static int l_rand_bytes( lua_State *L ) /** rand_bytes( number bytes ) */ +{ + size_t len = luaL_checkint( L, 1 ); + unsigned char * result = (unsigned char *) malloc( len ); + if (!result) return luaL_error( L, "Couldn't allocate memory."); + + RAND_bytes( result, len ); + lua_pushlstring( L, (char *) result, len ); + free( result ); + return 1; +} + +static int l_rand_pseudo_bytes( lua_State *L ) /** rand_pseudo_bytes( number bytes ) */ +{ + size_t len = luaL_checkint( L, 1 ); + unsigned char * result = (unsigned char *) malloc( len ); + if (!result) return luaL_error( L, "Couldn't allocate memory."); + + RAND_pseudo_bytes( result, len ); + lua_pushlstring( L, (char *) result, len ); + free( result ); + return 1; +} + +static int l_md2(lua_State *L) /** md2(string s) */ +{ + size_t len; + const unsigned char *s = (unsigned char *) luaL_checklstring( L, 1, &len ); + unsigned char digest[16]; + + lua_pushlstring( L, (char *) MD2( s, len, digest ), 16 ); + return 1; +} + +static int l_md4(lua_State *L) /** md4(string s) */ +{ + size_t len; + const unsigned char *s = (unsigned char *) luaL_checklstring( L, 1, &len ); + unsigned char digest[16]; + + lua_pushlstring( L, (char *) MD4( s, len, digest ), 16 ); + return 1; +} + +static int l_md5(lua_State *L) /** md5(string s) */ +{ + size_t len; + const unsigned char *s = (unsigned char *) luaL_checklstring( L, 1, &len ); + unsigned char digest[16]; + + lua_pushlstring( L, (char *) MD5( s, len, digest ), 16 ); + return 1; +} + +static int l_sha1(lua_State *L) /** sha1(string s) */ +{ + size_t len; + const unsigned char *s = (unsigned char *) luaL_checklstring( L, 1, &len ); + unsigned char digest[20]; + + lua_pushlstring( L, (char *) SHA1( s, len, digest ), 20 ); + return 1; +} + +static int l_ripemd160(lua_State *L) /** ripemd160(string s) */ +{ + size_t len; + const unsigned char *s = (unsigned char *) luaL_checklstring( L, 1, &len ); + unsigned char digest[20]; + + lua_pushlstring( L, (char *) RIPEMD160( s, len, digest ), 20 ); + return 1; +} + +static int l_digest(lua_State *L) /** digest(string algorithm, string message) */ +{ + size_t msg_len; + unsigned int digest_len; + const char *algorithm = luaL_checkstring( L, 1 ); + const unsigned char *msg = (unsigned char *) luaL_checklstring( L, 2, &msg_len ); + unsigned char digest[EVP_MAX_MD_SIZE]; + const EVP_MD * evp_md; + EVP_MD_CTX mdctx; + + evp_md = EVP_get_digestbyname( algorithm ); + + if (!evp_md) return luaL_error( L, "Unknown digest algorithm: %s", algorithm ); + + EVP_MD_CTX_init(&mdctx); + if (!( + EVP_DigestInit_ex( &mdctx, evp_md, NULL ) && + EVP_DigestUpdate( &mdctx, msg, msg_len ) && + EVP_DigestFinal_ex( &mdctx, digest, &digest_len ))) { + EVP_MD_CTX_cleanup( &mdctx ); + return luaL_error( L, "OpenSSL error" ); + } + EVP_MD_CTX_cleanup( &mdctx ); + + lua_pushlstring( L, (char *) digest, digest_len ); + return 1; +} + +static int l_hmac(lua_State *L) /** hmac(string algorithm, string key, string message) */ +{ + size_t key_len, msg_len; + unsigned int digest_len; + const char *algorithm = luaL_checkstring( L, 1 ); + const unsigned char *key = (unsigned char *) luaL_checklstring( L, 2, &key_len ); + const unsigned char *msg = (unsigned char *) luaL_checklstring( L, 3, &msg_len ); + unsigned char digest[EVP_MAX_MD_SIZE]; + const EVP_MD * evp_md; + evp_md = EVP_get_digestbyname( algorithm ); + + if (!evp_md) return luaL_error( L, "Unknown digest algorithm: %s", algorithm ); + + HMAC( evp_md, key, key_len, msg, msg_len, digest, &digest_len ); + + lua_pushlstring( L, (char *) digest, digest_len ); + return 1; +} + +struct enumerator_data { + lua_State * L; + int index; +}; + +static void enumerate_algorithms( const OBJ_NAME * name, void * arg ) +{ + struct enumerator_data* data = (struct enumerator_data *) arg; + lua_pushstring( data->L, name->name ); + lua_rawseti( data->L, -2, data->index ); + data->index++; +} + +static int l_supported_digests(lua_State *L) /** supported_digests() */ +{ + enumerator_data data; + data.L = L; + data.index = 1; + + lua_newtable( L ); + OBJ_NAME_do_all_sorted( OBJ_NAME_TYPE_MD_METH,enumerate_algorithms, &data ); + + return 1; +} + +static int l_supported_ciphers(lua_State *L) /** supported_ciphers() */ +{ + enumerator_data data; + data.L = L; + data.index = 1; + + lua_newtable( L ); + OBJ_NAME_do_all_sorted( OBJ_NAME_TYPE_CIPHER_METH,enumerate_algorithms, &data ); + + return 1; +} + +static int l_encrypt(lua_State *L) /** encrypt( string algorithm, string key, string iv, string data, bool padding = false ) */ +{ + const char *algorithm = luaL_checkstring( L, 1 ); + const EVP_CIPHER * evp_cipher = EVP_get_cipherbyname( algorithm ); + if (!evp_cipher) return luaL_error( L, "Unknown cipher algorithm: %s", algorithm ); + + size_t data_len; + const unsigned char *key = (unsigned char *) luaL_checkstring( L, 2 ); + const unsigned char *iv = (unsigned char *) luaL_optstring( L, 3, "" ); + const unsigned char *data = (unsigned char *) luaL_checklstring( L, 4, &data_len ); + int padding = lua_toboolean( L, 5 ); + + EVP_CIPHER_CTX cipher_ctx; + EVP_CIPHER_CTX_init( &cipher_ctx ); + + int out_len, final_len; + unsigned char * out = (unsigned char *) malloc( data_len + EVP_MAX_BLOCK_LENGTH ); + if (!out) return luaL_error( L, "Couldn't allocate memory."); + + if (!( + EVP_EncryptInit_ex( &cipher_ctx, evp_cipher, NULL, key, *iv ? iv : NULL ) && + EVP_CIPHER_CTX_set_padding( &cipher_ctx, padding ) && + EVP_EncryptUpdate( &cipher_ctx, out, &out_len, data, data_len ) && + EVP_EncryptFinal_ex( &cipher_ctx, out + out_len, &final_len ) )) { + EVP_CIPHER_CTX_cleanup( &cipher_ctx ); + free( out ); + return luaL_error( L, "OpenSSL error" ); + } + + lua_pushlstring( L, (char *) out, out_len + final_len ); + + EVP_CIPHER_CTX_cleanup( &cipher_ctx ); + free( out ); + + return 1; +} + +static int l_decrypt(lua_State *L) /** decrypt( string algorithm, string key, string iv, string data, bool padding = false ) */ +{ + const char *algorithm = luaL_checkstring( L, 1 ); + const EVP_CIPHER * evp_cipher = EVP_get_cipherbyname( algorithm ); + if (!evp_cipher) return luaL_error( L, "Unknown cipher algorithm: %s", algorithm ); + + size_t data_len; + const unsigned char *key = (unsigned char *) luaL_checkstring( L, 2 ); + const unsigned char *iv = (unsigned char *) luaL_optstring( L, 3, "" ); + const unsigned char *data = (unsigned char *) luaL_checklstring( L, 4, &data_len ); + int padding = lua_toboolean( L, 5 ); + + EVP_CIPHER_CTX cipher_ctx; + EVP_CIPHER_CTX_init( &cipher_ctx ); + + int out_len, final_len; + unsigned char * out = (unsigned char *) malloc( data_len ); + if (!out) return luaL_error( L, "Couldn't allocate memory."); + + if (!( + EVP_DecryptInit_ex( &cipher_ctx, evp_cipher, NULL, key, *iv ? iv : NULL ) && + EVP_CIPHER_CTX_set_padding( &cipher_ctx, padding ) && + EVP_DecryptUpdate( &cipher_ctx, out, &out_len, data, data_len ) && + EVP_DecryptFinal_ex( &cipher_ctx, out + out_len, &final_len ) )) { + EVP_CIPHER_CTX_cleanup( &cipher_ctx ); + free( out ); + return luaL_error( L, "OpenSSL error" ); + } + + lua_pushlstring( L, (char *) out, out_len + final_len ); + + EVP_CIPHER_CTX_cleanup( &cipher_ctx ); + free( out ); + + return 1; +} + +static int l_DES_string_to_key(lua_State *L) /** DES_string_to_key( string data ) */ +{ + size_t len; + const unsigned char *data = (unsigned char *) luaL_checklstring( L, 1, &len ); + if ( len != 7 ) + return luaL_error( L, "String must have length of 7 bytes." ); + + DES_cblock key; + key[0] = data[0]; + for( int i = 1; i < 8; i++ ) + key[i] = data[i-1] << (8-i) | data[i] >> i; + + DES_set_odd_parity( &key ); + + lua_pushlstring( L, (char *) key, 8 ); + return 1; +} + + +static const struct luaL_reg bignum_methods[] = { + { "num_bits", l_bignum_num_bits }, + { "num_bytes", l_bignum_num_bytes }, + { "tobin", l_bignum_bn2bin }, + { "todec", l_bignum_bn2dec }, + { "tohex", l_bignum_bn2hex }, + { "is_bit_set", l_bignum_is_bit_set }, + { "set_bit", l_bignum_set_bit }, + { "clear_bit", l_bignum_clear_bit }, + { "is_bit_set", l_bignum_is_bit_set }, + { "set_negative", l_bignum_set_negative }, + { "is_negative", l_bignum_is_negative }, + { "__gc", l_bignum_free }, + { NULL, NULL } +}; + +static const struct luaL_reg openssllib[] = { + { "bignum_num_bits", l_bignum_num_bits }, + { "bignum_num_bytes", l_bignum_num_bytes }, + { "bignum_set_bit", l_bignum_set_bit }, + { "bignum_clear_bit", l_bignum_clear_bit }, + { "bignum_is_bit_set", l_bignum_is_bit_set }, + { "bignum_set_negative", l_bignum_set_negative }, + { "bignum_is_negative", l_bignum_is_negative }, + { "bignum_bin2bn", l_bignum_bin2bn }, + { "bignum_dec2bn", l_bignum_dec2bn }, + { "bignum_hex2bn", l_bignum_hex2bn }, + { "bignum_rand", l_bignum_rand }, + { "bignum_pseudo_rand", l_bignum_pseudo_rand }, + { "bignum_bn2bin", l_bignum_bn2bin }, + { "bignum_bn2dec", l_bignum_bn2dec }, + { "bignum_bn2hex", l_bignum_bn2hex }, + { "bignum_mod_exp", l_bignum_mod_exp }, + { "rand_bytes", l_rand_bytes }, + { "rand_pseudo_bytes", l_rand_pseudo_bytes }, + { "md2", l_md2 }, + { "md4", l_md4 }, + { "md5", l_md5 }, + { "sha1", l_sha1 }, + { "ripemd160", l_ripemd160 }, + { "digest", l_digest }, + { "hmac", l_hmac }, + { "encrypt", l_encrypt }, + { "decrypt", l_decrypt }, + { "DES_string_to_key", l_DES_string_to_key }, + { "supported_digests", l_supported_digests }, + { "supported_ciphers", l_supported_ciphers }, + { NULL, NULL } +}; + +LUALIB_API int luaopen_openssl(lua_State *L) { + + OpenSSL_add_all_algorithms(); + + luaL_register(L, OPENSSLLIBNAME, openssllib); + + // create metatable for bignum + luaL_newmetatable( L, "BIGNUM" ); + // metatable.__index = metatable + lua_pushvalue( L, -1 ); + lua_setfield( L, -2, "__index" ); + // register methods + luaL_register( L, NULL, bignum_methods ); + + lua_pop( L, 1 ); // BIGNUM + + return 1; +} + diff --git a/nse_openssl.h b/nse_openssl.h new file mode 100644 index 000000000..834ded1fc --- /dev/null +++ b/nse_openssl.h @@ -0,0 +1,19 @@ +#include "../nmap_config.h" + +#if HAVE_OPENSSL + +#ifndef OPENSSLLIB +#define OPENSSLLIB + +#define OPENSSLLIBNAME "openssl" + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +} + +LUALIB_API int luaopen_openssl(lua_State *L); + +#endif + +#endif diff --git a/nselib/pop3.lua b/nselib/pop3.lua index 413381cd8..1ce8c1106 100644 --- a/nselib/pop3.lua +++ b/nselib/pop3.lua @@ -3,15 +3,24 @@ module(... or "pop3",package.seeall) +local HAVE_SSL = false + require 'base64' require 'bit' +require 'stdnse' + +if pcall(require,'openssl') then + HAVE_SSL = true +end + err = { none = 0, userError = 1, pwError = 2, - informationMissing = 3 + informationMissing = 3, + OpenSSLMissing = 4, } --- @@ -112,8 +121,8 @@ end function login_apop(socket, user, pw, challenge) if type(challenge) ~= "string" then return false, err.informationMissing end - local apStr = hash.md5(challenge .. pw) - socket:send("APOP " .. user .. " " .. apStr .. "\r\n") + local apStr = stdnse.tohex(openssl.md5(challenge .. pw)) + socket:send(("APOP %s %s\r\n"):format(user, apStr)) status, line = socket:receive_lines(1) @@ -168,33 +177,6 @@ function capabilities(host, port) return capas end ---- --- Calculate HMAC-MD5 hash ---@param key Key for hash calculation ---@param msg Message to be hashed ---@return HMAC-MD5 of given message -function hmacMD5(key, msg) - local ipad = {} - local opad = {} - - if (string.len(key) > 64) then - key = hash.md5binary(key) - end - - -- create both pads, XORing with key - for i = 1, string.len(key) do - ipad[i] = string.char(bit.bxor(0x36, string.byte(string.sub(key, i)))) - opad[i] = string.char(bit.bxor(0x5c, string.byte(string.sub(key, i)))) - end - for i = #ipad + 1, 64 do - ipad[i] = string.char(0x36) - opad[i] = string.char(0x5c) - end - - -- calc HMAC-md5 - return hash.md5(table.concat(opad) .. hash.md5bin(table.concat(ipad) .. msg)) -end - --- -- Try to login using AUTH command using SASL/CRAM-MD5 method --@param socket Socket connected to POP3 server @@ -209,7 +191,7 @@ function login_sasl_crammd5(socket, user, pw) local challenge = base64.dec(string.sub(line, 3)) - local digest = hmacMD5(pw, challenge) + local digest = stdnse.tohex(openssl.hmac('md5', pw, challenge)) local authStr = base64.enc(user .. " " .. digest) socket:send(authStr .. "\r\n") @@ -221,3 +203,15 @@ function login_sasl_crammd5(socket, user, pw) return false, err.pwError end end + +--- overwrite functions requiring OpenSSL if we got no OpenSSL +if not HAVE_SSL then + + local no_ssl = function() + return false, err.OpenSSLMissing + end + + login_apop = no_ssl + login_sasl_crammd5 = no_ssl +end + diff --git a/nselib/ssh1.lua b/nselib/ssh1.lua new file mode 100644 index 000000000..9cbbd9182 --- /dev/null +++ b/nselib/ssh1.lua @@ -0,0 +1,152 @@ + +-- @author = Sven Klemm +-- @copyright See nmaps COPYING for licence + +module(... or "ssh1",package.seeall) + +local bin = require "bin" +local bit = require "bit" +local math = require "math" +local stdnse = require "stdnse" +local openssl = require "openssl" + +--- fetch SSH1 host key +--@param host nmap host table +--@param port nmap port table +fetch_host_key = function(host, port) + local socket = nmap.new_socket() + local catch = function() socket:close() end + local try = nmap.new_try(catch) + + try(socket:connect(host.ip, port.number)) + -- fetch banner + try(socket:receive_lines(1)) + -- send our banner + try(socket:send("SSH-1.5-Nmap-SSH1-Hostkey\r\n")) + + local data, packet_length, padding, offset + data = try(socket:receive()) + socket:close() + offset, packet_length = bin.unpack( ">i", data ) + padding = 8 - packet_length % 8 + offset = offset + padding + + if padding + packet_length + 4 == data:len() then + -- seems to be a proper SSH1 packet + local msg_code,host_key_bits,exp,mod,length,fp_input + offset, msg_code = bin.unpack( ">c", data, offset ) + if msg_code == 2 then -- 2 => SSH_SMSG_PUBLIC_KEY + -- ignore cookie and server key bits + offset, _, _ = bin.unpack( ">A8i", data, offset ) + -- skip server key exponent and modulus + offset, length = bin.unpack( ">S", data, offset ) + offset = offset + math.ceil( length / 8 ) + offset, length = bin.unpack( ">S", data, offset ) + offset = offset + math.ceil( length / 8 ) + + offset, host_key_bits = bin.unpack( ">i", data, offset ) + offset, length = bin.unpack( ">S", data, offset ) + offset, exp = bin.unpack( ">A" .. math.ceil( length / 8 ), data, offset ) + exp = openssl.bignum_bin2bn( exp ) + offset, length = bin.unpack( ">S", data, offset ) + offset, mod = bin.unpack( ">A" .. math.ceil( length / 8 ), data, offset ) + mod = openssl.bignum_bin2bn( mod ) + + fp_input = mod:tobin()..exp:tobin() + + return {exp=exp,mod=mod,bits=host_key_bits,key_type='rsa1',fp_input=fp_input, + full_key=exp:todec()..' '..mod:todec(),algorithm="RSA1", + fingerprint=openssl.md5(fp_input)} + end + end +end + +--- format key as hexadecimal fingerprint +fingerprint_hex = function( fingerprint, algorithm, bits ) + fingerprint = stdnse.tohex(fingerprint,{separator=":",group=2}) + return ("%d %s (%s)"):format( bits, fingerprint, algorithm ) +end + +--- format key as bubblebabble fingerprint +fingerprint_bubblebabble = function( fingerprint, algorithm, bits ) + local vowels = {'a','e','i','o','u','y'} + local consonants = {'b','c','d','f','g','h','k','l','m','n','p','r','s','t','v','z','x'} + local s = "x" + local seed = 1 + + for i=1,#fingerprint+2,2 do + local in1,in2,idx1,idx2,idx3,idx4,idx5 + if i < #fingerprint or #fingerprint / 2 % 2 ~= 0 then + in1 = fingerprint:byte(i) + idx1 = (bit.band(bit.rshift(in1,6),3) + seed) % 6 + 1 + idx2 = bit.band(bit.rshift(in1,2),15) + 1 + idx3 = (bit.band(in1,3) + math.floor(seed/6)) % 6 + 1 + s = s .. vowels[idx1] .. consonants[idx2] .. vowels[idx3] + if i < #fingerprint then + in2 = fingerprint:byte(i+1) + idx4 = bit.band(bit.rshift(in2,4),15) + 1 + idx5 = bit.band(in2,15) + 1 + s = s .. consonants[idx4] .. '-' .. consonants[idx5] + seed = (seed * 5 + in1 * 7 + in2) % 36 + end + else + idx1 = seed % 6 + 1 + idx2 = 16 + 1 + idx3 = math.floor(seed/6) + 1 + s = s .. vowels[idx1] .. consonants[idx2] .. vowels[idx3] + end + end + s = s .. 'x' + return ("%d %s (%s)"):format( bits, s, algorithm ) +end + +--- format key as visual fingerprint +-- ported from http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr.bin/ssh/key.c +fingerprint_visual = function( fingerprint, algorithm, bits ) + local i,j,field,characters,input,fieldsize_x,fieldsize_y,s + fieldsize_x, fieldsize_y = 17, 9 + characters = {' ','.','o','+','=','*','B','O','X','@','%','&','#','/','^','S','E'} + + -- initialize drawing area + field = {} + for i=1,fieldsize_x do + field[i]={} + for j=1,fieldsize_y do field[i][j]=1 end + end + + -- we start in the center and mark it + x, y = math.ceil(fieldsize_x/2), math.ceil(fieldsize_y/2) + field[x][y] = #characters - 1; + + -- iterate over fingerprint + for i=1,#fingerprint do + input = fingerprint:byte(i) + -- each byte conveys four 2-bit move commands + for j=1,4 do + if bit.band( input, 1) == 1 then x = x + 1 else x = x - 1 end + if bit.band( input, 2) == 2 then y = y + 1 else y = y - 1 end + + x = math.max(x,1); x = math.min(x,fieldsize_x) + y = math.max(y,1); y = math.min(y,fieldsize_y) + + if field[x][y] < #characters - 2 then + field[x][y] = field[x][y] + 1 + end + input = bit.rshift( input, 2 ) + end + end + + -- mark end point + field[x][y] = #characters; + + -- build output + s = ('\n+--[%4s %4d]----+\n'):format( algorithm, bits ) + for i=1,fieldsize_y do + s = s .. '|' + for j=1,fieldsize_x do s = s .. characters[ field[j][i] ] end + s = s .. '|\n' + end + s = s .. '+-----------------+\n' + return s +end + diff --git a/nselib/ssh2.lua b/nselib/ssh2.lua new file mode 100644 index 000000000..7c54c11c2 --- /dev/null +++ b/nselib/ssh2.lua @@ -0,0 +1,187 @@ + +-- @author = Sven Klemm +-- @copyright See nmaps COPYING for licence + +module(... or "ssh2",package.seeall) + +require "bin" +require "base64" +require "openssl" +require "stdnse" + +-- table holding transport layer functions +transport = {} + +-- table of SSH2 constants +local SSH2 + +--- pack multiprecision integer for sending +--@param bn openssl bignum +--@return packed multiprecision integer +transport.pack_mpint = function( bn ) + local bytes, packed + bytes = bn:num_bytes() + packed = bn:tobin() + if bytes % 8 == 0 then + bytes = bytes + 1 + packed = string.char(0) .. packed + end + return bin.pack( ">IA", bytes, packed ) +end + +--- build a ssh2 packet +--@param payload payload of the packet +--@return packet to send on the wire +transport.build = function( payload ) + local packet_length, padding_length + padding_length = 8 - ( (payload:len() + 1 + 4 ) % 8 ) + packet_length = payload:len() + padding_length + 1 + return bin.pack( ">IcAA", packet_length, padding_length, payload, openssl.rand_pseudo_bytes( padding_length ) ) +end + +--- extract the payload from a received SSH2 packet +--@param received SSH2 packet +--@return payload of the SSH2 packet +transport.payload = function( packet ) + local packet_length, padding_length, payload_length, payload, offset + offset, packet_length, padding_length = bin.unpack( ">Ic", packet ) + payload_length = packet_length - padding_length - 1 + offset, payload = bin.unpack( ">A" .. payload_length, packet, offset ) + return payload +end + +--- build kexdh_init packet +transport.kexdh_init = function( e ) + return bin.pack( ">cA", SSH2.SSH_MSG_KEXDH_INIT, transport.pack_mpint( e ) ) +end + +--- build kex_init packet +transport.kex_init = function( cookie, options ) + options = options or {} + kex_algorithms = "diffie-hellman-group1-sha1" + host_key_algorithms = options['host_key_algorithms'] or "ssh-dss,ssh-rsa" + encryption_algorithms = "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr" + mac_algorithms = "hmac-md5,hmac-sha1,hmac-ripemd160" + compression_algorithms = "none" + languages = "" + + local payload = bin.pack( ">cAaa", SSH2.SSH_MSG_KEXINIT, cookie, kex_algorithms, host_key_algorithms ) + payload = payload .. bin.pack( ">aa", encryption_algorithms, encryption_algorithms ) + payload = payload .. bin.pack( ">aa", mac_algorithms, mac_algorithms ) + payload = payload .. bin.pack( ">aa", compression_algorithms, compression_algorithms ) + payload = payload .. bin.pack( ">aa", languages, languages ) + payload = payload .. bin.pack( ">cI", 0, 0 ) + + return payload +end + +--- parse kexinit package +-- returns an empty table in case of an error +transport.parse_kex_init = function( payload ) + local _, offset, msg_code, parsed, fields, fieldname + parsed = {} + + -- check for proper msg code + offset, msg_code = bin.unpack( ">c", payload ) + if msg_code ~= SSH2.SSH_MSG_KEXINIT then return {} end + + offset, parsed.cookie = bin.unpack( ">A16", payload, offset ) + + fields = {'kex_algorithms','server_host_key_algorithms', + 'encryption_algorithms_client_to_server','encryption_algorithms_server_to_client', + 'mac_algorithms_client_to_server','mac_algorithms_server_to_client', + 'compression_algorithms_client_to_server','compression_algorithms_server_to_client', + 'languages_client_to_server','languages_server_to_client'} + for _, fieldname in pairs( fields ) do + offset, parsed[fieldname] = bin.unpack( ">a", payload, offset ) + end + + return parsed +end + + +--- fetch SSH2 host key +--@param host nmap host table +--@param port nmap port table +--@param key_type key type to fetch +--@return table containing the key and fingerprint +fetch_host_key = function( host, port, key_type ) + local socket = nmap.new_socket() + local catch = function() socket:close() end + local try = nmap.new_try(catch) + -- oakley group 2 prime taken from rfc 2409 + local prime = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF" + + try(socket:connect(host.ip, port.number)) + -- fetch banner + try(socket:receive_lines(1)) + -- send our banner + try(socket:send("SSH-2.0-Nmap-SSH2-Hostkey\r\n")) + + local cookie = openssl.rand_bytes( 16 ) + local packet = transport.build( transport.kex_init( cookie, {host_key_algorithms=key_type} ) ) + try(socket:send( packet )) + + local kex_init = try(socket:receive_bytes(1)) + kex_init = transport.parse_kex_init( transport.payload( kex_init ) ) + + if not tostring(kex_init.server_host_key_algorithms):find( key_type, 1, true ) then + -- server does not support host key type + stdnse.print_debug( 2, "Hostkey type '%s' not supported by server.", key_type ) + return + end + + local e, g, x, p + -- e = g^x mod p + g = openssl.bignum_dec2bn( "2" ) + p = openssl.bignum_hex2bn( prime ) + x = openssl.bignum_pseudo_rand( 1024 ) + e = openssl.bignum_mod_exp( g, x, p ) + + packet = transport.build( transport.kexdh_init( e ) ) + try(socket:send( packet )) + + kexdh_reply = try(socket:receive_bytes(1)) + kexdh_reply = transport.payload( kexdh_reply ) + -- check for proper msg code + if kexdh_reply:byte(1) ~= SSH2.SSH_MSG_KEXDH_REPLY then + return + end + + local _,public_host_key,bits,algorithm + _, _, public_host_key = bin.unpack( ">ca", kexdh_reply ) + + if key_type == 'ssh-dss' then + algorithm = "DSA" + local p + _, _, p = bin.unpack( ">aa", public_host_key ) + bits = openssl.bignum_bin2bn( p ):num_bits() + elseif key_type == 'ssh-rsa' then + algorithm = "RSA" + local n + _, _, _, n = bin.unpack( ">aaa", public_host_key ) + bits = openssl.bignum_bin2bn( n ):num_bits() + else + stdnse.print_debug( "Unsupported key type: %s", key_type ) + end + + return { key=public_host_key, key_type=key_type, fp_input=public_host_key, bits=bits, + full_key=('%s %s'):format(key_type,base64.enc(public_host_key)), + algorithm=algorithm, fingerprint=openssl.md5(public_host_key) } +end + +-- constants + +SSH2 = { + SSH_MSG_DISCONNECT = 1, + SSH_MSG_IGNORE = 2, + SSH_MSG_UNIMPLEMENTED = 3, + SSH_MSG_DEBUG = 4, + SSH_MSG_SERVICE_REQUEST = 5, + SSH_MSG_SERVICE_ACCEPT = 6, + SSH_MSG_KEXINIT = 20, + SSH_MSG_NEWKEYS = 21, + SSH_MSG_KEXDH_INIT = 30, + SSH_MSG_KEXDH_REPLY = 31, +} + diff --git a/scripts/SSH-hostkey.nse b/scripts/SSH-hostkey.nse new file mode 100644 index 000000000..75b20e9ba --- /dev/null +++ b/scripts/SSH-hostkey.nse @@ -0,0 +1,103 @@ +--- Shows SSH Hostkeys +-- +-- Shows fingerprint or fingerprint and key depending on verbosity level. +-- Puts the found hostkeys in nmap.registry for other scripts to use them. +-- You can control the output with the ssh_hostkey script argument. Possible +-- values are bubble,visual,full and all. +-- +--@usage +-- nmap host --script SSH-hostkey --script-args ssh_hostkey=full +-- nmap host --script SSH-hostkey --script-args ssh_hostkey=all +-- nmap host --script SSH-hostkey --script-args ssh_hostkey='visual bubble' +-- +--@output +-- 22/tcp open ssh +-- | SSH Hostkey: 2048 f0:58:ce:f4:aa:a4:59:1c:8e:dd:4d:07:44:c8:25:11 (RSA) +-- 22/tcp open ssh +-- | SSH Hostkey: 2048 f0:58:ce:f4:aa:a4:59:1c:8e:dd:4d:07:44:c8:25:11 (RSA) +-- | +--[ RSA 2048]----+ +-- | | .E*+ | +-- | | oo | +-- | | . o . | +-- | | O . . | +-- | | o S o . | +-- | | = o + . | +-- | | . * o . | +-- | | = . | +-- | | o . | +-- |_ +-----------------+ +-- 22/tcp open ssh +-- | SSH Hostkey: 2048 xuvah-degyp-nabus-zegah-hebur-nopig-bubig-difeg-hisym-rumef-cuxex (RSA) +-- |_ ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAwVuv2gcr0maaKQ69VVIEv2ob4OxnuI64fkeOnCXD1lUx5tTA+vefXUWEMxgMuA7iX4irJHy2zer0NQ3Z3yJvr5scPgTYIaEOp5Uo/eGFG9Agpk5wE8CoF0e47iCAPHqzlmP2V7aNURLMODb3jVZuI07A2ZRrMGrD8d888E2ORVORv1rYeTYCqcMMoVFmX9l3gWEdk4yx3w5sD8v501Iuyd1v19mPfyhrI5E1E1nl/Xjp5N0/xP2GUBrdkDMxKaxqTPMie/f0dXBUPQQN697a5q+5lBRPhKYOtn6yQKCd9s1Q22nxn72Jmi1RzbMyYJ52FosDT755Qmb46GLrDMaZMQ== + +id = "SSH Hostkey" +author = "Sven Klemm " +description = "Show SSH Hostkeys" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"safe","default","intrusive"} + +require("shortport") +require("stdnse") + +-- openssl is required for this script +if pcall(require,"openssl") then + require("ssh1") + require("ssh2") +else + action = function() + stdnse.print_debug( 3, "Skipping %s script because OpenSSL is missing.", id ) + end +end + +portrule = shortport.port_or_service(22, "ssh") + + +--- put hostkey in the nmap registry for usage by other scripts +--@param host nmap host table +--@param key host key table +local add_key_to_registry = function( host, key ) + nmap.registry[id] = nmap.registry[id] or {} + nmap.registry[id][host.ip] = nmap.registry[id][host.ip] or {} + table.insert( nmap.registry[id][host.ip], key ) +end + +action = action or function(host, port) + local output = {} + local keys = {} + local _,key + local format = nmap.registry.args.ssh_hostkey or "hex" + local all_formats = format:find( 'all', 1, true ) + + key = ssh1.fetch_host_key( host, port ) + if key then table.insert( keys, key ) end + + key = ssh2.fetch_host_key( host, port, "ssh-dss" ) + if key then table.insert( keys, key ) end + + key = ssh2.fetch_host_key( host, port, "ssh-rsa" ) + if key then table.insert( keys, key ) end + + for _, key in ipairs( keys ) do + add_key_to_registry( host, key ) + if format:find( 'hex', 1, true ) or all_formats then + table.insert( output, ssh1.fingerprint_hex( key.fingerprint, key.algorithm, key.bits ) ) + end + if format:find( 'bubble', 1, true ) or all_formats then + table.insert( output, ssh1.fingerprint_bubblebabble( openssl.sha1(key.fp_input), key.algorithm, key.bits ) ) + end + if format:find( 'visual', 1, true ) or all_formats then + -- insert empty line so table is not destroyed if this is the first + -- line of output + if #output == 0 then table.insert( output, " " ) end + table.insert( output, ssh1.fingerprint_visual( key.fingerprint, key.algorithm, key.bits ) ) + end + if nmap.verbosity() > 1 or format:find( 'full', 1, true ) or all_formats then + table.insert( output, key.full_key ) + end + end + + if #output > 0 then + return table.concat( output, '\n' ) + end +end +