Vulnerability: October CMS upload process vulnerable to RCE via Race Condition ### Impact

This advisory affects plugins that expose the `October\Rain\Database\Attach\File::fromData` as a public interface. This vulnerability does not affect vanilla installations of October CMS since this method is not exposed or used by the system internally or externally.

When the developer allows the user to specify their own filename in the `fromData` method, an unauthenticated user can perform remote code execution (RCE) by exploiting a race condition in the temporary storage directory.

### Patches

The issue has been patched in Build 476 (v1.0.476) and v1.1.12 and v2.2.15.

### Workarounds

Apply https://github.com/octobercms/library/commit/fe569f3babf3f593be2b1e0a4ae0283506127a83 to your installation manually if unable to upgrade to Build 476 (v1.0.476) or v1.1.12 or v2.2.15.

### References

Credits to:
- DucNT, HungTD and GiangVQ from RedTeam@VNG Security Response Center.

### For more information

If you have any questions or comments about this advisory:
- Email us at [hello@octobercms.com](mailto:hello@octobercms.com)
Vulnerability ID: GHSA-8v7h-cpc2-r8jp
CWE: CWE-362
Severity score: 8.1
Project: https://github.com/octobercms/library
Published: 2022-07-13
File: src/Database/Attach/File.php
Commit message: Refactor
Commit author: Sam Georges

Vulnerable version (before fix):
 public function fromPost($uploadedFile)     /**      * fromFile creates a file object from a file on the disk      */     public function fromFile($filePath)     {         if ($filePath === null) {             return;         }          $file = new FileObj($filePath);         $this->file_name = $file->getFilename();         $this->file_size = $file->getSize();         $this->content_type = $file->getMimeType();         $this->disk_name = $this->getDiskName(); public function fromData($data, $filename)             return;         }          $tempPath = temp_path(basename($filename));         FileHelper::put($tempPath, $data);          $file = $this->fromFile($tempPath);         FileHelper::delete($tempPath);          return $file;

Fixed version (after fix):
 public function fromPost($uploadedFile)     /**      * fromFile creates a file object from a file on the disk      */     public function fromFile($filePath, $filename = null)     {         if ($filePath === null) {             return;         }          $file = new FileObj($filePath);         $this->file_name = empty($filename) ? $file->getFilename() : $filename;         $this->file_size = $file->getSize();         $this->content_type = $file->getMimeType();         $this->disk_name = $this->getDiskName(); public function fromData($data, $filename)             return;         }          $tempName = str_replace('.', '', uniqid('', true)) . '.tmp';         $tempPath = temp_path($tempName);         FileHelper::put($tempPath, $data);          $file = $this->fromFile($tempPath, basename($filename));         FileHelper::delete($tempPath);          return $file;

Patch:
patch: @@ -124,14 +124,14 @@ public function fromPost($uploadedFile)
     /**
      * fromFile creates a file object from a file on the disk
      */
-    public function fromFile($filePath)
+    public function fromFile($filePath, $filename = null)
     {
         if ($filePath === null) {
             return;
         }
 
         $file = new FileObj($filePath);
-        $this->file_name = $file->getFilename();
+        $this->file_name = empty($filename) ? $file->getFilename() : $filename;
         $this->file_size = $file->getSize();
         $this->content_type = $file->getMimeType();
         $this->disk_name = $this->getDiskName();
@@ -152,10 +152,11 @@ public function fromData($data, $filename)
             return;
         }
 
-        $tempPath = temp_path(basename($filename));
+        $tempName = str_replace('.', '', uniqid('', true)) . '.tmp';
+        $tempPath = temp_path($tempName);
         FileHelper::put($tempPath, $data);
 
-        $file = $this->fromFile($tempPath);
+        $file = $this->fromFile($tempPath, basename($filename));
         FileHelper::delete($tempPath);
 
         return $file;
path: src/Database/Attach/File.php