CVE-2023-4651: Server-Side Request Forgery (SSRF) (CWE-918)
value: Server-Side Request Forgery (SSRF) in GitHub repository instantsoft/icms2 prior to 2.16.1.
CWE description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
CVSS v3 base score: 5.4
Published: 2023-08-31T01:15Z
Repository: https://github.com/instantsoft/icms2
Language: PHP
Files changed: system/core/uploader.php, system/libs/files.helper.php
Commit date: 2023-08-14 14:28:13 +0300
Commit message: Fix SSRF Blind in the image upload
Diff: diff --git a/system/core/uploader.php b/system/core/uploader.php
index bad651838..279f34de3 100755
--- a/system/core/uploader.php
+++ b/system/core/uploader.php
@@ -339,12 +339,29 @@ class cmsUploader {
      */
     public function uploadFromLink($post_filename, $allowed_size = 0, $destination = false) {
 
         $link = $file_name = trim($_POST[$post_filename]);
 
+
+        if (
+            // Валидный URL с PATH
+            filter_var($link, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED) !== $link ||
+            // По IP адресу не разрешаем
+            preg_match('#^(?:(?:https?):\/\/)([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*#ui', $link)
+            ) {
+
+            return [
+                'success' => false,
+                'error'   => 'Not allowed',
+                'name'    => '',
+                'path'    => ''
+            ];
+        }
+
         // проверяем редирект и имя файла
         $curl = curl_init();
+        curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
         curl_setopt($curl, CURLOPT_URL, $link);
         curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
         curl_setopt($curl, CURLOPT_HEADER, true);
         curl_setopt($curl, CURLOPT_NOBODY, true);
         curl_setopt($curl, CURLOPT_TIMEOUT, 5);
diff --git a/system/libs/files.helper.php b/system/libs/files.helper.php
index 3b548079d..fa41b13ea 100755
--- a/system/libs/files.helper.php
+++ b/system/libs/files.helper.php
@@ -313,10 +313,15 @@ function file_get_contents_from_url($url, $timeout = 5, $json_decode = false, $p
 
     if (!function_exists('curl_init')) {
         return null;
     }
 
+    // По IP адресу не разрешаем
+    if (preg_match('#^(?:(?:https?):\/\/)([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*#ui', $url)) {
+        return null;
+    }
+
     $curl = curl_init();
 
     if (strpos($url, 'https') === 0) {
         curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
         curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
@@ -340,10 +345,11 @@ function file_get_contents_from_url($url, $timeout = 5, $json_decode = false, $p
     if (!empty($params)) {
         foreach ($params as $key => $value) {
             $headers[] = $key . ': ' . $value;
         }
     }
+    curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
     curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
     curl_setopt($curl, CURLOPT_URL, $url);
     curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeout);
     curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
     curl_setopt($curl, CURLOPT_HEADER, false);
@@ -366,32 +372,35 @@ function file_get_contents_from_url($url, $timeout = 5, $json_decode = false, $p
  * Сохраняет удаленно расположенный файл
  * @param string $url url файла
  * @param string $destination Полный путь куда сохраненить файл
  * @return boolean
  */
-function file_save_from_url($url, $destination){
+function file_save_from_url($url, $destination) {
 
-    if (!function_exists('curl_init')){ return false; }
+    if (!function_exists('curl_init')) {
+        return false;
+    }
 
     $dest_file = @fopen($destination, "w");
 
     $curl = curl_init();
-    if(strpos($url, 'https') === 0){
+    if (strpos($url, 'https') === 0) {
         curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
         curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
     }
+    curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
     curl_setopt($curl, CURLOPT_URL, $url);
     curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
     curl_setopt($curl, CURLOPT_FILE, $dest_file);
     curl_setopt($curl, CURLOPT_HEADER, false);
     curl_exec($curl);
     curl_close($curl);
     fclose($dest_file);
 
     return true;
-
 }
+
 /**
  * Накладывает ваттермарк на изображение
  *
  * УСТАРЕВШАЯ ФУНКЦИЯ
  *

Vulnerable code: function file_save_from_url($url, $destination){
    if (!function_exists('curl_init')){ return false; }
    if(strpos($url, 'https') === 0){

Fixed code: 
        if (
            // Валидный URL с PATH
            filter_var($link, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED) !== $link ||
            // По IP адресу не разрешаем
            preg_match('#^(?:(?:https?):\/\/)([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*#ui', $link)
            ) {

            return [
                'success' => false,
                'error'   => 'Not allowed',
                'name'    => '',
                'path'    => ''
            ];
        }

        curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
    // По IP адресу не разрешаем
    if (preg_match('#^(?:(?:https?):\/\/)([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*#ui', $url)) {
        return null;
    }

    curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
function file_save_from_url($url, $destination) {
    if (!function_exists('curl_init')) {
        return false;
    }
    if (strpos($url, 'https') === 0) {
    curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);