FortWatch

Exposed Databases: Why an Open MongoDB, Elasticsearch, or Redis Port Means Full Compromise

FortWatch

FortWatch Team

Exposed Databases: Why an Open MongoDB, Elasticsearch, or Redis Port Means Full Compromise

In 2017, crews calling themselves cru3lty, wolsec, and mongodb wiped tens of thousands of MongoDB databases, dropped a ransom collection demanding a Bitcoin payment, and moved on. They didn't use an exploit. Every one of those databases was bound to the public internet with no password on the admin account, so the attackers used MongoDB's own administrative commands — show dbs then db.dropDatabase(), the same ones your DBA runs every day — against tens of thousands of hosts they'd found on Shodan. One wave that year alone added 26,000 new victims (45,000+ cumulative). Worse, many victims had no backup to restore from, and many of the attackers never bothered to keep a copy of the data they "ransomed," so the people who paid up got stiffed — they wired Bitcoin for data that no longer existed anywhere.

The short version: an exposed, unauthenticated data store — MongoDB, Elasticsearch, Redis, Memcached, CouchDB, etcd, Cassandra — is the cleanest CRITICAL in external security. An open port with no auth grants full read, full delete, and often full host RCE using the product's own commands, with no vulnerability involved.

  • This is happening at scale right now: the Shadowserver Foundation publishes daily per-protocol reports (Open MongoDB, Open Elasticsearch, Open Redis, and more) that together track hundreds of thousands of exposed datastores at any given moment.
  • Discovery is free and pre-built: attackers query Shodan/Censys — which already index all of IPv4 — with one-line dorks, then confirm with a single mongosh, curl _cat/indices, or redis-cli PING.
  • Several of these escalate from data exposure to a root shell on the host: Redis turns runtime persistence settings into a write-anywhere primitive, and CouchDB's CVE-2017-12635 → CVE-2017-12636 chain proves RCE-from-the-data-port isn't a single-product quirk.
  • Destruction is automated and indiscriminate — the 2016–2017 MongoDB wave wiped tens of thousands of databases and the 2020 Meow bot overwrote nearly 4,000 with no ransom note; exposed Memcached on UDP 11211 fueled GitHub's 1.35 Tbps DDoS in 2018.
  • The fix is identical across the class: bind to loopback/private, turn auth on, firewall to known app IPs, hold staging to the same bar, and monitor your external surface continuously so a misbind is caught in hours, not after indexing.

Redis has its own deep-dive. This article is the pillar for the whole exposed-datastore class — seven engines that share one failure mode. Redis is the one engine in the set whose data port doubles as a host-RCE port out of the box, so it gets its own canonical write-up: Exposed Redis on port 6379 covers the full FLUSHALLCONFIG SET dir → SSH-key/cron/MODULE LOAD attack chain, RediShell (CVE-2025-49844), the protected-mode and bind misconfigs, the self-check for 6379, and the complete Redis hardening checklist. Read this page for the breadth across all seven engines; read that page when the exposed port is Redis.

The internet's most-looted surface isn't a zero-day — it's a default

The MongoDB wave above would be comfortable to file under "history." It isn't. Three years later it got purer and meaner. Starting around July 20, 2020, an automated bot dubbed Meow connected to exposed, unauthenticated datastores and overwrote every record with random data ending in the string meow — no ransom note, no demand, no negotiation, no way back. The bot had destroyed nearly 4,000 databases within days (around 3,800 by July 25), of which more than 97% were Elasticsearch and MongoDB. There was nothing to extort and nothing to recover. Meow proved a point the security industry keeps having to relearn: an exposed datastore is not only a breach risk, it is a data-destruction risk. An unauthenticated write endpoint is an unauthenticated delete endpoint.

The severity ceiling is still rising. Shadowserver's per-protocol dashboards track hundreds of thousands of exposed, accessible datastores at any given moment — a standing population, not a backlog being cleared. The headlines change product names but never severity: in October 2025, Wiz disclosed RediShell (CVE-2025-49844), a Redis use-after-free Wiz rated a perfect 10.0 CVSS, which put roughly 60,000 no-auth Redis instances one step from remote code execution. Nine years after the MongoDB wave, the same class of mistake — a data store reachable from the open internet without authentication — is still producing the worst possible score on the worst possible scanner result. This is the one finding category where the news cycle never actually moves on.

An exposed, unauthenticated data store is the cleanest CRITICAL in external security: total data loss, frequently full host compromise, occasionally a weapon pointed at someone else — and no vulnerability involved. Nobody bypassed a control. The product did exactly what it was configured to do, for an attacker, using the attacker's own copy of the standard client. That is what makes it different from almost every other finding you'll triage: there is no patch that fixes "the database answered the question it was asked." The fix is to make sure the attacker can never ask.

