[tor-bugs] #31183 [Core Tor/Tor]: Situational symlink attacks on ControlPortWriteToFile etc.

Tor Bug Tracker & Wiki blackhole at torproject.org
Wed Jul 17 09:41:15 UTC 2019


#31183: Situational symlink attacks on ControlPortWriteToFile etc.
-------------------------+-------------------------------------------------
     Reporter:  asn      |      Owner:  (none)
         Type:  defect   |     Status:  new
     Priority:  Medium   |  Milestone:  Tor: 0.4.2.x-final
    Component:  Core     |    Version:
  Tor/Tor                |   Keywords:  hackerone bug-bounty security
     Severity:  Normal   |  041-should?
Actual Points:           |  Parent ID:
       Points:  1        |   Reviewer:
      Sponsor:           |
-------------------------+-------------------------------------------------
 Here is a bug report from paldium on hackerone. It's basically a very
 situational and restricted local priviledge escalation against certain
 setups and threat models:

 {{{
 # Summary

 It is possible to change permissions of files through tor or
 gain write access to newly created ones. The target file can be
 chosen by a local attacker if an adjusted configuration is used.

 #How to reproduce

 - Given is a FreeBSD or Mac OS X system.
 - Tor is configured with a torrc file containing this line:
     ControlPortWriteToFile /tmp/control.txt
 - Optionally (race condition) this line is included as well:
     ControlPortFileGroupReadable 1
 - /tmp is a directory with sticky bit, i.e. trwxrwxrwx

 ##or

 - Given is a Unix-like (Linux) system.
 - Tor is configured with a torrc file containing this line:
     ControlPortWriteToFile /tmp/proof/control.txt
 - Optionally (race condition) this line is included as well:
     ControlPortFileGroupReadable 1
 - /tmp/proof is writable by the local attacker

 When tor starts, then it will eventually write all available control
 ports into file configured by "ControlPortWriteToFile". This file is not
 written directly into, but a temporary file is used (the extension
 ".tmp" is added to file name). If this file already exists, then it is
 simply truncated.

 See src/lib/fs/files.c start_writing_to_file for details, especially
 line 321 and following.

 The problem is that an attacker can simply create the temporary file
 before tor gets the chance to. On Mac this attack works by default
 against /tmp, but Linux has a protection against symlink attacks on
 directories with sticky bit like /tmp or /var/tmp. Therefore it takes an
 unusual configuration on Linux or a different (regular) directory.

 Let the attacker create a file which tor, even with dropped privileges,
 can write to:

 `attacker$ install -m 666 /dev/null /tmp/control.txt.tmp`

 The tor process will use this file without adjusting the permissions,
 because O_CREAT was not actually performed. Instead, O_TRUNC simply
 truncated the file to remove existing content.

 Afterwards tor will write content to this file, which the attacker could
 simply override at this point. But there is no real need to, because
 the target file will still keep the permissions of this temporary file
 after rename.

 See src/lib/fs/files.c replace_file for details. Basically it's a
 simple rename() call which therefore removes the target inode and
 renames the temporary file to the target one (in this setup).


 At this point, the first attack is finished. The file /tmp/control.txt
 is under control of the attacker. If "ControlPortFileGroupReadable 1"
 is not given, this exploit code works on Linux systems as well (you
 can skip the proof/ part on Mac due to lack of /tmp protection):

 ```In torrc: ControlPortWriteToFile /tmp/proof/control.txt
 attacker$ install -Dm 666 /dev/null /tmp/proof/control.txt.tmp
 root$ tor -f torrc
 attacker$ ls -l /tmp/proof/control.txt
 -rw-rw-rw- 1 attacker  attacker    0 Jun 24 22:59
 /tmp/proof/control.txt```


 ##Second attack

 The second attack uses a race condition. Because /tmp/control.txt is
 under control of the attacker, the file can be deleted and replaced with
 a symbolic link to a target which we want to get group read permissions
 for. This is a possible scenario if "ControlPortFileGroupReadable 1" is
 given.

 In this case, chmod() is called in tor process. See
 src/feature/control/control.c control_ports_write_to_file for more
 details, especially line 149 for more details (chmod).

 The system call chmod() uses a file path as an argument. There is no
 guarantee that the file path still refers to the file which has been
 created by the tor process. Furthermore, chmod() follows symbolic
 links, therefore the referenced target file is adjusted by chmod.

 This is obviously a race condition but can be used to gain read access
 to files which -- even by configuration -- should be private to the
 tor user.

 The attacker still needs group-read permissions, otherwise the newly
 revealed files still cannot be accessed.

 ##Example

 `tor just renamed controlled /tmp/control.txt.tmp to /tmp/control.txt`
 `attacker$ rm /tmp/control.txt`
 `attacker$ ln -s /var/lib/tor /tmp/control.txt`
 `tor calls chmod on /tmp/control.txt and therefore on /var/lib/tor`
 The attacker, if part of tor's group, can access /var/lib/tor now as well
 (only read, but hierarchy is known through other tor installations)

 #Solution

 Use fchmod() on a file which has been opened with open() and O_NOFOLLOW
 to prevent changing any files which have been reached through symbolic
 links. Preventing symbolic links is already a great deal in file
 src/lib/fs/dir.c check_private_dir, line 85. Could be implemented as
 tor_chmod() to fix other chmod() calles in the code as well.

 Use mkstemp() to atomically create a temporary file which has the
 guaranteed permission 600 and owned by the tor user.

 ## Impact

 A local attacker can modify the content of files which are considered
 trusted by dependent tools, e.g. the control port file.
 Also a local attacker can extend privileges of files which are supposed to
 be private to the tor user and not readable by the group.

 Timeline:
 2019-06-25 10:24:59 +0000: @paldium (comment)
 To clarify one aspesct about attack 1: Of course it is not a tor issue if
 someone creates a world writable directory and lets tor create files in
 there. Everyone could simply override the resulting file and tor will
 never be able to prevent that. Therefore, the rename()-issue is a non-
 issue on Linux. But as a user I would expect that temporary files within
 /tmp with sticky bits are properly handled.

 But attack 2 (race condition) should even be prevented in a world writable
 directory (or writable by a possibly malicious user). Therefore I consider
 attack 2 on Linux to be unlikely but plausible.

 I would like to add a patch to this, but I am not sure how you want to
 handle file permissions.

 The nicest approach would be to supply the permissions to the function
 which creates the file, but that would be a no-op on Windows.

 Otherwise tor_chmod could open() the file and at least prevent symbolic
 link attacks. Yet, files could still be modified which are not the ones we
 created.

 As this is a design decision, I haven't started writing a patch because I
 do not know which one you would prefer.
 }}}

 Here is some further information:
 {{{

 ControlPortWriteFile is one example. The same attack scenarios are true
 for:
 - ExtORPortCookieAuthFileGroupReadable 1
 - CookieAuthFileGroupReadable 1
 - DataDirectoryGroupReadable 1
 - CacheDirectoryGroupReadable 1
 - KeyDirectoryGroupReadable 1
 }}}

 Here is some of my analysis for attack scenarios:

 {{{
 Hello @paldium,

 thanks for submitting this report.

 Here is the best attacks we could find given the bugs you gave us:

 Attack 1) The most realistic attack scenario I could think of is a system
 where the attacker is a local user who cannot establish outgoing
 connections, but is able to overwrite the ControlPortWriteToFile file, and
 replaces it with an attacker controlled IP:PORT, and then a controller
 program connects to the evil IP:PORT thereby deanonymizing the user. This
 seems to be a very artificial scenario, which assumes a particular threat
 model, and a tor with specific configuration parameters, and a very
 specific system,.

 Attack 2) The most realistic attack scenario here would be the attacker
 using this "read anything controlled by Tor" race condition attack to
 learn the private keys of an onion service that are on the same system but
 not able to read them... I'm actually not sure if that would work, but I
 think it's possible. This also assumes a particular threat-model, a multi-
 user system, and specific configuration parameters.

 @paldium, would it be possible to outline the various solutions we have
 for fixing this issue?I don't think specifying the permissions in the
 torrc is a nice thing from a UX perspective. Perhaps not following
 symlinks is a start but not the whole thing? Maybe we should just abort if
 there is a dangerous configuration? I wonder how prevalent this is.
 }}}

 and here is suggestions on patching:
 {{{
 Hi @asn,

 I agree here, the attack is impossible against default setups and takes
 quite specific steps to be exploitable.

 To fix this vulnerability at its root, I recommend to adjust the function
 `start_writing_to_file` in `src/lib/fs/files.c`. The system call `mkstemp`
 (included in POSIX) guarantees to create a unique file name for the
 (optional) temporary file. This way, an attacker cannot prepare a file
 before tor tries to create it. In case of conflict, mkstemp iterates
 through a huge pool of possible names and if all fail, it returns -1.

 It must be checked if mkstemp is a viable option on all target systems,
 especially Windows. But it's POSIX, so it should work.

 The attached patch performs these changes, but breaks a test which would
 be redundant then (it fails because it tries to create a temporary file
 beforehand, which `mkstemp` successfully prevents).

 ###Next improvement to consider (for attack 2):

 As far as I understand the code, possible modes for files are:
 - 0600 (default)
 - 0640 (if configuration requests group-writable files, non-Windows
 systems only)
 - 0400 (tor-gencert, which is not expected to run on Windows according to
 manual page)

 If the special case 0400 is handled in tor-gencert directly, the functions
 in `src/lib/fs/files.c` can be further reduced in their feature set: Just
 add a "group readable" attribute to these functions and remove the
 explicit `mode` (if present):
 - start_writing_to_stdio_file
 - start_writing_to_file
 - write_str_to_file
 - write_bytes_to_file

 All these functions would use 0600 by default and only support 0640 if the
 boolean "group readable" flag is set to true -- and that will only happen
 on non-Windows systems.

 With these changes in place, the remaining `chmod` calls (except for the
 control socket) can be removed and that will also fix the second attack
 (and gives full atomic control to the newly introduced `fchmod` call):

 ###Before:

 ```
   if (write_str_to_file(options->ControlPortWriteToFile, joined, 0) < 0) {
     log_warn(LD_CONTROL, "Writing %s failed: %s",
              options->ControlPortWriteToFile, strerror(errno));
   }
 #ifndef _WIN32
   if (options->ControlPortFileGroupReadable) {
     if (chmod(options->ControlPortWriteToFile, 0640)) {
       log_warn(LD_FS,"Unable to make %s group-readable.",
                options->ControlPortWriteToFile);
     }
   }
 #endif /* !defined(_WIN32) */
 ```
 ###After:
 ```
   if (write_str_to_file(options->ControlPortWriteToFile,
                         options->ControlPortFileGroupReadable, joined, 0)
 < 0) {
     log_warn(LD_CONTROL, "Writing %s failed: %s",
              options->ControlPortWriteToFile, strerror(errno));
   }
 }}}

--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/31183>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online


More information about the tor-bugs mailing list