No Description

panda 02efd129c5 Update '' 1 month ago 02efd129c5 Update '' 1 month ago



What if we, hypothetically, wanted to retrieve all the banners of ssh server exposed on the internet? This post analyzes the hypothetical steps I would undertake.


Say that we have an honeypot that has hardcoded the ssh banner, I may want to see which are the most common banners to better blend it amongst real ssh servers.

There's also a simpler way described at the end of the post.

The scan:

First of all we need to define what we mean by "all ssh servers exposed", I think we can be more than happy considering only port 22 TCP and not possible different custom ports.

Furthermore I sensed that it would be not wise to scan the whole subnet, since there are organizations and people on it's space that are touchy towards unwanted scans, it would be a clever idea to use an exclusion list to skip those IPs and CIDRs, a suitable list looks like this: exclude.conf (in the same repo of masscan).

The scanner:

I would use masscan since it has many features and it looks like it's very fast.

The command I think it would look like this:

masscan -p22 --banners --source-ip --excludefile exclude.conf -oJ output.json --rate 100000 --connection-timeout 1

Note that for banner scanning we need to provide another ip in the same network, the "--source-ip", see [0]

I would then output everything to a Json file to be able to work the data with Elastic.

For the rate I expect 100000 to be a fair value.

For the connection timeout I expect that 1sec would be more than ok to distinguish between a valid ssh server and something else.

I expect the final file to weight abount 4GB and the scan to take about 10+ hours to complete.

Processing data:

After grepping only the lines containing the banners from the output file of masscan, I would totally use Elastic importing the Json and applying the geo-ip filter on the ip field:

I would filter the outputed json data of masscan selecting only the lines with banners:

grep "banner" output.json > output_onlybanners.json

the Logstash config file to import that data would be like:

input {
   file {
      start_position => "beginning"
      path => "output_onlybanners.json"
      sincedb_path => "/dev/null"
      codec =>   json
      type => "json-log"

filter {
    if [type] == "json-log" {
        date {
            match => [ "timestamp", "UNIX" ]

    geoip {
      default_database_type => "City"
      source => "ip"
      tag_on_failure => ["geoip-city-failed"]

    geoip {
      default_database_type => "ASN"
      source => "ip"
      tag_on_failure => ["geoip-asn-failed"]


output {
  elasticsearch {
       hosts => "http://localhost:9200"
       index => "bannerscan"

and I would start the import with this command:

/usr/share/logstash/bin/logstash -f logstash.conf

Reading data:

Once imported in Elastic we could create our dashboards with Kibana and see which banners are more common so we could select one to use with our honeypot to better blend it.

so our hypothetical list of the most common banners might be something like this:

  • SSH-2.0-OpenSSH_8.4p1 Debian-5
  • SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.5
  • SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.10
  • SSH-2.0-dropbear_2017.75

The much simpler way:

from your machine ask your ssh client the version:

ssh -V 2>&1 >/dev/null | cut -f1 -d','

which results in:

OpenSSH_7.9p1 Debian-10+deb10u2

where the banner taken from a telnet/other connection on the ssh port would be:

SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2

so the difference lies just in that "SSH-2.0-" we can add to the string, resulting in this command:

ssh_banner=$(ssh -V 2>&1 >/dev/null | cut -f1 -d',') ; echo "SSH-2.0-"$ssh_banner


The IPs in the IPv4 space are 4,294,967,296 (2^32) in total, since there are 588,514,304 reserved addresses in the end we have 3,706,452,992 public addresses. [1]


  • [ ]