This is not one product's problem. It is a class: MongoDB on 27017, Elasticsearch on 9200, Redis on 6379, Memcached on 11211, CouchDB on 5984, etcd on 2379, Cassandra on 9042. The protocols differ, the blast radius differs in the details — Redis hands you a root shell, Memcached hands you a DDoS cannon aimed at a stranger, etcd hands you every secret in a Kubernetes cluster — but the severity verdict is identical across all of them, and it sits at the top of the scale. The same logic that puts an exposed .env file at the top — direct, unauthenticated access to the crown jewels with a single command — applies here, often with the host thrown in for free. And like the .env file, it lives in exactly one place: your external attack surface, the set of things an attacker can reach without already being inside. The rest of this article walks the whole class — how these instances are found for free, how a single command confirms them, what each engine hands over when it answers an attacker, and how to find your own before someone with a Shodan subscription finds them for you.

The ports attackers already have a list for

Here is the part most "secure your database" guides skip: you are not the first thing that scans your IP. Long before any attacker types your address into a terminal, Shodan and Censys have already done the work. Both engines crawl the entire IPv4 internet continuously, port by port, banner by banner, and publish the results as a searchable database. The target list is pre-built. An attacker hunting for exposed data stores doesn't run a scan — they run a query, and get back thousands of live, unauthenticated instances, often with version strings and parsed index or collection names already attached.

That is the discovery story in one sentence: passive, free, and indexed in advance. The dorks are short and public:

"MongoDB Server Information" port:27017 -authentication   # MongoDB, auth disabled
port:9200 all:"elastic indices"            # Elasticsearch with readable indices
port:6379                                  # Redis
port:11211                                 # Memcached
product:"CouchDB"                           # CouchDB

Run one of these and the screen fills with hosts. The -authentication filter on the MongoDB dork is the giveaway: it pre-filters toward instances whose banner shows no auth in front — a strong heuristic, then confirmed with a one-line probe — skipping most hardened boxes. This is recon as a database lookup, not a network operation. The defender's mirror image is exactly the same query scoped to your own ranges — net:<your CIDR> — which shows you precisely what an attacker sees about your address space, banners and all.

Even self-scanning is faster than your patch window

Suppose an attacker ignores Shodan entirely and scans for themselves. It still doesn't buy you time. masscan — Robert David Graham's asynchronous SYN scanner — sweeps the entire IPv4 internet for a single port in under five minutes, transmitting 10 million packets per second from a single machine. One machine, one port, the whole internet, before your coffee is cool. There is no scenario where a misbound data port stays quietly undiscovered because "nobody knows it's there." A port reachable from the internet is a port that will be enumerated; the only variable is whether it happens in the next hour or the next minute.

And discovery isn't the end of the timeline — it's the trigger for everything after it. Internet-measurement research backs this up: in the "Cloud Watching" honeypot study, traffic to a newly-stood-up service jumped sharply once the scanning engines indexed it — roughly 7.7x more probing after Censys listed an HTTP host, and about 15.7x more after Shodan did. Getting indexed is not a passive event. It is the moment your host moves from "unknown" to "on a list everyone reads," and the probing follows within minutes to hours. Treat the gap between misconfiguration and indexing as your entire window, and assume it is short. This is exactly why a one-time clean scan means little and continuous scanning instead of an annual pentest is the only cadence that matches how the threat actually arrives.

The default-port and default-auth reference

These are the ports to know cold — and the auth states that explain why so many of them sit wide open. The "default auth" column is the one that does the damage: for most of this class, the out-of-the-box state is "anyone who reaches the port has full read and write," and the operator has to actively turn protection on rather than turn it off.

ServiceDefault port(s)Default auth stateWhat an open instance hands over
MongoDB27017/tcpOff unless started with --authEvery database and collection; db.dropDatabase() is one command
Elasticsearch9200/tcp (HTTP), 9300/tcp (transport)None in 7.x and earlierAll indices via _cat/indices; full documents via _search
Redis6379/tcpNone beyond protected-modeFull keyspace read/write; runtime CONFIG SET = arbitrary file write → root shell
Memcached11211/tcp + 11211/udpNoneCached objects; UDP listener = a DDoS amplifier aimed at others
CouchDB5984/tcp"Admin Party" (all users admin) historicallyAll documents; admin-self-promotion CVE chain to RCE
etcd2379/tcp (client/data)NoneEvery key — and on Kubernetes, all cluster secrets and config
Cassandra9042/tcp (native transport)Off by default (AllowAllAuthenticator)All keyspaces and tables via CQL after the standard CQL handshake

