EZ LearnEZ Learn
Courses
EZ Board
Dashboard
All articles
python-for-cyber-securitypythonsocketsbanner-grabbing

IP Resolution and Banner Grabbing

Every shop hangs a signboard, and many network services announce their name and version the moment you connect. That one habit is what Shodan indexes across the whole internet.

Ashish Revar17 October 202614 min read
1 video lesson 5:19 total
Sign in to track your progress

Video lesson

Most network services are polite. The moment you connect, before you ask for anything, they introduce themselves: their software name, often their version. That introduction is called a banner, and reading one takes about ten lines of Python. It is also, quietly, the idea behind one of the most famous tools in security.

Analogy: The shop signboard

Walk down a market street and you can tell what each shop sells without entering, because every shop hangs a signboard. Many network services do the same: the moment you connect, before you ask anything, they announce their software name and version in the first few bytes they send.

Reading that announcement is called banner grabbing, and it is often enough to tell an analyst whether a host is running old, unpatched software.

Step 1: turn a name into an address

A hostname must be turned into an IP address first, the way a name in a phone book is turned into a number:

resolve.py

import socket

ip = socket.gethostbyname("example.com")
print(f"example.com resolves to {ip}")   # a dotted IPv4 address

try:
    hostname, _, _ = socket.gethostbyaddr(ip)
    print(f"Reverse lookup: {hostname}")
except socket.herror:
    print("No reverse DNS record for this address")

Sample output (the address will differ; it can change over time)

example.com resolves to 104.20.23.154
No reverse DNS record for this address

gethostbyname asks DNS "what is the number for this name?". gethostbyaddr asks the reverse: "what is the name for this number?". Many addresses have no reverse record, which is why that call is wrapped in a try and the socket.herror is caught. A name that does not exist at all raises a different error, socket.gaierror, which you will meet in the exception-handling article.

example.coma name people remember104.20.23.154a number machines route bygethostbyname()gethostbyaddr()the reverse lookup often finds nothing: not every address has a name
Figure 12.1. DNS is the phone book. gethostbyname goes from name to number; gethostbyaddr goes back.

Step 2: read the banner

Then the banner grab itself: connect to an open port and read whatever greeting arrives before any request is made.

banner_grab.py

# banner_grab.py
import socket

def grab_banner(host, port, timeout=2.0):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(timeout)
    try:
        sock.connect((host, port))
        banner = sock.recv(1024)
        return banner.decode(errors="replace").strip()
    except (socket.timeout, ConnectionRefusedError):
        return None
    finally:
        sock.close()

banner = grab_banner("10.0.0.5", 22)
if banner:
    print(f"Banner: {banner}")      # e.g. SSH-2.0-OpenSSH_7.4
else:
    print("No banner received, or the port is closed")

Sample output

Banner: SSH-2.0-OpenSSH_7.4
Part of the functionWhy it is there
sock.settimeout(timeout)A silent host must not hang your script forever; give up after two seconds
sock.connect((host, port))The three-way handshake. If the door is closed, this raises ConnectionRefusedError
sock.recv(1024)Listen for the greeting. A polite service has already sent it
.decode(errors="replace").strip()Bytes become readable text; stray bytes become a placeholder; the trailing newline goes
except (socket.timeout, ConnectionRefusedError)Silent host or closed door: return None instead of crashing
finally: sock.close()Whatever happened, hang up. Cleanup always lives in finally

That one line, SSH-2.0-OpenSSH_7.4, carries two facts an analyst acts on at once: the service is OpenSSH, and its version is 7.4. A quick search of a vulnerability database against "OpenSSH 7.4" returns every weakness fixed in later versions, which is the next move in any assessment.

Practise safely: a lab with three pretend services

You should not point a banner grabber at a machine you do not own, so build a safe target. This script starts three pretend services on your own computer. Each one speaks first, like a real SSH, FTP or mail server, and then hangs up. (It uses threads so the three can run together. You will meet threads properly two articles from now.)

lab_banners.py

# lab_banners.py: three pretend services for banner-grabbing practice.
# Run it on your own machine only. It speaks first, like a real SSH, FTP or mail server.
import socket
import threading

SERVICES = {
    9991: b"SSH-2.0-OpenSSH_7.4\r\n",
    9992: b"220 ProFTPD 1.3.5 Server ready.\r\n",
    9993: b"220 mail.lab.local ESMTP Postfix\r\n",
}

def serve(port, banner):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(("127.0.0.1", port))
    server.listen(5)
    while True:
        client, _ = server.accept()
        client.sendall(banner)          # announce ourselves before being asked
        client.close()

