Picture of Jürgen Kreileder

OS X Applications Insecurely Installing World-Writable Files

Files, directories, and devices that are writable by any user (“world-writable”) on a multi-user system can be dangerous locally exploitable security holes. There are very few legitimate reasons for having world-writable files and directories on a system.

Many UNIX and Linux systems actually have cron jobs that check for world-writable files. On Apple’s OS X there is no such safeguard and many vendors do not seem to care about file permissions much at all. Several well-known applications are either installed with world-writable files or create them when used:

World-writable files in system directories

The following applications install world-writable files in shared directories (/Applications, /Library, …):

  • Adobe CS 4, CS 5: Some uninstallers + several files and directories in /Library/Application Support + various stuff in other locations
  • Adobe Media Player: directory + some files in Contents/Resources
  • Adobe Flash Player: directories (including AddIns und AddIns/airappinstaller)
  • Amazon MP3 Downloader: some directories
  • EPSON (Scan, TWAIN data source, Easy Photo Print, …): pretty much everything, including executables
  • Eye-One Match 3: complete app, including executable
  • eMusic Download Manager: complete app, including executable and JavaScript (the application is based on Mozilla)
  • Telltale games: complete apps including executable and libraries
  • Apple OS X: some plist and cache files, including at least one LaunchDaemon plist file
  • Google+Growl Utility (not a Google product): whole app including executable
  • HP Scan Pro (plus supporting files): everything including executables
  • DivX Converter: resource files
  • Apple Remote Desktop: some plist files
  • Apple GarageBand: several plist and data files
  • Apple ColorSync: some profiles
  • Microsoft Office 2011: directory in /Library Application Support
  • Elgato EyeTV: several plist files
  • ABBYY FineReader Sprint 8.0: several data files
  • ArcSoft (Connect Suite, MediaImpression 2): all files, including executables
  • Extensis Suitcase Fusion 2: all files, including executables

World-writable files in user directories

The following applications install world-writable files in user directories (/Users/$USER):

  • GoogleGrowl.plugin: whole plugin including executable
  • 3rd-party extensions for Apple Safari: some extensions (e.g. AdBlock) install world-writable files
  • Apple iPhoto: the whole library seems to be world-writable
  • Adium add-ons: several add-ons install world-writable files
  • eMusic Download Manager:some preferences files are world-writable
  • Adobe (CS 4, CS5, Flash, …): several preferences files
  • Apple MobileDevice: crash logs are world-writable

The lists have been compiled by inspecting my own systems and those of several friends by running

sudo sh -c \
  "find / -xdev -perm +o=w ! \( -type d -perm +o=t \) ! -type l -print0 | \
   xargs -0 ls -dl 2>&1 | \
   tee world-writable-files.txt"

and analyzing the output.

Note that running Disk Utility‘s “Repair Disk Permissions” does not have any influence on the issues described here.

Most OS X installations are probably single-user systems in reality but the situation is still somewhat ugly.

This article Jürgen Kreileder is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

2 Comments

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post. Both comments and pings are currently closed.

Injerto said

It’s very disgusting, indeed. I wonder what happens with applications installed via Appstore. The way you show it, if you grant remote access to a guest account, they (or some malicious script running on their machines) could replace your applications with infected executables. May be a chmod -R could help?

Besides dangerous file and directory permissions I am also concerned with ownerships: everything in /System, /Library and /Applications should be owned by system uids and not by real human users as the security implications are quite similar. Unfortunately, the OSX-typical practice of users installing apps by moving them into /Applications will create just this scenario, and also apps which do come with installers will often have their items installed using undesirable user and group ownerships (even non-existing users and groups which probably slipped through from the development or build systems).

This problem has bothered me enough to come up with below script to check and fix the /Applications and /Library directory trees. (I leave /System, /usr, /bin etc. alone since I found no problems here on my machines, but the script will still display a short report about “noteworthy stuff” in these directories, too.)

Enjoy!
Arndt

#!/bin/bash -e
# vi: set ts=4 sw=4 ai:

# normalize file ownerhip and permissions for /Applications and /Library
# version: 1.0 2013-08-03 for Mac OS X 10.8.4

DOIT=-1 # default: ask for confirmation before making any changes
AREA=ALO # default: do /Applications, /Library and other checks