Three rows deserve a note. etcd is the one most people underweight: port 2379 is the client API — the data port — and it ships with no authentication, so an anonymous client walks the entire keyspace. Confirm it with the v3 API, which is what modern etcd and Kubernetes actually speak: ETCDCTL_API=3 etcdctl --endpoints=http://target:2379 get "" --prefix --keys-only lists every key when no auth or TLS is in front, or hit the gRPC-gateway directly with curl http://target:2379/v3/kv/range -d '{"key":"AA==","range_end":"AA=="}' (base64 empty-key/empty-range = the whole keyspace). Don't reach for the legacy /v2/keys REST endpoint — the v2 API has been off by default since etcd 3.4 (--enable-v2 defaults to false), so on any current build it just returns 404 and tells you nothing. Because etcd is the backing store for Kubernetes — which uses exactly this v3 store — that keyspace is not "some cache"; it is every Secret, every ConfigMap, every service-account token in the cluster, per the etcd configuration docs. (You'll also see 2380 on etcd hosts; that is the peer port for cluster-internal replication, not a data port to expose — don't confuse it with 2379.) Cassandra on 9042 needs one extra step the others don't — a CQL STARTUP handshake before it accepts queries — but under the default AllowAllAuthenticator, cqlsh TARGET 9042 connects with no credentials and DESCRIBE KEYSPACES walks every keyspace and table. And Elasticsearch: the reason 7.x clusters litter Shodan is that authentication and TLS lived behind a paid tier until 8.0 made them free and on-by-default, so an enormous installed base ran genuinely open with no add-on to misconfigure — there was simply nothing to turn on.

Identity beats port number

Now the rule that makes this table a starting point and not a checklist: identity beats port number. A data store is critical because of what it is, not which port it happens to answer on. MongoDB on 9876 is exactly as critical as MongoDB on 27017 — same unauthenticated dropDatabase, same full collection read. Redis on 7777 is exactly the root shell Redis on 6379 is. Moving a store off its default port changes nothing an attacker cares about, because the same Shodan dork plus a service-fingerprint pass identifies the protocol wherever it lives. The banner gives it away; the port number is cosmetic.

This cuts directly against a comforting habit: "we run it on a weird port, so it's hidden." A nonstandard port is not a control. It is not authentication, it is not a firewall rule, and it is not even meaningful obscurity against an adversary who reads protocol responses rather than port labels. Treating "it's on 9876, not 27017" as a mitigation is how a MongoDB instance ends up in the next Meow tally. When you prioritize a port-scan result, classify by the service you fingerprinted, then fall back to the default-port heuristic — never the other way around. That ordering — identity first, port second — is the whole difference between catching the relocated datastore and rubber-stamping it as "unknown service, low risk."

The defaults that should save you — and where they don't

Here is the uncomfortable thing about every exposure wave in this article: the vendors mostly fixed the default. MongoDB binds to localhost out of the box. Elasticsearch finally turns auth on by itself. Redis ships with a guard against open binds. And yet Shodan still returns tens of thousands of wide-open instances on any given afternoon. The gap between "the default is safe" and "the internet is full of unsafe instances" is not a vendor failure — it is a single line that one developer changes to make their app connect, that works, and that ships to production. Understanding exactly what each protection does, and the exact move that defeats it, is the root cause behind every dork hit and every Meow wipe.

MongoDB localhost-by-default: a deliberate override every time

MongoDB drew the right lesson from the 2016–2017 ransom wave. Since version 3.6 (December 2017), via SERVER-28229, every mongod and mongos binary binds to 127.0.0.1 by default. A stock MongoDB 3.6+ install is not reachable from another machine at all. That means a post-3.6 MongoDB exposed on the public internet is almost never an accident of the defaults — it is a deliberate net.bindIp: 0.0.0.0 (or the --bind_ip_all flag) that someone added on purpose, usually to let the app server connect, and then never scoped down.

And binding wide is only half the exposure. MongoDB has no authentication at all until you create the first user and start with authorization enabled — there is no built-in admin password to guess, because there is no admin user. So the canonical exposed MongoDB is the combination of two omissions: someone widened the bind, and nobody ever ran db.createUser with security.authorization: enabled. With auth off, bindIp is the only thing standing between the internet and show dbs followed by db.dropDatabase() — which is precisely how the tens-of-thousands MongoDB wave and the Meow bot worked, with no exploit and no credentials.

Elasticsearch pre-8.x: years of clusters that never had a lock

Elasticsearch is the cleanest example of "the default was unsafe for a long time, and that debt is still on the internet." Before version 8.0 (February 2022), authentication and TLS lived in X-Pack — a paid tier. The free distribution most teams ran had no auth, no TLS, no users. Spinning up a cluster and pointing it at a network interface gave you an open REST API on port 9200 with nothing in front of it, and that was the documented, expected behavior. Millions of 7.x and earlier clusters were deployed exactly that way and many are still running.

What "open on 9200" means in practice is total: curl http://target:9200/_cat/indices?v over plain HTTP dumps every index name in the cluster, and _all/_search pages through the documents. There is no exploit in that sentence — it's the API answering the way it answers your own dashboards. The exposed Elasticsearch 9200 instances that fill Shodan and that made up the overwhelming majority of the Meow wave are nearly all pre-8.x clusters where security was a feature behind a paywall the operator never bought or never enabled. From 8.0 onward, security ships on by default — auth, a built-in elastic superuser, and TLS auto-configured on first start (Elastic minimal-security docs). That fixed the default going forward, but it did nothing for the enormous installed base of 7.x clusters that have to enable xpack.security.enabled: true by hand — and most never will.

Redis protected-mode: a tripwire one Stack Overflow answer disarms

