imapsync / imapsync

Imapsync is an IMAP transfers tool. The purpose of imapsync is to migrate IMAP accounts or to backup IMAP accounts. IMAP is one of the three current standard protocols to access mailboxes, the two others are POP3 and HTTP with webmails, webmails are often tied to an IMAP server. Upstream website is

Home Page:http://imapsync.lamiral.info

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Secure /tmp usage

orlitzky opened this issue · comments

I was running imapsync recently and noticed this flag description,

  --pidfile      str  : The file where imapsync pid is written,
                        it can be dirname/filename complete path.
                        The default name is imapsync.pid in tmpdir.

This was a red flag because using predictable filenames under /tmp is a well-known source of vulnerabilities: https://owasp.org/www-community/vulnerabilities/Insecure_Temporary_File

In this case, I verified that (with the default options), imapsync can be tricked into overwriting files belonging to other users. The simplest proof-of-concept uses a symlink. Newer linux distributions enable a sysctl to protect against this specific attack, but the example should still work on a vanilla kernel or on other UNIX systems, and more complicated attacks (not involving a symlink) are possible.

If you're on linux, start by ensuring that the aforementioned sysctl is disabled:

root # sysctl fs.protected_symlinks=0

Now, as a normal user, create a symlink from /tmp/imapsync.pid to /etc/passwd:

mjo $ ln -s /etc/passwd /tmp/imapsync.pid

This works because /tmp is world-writable. Anyone can create a file, directory, or symlink at that location before imapsync tries to use it. Now, run imapsync as root:

root # imapsync <whatever>

You'll find that /etc/passwd, belonging to root, has essentially been deleted by the unprivileged user. Most file operations will follow a symlink, and in this case, the PID file was written to /etc/passwd.

The fundamental issue is that /tmp is world-writable by design which leads to all sorts of exploits like this. There are a lot of complicated and clever workarounds needed to make using /tmp (or /var/tmp) secure. In C, there are standard functions like mkstemp() that will create a temporary file securely. I'm not much of a perl programmer, but I believe File::Temp does the same thing in perl.

The imapsync_cache directory has the same issue; I can create it before you run imapsync and then I'm allowed to modify your cache. And while I haven't tried it, I would expect that $CGI_TMPDIR_TOP and $CGI_HASHFILE have a similar fate.

root # imapsync whatever
You'll find that /etc/passwd, belonging to root, has essentially been deleted by the unprivileged user.

No. In this case, /etc/passwd, belonging to root, has essentially been deleted by root himself.

Show me an example with an unprivileged user.

It has literally been deleted by root himself, but the same can be said about any security exploit:

  1. User visits a malicious website
  2. The website exploits a buffer overflow in the browser
  3. The exploit installs ransomware and encrypts all of the user's files
  4. The user has installed the ransomware himself, so there's no problem?

I used "essentially" because, from root's perspective, all he has done is run imapsync normally, and one of his files was deleted without his knowledge. The attacker has leveraged imapsync in this case to do something that he normally wouldn't be able to do (delete /etc/passwd).

For a second proof of concept, create /tmp/imapsync_cache as one unprivileged user and make it world-writable. As another unprivileged user, run imapsync. While it's running, the first user can change the permissions on the cache, because he owns the directory that it lives in. Afterwards, the first user can change or delete the second user's cache files.

You verified that with root privileges, imapsync can be tricked into overwriting files belonging to other users.
Well, I don't have to verify that with root privileges, any command can be tricked into overwriting files belonging to other users, or doing infinite nasty things to any user, root included.

  • Give me an example tested without root privileges.

  • Give me a safer solution to the following need: find a default writable directory for a user where pieces of information from previous runs are stored and can be retrieved.

You verified that with root privileges, imapsync can be tricked into overwriting files belonging to other users.

It's tricked by another user into overwriting files that belong to root.

Well, I don't have to verify that with root privileges, any command can be tricked into overwriting files belonging to other users, or doing infinite nasty things to any user, root included.

That may be true in a very general sense, but the important details here are that (1) the unprivileged user gets to choose a file, belonging to root, to delete; and (2) root hasn't done anything else "wrong," all he did was run imapsync normally.

Give me an example tested without root privileges.

