ssh client to show server-supported algorithms - go

In order to check that all the servers across a fleet aren't supporting deprecated algorithms, I'm (programmatically) doing this:
telnet localhost 22
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
SSH-2.0-OpenSSH_8.0p1 Ubuntu-6build1
SSH-2.0-Censor-SSH2
4&m����&F �V��curve25519-sha256,curve25519-sha256#libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1Arsa-sha2-512,rsa-sha2-256,ssh-rsa,ecdsa-sha2-nistp256,ssh-ed25519lchacha20-poly1305#openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm#openssh.com,aes256-gcm#openssh.comlchacha20-poly1305#openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm#openssh.com,aes256-gcm#openssh.com�umac-64-etm#openssh.com,umac-128-etm#openssh.com,hmac-sha2-256-etm#openssh.com,hmac-sha2-512-etm#openssh.com,hmac-sha1-etm#openssh.com,umac-64#openssh.com,umac-128#openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1�umac-64-etm#openssh.com,umac-128-etm#openssh.com,hmac-sha2-256-etm#openssh.com,hmac-sha2-512-etm#openssh.com,hmac-sha1-etm#openssh.com,umac-64#openssh.com,umac-128#openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1none,zlib#openssh.comnone,zlib#openssh.comSSH-2.0-Censor-SSH2
Connection closed by foreign host.
Which is supposed to be a list of supported algorithms for the various phases of setting up a connection. (kex, host key, etc). Every time I run, I get a different piece of odd data at the start - always a different length.
There's an nmap plugin - ssh2-enum-algos - which returns the data in it's complete form, but I don't want to run nmap; I have a go program which opens the port, and sends the query, but it gets the same as telnet. What am I missing, and how do I fix it?
For comparison, here's the top few lines from the output of nmap script:
$ nmap --script ssh2-enum-algos super
Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-27 22:15 GMT
Nmap scan report for super (192.168.50.1)
Host is up (0.0051s latency).
rDNS record for 192.168.50.1: supermaster
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
| ssh2-enum-algos:
| kex_algorithms: (12)
| curve25519-sha256
| curve25519-sha256#libssh.org
| ecdh-sha2-nistp256
| ecdh-sha2-nistp384
| ecdh-sha2-nistp521

