Skip to content
solved35 pts

Attack from the middle of the ocean

The login map shows 4,000 sign-ins from 0°N 0°E. Is a botnet floating in the Gulf of Guinea?

The scene

Reported: the security dashboard's geo map lights up on Null Island. 80% of logins over the past week plot at exactly 0.0, 0.0.

Auth events (structured):

{"user":"jdoe",   "ip":"",            "geo":[0,0],      "result":"ok"}
{"user":"asmith", "ip":"",            "geo":[0,0],      "result":"ok"}
{"user":"admin",  "ip":"203.0.113.9", "geo":[9.1,38.7], "result":"ok"}

Topology: client → ALB → nginx → app.

nginx:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

app:

ip = request.headers.get("X-Real-IP", "")
geo = geoip.lookup(ip) or (0, 0)
  • The 20% that geolocate correctly all came through an older internal path that sets X-Real-IP.

Diagnoses

    The real writeup

    Root cause: the app reads a header that nothing on the public path sets.

    nginx forwards X-Forwarded-For but never sets X-Real-IP. On the public path, the app gets "", the GeoIP lookup misses, and the fallback (0, 0) plots the login on Null Island. The few logins with correct locations came through the legacy path that does set the header.

    Worse: since nginx passes client headers through untouched, any client could send its own X-Real-IP: 1.2.3.4 and forge its location in the audit log.

    Fix: at the edge, trust only the ALB (set_real_ip_from <alb-cidr>; real_ip_header X-Forwarded-For;), set proxy_set_header X-Real-IP $remote_addr;, and strip any client-supplied value. In the app, make an empty IP a hard error, not a (0, 0) default.

    Lesson: 0,0 is never a location. It's a null wearing a disguise.