Redis is the one engine in this set whose data port doubles as a host-RCE port, so it carries its own deep-dive — Exposed Redis on port 6379 walks the full chain and fix. The short version that matters for the class: since Redis 3.2.0 (2016), protected mode refuses remote connections when Redis is bound to all interfaces with no password and no custom bind directive, returning DENIED Redis is running in protected mode... instead of a PONG (Redis security docs). It's a good default — and it's a tripwire, not a wall. The most common way people make Redis reachable is to silence that exact error: a developer hits the DENIED message, finds a Stack Overflow answer that says set protected-mode no, pastes it in, and the net is gone. The fix that "worked" is the fix that opened the box — and because Redis's runtime CONFIG SET dir primitive writes files as whatever user Redis runs as, an open 6379 is one step from a root shell, not just data loss. The full CONFIG SET → SSH-key/cron/MODULE LOAD chain, the RediShell post-auth angle, and the ACL-based hardening all live in the Redis deep-dive.

CouchDB Admin Party — and the unauth-to-RCE chain

CouchDB on port 5984 had the most permissive default of the lot, with a name to match: Admin Party. Until you configured an admin user, everyone is an admin — anonymous, unauthenticated, full administrative control of the database. That alone puts an exposed CouchDB at the top of the severity scale. But CouchDB also gives us a second worked path from data port straight to remote code execution, and it proves the RCE-from-the-data-port property is not Redis-only.

Even a hardened CouchDB that left Admin Party and created real admins was vulnerable to a documented two-CVE chain disclosed on November 14, 2017. CVE-2017-12635 (CVSS 9.8, CRITICAL) is a privilege escalation: CouchDB validates a new user document with one JSON parser (Erlang) and stores it with another (JavaScript/SpiderMonkey), and the two disagree about what to do with duplicate keys. By submitting a _users document with two roles keys — one empty array to pass the "you can't set your own roles" check, one containing _admin to actually land in storage — an unauthenticated attacker promotes themselves to administrator. Then CVE-2017-12636 (CVSS 7.2, HIGH) takes over: a CouchDB admin can set configuration values that specify OS-level binary paths the server then executes (query servers, native query handlers). The now-admin attacker points one at a shell command and CouchDB runs it as the CouchDB user.

Chain them and the result is unambiguous: an unauthenticated request becomes an administrator, and an administrator becomes arbitrary shell commands on the host. Full unauth-to-RCE, weaponized internet-wide, fixed only in 1.7.1 and 2.1.1. The lesson generalizes: when the data port speaks a protocol rich enough to reconfigure the service — set storage paths, set executable paths, set roles — reaching the port is reaching the operating system. That is why this whole class earns CRITICAL on impact, not on category.

The one misconfiguration that defeats all of them

The pattern across all of these is identical, and it has a single root cause. A developer can't connect to the datastore from the app server. They change bind 127.0.0.1 to bind 0.0.0.0 — or comment the line out, or set --bind_ip_all, or flip protected-mode no to kill an error. It works immediately. The ticket closes. The "temporary" change is never reverted, and it rides the next deploy straight into production. In that one edit, the safe default bind is gone, auth (which was never enabled because the bind was always loopback) is now the only control and it isn't there, and the port is public. Every vendor protection in this section is downstream of that line, and that line is the most common misconfiguration in the entire exposed-datastore class.

Orchestration-layer exposure: the port opens with no .conf touched

There is a second class of exposure that defeats every protection above without anyone editing a config file at all, and host-level audits miss it entirely because nothing on the host changed. The port gets opened in the orchestration layer:

  • A cloud security group with an ingress rule of 0.0.0.0/0 on 27017 / 9200 / 6379 — often copied from another tier's group without trimming the source range.
  • A managed datastore — RDS, ElastiCache, MongoDB Atlas, OpenSearch Service — flipped to publicly accessible, or with a permissive IP allowlist, to "make it work" from a laptop or CI runner.
  • A Kubernetes Service of type LoadBalancer or NodePort in front of the datastore pod, which provisions a public endpoint and routes traffic to the port regardless of what the container's config says inside.

In all three, the application's own config can be flawless — bound to loopback or a private interface, auth on — and the datastore is still on the public internet, because the routing decision lives above the host. A config audit of the box reports green. This is also why staging and dev are not exempt and routinely get hit: they get the least hardening, the most casually-copied security groups, and the loosest publicly accessible toggles, yet they hold production-shaped data — real dumps, real PII copied in for testing. Meow and the MongoDB ransom waves never checked whether a reachable instance was labeled "prod"; they wiped whatever answered. The only detection that matches how this exposure actually appears is looking at your perimeter the way an attacker does — from the outside, continuously, at the routable port — not auditing config files on hosts that were never the place the door opened.

Confirming exposure takes one command