Opening a tcp connection to port 22, (in golang, with net.Dial) then accepting and sending connection strings leaves us able to Read() from the Reader for the connection. Thence the data is in a standard format described by the RFC. From this, I can list the algorithms supported in each phase of an ssh connection. This is very useful for measuring what is being offered, rather than what the appears to be configured (it's easy to configure sshd to use a different config file).
It's a useful thing to be able to do from a security POV.
Tested on every version of ssh I can find from 1.x on a very old solaris or AIX box, to RHEL 8.1.

In some cases you can specify an algorithm to use, and if you specify one that is not supported the server will reply with a list of supported algorithms.
For example, to check for supported key exchange algorithms you can use:
ssh 127.0.0.1 -oKexAlgorithms=diffie-hellman-group1-sha1
diffie-hellman-group1-sha1 is insecure and should be missing from most modern servers. The server will probably respond with something like:
Unable to negotiate with 127.0.0.1 port 22: no matching key exchange method found. Their offer: curve25519-sha256,curve25519-sha256#libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256
Exit 255
Typing: "ssh -Q cipher | cipher-auth | mac | kex | key"
will give you a list of the algorithms supported by your client
Typing: "man ssh"
will let you see what options you can specify with the -o argument, including Cipher, MACs, and KexAlgorithms

Related

How can I analyze multiple blobs (multi-lines) of text for patterns in a single file?

I have run an nmap scan using the --script ssl-enum-ciphers -p443 192.168.0.0/24 options against multiple IP addresses. I have also run an extremely similar scan using the --script ssh2-enum-algos -p22 options, that produces output in the same format.
I want to quickly analyze this data and zero in on specific matches of specific ciphers or algorithms. The overarching goal is to run an ad-hoc internal vulnerability assessment without access to fancy tools such as Nessus or Rapid7 InsightVM.
While nmap supports the -oX option to output to XML, I have found that neither Microsoft Word, Excel, or a web browser know how to open the file. The Microsoft products keep producing an error that the xml format is incorrect.
So then I tried the nmap-parse-output code on Github. While it will easily group IP addresses by ports, it doesn't appear to be able to take that a step further and analyze the ciphers or algorithms for me.
So now I'm trying to figure out a way to manually parse these blobs of data.
A typical result might contain data for multiple IP addresses in the following format (the following example stdout is edited for brevity):
Nmap scan report for 192.168.1.1
Host is up (0.00064s latency).
PORT STATE SERVICE VERSION
443/tcp open ssl/http lighttpd
| ssl-enum-ciphers:
| TLSv1.2:
| ciphers:
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
| TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
| compressors:
| NULL
| cipher preference: server
|_ least strength: A
Nmap scan report for 192.168.1.2
Host is up (0.00048s latency).
PORT STATE SERVICE VERSION
443/tcp open ssl/http nginx (reverse proxy)
| ssl-enum-ciphers:
| TLSv1.2:
| ciphers:
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
| compressors:
| NULL
| cipher preference: server
|_ least strength: A
Nmap scan report for 192.168.1.3
....
Nmap scan report for 192.168.1.4
....
How can write a script, using standard applications available to bash, to loop through each blob of text? We don't know the length of each blob, so I need to match on the string "Nmap scan report" or something similar, and extract the data that is in between each of those matches.
Something like this would get me started, but it isn't complete, and doesn't actually separate each blob individually:
for i in $(cat scan-results | grep "Nmap scan report for"); do more data analysis here; done
For example, I might want to search for any IP address that support RC4 ciphers on port 443, so in the "do more data analysis", I would like to run: grep -i rc4
Or in the case of ssh algorithms, I want to ensure all cbc algorithms are disabled, so I could run: grep -i cbc
The resulting goal would be to list anything that matches so that I can quickly attribute the match to the specific IP address. I don't care how the results look, I just care about finding the results quickly.
Any help would be appreciated!
Without more sample data, and going solely on the limited examples, and keeping in mind that output format isn't of importance ...
NOTE: My sample data file - nmap.dat - is a cut-n-paste copy of the sample nmap data provided by the OP.
I'm thinking a multi-pattern grep may suffice, eg:
# search for any IP address that support RC4 ciphers on port 443
$ grep -i "Nmap scan report for|443|RC4" nmap.dat
Nmap scan report for 192.168.1.1
443/tcp open ssl/http lighttpd
Nmap scan report for 192.168.1.2
443/tcp open ssl/http nginx (reverse proxy)
Nmap scan report for 192.168.1.3
Nmap scan report for 192.168.1.4
# want to ensure all cbc algorithms are disabled
$ egrep -i "Nmap scan report for|cbc" nmap.dat
Nmap scan report for 192.168.1.1
Nmap scan report for 192.168.1.2
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
Nmap scan report for 192.168.1.3
Nmap scan report for 192.168.1.4

Hostnames resolution fails with "unknown host" error for hostnames containing utf-8 characters

I am trying to ping a hostname "win-2k12r2-addc.阿伯测阿伯测ad.hai.com" from a linux client.
I see that DNS requests go over the wire with hostname being sent in utf-8 format
and i get a response from the DNS server also with the correct IP address.
But ping fails with the following error :
ping: unknown host win-2k12r2-addc.阿伯测阿伯测ad.hai.com
If i add an entry into /etc/hosts, it works fine
I have the following entries in /etc/hosts when it works.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
127.0.0.1 localhost ava-dev
::1 localhost
10.141.33.93 win-2k12r2-addc.阿伯测阿伯测ad.hai.com
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
The /etc/nsswitch.conf file has the following entries for hosts.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hosts: files dns
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
I somewhat suspect that getaddrInfo() call fails when we try to resolve the address i.e it is not able to handle the DNS responses correctly for hostnames
containing unicode characters.
Has anyone faced this issue before ?
Or has anyone tried resolving a unicode hostname from a linux client ?
The reason i m suspecting getaddrinfo() is because of the following.
Apart from ping, i m trying the following ldap command to the same host and it fails with the below mentioned error
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ldapsearch -d 255 -x -h win-2k12r2-addc.阿伯测阿伯测ad.hai.com
ldap_create
ldap_url_parse_ext(ldap://win-2k12r2-addc.%E9%98%BF%E4%BC%AF%E6%B5%8B%E9%98%BF%E4%BC%AF%E6%B5%8Bad.hai.com)
ldap_sasl_bind
ldap_send_initial_request
ldap_new_connection 1 1 0
ldap_int_open_connection
ldap_connect_to_host: TCP win-2k12r2-addc.阿伯测阿伯测ad.hai.com:389
ldap_connect_to_host: getaddrinfo failed: Name or service not known
ldap_err2string
ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
In both the scenarios (ping / ldap), i see the DNS query request going to the DNS server and the correct response from the DNS server back to the linux client.
The following is the value of the hostname sent in the DNS query
win-2k12r2-addc.\351\230\277\344\274\257\346\265\213\351\230\277\344\274\257\346\265\213ad.hai.com: type A, class IN
It looks like you are trying to use UTF-8 or unicode within the DNS system while the DNS system really doesn't like that. It wants ascii (See RFCs 5890, 5891, 5892, 5893 - but mostly 5891). Escaping the utf-8 characters does not turn them into the required ascii encoding, called punycode (prefixed by "xn--"). You want to use the version of your IDN that has punycode instead of the UTF-8:
ping win-2k12r2-addc.xn--ad-tl3ca3569aba8944eca.hai.com

cant get dnsmasq to push multiple search prefixes

I'm trying to get dnsmasq to push multiple search prefixes to windows machines. If I look in the MS dhcp server, it looks to be using dhcp option 135, but any attempt to configure that eg
dhcp-option=135,domain.local1,domain.local2
doesnt get pushed at all (I'm using tcpdump -i br0 -lenx -s 1500 port bootps or port bootpc | dhcpdump to view wat dnsmasq is sending)
I have minor success using dhcp option 15, but it only pushes a single name into the search prefix as displayed by ipconfig /all on windows
Any suggestions ?
Checking the ISC dhcp option list I found this:
119 Domain Search domain-search
One or more domain names, each enclosed in quotes and separated by commas
But note that dnsmasq actually provides you special option (although I'm not sure from which version it starts)
dhcp-option=option:domain-search,eng.apple.com,marketing.apple.com
Our client machines (Ubuntu 18 server using netplan/systemd-resolve) were not requesting DHCP option 119, but I could solve the problem by forcing the server (dnsmasq) to sentd that option in the reply anyway:
dhcp-option-force=option:domain-search,internal,maindomain.com
dhcp-option=option:domain-name,maindomain.com
Using the dhcp-option-force parameter makes sure that the list is sent to the clients regardless of what they ask for.

LFTP active mode with servers that do not recognize the PORT command

I am using LFTP to transfer files from a server, which unfortunately does not recognize the PORT command. I do not have control over the server (do not know in detail what server is) and I have to use the active mode.
This is the command line as:
lftp -e 'debug 10;set ftp:passive-mode off; set ftp:auto-passive-mode no; ls; bye;' -u user,password ftp://ftp.site.com
This is the debug output:
<--- 200 Using default language en_US
---> OPTS UTF8 ON
<--- 200 UTF8 set to on
---> OPTS MLST modify;perm;size;type;UNIX.group;UNIX.mode;UNIX.owner;
<--- 200 OPTS MLST modify;perm;size;type;UNIX.group;UNIX.mode;UNIX.owner;
---> USER xxxxx
<--- 331 Password required for xxxxx
---> PASS xxxxxx
<--- 230 User xxxxx logged in
---> PBSZ 0
<--- 200 PBSZ 0 successful
---> PROT P
<--- 200 Protection set to Private
---> PORT 172,16,133,11,146,168
<--- 500 Illegal PORT command
---> LIST
---> ABOR
---- Closing aborted data socket
---- Chiusura del socket di controllo
It seems that LFTP renounces to connect to data socket because the remote server does not support the PORT command. Is there a way to convince LFTP can still connect to port 20? By FTP manual obviously no problem.
The issue, I think, is not that the FTP server doesn't support the PORT command (it does), but rather, it doesn't like the IP address/port that your FTP client is sending in the PORT command.
PORT 172,16,133,11,146,168
...tells the server to connect to address 172.16.133.11, port 37544*. The interesting part here is the IP address: it's an RFC 1918 address (i.e. it's a private network address). That, in turn, suggests that your FTP client is in a LAN somewhere, and is connecting to an FTP server using a public IP address.
That remote FTP server cannot connect to a private network address; by definition, RFC 1918 address are not publicly routable.
Thus it very well could be that the FTP server is trying to make a connection to the address/port given in your PORT command, fails, thus that is why the FTP server fails the command, saying:
500 Illegal PORT command
To make a PORT command work with that FTP server, you would need to discover the public IP address that that server can connect to, to reach your client machine. Let's say that this address is 1.2.3.4. Then you would need to tell lftp to use that address in its PORT command, using the ftp:port-ipv4 option.
Chances are, though, that public IP address is the address of a NAT/router/firewall, and that that NAT/router/firewall will not allow connections, from the outside world to a high numbered port (e.g. 37544), to be routed to a machine within the LAN. This is one of the issues with active FTP data transfers, i.e. FTP data transfers which use the PORT (or EPRT) commands: they are not considered "firewall-friendly".
Hope this helps!
* - why 146,168 translates to port 37544?
According to FTP's RFC959 those parameters are:
(...) 16-bit TCP port address. This address information is broken into
8-bit fields and the value of each field is transmitted as a decimal
number (in character string representation).
146 dec = 10010010 bin = A
168 dec = 10101000 bin = B
A B
10010010 10101000 bin = 37544 dec

Faking a FTP service

I am writing a piece of software which can fool Nmap into believing that GuildFTPd FTP Server is running on port 21. My python code so far looks like this:
import socket
s = socket.socket( )
s.bind(('', 21))
s.listen(1)
conn, addr = s.accept()
conn.send("220-GuildFTPd FTP Server (c) 1997-2002\r\n220-Version 0.999.14")
conn.close()
The Nmap regexp for matching this particular service is:
match ftp m|^220-GuildFTPd FTP Server \(c\) \d\d\d\d(-\d\d\d\d)?\r\n220-Version (\d[-.\w]+)\r\n| p/Guild ftpd/ v/$2/ o/Windows/
However when I scan the host which is running the script with Nmap the results are:
21/tcp open ftp?
How can this be? When I scan the real service with Nmap it identifies the service correctly.
First, you're missing a \r\n at the end of your fake response that the match line needs.
The other major issue is that your program only handles one connection, then closes. Nmap will first do a port scan, then send service fingerprinting probes. If you ran nmap as root (or Administrator on Windows), it would use a half-open TCP SYN scan, and your application wouldn't see the portscan as a connection, but otherwise it will accept the portscan, close the connection, and be unavailable for the service scan phase.
Here's a very basic adaptation of your script that can handle sequential connections (but not parallel), which is enough to fool Nmap:
import socket
s = socket.socket( )
s.bind(('', 21))
s.listen(1)
while True:
conn, addr = s.accept()
conn.send("220-GuildFTPd FTP Server (c) 1997-2002\r\n220-Version 0.999.14\r\n")
conn.close()

Resources