Introduction:
In today’s interconnected world, understanding network security is essential. One essential tool in a security professional’s arsenal is a port scanner. You probably thinking what? are you reinventing the wheel? Well not exactly I am building a deeper understanding of what happens under the hood.
There are plenty of tools out there that you can use for network scanning starting from the most famous and popular nmap, you can become a master at using them but have you ever tried to read them and understand the engineering behind? I believe there is a lot to learn!!!
Overview:
What is a Port Scanner and Why is it Important?
A port scanner is a software tool used to discover open ports on a networked device or system. Ports are communication endpoints used by networking protocols to facilitate data transfer between devices. Each port is associated with a specific service or application, and knowing which ports are open can provide valuable insights into the security posture of a network.
Port scanning is crucial for several reasons:
Security Assessment: By identifying open ports, security professionals can assess potential vulnerabilities and security risks within a network. Open ports may indicate services or applications that could be exploited by malicious actors.
Network Troubleshooting: Port scanning helps diagnose connectivity issues by pinpointing which ports are accessible and which are not. It aids in troubleshooting network configurations and firewall settings.
Penetration Testing: Ethical hackers and security researchers use port scanners during penetration testing to simulate attacks and identify potential entry points into a network. Understanding the network’s exposed ports allows for the implementation of appropriate security measures.
Introduction to Golang and its Networking Capabilities
Golang, or Go, is a statically typed, compiled programming language known for its simplicity, efficiency, and concurrency support. Developed by Google, Go is increasingly popular for building scalable, high-performance applications, including network services and tools.
Golang’s standard library provides robust support for networking, making it well-suited for developing network-oriented applications. Key networking features in Go include the net package for basic networking operations, the net/http package for building web servers and clients, and the golang.org/x/net subpackage for more advanced networking functionality. Scanme uses gopacket, the idea of writing a library that would support network scanning was inspired from synscan. The routing module has support only for Linux systems, hence at this stage also this library offers only that support see here.
High-Level Overview of the Code Structure and Functionality
In this blog post, we’ll explore how to build a port scanner using Golang and the gopacket library. gopacket is a powerful packet processing library that allows developers to work with network packets at a granular level. It provides a flexible framework for packet capture, decoding, manipulation, and analysis.
The code structure consists of several key components:
Scanner Struct: Defines the core functionality of the port scanner, including methods for sending ARP requests, ICMP Echo Requests, SYN packets for SYN scanning, and full TCP handshake packets for connect scanning.
type Scanner struct {
iface *net.Interface
dst, gw, src net.IP
handle *pcap.Handle
opts gopacket.SerializeOptions
buf gopacket.SerializeBuffer
tcpsequencer *TCPSequencer
}
iface: This field is a pointer to a net.Interface, which represents a network interface. It refers to the network interface through which network packets will be sent and received.
dst: This field is of type net.IP, represents the destination IP address. It is the IP address where the network packets will be sent.
gw: This field is also of type net.IP, representing the gateway IP address. The gateway is the device that routes network traffic between different networks.
src: This field is of type net.IP, representing the source IP address. It is the IP address from which the network packets will be sent.
handle: This field is a pointer to a pcap.Handle, which is used for capturing and processing network packets using the pcap library. pcap stands for Packet Capture. Note: if you read carefully the code you will notice that the shared pcap handle is used only to write the packets with the various send methods, after multiple tests I realized that separating R/W pcap handles would speed up operations, I am still not 100% sure this happens but I made sure to let them know 1152.
opts: This field is of type gopacket.SerializeOptions, which contains options for serializing packets. It’s used for configuring how packets are serialized before being sent over the network.
buf: This field is of type gopacket.SerializeBuffer, which is a buffer used for serializing packets. It’s used in conjunction with the opts field for packet serialization.
tcpsequencer: This field is a pointer to a TCPSequencer. It deals with TCP packet sequencing. This was stolen from project naabu-tcpsequencer, I found this to be an awesome implementation and I learnt from it. The code defines a TCPSequencer struct and associated methods for generating linear TCP sequence numbers. It ensures that the sequence wraps around to 0 after reaching its maximum value. The NewTCPSequencer function initializes a new sequencer with a starting value of math.MaxUint32, and the Next method returns the next number in the sequence, ensuring safe concurrent access.
Packet Handling: Implements functions for decoding and handling network packets captured during the scanning process. This includes parsing Ethernet, IPv4, TCP, and ICMPv4 layers to identify relevant packet types and extract useful information.
Scanning Techniques: The port scanner supports multiple scanning techniques, including SYN scanning and full TCP handshake (connect) scanning. Each technique involves sending specific types of packets to target ports and analyzing the responses to determine port status (open, closed, filtered). Choosing between a pcap-based TCP port scanner and a socket-based one depends on various factors, including your specific requirements, the environment in which the scanner will be deployed, and the capabilities you need. Here are some considerations for each approach and some of the reasons I decided to implement both :)
PCAP-based TCP Port Scanner (IPv4):
Packet Analysis: PCAP-based scanners capture raw network packets, allowing for in-depth analysis beyond TCP connections. You can inspect headers, payloads, and other packet attributes, enabling more advanced scanning techniques. Passive Scanning: PCAP scanners can operate passively, meaning they can monitor network traffic without actively sending packets. This can be useful for monitoring network activity or analyzing traffic patterns. Advanced Features: PCAP libraries offer advanced features like packet filtering, packet reassembly, and protocol analysis, which can be beneficial for complex scanning scenarios.
Socket-based TCP Port Scanner (IPv4, IPv6): Simplicity: Socket-based scanners are generally simpler to implement and understand. They use standard TCP sockets for communication, which are familiar to most developers. Control: Socket-based scanners provide more control over packet generation and manipulation. You can customize packet headers, payloads, and timing parameters as needed. Performance: Socket-based scanners may offer better performance for certain tasks, especially when dealing with high-speed networks or large volumes of traffic. Although I am still researching why this implementation (PCAP-based) is actually faster. In summary, if you need advanced packet analysis capabilities, passive scanning, or platform independence, a PCAP-based scanner may be the better choice. However, if simplicity, control, performance, or portability are more critical for your application, a socket-based scanner might be preferable. Ultimately, the choice depends on your specific requirements and the trade-offs you’re willing to make.
Grabbing Banners: An experimental feature so far implemented for FTP, SSH, IRC, MYSQL, LDAPS, HTTP, NNTP, IMAP, POP. Only implemented and working for the PCAP-based. For most of the protocols the banners are grabbed with a generic approach by establishing a TCP connection and reading the banner from it. If successful, it returns the banner or header. The interesting one here is LDAPS, The GetLDAPBanner function establishes a TLS connection to an LDAP server specified by an IP address and port. It then performs an unauthenticated bind to the LDAP server and searches for server information in the root DSE (Directory Service Entry). After retrieving the search result, it formats the server information into a string and returns it along with any encountered errors. This function essentially retrieves LDAP server information from the root DSE and formats it as a banner string. Here a recommended read pentesting-ldap, I basically ported the code snippet below in Go.
>>> import ldap3
>>> server = ldap3.Server('x.X.x.X', get_info = ldap3.ALL, port =636, use_ssl = True)
>>> connection = ldap3.Connection(server)
>>> connection.bind()
True
>>> server.info
Some samples to get the feel of the grabbers
024/03/07 14:20:34 Port 21(ftp) open Version: 220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
2024/03/06 16:42:16 Port 22(ssh) open Version: SSH-2.0-OpenSSH_7.4
2024/03/07 14:20:34 Port 25(smtp) open Version: 220-tomasi.dnshigh.com ESMTP Exim 4.96.2 #2 Thu, 07 Mar 2024 14:20:34 +0100
2024/03/07 14:20:34 Port 80(http) open Version: Apache
2024/03/07 14:20:34 Port 110(pop3) open Version: +OK Dovecot ready.
2024/03/07 14:20:34 Port 143(imap) open Version: * OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE NAMESPACE LITERAL+ STARTTLS AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
2024/03/06 16:43:36 Port 3306(mysql) open Version: 8.0.36
2024/03/07 14:15:59 Port 636(ldaps) open Version: objectClass: [top vmwDseRoot]cn: [DSE Root]supportedLDAPVersion: [3]vmwPlatformServicesControllerVersion: [6.5.0]msDS-SiteName: [Default-First-Site]subSchemaSubEntry: [cn=aggregate,cn=schemacontext]defaultNamingContext: [dc=vsphere,dc=local]
Setting Up the Environment:
Installing scanme is pretty simple, you will have to install libpcap and the go package:
sudo apt install libpcap-dev
go get -u github.com/CyberRoute/scanme
Example Usage:
alessandro@xps:~/Development/scanme$ sudo go run examples/synscan.go -ip {redacted}
2024/03/08 17:16:33 scanning ip {redacted} with interface wlp0s20f3, gateway 192.168.86.1, src 192.168.86.151
2024/03/08 17:16:33 ICMP Echo Reply received from {redacted}
2024/03/08 17:16:47 last port scanned for {redacted} dst port 65535
2024/03/08 17:16:47 Port 2087(eli) open
2024/03/08 17:16:47 Port 21(ftp) open Version: 220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
2024/03/08 17:16:48 Port 80(http) open Version: Apache
2024/03/08 17:16:48 Port 465(urd) open
2024/03/08 17:16:48 Port 995(pop3s) open
2024/03/08 17:16:48 Port 2082(infowave) open
2024/03/08 17:16:48 Port 2078(tpcsrvr) open
2024/03/08 17:16:48 Port 53(domain) open
2024/03/08 17:16:48 Port 110(pop3) open Version: +OK Dovecot ready.
2024/03/08 17:16:48 Port 2077(tsrmagt) open
2024/03/08 17:16:48 Port 2095(nbx-ser) open
2024/03/08 17:16:48 Port 25(smtp) open Version: 220-tomasi.dnshigh.com ESMTP Exim 4.96.2 #2 Fri, 08 Mar 2024 17:16:48 +0100
2024/03/08 17:16:48 Port 2096(nbx-dir) open
2024/03/08 17:16:48 Port 143(imap) open Version: * OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE NAMESPACE LITERAL+ STARTTLS AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
2024/03/08 17:16:48 Port 443(https) open
2024/03/08 17:16:48 Port 993(imaps) open
2024/03/08 17:16:48 Port 2086(gnunet) open
2024/03/08 17:16:48 Port 2083(radsec) open
2024/03/08 17:16:48 Execution time: 14.979338609s
The project it is a work progress, feel free to contribute and/or comment if you enjoyed the article I’d love to hear from you. Ciao and to the next! Keep hacking!!!