CVE-2016-10345: Permissions, Privileges, and Access Controls (CWE-264)
value: In Phusion Passenger before 5.1.0, a known /tmp filename was used during passenger-install-nginx-module execution, which could allow local attackers to gain the privileges of the passenger user.
CWE description: Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.
CVSS v3 base score: 7.8
Published: 2017-04-18T20:59Z
Repository: https://github.com/phusion/passenger
Language: Other
Files changed: CHANGELOG, bin/passenger-install-nginx-module
Commit date: 2016-12-05 16:15:24 +0100
Commit message: Fix writing a tmp file with a predictable name in
passenger-install-nginx-module.

With access to the system, a user could plant a symlink in /tmp that
resulted in a chosen-file overwrite attempt whenever
passenger-install-nginx-module was run, using the access rights of
the executing user, potentially even with chosen content.
Diff: diff --git a/CHANGELOG b/CHANGELOG
index f6396fd05..d6eaf2e9b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -14,10 +14,11 @@ Next version (not yet released)
  * Passenger now reports when you try to use Node.js or Meteor clustering, and tries to continue with just a nonfunctional shim in place, so that if your code uses the clustering APIs your app may still work.
  * Updates libev config.sub and config.guess to support newer platforms such as the IBM power 8.
  * Fixes an issue where passenger-config couldn't restart an app if the TMPDIR variable was set to /tmp
  * `passenger-install-apache-module` now suggests the correct apache package on Ubuntu Xenial. Closes GH-1884.
  * [Standalone] The TempDirToucher will now spend most of its time with reduced privileges, except when it's actively touching files. This allows it to be killed when Passenger is quit in most circumstances. Closes GH-1678.
+ * Fixes a file overwrite vulnerability caused by a predictable temporary file being written by `passenger-install-nginx-module`. Thanks to Jeremy Evans for reporting this.
  * [Standalone] Fixes starting Passenger as a non-extant user. Closes GH-1849.
  * Improved look of the error pages for failing to spawn an application (development & production mode), and Error ID is now also shown in production mode.
  * [Standalone] Enable ipv6 support by default in builtin nginx. Closes GH-1873.
  * [Nginx] Updates to APT package builder (Debian & Ubuntu) with fix for www-data to root privilege escalation via log file handling (CVE-2016-1247/USN-3114-1).
  * [Nginx] Updates to RPM package builder (CentOS & RHEL) with fix for 1.10.x system nginx package overriding the nginx from the Passenger repo. Closes GH-1895.
diff --git a/bin/passenger-install-nginx-module b/bin/passenger-install-nginx-module
index 73bc9aafe..da66ac20b 100755
--- a/bin/passenger-install-nginx-module
+++ b/bin/passenger-install-nginx-module
@@ -36,15 +36,15 @@
 PhusionPassenger.locate_directories
 
 require 'digest/sha2'
 require 'optparse'
 require 'fileutils'
+require 'tmpdir'
 PhusionPassenger.require_passenger_lib 'platform_info/ruby'
 PhusionPassenger.require_passenger_lib 'platform_info/openssl'
 PhusionPassenger.require_passenger_lib 'abstract_installer'
 PhusionPassenger.require_passenger_lib 'utils/terminal_choice_menu'
-PhusionPassenger.require_passenger_lib 'utils/tmpio'
 PhusionPassenger.require_passenger_lib 'utils/shellwords'
 
 DOWNLOAD_OPTION = {
   :connect_timeout => 30,
   :idle_timeout    => 30
@@ -564,23 +564,25 @@ def build_nginx_configure_command(prefix, extra_configure_flags = nil)
     return command
   end
 
   def pcre_is_installed?
     if @pcre_is_installed.nil?
-      @pcre_is_installed = begin
-        File.open('/tmp/passenger-check.c', 'w') do |f|
-          f.puts("#include <pcre.h>")
-        end
-        Dir.chdir('/tmp') do
-          # Nginx checks for PCRE in multiple places...
-          system("(gcc -I/usr/local/include -I/usr/include/pcre " <<
-            "-I/usr/pkg/include -I/opt/local/include " <<
-            "-c passenger-check.c) >/dev/null 2>/dev/null")
+      Dir.mktmpdir do |safe_tmpdir|
+        @pcre_is_installed = begin
+          File.open("#{safe_tmpdir}/passenger-check.c", 'w') do |f|
+            f.puts("#include <pcre.h>")
+          end
+          Dir.chdir("#{safe_tmpdir}") do
+            # Nginx checks for PCRE in multiple places...
+            system("(gcc -I/usr/local/include -I/usr/include/pcre " <<
+              "-I/usr/pkg/include -I/opt/local/include " <<
+              "-c passenger-check.c) >/dev/null 2>/dev/null")
+          end
+        ensure
+          File.unlink("#{safe_tmpdir}/passenger-check.c") rescue nil
+          File.unlink("#{safe_tmpdir}/passenger-check.o") rescue nil
         end
-      ensure
-        File.unlink('/tmp/passenger-check.c') rescue nil
-        File.unlink('/tmp/passenger-check.o') rescue nil
       end
     end
     return @pcre_is_installed
   end
 

Vulnerable code: PhusionPassenger.require_passenger_lib 'utils/tmpio'
      @pcre_is_installed = begin
        File.open('/tmp/passenger-check.c', 'w') do |f|
          f.puts("#include <pcre.h>")
        end
        Dir.chdir('/tmp') do
          # Nginx checks for PCRE in multiple places...
          system("(gcc -I/usr/local/include -I/usr/include/pcre " <<
            "-I/usr/pkg/include -I/opt/local/include " <<
            "-c passenger-check.c) >/dev/null 2>/dev/null")
      ensure
        File.unlink('/tmp/passenger-check.c') rescue nil
        File.unlink('/tmp/passenger-check.o') rescue nil
Fixed code:  * Fixes a file overwrite vulnerability caused by a predictable temporary file being written by `passenger-install-nginx-module`. Thanks to Jeremy Evans for reporting this.
require 'tmpdir'
      Dir.mktmpdir do |safe_tmpdir|
        @pcre_is_installed = begin
          File.open("#{safe_tmpdir}/passenger-check.c", 'w') do |f|
            f.puts("#include <pcre.h>")
          end
          Dir.chdir("#{safe_tmpdir}") do
            # Nginx checks for PCRE in multiple places...
            system("(gcc -I/usr/local/include -I/usr/include/pcre " <<
              "-I/usr/pkg/include -I/opt/local/include " <<
              "-c passenger-check.c) >/dev/null 2>/dev/null")
          end
        ensure
          File.unlink("#{safe_tmpdir}/passenger-check.c") rescue nil
          File.unlink("#{safe_tmpdir}/passenger-check.o") rescue nil