Here is the uncomfortable part of this whole category: there is no exploit. Once an attacker has your IP and a port that answers, confirming the data store is wide open takes a single line — and it is the product's own normal command, the same one your application sends a thousand times a second, answering an attacker exactly the way it answers you. No CVE, no payload, no memory corruption. A client connects and asks, and an unauthenticated server replies. That symmetry is the whole point of this section: the commands an attacker runs to loot you are identical to the commands you run to check yourself. Run them against your own ranges before someone else does.

One line per service tells you everything:

# MongoDB — returns the full database list if authorization is off
mongosh "mongodb://TARGET:27017" --eval 'db.adminCommand({listDatabases:1})'

# Elasticsearch — list every index, then pull documents
curl -s http://TARGET:9200/_cat/indices?v
curl -s "http://TARGET:9200/_all/_search?size=10"

# Redis — PONG = wide open, NOAUTH = protected
redis-cli -h TARGET -p 6379 PING

# Memcached — stats over UDP (the amplification surface)
echo -e 'stats\r' | nc -u -w1 TARGET 11211

Read those responses like an attacker does. mongosh handing back a JSON document of database names means MongoDB's authorization is disabled — and MongoDB has no users until you create one, so a fresh install with a public bind is open by definition. The Elasticsearch pair is the bluntest of the set: _cat/indices?v prints a table of every index name on the cluster, and _all/_search?size=10 hands back actual documents — over plain, unencrypted HTTP, no token, no header. redis-cli ... PING returning PONG means no password is set; if auth is on you get NOAUTH instead, and the door is shut. None of this is exploitation in the vulnerability sense. It is the API working as designed for an attacker who simply isn't authenticated, because nobody told it to require authentication.

Bulk exfiltration is trivial by design

Confirming a read is the floor. The protocols are built to move data in volume, so emptying the store is a scripting exercise, not a hacking one. Elasticsearch's scroll and search APIs exist specifically to page through millions of documents efficiently — point them at an open cluster and you stream the entire index set over plain HTTP. MongoDB ships mongodump, which copies whole databases from a connection string; against an unauthenticated instance that connection string is just mongodb://TARGET:27017. This is why the campaigns that hit this surface were never one-host-at-a-time. The 2016–2017 MongoDB ransom wave and the 2020 Meow attacks were both fully automated — a bot iterated a pre-built list of exposed hosts, ran the same handful of commands against each, and read, copied, or destroyed without a human in the loop per target.

nmap for direct confirmation

When you want service and version confirmation across a block of IPs rather than a hand probe per host, nmap is the tool — and the port list should match the reference table earlier in this post exactly, so the table, the dorks, and the scan all cover the same set:

nmap -p 27017,9200,6379,11211,5984,9042,2379 -sV TARGET

That sweeps MongoDB (27017), Elasticsearch HTTP (9200), Redis (6379), Memcached (11211), CouchDB (5984), Cassandra native transport (9042), and etcd client (2379) in one pass, with -sV fingerprinting the service and version behind each open port — which is how you catch a relocated data store sitting on a port nmap wouldn't otherwise guess. Go a step further with the NSE scripts that interrogate auth state and configuration directly:

nmap -p 27017,6379,11211 --script mongodb-info,redis-info,memcached-info TARGET

These don't just tell you the port is open — mongodb-info pulls build and database details, redis-info dumps the server's INFO output, and memcached-info returns its stats, all of which only respond fully when no auth stands in the way. One critical addition for Memcached: add a UDP scan.

nmap -sU -p 11211 TARGET

The Memcached amplification vector — the one behind the 1.35 Tbps flood that hit GitHub in 2018 — rides UDP/11211, and a TCP-only scan will report the host as fine while the UDP listener sits there as a DDoS reflector aimed at strangers. If you only ever scan TCP, you are blind to the single most dangerous thing exposed Memcached does.

Audit the listening socket on every host

External probing tells you what an attacker sees. The internal counterpart tells you why — and it is faster to run on a box you control. On each host, list the listening TCP sockets and grep for the data-store ports:

ss -tlnp | grep -E '27017|9200|6379|11211'

The thing to read here is the bind address, not just whether the port is up. A line showing 127.0.0.1:27017 is correct — the service is loopback-only and unreachable from outside the host. A line showing 0.0.0.0:27017 (or *:27017, or an IPv6 [::]:) is the bug. That single difference — loopback versus all-interfaces — is the misconfiguration that gets you indexed by Shodan and wiped by a bot. Then cross-check that local finding against the perimeter: a service bound correctly to a private interface can still be exposed by a cloud security group with 0.0.0.0/0 ingress or a Kubernetes LoadBalancer Service, neither of which touches a .conf file. Both layers have to be right; ss catches the host side, the security-group review catches the orchestration side.

Why a CDN won't save these ports