for port, banner in SERVICES.items():
    threading.Thread(target=serve, args=(port, banner), daemon=True).start()

print("Lab services up on 127.0.0.1:", ", ".join(str(p) for p in SERVICES), flush=True)
threading.Event().wait()                # keep the program alive

With that running, save grab_banner() in banner_grab.py (with the if __name__ == "__main__": guard from the modules article around the demo lines) and sweep five ports. Start a throwaway web server on 9994 too (python3 -m http.server 9994 --bind 127.0.0.1) so you can see what a service that does not speak first looks like:

sweep_banners.py

from banner_grab import grab_banner

for port in (9991, 9992, 9993, 9994, 9995):
    banner = grab_banner("127.0.0.1", port, timeout=1.0)
    print(f"{port}: {banner if banner else '(nothing)'}")

Sample output

9991: SSH-2.0-OpenSSH_7.4
9992: 220 ProFTPD 1.3.5 Server ready.
9993: 220 mail.lab.local ESMTP Postfix
9994: (nothing)
9995: (nothing)

Three ports greeted you with a name and a version. Port 9994 is open but silent, and port 9995 is simply closed. Both give (nothing), but for different reasons, and that difference is the heart of the lab exercise at the end of this unit.

Python concept: Some services wait for you to speak first

A mail, FTP or SSH server announces itself. A web server does not: it waits for a request. grab_banner() listens without sending anything, so against a web server it hears silence until the timeout. To read a web server's banner you must send a request first:

import socket
s = socket.socket(); s.settimeout(2)
s.connect(("127.0.0.1", 9994))
s.sendall(b"HEAD / HTTP/1.0\r\n\r\n")           # ask politely
reply = s.recv(1024).decode(errors="replace")
print([line for line in reply.splitlines() if line.lower().startswith("server")])
['Server: SimpleHTTP/0.6 Python/3.14.8']

Different protocols, different manners. A good banner grabber knows which services speak first.

From a banner to a finding

A banner is raw text. To act on it, a script must pull out the software name and the version. That is a string-handling job, and you already know the tools:

parse_banner.py

def parse_ssh_banner(banner):
    # SSH-2.0-OpenSSH_7.4  ->  protocol 2.0, software OpenSSH, version 7.4
    parts = banner.split("-", 2)
    protocol = parts[1]
    software, _, version = parts[2].partition("_")
    return protocol, software, version

for b in ["SSH-2.0-OpenSSH_7.4", "SSH-2.0-dropbear_2020.81", "SSH-2.0-OpenSSH_9.6"]:
    protocol, software, version = parse_ssh_banner(b)
    print(f"{b:<26} protocol {protocol}  software {software:<9} version {version}")

Sample output

SSH-2.0-OpenSSH_7.4        protocol 2.0  software OpenSSH   version 7.4
SSH-2.0-dropbear_2020.81   protocol 2.0  software dropbear  version 2020.81
SSH-2.0-OpenSSH_9.6        protocol 2.0  software OpenSSH   version 9.6

Now a script can sweep a hundred hosts, parse every banner, and list which ones announce an old version. That list is the first page of a vulnerability report. Equifax shows why it must not stay a list.

Real incident: Equifax, 2017: the scan found it, nobody acted

In 2017, the credit-reporting agency Equifax suffered a breach that exposed the personal data of roughly 147 million people. The way in was a known vulnerability in the Apache Struts web framework, catalogued as CVE-2017-5638, for which a patch had been available since March 2017, months before the breach. Investigations afterward found that Equifax's own scanning had not reliably flagged the vulnerable system, and the patch was never applied to it.

What this teaches: knowing what software and version a system runs is only half the job. The other half is closing the loop between the finding and the fix. A banner sweep is the cheap way to learn the first half. A person or a process must do the second.

A banner is a claim, not proof

A returned banner such as SSH-2.0-OpenSSH_7.4 names the exact software and version, which an analyst can look up against a vulnerability database in seconds. But a banner is a claim a service makes about itself, not proof, and a well-hardened host may suppress or fake it on purpose. Here is a service that lies. It is a few lines of Python, yet it announces itself as a recent OpenSSH:

spoof.py

# a lab service that LIES: it is a few lines of Python, but it claims to be a newer OpenSSH
import socket
s = socket.socket(); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("127.0.0.1", 9996)); s.listen(5)
while True:
    c, _ = s.accept(); c.sendall(b"SSH-2.0-OpenSSH_9.6\r\n"); c.close()

grab_banner("127.0.0.1", 9996)

Banner: SSH-2.0-OpenSSH_9.6