You can repeat the same example as an unprivileged user. I only chose root because it's the worst-case scenario and obviously unprivileged users should not be able to overwrite /etc/passwd (which breaks the machine). Here's an example that works on my machine:

  1. User apache creates a symlink from /tmp/imapsync.pid to /home/mjo/.ssh/github
  2. I run imapsync as my normal user account, mjo
  3. My github SSH key is deleted

This allows websites (often managed by third parties -- I work at a hosting company) to delete the admin's SSH keys.

Give me a safer solution to the following need: find a default writable directory for a user where pieces of information from previous runs are stored and can be retrieved.

A location under $HOME should do the trick. The XDG specification says,

The $XDG_STATE_HOME contains state data that should persist between (application) restarts, but that is not important or portable enough to the user that it should be stored in $XDG_DATA_HOME. It may contain:

  • actions history (logs, history, recently used files, …)
  • current state of the application that can be reused on a restart (view, layout, open files, undo history, …)

I think that sounds about right?

Ok, it sounds right.

User apache has usually no writable $HOME, for safety reasons.

The funny part is that I use:
$sync->{ tmpdir } ||= File::Spec->tmpdir( ) ;

So feel free to also report the issue to Perl designers.

perldoc File::Spec
...
    tmpdir
      Returns a string representation of the first writable directory from a
      list of possible temporary directories. Returns the current directory if
      no writable temporary directories are found. The list of directories
      checked depends on the platform; e.g. File::Spec::Unix checks
      $ENV{TMPDIR} (unless taint is on) and /tmp.

          $tmpdir = File::Spec->tmpdir();

I updated
https://imapsync.lamiral.info/TODO

...
SUGGESTED 2023_05_23 by Michael Orlitzky
Change default tmpdir location to $HOME/tmp/ or $HOME/
https://github.com/imapsync/imapsync/issues/399

Great, thank you.

The funny part is that I use: $sync->{ tmpdir } ||= File::Spec->tmpdir( ) ;

So feel free to also report the issue to Perl designers.

Hm, there should at least be a warning in the documentation for that function. These /tmp vulnerabilities have been around forever, and it's pretty stupid that programmers still have easy access to vulnerable functions. If you want to hear a good joke, read man 3 mktemp:

DESCRIPTION
       Never use this function; see BUGS.
...

BUGS
       Never  use  mktemp().   Some  implementations follow 4.3BSD and replace
       XXXXXX by the current process ID and a single letter, so that  at  most
       26  different  names  can be returned.  Since on the one hand the names
       are easy to guess, and on the other hand there is a race between  test‐
       ing whether the name exists and opening the file, every use of mktemp()
       is a security risk.  The race is avoided by mkstemp(3) and mkdtemp(3).

Change default tmpdir location to $HOME/tmp/ or $HOME/

To share a top-level location with other apps, you could try $XDG_STATE_HOME and default to $HOME/.local/state if $XDG_STATE_HOME is unset. But anywhere under $HOME should be safe; that's the important part. Thanks again!

How about using /tmp/imapsync-UID/
where UID is the Unix user identifier?

How about using /tmp/imapsync-UID/ where UID is the Unix user identifier?

I don't think it helps much because the user ids on a machine are all predictable as well. I can create /tmp/imapsync-0 through /tmp/imapsync-65535 almost as easily as I can create /tmp/imapsync_cache. Then it doesn't matter which UID executes imapsync, because I control all of the possible directories.

There is no Linux "standard" for a $HOME based ./tmp directory location afaik, but you might consider using $HOME/.local/tmp as I have noticed '.local' in a given users home area being used more and more for user based data (as opposed to system data). Especially for Debian/Ubuntu (since that's what I tend to use). I am sure "$HOME/tmp" will work fine, though, with that said. See: https://unix.stackexchange.com/a/313001/186861 and others..

Here is the current tmpdir.
It is "User defined or $HOME/tmp if $HOME defined or current directory"

sub tmpdir
{
        my $mysync = shift @ARG ;
        
        if ( ! defined( $mysync ) ) 
        {
                return( './' ) ;
        }
        elsif ( defined( $mysync->{ tmpdir } ) )
        {
                return( $mysync->{ tmpdir } ) ;
        }
        elsif ( defined( $ENV{ 'HOME' } ) )
        {
                return( $ENV{ 'HOME' } . '/tmp' ) ;
        }
        else
        {
                return( './' ) ;
        }
        
        return ;
}