One nuance trips people up, because it's the opposite of how web traffic behaves. With HTTP and HTTPS on 80 and 443, a CDN like Cloudflare sits in front of your origin, and a finding on those ports may well be an edge artifact rather than real origin exposure — which is exactly why CDN-managed header and TLS findings get filtered out before they ever become issues. Data-store ports do not work that way. 27017, 9200, 6379, 11211, 5984, 9042, and 2379 are not proxied or fronted by a CDN. MongoDB speaks its own binary wire protocol, Cassandra speaks CQL, Redis speaks RESP — these are not HTTP, and a CDN has nothing to proxy. So when one of these ports answers from your perimeter, there is no edge layer to hand-wave it away with. It is your origin, reachable from the internet, full stop. That is the structural reason an open data port is a different class of finding from a header or cipher result: it can never be a false positive caused by a CDN edge, which is why it belongs at issue creation, not in a filtered bucket.

Memcached: the cannon you point at strangers

Every other datastore in this article hurts you because of what it holds. Memcached is different — it hurts other people, and it does so even when it holds nothing worth stealing. That makes it the cleanest possible proof that severity is impact-on-compromise and not data-sensitivity, which is the whole reason this class sits at the top of the scale.

Here is the mechanism. Memcached ships with UDP enabled on port 11211 by default, and UDP has no handshake — the server answers whatever source address is in the packet, no questions asked. An attacker spoofs the victim's IP as the source, sends a tiny request to your exposed cache, and your cache fires the response not back at the attacker but at the victim. Because a cache's job is to return a lot of data for a small lookup, the response dwarfs the request. Cloudflare, in the blog post that coined the name "Memcrashed," measured the amplification factor at up to 51,000x — each byte an attacker sends can yield up to ~51KB aimed at someone who never touched your server (Cloudflare, Feb 2018). String a few thousand exposed caches together and you have a firehose nobody can trace back to its real operator.

1.35 Tbps, no exploit, no warning

This is not a lab curiosity. On February 28, 2018, GitHub absorbed a flood that peaked at 1.35 Tbps driven by roughly 126.9 million packets per second — at the time the largest DDoS publicly disclosed (GitHub Engineering, Feb 2018). The peak hit between 17:21 and 17:26 UTC, with intermittent unavailability through about 17:30. Every packet of that traffic came from memcached servers reflecting spoofed requests. None of those servers belonged to the attacker. They belonged to ordinary companies who had stood up a cache, bound it to 0.0.0.0 "to make it work," and never learned their box was reachable from the internet. Their cache held nothing sensitive — and it still ended up conscripted into the biggest DDoS on record.

Note the asymmetry this creates for your own exposure: a TCP-only port sweep misses this entirely. The amplification vector is UDP, so confirm it the way an attacker would — over UDP — and the fix is mercifully simple. Unlike the Redis RCE chain, which needs ACL lockdowns and a non-root service account to fully defang, the memcached amplification problem closes with a single setting: disable UDP. The reflection vector is UDP-only, so turning it off removes it completely.

# Confirm the UDP surface a TCP scan never sees
echo -e 'stats\r' | nc -u -w1 TARGET 11211
nmap -sU -p 11211 --script memcached-info TARGET

# The entire fix for the amplification vector
memcached -U 0    # disable the UDP listener

If you genuinely need UDP (almost nobody does anymore — modern clients use TCP), then bind to loopback or a private interface with -l 127.0.0.1 and firewall 11211/udp to your known app servers as defense in depth. But for the overwhelming majority of deployments, -U 0 is the entire fix, and it costs you nothing — your application traffic was already TCP.

Why "it holds nothing sensitive" is not a downgrade

It is tempting to file an exposed memcached below an exposed MongoDB on the theory that a cache is "just transient lookups, no real data." Resist that. The GitHub operators could have made exactly that argument the morning of February 28, and it would have been true — and irrelevant, because their boxes still amplified a terabit-scale attack. An open, unauthenticated memcached on UDP 11211 is a CRITICAL exposure regardless of its contents, for the same reason a loaded weapon left in a public park is a problem regardless of whose name is on the registration: the harm is in what it enables, not in what it contains. And the harm here lands on a third party you've never met, with your IP address on the return label.

That third-party-harm axis is the last piece of the severity case, and it's why the whole datastore class earns the same verdict. Walk the axes one finding produces at a time. Confidentiality: an open MongoDB, Elasticsearch, or Redis is a full read of the entire dataset with one curl or mongodump — no exploit, the product's own commands. Integrity and availability: the same open port that reads also deletes, which is exactly what the 2016–2017 MongoDB ransom wave (tens of thousands of databases) and the 2020 Meow attacks (~4,000 databases overwritten with no ransom note and no recovery) did, fully automated, indiscriminate. Lateral movement and remote code execution: Redis's runtime CONFIG SET dir primitive turns a cache into a root shell, and CouchDB's CVE-2017-12635 → CVE-2017-12636 chain (CVSS 9.8 into 7.2) walks an unauthenticated request to arbitrary shell commands — two independent products, same outcome. Third-party harm: memcached's 51,000x reflection turned strangers' caches into the engine of a 1.35 Tbps attack. An exposed unauthenticated datastore doesn't fail on one of these axes; it fails on all of them at the highest level simultaneously. That is the definition of CRITICAL, and it's why none of these findings should ever be triaged below the top of the queue. If you're building a triage process around this principle, our guide on how to prioritize vulnerabilities works the same impact-over-novelty logic across every finding type. The corollary matters as much as the rule: because severity comes from impact, the port number is noise. Memcached amplifying off 11299 is exactly as dangerous as memcached on 11211; MongoDB on 9876 is exactly the data loss that MongoDB on 27017 is. Identity beats default classification every time.