Your banner grabber reports a modern OpenSSH, and there is no OpenSSH there at all. Treat a banner as the first clue in an investigation, never the last word. Confirm with a second source before you write a finding.

1. Connectthe handshakecompletes2. Listenrecv() beforesending anything3. Readname and versiona claim, not proof4. Actlook the version up,confirm, then patchsilence can mean a web server waiting for you,a hidden banner, a firewall, or a closed port
Figure 12.2. Banner grabbing in four steps. The third step is where a hardened or lying service can mislead you.

Case study: Shodan: a search engine built from banners

Shodan, launched in 2009 by John Matherly, is often described as "the search engine for internet-connected devices". It works by doing, continuously and at the scale of the entire public internet, exactly what the grab_banner() function above does to one host: it connects to common ports across billions of addresses and records the banner each service returns. The result is a searchable index of what software every reachable device on the internet is running, from web servers and databases to industrial control systems, traffic cameras and building management panels.

Shodan is used every day by defenders to find their own organisation's exposed systems before an attacker does, and by researchers to measure how long a newly disclosed vulnerability takes to be patched across the internet. The same index, of course, is equally visible to attackers looking for unpatched versions.

What this teaches: banner grabbing is not a toy exercise. An entire industry of attack-surface management is built on the handful of lines you just read, and the lesson for a defender is blunt: if a service announces an old, vulnerable version to anyone who connects, assume an automated index has already recorded it.

Caution: Authorised systems only

Run grab_banner() only against hosts inside your own lab network. Pointing a banner grabber at a system you do not own is unauthorised access under Sections 43 and 66 of the Information Technology Act, 2000. The practice services above exist so that you never need to.

Check your understanding

Question 1: A banner grab against a hardened host returns nothing. Give two different reasons this might happen that do not involve the port being closed.

Any two of these: the service is one that waits for the client to speak first (like a web server), so there is nothing to read until you send a request; the service has been configured to suppress its banner; a firewall is silently dropping your connection so the read times out; the host replies slowly and your timeout was too short.

Question 2: Why is a banner "a claim, not proof"?

The banner is simply text the service chooses to send. A hardened host can hide it or change it, and a fake service can announce any name and version it likes, as spoof.py does. It is a first clue to be confirmed, not a finding on its own.

Question 3: What does Shodan have to do with the grab_banner() function?

Shodan does at internet scale what grab_banner() does to one host. It connects to common ports across billions of addresses, records each banner, and makes the result searchable.

Key takeaway: Key takeaway

Resolve the name, connect, read the greeting, and treat what you read as a clue. A banner names the software and version at a glance, which is why defenders and attackers both collect them, and why an old version announced to the world should be assumed to have been recorded already.

Assessments

Unit Assessment

Python and Networking Foundations

20 questions30 minPass: 60%

Start exam →

Comprehensive Assessment

Python for Cyber Security: Complete Assessment

100 questions120 minPass: 60%

Start final exam →

Related to this topic

Continue learning

Reference material

Cheatsheet

Python for Cyber Security — Cheatsheet

v1.0

Open resource

MCQ Bank

Python for Cyber Security — MCQ Bank

v1.0

Open resource

Question Bank

Python for Cyber Security — Question Bank

v1.0

Open resource

eBook

Python for Cyber Security — eBook

v1.0

Open resource

Discussion

Loading comments…
More articlesTest your knowledge
EZ MentorAI · Free

IP Resolution and Banner Grabbing

Hi! I'm EZ Mentor

Your AI tutor for
IP Resolution and Banner Grabbing

Sign in to ask, it's free.

EZ Mentor is AI-powered · Answers may not be perfect · 10 free questions / day

EZ LearnEZ Learn

Structured learning across Reverse Engineering, Cloud Security, and Research Methodology, plus games and practice tools in EZ Arena. Free, and open to anyone.

EZ Learn

  • Courses
  • EZ Articles
  • EZ Watch
  • EZ Talks
  • EZ Bytes
  • Cyber Hygiene

EZ Library

  • All Resources
  • eBooks & PDFs
  • Question Banks
  • MCQ Banks
  • Cheatsheets

EZ Exams

  • All Exams
  • REMA
  • Cloud Security
  • Research Methodology
  • Python for Cyber Security
  • Cyber Law & IPR
  • Community
  • EZ Board

EZ Arena

  • EZ Games
  • Live Quiz
  • ML Challenges
  • दिशा - Hygiene
  • सजग - Awareness
  • अभ्यास - Practice

Platform

  • About
  • Contact
  • Terms of Use
  • Disclaimer
  • Verify Certificate
  • Events

© 2026 EZ Learn by EpochZero. Educational content for learning purposes.

Platform Lead: Ashish Revar