while (($# > 0))
do
case “$1” in
-y|–yes) DOIT=1;; # don’t ask, just do it
-n|–no) DOIT=0;; # don’t ask, don’t do it
-A) OAPP=1;; # select /Applications
-L) OLIB=1;; # select /Library
-O) OOTH=1;; # select other checks
*) echo >&2 “${0##*/}: bad args”; exit 2;;
esac
shift
done

[[ -z “$OAPP$OLIB$OOTH” ]] || AREA=”${OAPP:+A}${OLIB:+L}${OOTH:+O}”

if [[ $(id -u) != 0 ]]
then
echo “${0##*/}: please run this script as root”
exit 2
fi

M() { (IFS=” “; printf “\n### %s\n\n” “$*”) }

A()
{
if ((${#ITEMS[*]} == 0))
then
echo “(nothing to do)”
return 0
fi

# list all offending files and dirs
printf “%s\n” “${ITEMS[@]}” | tr ’12’ ” | xargs -0 ls -leOd

if (($DOIT == 0))
then
return 0

elif (($DOIT < 0))
then
while :
do
(IFS=" "; echo -n "Command for these items: $* — execute (y|n) ? ")
read yn
[[ $yn = n ]] && return 2
[[ $yn = y ]] && break
done
fi

# execute the given command on all offending files and dirs
if [[ $1 != SetOwnerGroup ]]
then
printf "%s\n" "${ITEMS[@]}" | tr '12' '' | xargs -0t "$@"
return $?
else
for i in "${ITEMS[@]}"
do
grp=$(stat -f %Sg "${i%/*}") && [[ $grp = admin ]] || grp=wheel
(set -x; chown -h "root:$grp" "$i")
done
fi
}

# IFS must be set to \n for the ITEMS=($(…)) assignments below
IFS="${IFS#??}"

if [[ $AREA = *A* ]]
then
echo
echo ====== /Applications ======

M remove .DS_Store files except those inside an app and belonging to root

ITEMS=($(
find -s /Applications \
-name .DS_Store "(" ! -user root -or ! -path "*.app/.DS_Store" ! -path "*.app/*/.DS_Store" ")"
))

A rm -f

M make everything below /Applications world readable and executable

ITEMS=($(
find -s /Applications ! -perm -0044 -or -perm +0100 ! -perm -0011
))

A chmod -h go+rX

M revoke write permission for group and other

ITEMS=($(
# valid as of 10.8.4:
# all files and dirs should be ???r-Xr-X, except
# – g=w: /Applications/, /Applications/System Preferences.app//
# – g=w is okay if user=root and group=admin
# A violation of the exception condition (i.e. if o=w) will cause
# the removal of the g=w permission, too.
find -sE /Applications \
! "(" -regex "/Applications/System Preferences.app(/.*)?" -or -user root -group admin ")" -perm +0022 \
-or -perm +0002
))

A chmod -h go-w

M assign all apps, subdirectories and files to user root, group wheel

ITEMS=($(
# 10.8.4: all files and dirs should be root:wheel or root:admin
find -s /Applications ! "(" -user root "(" -group wheel -or -group admin ")" ")"
))

A chown -h root:wheel
fi

if [[ $AREA = *L* ]]
then
echo
echo "====== /Library ======"

M remove all .DS_Store files

ITEMS=($(
find -s /Library -name .DS_Store
))

A rm -f

M ensure universal readability and executability

ITEMS=($(
find -s /Library \
-user root -or -path "/Library/Caches/*" \
-or "(" ! -perm -0044 -or -perm +0100 ! -perm -0011 ")" -print
))

A chmod -h go+rX

M revoke write permission for other

ITEMS=($(
# generally leave files alone which are owned by root, but make sure
# /Library/Application Support// gets fixed
find -sE /Library \
-regex "/Library/Application Support(/.*)?" -perm +0002 -print \
-or -user root -or -path "/Library/Caches/*" -or -path "/Library/Preferences/*" \
-or -perm +0002 -print
))

A chmod -h o-w

M assign stray items of group wheel or admin to user root

ITEMS=($(
find -s /Library \
! -path "/Library/Caches/*" ! -path "/Library/Logs/*" \
! -user root "(" -group wheel -or -group admin ")" |
while read i; do
# filter by uid to ignore items owned by system users
[[ $(stat -f %u "$i") -lt 500 && $(stat -f %Su "$i") != "("*")" ]] || echo "$i"
done
))

A chown -h root

# only proceed if above step has been successfully executed
if (($? == 0 && $DOIT != 0))
then
M for stray items of other groups, use the group of the parent dir

ITEMS=($(
find -s /Library \
! -path "/Library/Caches/*" ! -path "/Library/Logs/*" \
"(" ! -user root -or ! -group wheel ! -group admin ")" |
perl -e 'print reverse ‘ |
while read i; do
# filter by uid to ignore items owned by system users
[[ $(stat -f %u “$i”) -lt 500 && $(stat -f %Su “$i”) != “(“*”)” ]] || echo “$i”
done
))

A SetOwnerGroup
fi
fi

if [[ $AREA = *O* ]]
then
echo
echo “====== other checks ======”

M check for unsual permissions and ownerships in /bin, /etc, /sbin, /usr, /System

# ignores softlinks, and items owned by root with group wheel or admin
# which don’t have the setuid, setgid and sticky bit set and which are
# only writable by user; lists everything else
find -s /bin /etc /sbin /usr /System \
-type l -or -user root “(” -group wheel -o -group admin “)” ! -perm +7022 -or -ls
fi

###