Common questions

These are the questions people actually type into search and ask AI assistants about exposed data stores. The short version of every answer below: an open unauthenticated datastore is total compromise, not a leak you patch later — and it stays that way no matter which port it sits on.

What happens if a MongoDB or Elasticsearch server is exposed to the internet?

Anyone who can reach the port can read, copy, and delete everything with no credentials, because the service answers an attacker's commands exactly the way it answers yours. For MongoDB, mongosh "mongodb://<ip>:27017" followed by show dbs lists every database when authorization is off, and mongodump copies all of it; db.dropDatabase() destroys it. For Elasticsearch, curl http://<ip>:9200/_cat/indices?v lists every index and the scroll/search API pages through millions of documents over plain HTTP. The whole process is scripted across an entire Shodan result set with no human per target — exactly how the 2016–2017 MongoDB ransom wave and the 2020 Meow campaign hit tens of thousands of hosts.

How many databases are exposed on the internet right now?

On the order of hundreds of thousands at any given moment. The Shadowserver Foundation publishes daily per-protocol reports — Open MongoDB, Open Elasticsearch, Open Redis, and more — that together track hundreds of thousands of exposed datastores across the internet. It is a steady-state count, not a one-time spike, because the misconfiguration that creates exposure (binding to 0.0.0.0) gets reintroduced by deploys, autoscaling, and "temporary" debug changes faster than anyone fixes the last batch.

What ports do exposed databases run on?

The defaults worth memorizing: MongoDB 27017, Elasticsearch 9200 (REST) and 9300 (node-to-node transport), Redis 6379, Memcached 11211 (TCP and UDP), CouchDB 5984, etcd 2379 (client) / 2380 (peer), and Cassandra 9042. But identity beats port number: the same service on a nonstandard port is exactly as exposed and just as critical. MongoDB on 9876 is MongoDB. Never treat an unusual port as a control — service fingerprinting first, defaults second, never the other way around.

What was the Meow attack and how many databases did it wipe?

The Meow attack was a fully automated bot that started around July 20, 2020, connecting to exposed, unauthenticated data stores and overwriting their contents with random characters ending in the string meow. By July 25 it had destroyed nearly 4,000 databases, more than 97% of them Elasticsearch and MongoDB, per BleepingComputer. There was no ransom note and no copy — purely destructive, no recovery path. The lesson is blunt: an unauthenticated write endpoint is an unauthenticated delete endpoint.

Can an exposed datastore lead to remote code execution, not just data loss?

Yes, on more than one engine. Redis is the headline case — its runtime CONFIG SET dir + dbfilename + SAVE primitive is a write-anywhere gadget that drops an SSH key into /root/.ssh/authorized_keys or a job into /var/spool/cron/, and a separate rogue-master replication path loads attacker code via MODULE LOAD; the full chain and fix are in the Redis deep-dive. But it isn't Redis-only: CouchDB chains CVE-2017-12635 (unauth privilege escalation) into CVE-2017-12636 (admin-to-shell) to reach arbitrary OS commands from an unauthenticated request. Any time the data port speaks a protocol rich enough to set storage paths, executable paths, or roles, reaching the port is reaching the host.

How do I check if my database is exposed to the internet?

Three checks, attacker's-eye-view first. Run the Shodan dork for each port against your own ranges — net:<your CIDR> port:27017, port:9200, port:6379 — to see exactly what an attacker sees. Then confirm directly with nmap across your external IPs: nmap -p 27017,9200,6379,11211,5984,9042,2379 -sV <target> (add -sU -p 11211 for the UDP memcached surface a TCP scan misses). Finally, on each host run ss -tlnp | grep -E '27017|9200|6379|11211' — anything bound to 0.0.0.0 instead of 127.0.0.1 is the bug.

What do I do with this?

