{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "AJ Van Beest — Playbooks",
  "home_page_url": "https://ajvanbeest.com/playbooks/",
  "feed_url": "https://ajvanbeest.com/playbooks/feed.json",
  "description": "Playbooks from ajvanbeest.com. I build robots to fight cybercrime. Writing, projects, notes and playbooks on security automation and agentic systems; every page is also available as markdown (.md) and JSON (.json).",
  "language": "en-US",
  "authors": [
    {
      "name": "AJ Van Beest",
      "url": "https://ajvanbeest.com/"
    }
  ],
  "items": [
    {
      "id": "https://ajvanbeest.com/playbooks/geoip-lookup/",
      "url": "https://ajvanbeest.com/playbooks/geoip-lookup/",
      "title": "Playbook: Find the Geolocation of an External IP Address",
      "summary": "Determine the geographic location of an external IP address using multiple sources, and validate that the results are consistent.",
      "content_html": "<h2 id=\"purpose\">Purpose</h2>\n<p>Determine the geographic location of an external IP address using multiple sources, and validate that the results are consistent.</p>\n<h2 id=\"required-tools--access\">Required Tools &amp; Access</h2>\n<table>\n<thead>\n<tr>\n<th>Tool</th>\n<th>Access Needed</th>\n<th>Free Tier?</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"https://ipinfo.io\">IPinfo.io</a></td>\n<td>API token or web interface</td>\n<td>Yes (50k/month)</td>\n<td>Privacy detection requires paid plan</td>\n</tr>\n<tr>\n<td><a href=\"http://ip-api.com\">ip-api.com</a></td>\n<td>No auth for non-commercial</td>\n<td>Yes</td>\n<td>Includes proxy/hosting flags</td>\n</tr>\n<tr>\n<td><a href=\"https://virustotal.com\">VirusTotal</a></td>\n<td>API key or web interface</td>\n<td>Yes (limited)</td>\n<td></td>\n</tr>\n<tr>\n<td><a href=\"https://abuseipdb.com\">AbuseIPDB</a></td>\n<td>API key or web interface</td>\n<td>Yes (1k/day)</td>\n<td>Tor/proxy detection</td>\n</tr>\n<tr>\n<td><a href=\"https://check.torproject.org/torbulkexitlist\">Tor Exit Node List</a></td>\n<td>None</td>\n<td>Yes</td>\n<td>Official Tor Project list</td>\n</tr>\n</tbody>\n</table>\n<p>You need at least two geolocation sources to cross-validate results.</p>\n<h2 id=\"input\">Input</h2>\n<ul>\n<li>One external IPv4 or IPv6 address (e.g., <code>8.8.8.8</code>)</li>\n</ul>\n<h2 id=\"steps\">Steps</h2>\n<h3 id=\"1-query-ipinfoio\">1. Query IPinfo.io</h3>\n<p><strong>Web interface:</strong></p>\n<ol>\n<li>Go to <a href=\"https://ipinfo.io\">https://ipinfo.io</a></li>\n<li>Enter the IP address in the search box</li>\n<li>Record: City, Region, Country, Organization, ASN</li>\n</ol>\n<p><strong>API:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">https://ipinfo.io/{IP_ADDRESS}?token={YOUR_TOKEN}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<p>Record the following fields:</p>\n<ul>\n<li><code>city</code></li>\n<li><code>region</code></li>\n<li><code>country</code></li>\n<li><code>org</code></li>\n<li><code>asn</code></li>\n</ul>\n<h3 id=\"2-query-ip-apicom\">2. Query ip-api.com</h3>\n<p><strong>Web interface:</strong></p>\n<ol>\n<li>Go to <a href=\"http://ip-api.com\">http://ip-api.com</a></li>\n<li>The IP appears in the URL; modify to: <code>http://ip-api.com/json/{IP_ADDRESS}</code></li>\n<li>Record: City, Region, Country, ISP, AS</li>\n</ol>\n<p><strong>API:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">http://ip-api.com/json/{IP_ADDRESS}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<p>Record the following fields:</p>\n<ul>\n<li><code>city</code></li>\n<li><code>regionName</code></li>\n<li><code>country</code></li>\n<li><code>isp</code></li>\n<li><code>as</code></li>\n</ul>\n<h3 id=\"3-query-virustotal-optional-but-recommended\">3. Query VirusTotal (optional but recommended)</h3>\n<p><strong>Web interface:</strong></p>\n<ol>\n<li>Go to <a href=\"https://virustotal.com\">https://virustotal.com</a></li>\n<li>Click “Search” and enter the IP address</li>\n<li>Go to the “Details” tab</li>\n<li>Record: Country, ASN, Network</li>\n</ol>\n<p><strong>API:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> --header</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">x-apikey: {YOUR_API_KEY}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> \\</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">  \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">https://www.virustotal.com/api/v3/ip_addresses/{IP_ADDRESS}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<p>Record from <code>data.attributes</code>:</p>\n<ul>\n<li><code>country</code></li>\n<li><code>asn</code></li>\n<li><code>as_owner</code></li>\n</ul>\n<h3 id=\"4-check-for-vpnproxytor\">4. Check for VPN/Proxy/Tor</h3>\n<p>This step determines whether the IP is masking its true origin.</p>\n<h4 id=\"4a-check-ip-apicom-flags\">4a. Check ip-api.com flags</h4>\n<p>If you queried ip-api.com in Step 2, check these additional fields in the response:</p>\n<ul>\n<li><code>proxy</code>: true/false (detects proxy/VPN)</li>\n<li><code>hosting</code>: true/false (datacenter/hosting provider, often indicates VPN)</li>\n</ul>\n<p><strong>API (with extra fields):</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">http://ip-api.com/json/{IP_ADDRESS}?fields=status,country,city,isp,as,proxy,hosting</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<h4 id=\"4b-check-abuseipdb\">4b. Check AbuseIPDB</h4>\n<p><strong>Web interface:</strong></p>\n<ol>\n<li>Go to <a href=\"https://abuseipdb.com\">https://abuseipdb.com</a></li>\n<li>Enter the IP address</li>\n<li>Look for: Usage Type, ISP, and “Is Tor” flag</li>\n</ol>\n<p><strong>API:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> -G</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">https://api.abuseipdb.com/api/v2/check</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> \\</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\">  --data-urlencode</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">ipAddress={IP_ADDRESS}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> \\</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\">  -H</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">Key: {YOUR_API_KEY}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> \\</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\">  -H</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">Accept: application/json</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<p>Record from response:</p>\n<ul>\n<li><code>data.usageType</code> (e.g., “Data Center/Web Hosting/Transit”)</li>\n<li><code>data.isTor</code> (true/false)</li>\n<li><code>data.totalReports</code> (abuse report count)</li>\n</ul>\n<h4 id=\"4c-check-tor-exit-node-list\">4c. Check Tor Exit Node List</h4>\n<p><strong>Manual check:</strong></p>\n<ol>\n<li>Download the list: <a href=\"https://check.torproject.org/torbulkexitlist\">https://check.torproject.org/torbulkexitlist</a></li>\n<li>Search for the IP address in the file</li>\n<li>If found, the IP is a known Tor exit node</li>\n</ol>\n<p><strong>Command line:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\">curl</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> -s</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">https://check.torproject.org/torbulkexitlist</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#427B58;--shiki-dark:#8EC07C\"> |</span><span style=\"--shiki-light:#B57614;--shiki-dark:#FABD2F\"> grep</span><span style=\"--shiki-light:#8F3F71;--shiki-dark:#D3869B\"> -q</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">{IP_ADDRESS}</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> &amp;&amp;</span><span style=\"--shiki-light:#AF3A03;--shiki-dark:#FE8019\"> echo</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">TOR EXIT NODE</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span><span style=\"--shiki-light:#427B58;--shiki-dark:#8EC07C\"> ||</span><span style=\"--shiki-light:#AF3A03;--shiki-dark:#FE8019\"> echo</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\"> \"</span><span style=\"--shiki-light:#79740E;--shiki-dark:#B8BB26\">Not a Tor exit</span><span style=\"--shiki-light:#7C6F64;--shiki-dark:#A89984\">\"</span></span></code></pre>\n<h4 id=\"summary-privacyanonymization-flags\">Summary: Privacy/Anonymization Flags</h4>\n<table>\n<thead>\n<tr>\n<th>Check</th>\n<th>Result</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>ip-api proxy flag</td>\n<td></td>\n</tr>\n<tr>\n<td>ip-api hosting flag</td>\n<td></td>\n</tr>\n<tr>\n<td>AbuseIPDB usageType</td>\n<td></td>\n</tr>\n<tr>\n<td>AbuseIPDB isTor</td>\n<td></td>\n</tr>\n<tr>\n<td>Tor exit node list</td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<p><strong>Interpretation:</strong></p>\n<ul>\n<li><strong>Tor exit node</strong>: Location data is meaningless; this is the exit point, not the user</li>\n<li><strong>Proxy/VPN detected</strong>: Location may be the VPN endpoint, not the user</li>\n<li><strong>Hosting/Datacenter</strong>: Likely a server, VPN endpoint, or automated traffic—not a typical end user</li>\n</ul>\n<h2 id=\"validation\">Validation</h2>\n<p>Compare the results from each source:</p>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>IPinfo.io</th>\n<th>ip-api.com</th>\n<th>VirusTotal</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Country</td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>City/Region</td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>ASN/Org</td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<p><strong>Validation checks:</strong></p>\n<ol>\n<li>\n<p><strong>Country match</strong>: Do all sources agree on the country?</p>\n<ul>\n<li>If YES: High confidence in country-level location</li>\n<li>If NO: Flag discrepancy; IP may use anycast or be misattributed</li>\n</ul>\n</li>\n<li>\n<p><strong>City/Region match</strong>: Do at least 2 sources agree on city or region?</p>\n<ul>\n<li>If YES: Reasonable confidence in city-level location</li>\n<li>If NO: Report country only; city-level data is unreliable for this IP</li>\n</ul>\n</li>\n<li>\n<p><strong>ASN/Org match</strong>: Do the ASN numbers match?</p>\n<ul>\n<li>If YES: Confirms the network owner</li>\n<li>If NO: Investigate further; one source may have stale data</li>\n</ul>\n</li>\n</ol>\n<p><strong>Known limitations:</strong></p>\n<ul>\n<li>VPNs, proxies, and Tor exit nodes will show the endpoint location, not the user (Step 4 helps detect these)</li>\n<li>CDN and anycast IPs (e.g., Cloudflare, Google) may return multiple valid locations</li>\n<li>Mobile IPs may geolocate to carrier headquarters, not actual user location</li>\n<li>Privacy detection is not perfect; some VPNs use residential IPs that evade detection</li>\n</ul>\n<h2 id=\"output\">Output</h2>\n<p>Produce a summary in this format:</p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"plaintext\"><code><span class=\"line\"><span>IP Address: {IP}</span></span>\n<span class=\"line\"><span>Lookup Date: {YYYY-MM-DD}</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Location:</span></span>\n<span class=\"line\"><span>  Country: {country} (confidence: high/medium/low)</span></span>\n<span class=\"line\"><span>  City/Region: {city, region} (confidence: high/medium/low)</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Network:</span></span>\n<span class=\"line\"><span>  ASN: {asn}</span></span>\n<span class=\"line\"><span>  Organization: {org name}</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Privacy/Anonymization:</span></span>\n<span class=\"line\"><span>  Tor Exit Node: {yes/no}</span></span>\n<span class=\"line\"><span>  Proxy/VPN Detected: {yes/no/unknown}</span></span>\n<span class=\"line\"><span>  Hosting/Datacenter: {yes/no}</span></span>\n<span class=\"line\"><span>  ⚠️ Location Reliability: {reliable / unreliable - anonymized}</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Validation:</span></span>\n<span class=\"line\"><span>  Sources queried: {list sources}</span></span>\n<span class=\"line\"><span>  Country consensus: {yes/no}</span></span>\n<span class=\"line\"><span>  City consensus: {yes/no}</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Notes:</span></span>\n<span class=\"line\"><span>  {any discrepancies or flags}</span></span></code></pre>\n<p><strong>Important:</strong> If any privacy/anonymization flag is positive, add a warning that the geolocation data represents the proxy/VPN/Tor endpoint, NOT the actual user location.</p>\n<h2 id=\"logging\">Logging</h2>\n<p>Record the following in your investigation log or ticket:</p>\n<ol>\n<li>The IP address queried</li>\n<li>Date/time of lookup</li>\n<li>Sources consulted</li>\n<li><strong>Location results</strong>: Country, City/Region, confidence level</li>\n<li><strong>Network info</strong>: ASN, Organization</li>\n<li><strong>Privacy flags</strong>: Tor, Proxy/VPN, Hosting/Datacenter</li>\n<li>Location reliability assessment</li>\n<li>Any discrepancies noted</li>\n<li>Analyst name or “automated” if run by script</li>\n</ol>\n<p><strong>Example log entry:</strong></p>\n<pre class=\"astro-code astro-code-themes gruvbox-light-medium gruvbox-dark-medium\" style=\"--shiki-light:#3c3836;--shiki-dark:#ebdbb2;--shiki-light-bg:#fbf1c7;--shiki-dark-bg:#282828; overflow-x: auto;\" tabindex=\"0\" data-language=\"plaintext\"><code><span class=\"line\"><span>[2026-01-01 14:30] GeoIP lookup for 203.0.113.42</span></span>\n<span class=\"line\"><span>Sources: IPinfo.io, ip-api.com, VirusTotal, AbuseIPDB, Tor list</span></span>\n<span class=\"line\"><span>Result: Netherlands, Amsterdam (high confidence)</span></span>\n<span class=\"line\"><span>ASN: AS12345 - Example Hosting Inc.</span></span>\n<span class=\"line\"><span>Privacy: Not Tor, no proxy detected, hosting=yes (datacenter IP)</span></span>\n<span class=\"line\"><span>Notes: All geo sources agree. Datacenter IP suggests server or VPN endpoint.</span></span>\n<span class=\"line\"><span>Analyst: AJ</span></span></code></pre>\n<h2 id=\"automation-notes\">Automation Notes</h2>\n<p>This playbook is a good candidate for scripting because:</p>\n<ul>\n<li>All steps are deterministic</li>\n<li>APIs return structured data</li>\n<li>Validation logic is straightforward</li>\n</ul>\n<p>A script should:</p>\n<ol>\n<li>Accept IP as input</li>\n<li>Query all configured sources</li>\n<li>Compare results automatically</li>\n<li>Output the summary format above</li>\n<li>Flag discrepancies for human review</li>\n</ol>",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "security",
        "playbooks",
        "geoip",
        "osint",
        "investigation"
      ],
      "_ajvanbeest": {
        "lane": "playbooks",
        "maturity": "evergreen",
        "markdown_url": "https://ajvanbeest.com/playbooks/geoip-lookup.md",
        "json_url": "https://ajvanbeest.com/playbooks/geoip-lookup.json"
      }
    }
  ]
}
