1
0
mirror of https://github.com/misterunknown/ifm.git synced 2025-08-09 01:26:32 +02:00

Security update: Prevent SSRF attacks by default.

This commit is contained in:
Marco Dickert
2023-11-23 15:42:15 +01:00
parent 42eee5c283
commit deb707865b
3 changed files with 56 additions and 3 deletions

View File

@@ -105,6 +105,7 @@
"upload_overwrite_hint": "Die folgenden Dateien würden überschrieben:", "upload_overwrite_hint": "Die folgenden Dateien würden überschrieben:",
"upload_remote": "Hochladen von außerhalb", "upload_remote": "Hochladen von außerhalb",
"upload_remote_url": "Entfernte URL zum Hochladen", "upload_remote_url": "Entfernte URL zum Hochladen",
"url_not_allowed": "Diese URL ist nicht erlaubt.",
"username": "Benutzername", "username": "Benutzername",
"word_wrap": "Zeilenumbruch" "word_wrap": "Zeilenumbruch"
} }

View File

@@ -107,6 +107,7 @@
"upload_overwrite_hint": "The following files will be overwritten:", "upload_overwrite_hint": "The following files will be overwritten:",
"upload_remote": "Remote Upload", "upload_remote": "Remote Upload",
"upload_remote_url": "Remote Upload URL", "upload_remote_url": "Remote Upload URL",
"url_not_allowed": "This URL is not allowed.",
"username": "Username", "username": "Username",
"word_wrap": "Word Wrap" "word_wrap": "Word Wrap"
} }

View File

@@ -47,6 +47,7 @@ class IFM {
"extract" => 1, "extract" => 1,
"upload" => 1, "upload" => 1,
"remoteupload" => 1, "remoteupload" => 1,
"remoteupload_disable_ssrf_check" => 0,
"rename" => 1, "rename" => 1,
"zipnload" => 1, "zipnload" => 1,
"createarchive" => 1, "createarchive" => 1,
@@ -842,17 +843,19 @@ f00bar;
if (!isset($d['method']) || !in_array($d['method'], ["curl", "file"])) if (!isset($d['method']) || !in_array($d['method'], ["curl", "file"]))
throw new IFMException($this->l('invalid_params')); throw new IFMException($this->l('invalid_params'));
if ($this->config['remoteupload_disable_ssrf_check'] != 1)
if (!$this->checkUrlSsrf($d['url']))
throw new IFMException($this->l('url_not_allowed'));
if ($d['method'] == "curl" && $this->checkCurl() == false) if ($d['method'] == "curl" && $this->checkCurl() == false)
throw new IFMException($this->l('error')." cURL extention not installed."); throw new IFMException($this->l('error')." cURL extention not installed.");
if ($d['method'] == "curl" && $this->checkCurl() == true) { if ($d['method'] == "curl") {
$filename = (isset($d['filename']) && $d['filename'] != "") ? $d['filename'] : "curl_".uniqid(); $filename = (isset($d['filename']) && $d['filename'] != "") ? $d['filename'] : "curl_".uniqid();
$ch = curl_init(); $ch = curl_init();
if ($ch) { if ($ch) {
if ($this->isFilenameValid($filename) == false) if ($this->isFilenameValid($filename) == false)
throw new IFMException($this->l('invalid_filename')); throw new IFMException($this->l('invalid_filename'));
elseif (filter_var($d['url'], FILTER_VALIDATE_URL) === false)
throw new IFMException($this->l('invalid_url'));
else { else {
$fp = fopen($filename, "w"); $fp = fopen($filename, "w");
if ($fp) { if ($fp) {
@@ -1297,6 +1300,54 @@ f00bar;
return true; return true;
} }
/**
* This function checks the URL for potential SSRF attacks. Allowed is only
* http/ftp and only global IP addresses. You can disable the SSRF check in
* the configuration.
*/
public function checkUrlSsrf($url) {
if (!filter_var($url, FILTER_VALIDATE_URL))
return false;
$parts = parse_url($url);
if (!$parts)
return false;
// no host is not acceptable
if (!isset($parts['host']))
return false;
// other protocols than http(s) or ftp are not allowed (curl assumes http per default)
if (isset($parts['scheme']) && !in_array(strtolower($parts['scheme']), ['http', 'https', 'ftp']))
return false;
// if the host is no IP, resolve the hostname
$ips = [];
if (filter_var($parts['host'], FILTER_VALIDATE_IP))
array_push($ips, $parts['host']);
else
$ips = array_merge($ips, array_map(fn($i) => $i['ip'] ?? $i['ipv6'], dns_get_record($parts['host'], DNS_A + DNS_AAAA)));
if (empty($ips))
return false;
// check if any of the IPs is not global, if so then fail
foreach ($ips as $ip) {
if (version_compare(PHP_VERSION, '8.2.0') >= 0) {
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_GLOBAL_RANGE)) {
return false;
}
} else {
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE + FILTER_FLAG_NO_RES_RANGE)) {
return false;
}
}
}
return true;
}
private function fileDownload(array $options) { private function fileDownload(array $options) {
if (!isset($options['name']) || trim($options['name']) == "") if (!isset($options['name']) || trim($options['name']) == "")
$options['name'] = basename($options['file']); $options['name'] = basename($options['file']);