Everything above comes down to one operational fact: an exposed datastore is fixed at the network boundary, not in the application. The work below is ordered by attacker-prevention leverage — find your own exposure first, then make the port unreachable, then keep it that way. Run the detection step before you touch any config; you cannot fix what you have not confirmed, and you want to see exactly what an attacker's Shodan watch already sees.

  1. Find your own exposure first, from the outside in. Query the same internet-scan engines an attacker would — Shodan and Censys both index the whole IPv4 space — scoped to your own ranges. In Shodan that is net:<your CIDR> alongside a service filter (port:27017 -authentication, port:9200, port:6379, port:11211); Censys uses its own query syntax for the same lookups. Then confirm directly with a port sweep across every external IP you own:

    nmap -p 27017,9200,6379,11211,5984,9042,2379 -sV <your-external-range>
    nmap -sU -p 11211 <your-external-range>   # memcached UDP — TCP scans miss it

    The 9042 is Cassandra, 5984 is CouchDB, and 2379 is etcd — they belong in the sweep because they are CRITICAL on the same impact scale as MongoDB and Redis, not footnotes to it. On each host, audit listening sockets with ss -tlnp | grep -E '27017|9200|6379|11211'; anything bound to 0.0.0.0 instead of 127.0.0.1 is the bug — the single most common root cause across every exposure in this article. And remember: these ports are not CDN-fronted, so an open one is real origin exposure, not an edge artifact.

  2. Bind to loopback or a private interface — never 0.0.0.0. This is the highest-leverage control because it stops the packet from ever being accepted, independent of any auth setting. It is also the line a "temporary" debugging change quietly reverts, so check it first and last. MongoDB: net.bindIp: 127.0.0.1 (or a specific private address) in mongod.conf — the default since 3.6, so an exposed Mongo is almost always a deliberate override someone forgot to undo. Elasticsearch: network.host: 127.0.0.1 or a private address in elasticsearch.yml. Redis: bind 127.0.0.1 -::1 and keep protected-mode yes in redis.conf.

  3. Turn authentication on, and read "no password" as "public read, write, and delete." The same open port that serves reads serves DROP and FLUSHALL — the Meow bot and the MongoDB ransom crews proved an unauthenticated write endpoint is an unauthenticated delete endpoint. MongoDB: start with --auth (or security.authorization: enabled) and create a real admin user — with auth off, bindIp is your only protection. Elasticsearch: xpack.security.enabled: true (8.x ships this on; 7.x and earlier ran with no auth at all). Redis: requirepass <long-random> plus per-user ACLs on Redis 6+. CouchDB: configure an admin to exit "Admin Party," and patch to a current 1.7.1 / 2.1.1 or later release — older builds chain CVE-2017-12635 into CVE-2017-12636 for unauthenticated RCE. Cassandra: switch off AllowAllAuthenticator for PasswordAuthenticator and create real roles. etcd: enable auth and TLS-client certs, and never expose 2379 beyond the cluster.

  4. Firewall the port to known app-server IPs — at the host and in the cloud. Defense in depth means the bind and the firewall must both be correct; either one alone is a single config change from exposure. At the host, allow only the app tier (ufw allow from <app-ip> to any port 27017, or the equivalent nftables rule). In the cloud, audit the security group — a data port in a group with 0.0.0.0/0 ingress is exposed even if every .conf on the box is perfect, and a NodePort or LoadBalancer Service does the same without touching host config. For memcached, kill the amplification surface entirely with memcached -U 0 to disable UDP.

  5. For Redis specifically, neuter the RCE primitive too. Binding and auth stop the unauthenticated case, but Redis is the one engine where a reached port can still become a root shell via its runtime CONFIG SET dir / dbfilename + SAVE write-anywhere gadget — so it gets one extra step the others don't: deny the dangerous commands with ACLs and run the process as a low-privilege service account, never root. The exact ACL recipe, the SSH-key and cron pivots it blocks, and the module/replication fallback are all in the Redis deep-dive.

  6. Hold staging, dev, and "it's just a cache" instances to the exact same bar. These get the least hardening and the loosest firewalling, yet they routinely hold production-shaped data — copied dumps, real PII for testing — on the same network as prod. The destructive campaigns did not check labels: Meow and the MongoDB ransom waves wiped anything reachable, staging included. A non-prod exposure is a prod exposure with extra steps.

  7. Monitor continuously, not once — because the threat is drift, not a static state. A clean scan is true only for the deploy you ran it on. The next release, an autoscale event, a security-group rule copied from another tier, or a debugging tweak that never got reverted can reopen any of these ports, and you will not notice from inside the host. Continuous external port scanning watches your perimeter for the 27017 / 9200 / 6379 / 11211 / 5984 / 9042 / 2379 set the way an attacker's Shodan alert would — flagging a datastore the moment it becomes reachable, before it gets indexed and before it makes anyone's pre-built target list.

  8. Further reading. If the exposed port is Redis, go straight to Exposed Redis on port 6379 for the full attack chain, RediShell, and the complete hardening checklist. If you're a small team and this deep-dive is more than you have time for today, start with other fast hardening wins for small teams — the lighter, broader sibling to this post. Come back here when you've got a datastore on a public IP and need the whole-class picture.

If you take one thing away, make it this: the only durable control is making sure the port is never reachable in the first place, and the only way to know that stayed true after your last deploy is to keep watching from the outside. FortWatch's port monitoring scans your external IPs for exactly this datastore set on a recurring cadence and pages you the moment 27017, 9200, 6379, 11211, 5984, 9042, or 2379 becomes reachable — so a misbind is something you fix in hours, not something Shodan finds for you first.

Share this post
Get started

Ready to secure your infrastructure?

Try for free — scan your entire attack surface in under 5 minutes. No credit card required.

  • No credit card required

  • 14-Day free trial

Ready to secure your stack?

Secure your entire stack today

Start scanning in under 5 minutes. No credit card required. 14-day free trial included.