Skip to content

Commit 34b1087

Browse files
committed
Fix low-urgency security vulnerability: writing files to arbitrary directory by hijacking temp directories.
1 parent 74ed789 commit 34b1087

File tree

4 files changed

+67
-2
lines changed

4 files changed

+67
-2
lines changed

NEWS

+30
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,36 @@ Release 4.0.37
1717
using this command guarantees that clients see no errors.
1818
* Fixed a crash occurs when an application fails to spawn, but the HTTP
1919
client disconnects before the error page is generated. Fixes issue #1028.
20+
* Fixed a symlink-related security vulnerability.
21+
22+
Urgency: low
23+
Scope: local exploit
24+
Summary: writing files to arbitrary directory by hijacking temp directories
25+
Affected versions: 4.0.5 and later
26+
Fixed versions: 4.0.37
27+
28+
Description:
29+
Phusion Passenger creates a "server instance directory" in /tmp during startup,
30+
which is a temporary directory that Phusion Passenger uses to store working files.
31+
This directory is deleted after Phusion Passenger exits. For various technical
32+
reasons, this directory must have a semi-predictable filename. If a local attacker
33+
can predict this filename, and precreates a symlink with the same filename that
34+
points to an arbitrary directory with mode 755, owner root and group root, then
35+
the attacker will succeed in making Phusion Passenger write files and create
36+
subdirectories inside that target directory. The following files/subdirectories
37+
are created:
38+
39+
* control_process.pid
40+
* generation-X, where X is a number.
41+
42+
If you happen to have a file inside the target directory called `control_process.pid`,
43+
then that file's contents are overwritten.
44+
45+
These files and directories are deleted during Phusion Passenger exit. The target
46+
directory itself is not deleted, nor are any other contents inside the target
47+
directory, although the symlink is.
48+
49+
Thanks go to Jakub Wilk for discovering this issue.
2050

2151

2252
Release 4.0.36

ext/common/ServerInstanceDir.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class ServerInstanceDir: public noncopyable {
213213
* generations no matter what user they're running as.
214214
*/
215215
if (owner) {
216-
switch (getFileType(path)) {
216+
switch (getFileTypeNoFollowSymlinks(path)) {
217217
case FT_NONEXISTANT:
218218
createDirectory(path);
219219
break;

ext/common/Utils.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,35 @@ getFileType(const StaticString &filename, CachedFileStat *cstat, unsigned int th
143143
}
144144
}
145145

146+
FileType
147+
getFileTypeNoFollowSymlinks(const StaticString &filename) {
148+
struct stat buf;
149+
int ret;
150+
151+
ret = lstat(filename.c_str(), &buf);
152+
if (ret == 0) {
153+
if (S_ISREG(buf.st_mode)) {
154+
return FT_REGULAR;
155+
} else if (S_ISDIR(buf.st_mode)) {
156+
return FT_DIRECTORY;
157+
} else if (S_ISLNK(buf.st_mode)) {
158+
return FT_SYMLINK;
159+
} else {
160+
return FT_OTHER;
161+
}
162+
} else {
163+
if (errno == ENOENT) {
164+
return FT_NONEXISTANT;
165+
} else {
166+
int e = errno;
167+
string message("Cannot lstat '");
168+
message.append(filename);
169+
message.append("'");
170+
throw FileSystemException(message, e, filename);
171+
}
172+
}
173+
}
174+
146175
void
147176
createFile(const string &filename, const StaticString &contents, mode_t permissions, uid_t owner,
148177
gid_t group, bool overwrite)

ext/common/Utils.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ typedef enum {
6565
FT_REGULAR,
6666
/** A directory. */
6767
FT_DIRECTORY,
68+
/** A symlink. Only returned by getFileTypeNoFollowSymlinks(), not by getFileType(). */
69+
FT_SYMLINK,
6870
/** Something else, e.g. a pipe or a socket. */
6971
FT_OTHER
7072
} FileType;
@@ -110,7 +112,7 @@ bool fileExists(const StaticString &filename, CachedFileStat *cstat = 0,
110112
/**
111113
* Check whether 'filename' exists and what kind of file it is.
112114
*
113-
* @param filename The filename to check.
115+
* @param filename The filename to check. It MUST be NULL-terminated.
114116
* @param mstat A CachedFileStat object, if you want to use cached statting.
115117
* @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL.
116118
* @return The file type.
@@ -121,6 +123,10 @@ bool fileExists(const StaticString &filename, CachedFileStat *cstat = 0,
121123
*/
122124
FileType getFileType(const StaticString &filename, CachedFileStat *cstat = 0,
123125
unsigned int throttleRate = 0);
126+
/**
127+
* Like getFileType(), but does not follow symlinks.
128+
*/
129+
FileType getFileTypeNoFollowSymlinks(const StaticString &filename);
124130

125131
/**
126132
* Create the given file with the given contents, permissions and ownership.

0 commit comments

Comments
 (0)