Iphunting
🔎 Unmasking the Network: Hunting IP Ranges with Python and RIPE
The RIPE Network Coordination Centre (NCC) maintains one of the five Regional Internet Registries (RIRs), providing critical data on how IP addresses are allocated in Europe, the Middle East, and parts of Central Asia. Querying this data—specifically the inetnum (IPv4) and inet6num (IPv6) records—can reveal who owns a block of IP addresses, for what purpose, and when it was registered.
However, standard WHOIS tools sometimes make it cumbersome to search for all sub-ranges within a large IP prefix. That’s why I wrote IpRangeHunter—a simple, yet powerful Python script that leverages the RIPE REST API to efficiently scan an entire IP range and extract all associated allocation records.
đź’ˇ The Problem: Sub-Allocations
When you look up a large IP range (like a /8 or a /16), you often find that the initial allocator (e.g., an ISP or large corporation) has further divided and registered smaller blocks to various customers or internal departments.
A single WHOIS query on the main prefix usually only returns the top-level record. IpRangeHunter is designed to broadly search the RIPE database to find all of these registered sub-ranges and consolidate them into one clean output.
🛠️ The Tool: IpRangeHunter
The script is a command-line utility built around the IpRangeHunter class. It takes any IPv4 or IPv6 prefix (in CIDR notation) and queries the RIPE REST API to retrieve relevant database objects.
Key Features:
- Prefix Agnostic: Handles both IPv4 (
192.168.1.0/24) and IPv6 (2001:db8::/32) prefixes using Python’s robustipaddressmodule. - RIPE REST API Integration: Uses the official RIPE REST API (
rest.db.ripe.net) for fast, JSON-based queries. - CIDR Conversion: RIPE often stores IPv4 ranges as
start_ip - end_ip. The script converts these into the more useful CIDR format (e.g.,10.0.0.0/8). - Flexible Output: Supports human-readable Table (default), JSON, and CSV formats for easy integration into other tools or spreadsheets.
The Python Code (Snippet)
The core logic is housed in the IpRangeHunter class:
Full code is found here
class IpRangeHunter:
BASE_URL = "[https://rest.db.ripe.net](https://rest.db.ripe.net)"
def __init__(self, debug=False):
# Initializes the requests session for efficiency and proper headers
self.session = requests.Session()
self.session.headers.update({
'Accept': 'application/json',
'User-Agent': 'RIPE-Inetnum-Query-Tool/1.0'
})
self.debug = debug
def hunt_for_ranges(self, prefix):
# ... main search logic ...
try:
# Query range preparation is different for IPv4/IPv6
if prefix.version == 4:
# IPv4 is queried as 'start_ip - end_ip'
query_range = f"{str(prefix.network_address)} - {str(prefix.broadcast_address)}"
object_type = 'inetnum'
else:
# IPv6 is queried as a standard CIDR prefix
query_range = str(prefix)
object_type = 'inet6num'
self._ask_ripe_nicely(query_range, object_type, results)
except Exception as e:
print(f"Unexpected error: {e}", file=sys.stderr)
return results
# ... _ask_ripe_nicely and other methods ...