On Wed, 22 Apr 2020 17:00:37 +0200 Stefan Schmidt <ste...@datenfreihafen.org>
said:

> Hello.
> 
> On 22.04.20 16:54, Carsten Haitzler (The Rasterman) wrote:
> > On Wed, 22 Apr 2020 11:02:46 +0200 Matthias Gerstner <mgerst...@suse.de>
> > said:
> > 
> > 
> > fixed. :)
> 
> And obviously a big thanks to Matthias for taking the time to analyze 
> and report the issues!

and simotek too for pushing this along. i though the best way to respect this
was to handle it asap. :)

> regards
> Stefan Schmidt
> 
> > 
> >> Hi,
> >>
> >> the SUSE security team has been asked [1] to review the new
> >> `enlightenment_system` setuid-root binary for inclusion into openSUSE
> >> Tumbleweed. Therefore I looked into the snapshot that our packager
> >> provided me with. It seems to correspond to git [2] commit
> >> b5fa86e7f5301452f4156ba62bc073f27280c798, at least with regard to the
> >> `enlightenment_system` code itself.
> >>
> >> [1]: https://bugzilla.suse.com/show_bug.cgi?id=1169238
> >> [2]: https://git.enlightenment.org/core/enlightenment.git
> >>
> >> # Security Issues
> >>
> >> After reviewing this setuid-root binary I don't deem the current state of
> >> it fit for production use. I have found the following individual issues:
> >>
> >> ## a) `_store_mount_verify()` follows symlinks in /media/$user
> >>
> >> This function rejects relative path components in the target mount path.
> >> It is unaware of symlinks, however. Furthermore it makes sure that
> >> /media/$user and /media/$user/$sub are existing and are owned by the
> >> $uid:$gid of the unprivileged user.
> >>
> >> - by placing a symlink in /media/$user/$sub the setuid-root binary can be
> >>    tricked into creating attacker owned directories in arbitrary locations.
> >>    This can quite likely lead to full root access by creating user owned
> >>    directories e.g. beneath /etc that are then used by other privileged
> >>    programs.
> >> - if the attacker wins a race condition he can also cause the setuid-root
> >>    binary to pass ownership of arbitrary existing directories to him. The
> >>    `_store_mount_verify()` function performs a single `stat()` call on the
> >>    target mount path. The operation is only rejected if it exists and is
> >>    not owned by the unprivileged user. Therefore if the attacker places
> >>    a suitable symlink in the target path just after this `stat()` is
> >>    performed by the setuid-root binary, the following `_mkdir()`
> >>    invocation will `mkdir()` and `chown()` the path components
> >>    nonetheless. This allows full root system access by gaining ownership
> >>    of e.g. /etc or /root.
> >>
> >> To fix this I suggest not to pass ownership of /media/$user or of any
> >> sub-directories to the unprivileged user. If /media/$user is user
> >> controlled then the mount operation should be rejected.
> >>
> >> ## b) `_store_umount_verify()` does not protect against shell
> >>        metacharacters and relative path components
> >>
> >> This function tries to make sure that the user can only unmount his own
> >> mounts below /media/$user. It also rejects backslashes in the path.
> >> However it does not reject relative path components or shell characters.
> >>
> >> - this allows a regular user to unmount arbitrary file systems by passing
> >>    paths like "/media/$user/../../tmp.
> >> - since the unmount is performed by calling the `umount` utility via
> >>    "/bin/sh", shell metacharacters will be interpreted. Passing a path
> >>    like '/media/testuser/$(date)' will cause the setuid-root program to
> >>    execute the `date` program as root. This leads to full code execution
> >>    as root. The only requirement is that a directory of the same name
> >>    exists. Spaces are also allowed in the path, therefore even complex
> >>    commands can be executed as root.
> >>
> >> I recommend to reject relative path components and shell metacharacters in
> >> this function to fix the issue.
> >>
> >> ## c) `_store_device_verify()` limitations are insufficient
> >>
> >> This function tries to make sure that the source device path argument
> >> for block device operations is within the confines of the /dev
> >> directory. To do so a lot of special characters are rejected as well as
> >> relative path components "/..". It fails to consider symlinks, however:
> >>
> >> - The /dev/fd directory on Linux is a symlink to /proc/self/fd.
> >>    Therefore an already open file descriptor can be used as device
> >>    argument. Open files are inherited from a potential attacker's context
> >>    into the setuid-root context, therefore this can be used to circumvent
> >>    the limitation. A prerequisite is that the attacker needs to have
> >>    necessary privileges to open a file descriptor for the source file.
> >>
> >> - The /dev/shm directory on Linux is a world-writable sticky-bit
> >>    directory. Therefore an unprivileged user can place symlinks in this
> >>    directory. `_store_device_verify()` will not reject such paths. Such
> >>    a symlink attack only works if the kernel symlink protection feature
> >>    is off, however. Or if the attacker wins a race condition, because
> >>    `_store_device_verify()` performs a `stat()` on the path and only
> >>    rejects the operation if the file can't be accessed. So an attacker
> >>    could first place a regular file in there and after the `stat()` is
> >>    performed it can replaced the file by a symlink. The setuid-root
> >>    program will then pass the path to the symlink when invoking child
> >>    programs e.g. an `eject /dev/shm/test` which points to /dev/sr0 worked
> >>    for me.
> >>
> >> To fix this, these two cases can be rejected by the function's logic.
> >> But in general it's difficult to filter out bad paths like this. To make
> >> it safer the path components would need to be opened step by step and
> >> each component would need to be checked against symlinks or otherwise
> >> user controlled content. This can be achieved by using system calls
> >> like `openat()` using O_PATH and `fstat()` starting from the root path
> >> component.
> >>
> >> ## d) `_cb_l2ping_ping()` performs an unbounded `sscanf()` on untrusted
> >>        input data, allowing a stack buffer overflow
> >>
> >> A `sscanf()` call in this function passes a `%s` format for the `params`
> >> input parameter. The target buffer has a length of 1024 bytes. Thus if a
> >> client passes a very long device name the setuid-root binary's stack
> >> will be overwritten. The parsing by `sscanf()` stops at whitespace
> >> characters thus the stack overflow data cannot be chosen arbitrarily.
> >> Still it is a pretty dangerous security issue.
> >>
> >> ## e) `_cb_l2ping_ping()` SEGFAULTs when no parameter is passed.
> >>
> >> This function unconditionally dereferences the `params` string in a
> >> `sscanf()` call, which causes a SEGFAULT by dereferencing a NULL pointer
> >> when a user is not passing any parameter data.
> >>
> >> ## f) /etc/enlightenment/sysactions.conf limitations are ineffective
> >>
> >> There is a security mechanism implemented based on the file
> >> /etc/enlightenment/sysactions.conf. This file defines which users/groups
> >> are allowed to execute the `enlightenment_system` binary in the first
> >> place.
> >>
> >> The `_etc_enlightenment_system_conf()` function parses this file.
> >> However there seems to be a `while` or `for` loop body missing. Instead
> >> only the first line of the file is ever parsed, which happens to be a
> >> comment line by default. The logic in the function defaults to "allow
> >> everything" if nothing else was determined. Thus this security mechanism
> >> is currently ineffective and all users in the system can use the full
> >> functionality of the setuid-root program.
> >>
> >> I suggest to deny access by default and correct the algorithm to
> >> correctly parse the configuration file.
> >>
> >> ## g) setcwd() is missing and a check for "libcurl.so.5" allows arbitrary
> >>        file existence tests
> >>
> >> The setuid-root program does not reset the current working directory to
> >> a safe path like "/". Therefore whichever directory the unprivileged
> >> user sits in when invoking the setuid-root program will affect relative
> >> path lookups performed in the setuid-root context.
> >>
> >> One such instance is found in the context of the call to
> >> `ecore_file_download_init()´ which later down the path tries to load the
> >> shared library "libcurl.so.5" via `eina_module_load()`. If the
> >> `dlopen()` fails the latter function performs a `stat()` on the library
> >> basename "to determine" whether the library existed or not. If it thinks
> >> the library existed but couldn't be loaded then it prints out an error
> >> message.
> >>
> >> An attacker can place a symlink named "libcurl.so.5" in the current
> >> working directory and enlightenment_system will follow the link and
> >> behave differently depending on whether the link target exists or not.
> >> Therefore an attacker can test for existence of arbitrary files also in
> >> paths usually not accessible to him. Example:
> >>
> >> ```
> >> # symlink pointing to non-existing file
> >> user /home/user $ ln -s /root/.notexisting libcurl.so.5
> >> user /home/user $ /usr/lib64/enlightenment/utils/enlightenment_system
> >> ddc-list^C
> >>
> >> # symlink pointing to existing file
> >> user /home/user $ ln -s /root/.bashrc libcurl.so.5
> >> user /home/user $ /usr/lib64/enlightenment/utils/enlightenment_system
> >> ERR<4165>:eina_module ../src/lib/eina/eina_module.c:319 eina_module_load()
> >> could not dlopen("libcurl.so.5", libcurl.so.5: cannot open shared object
> >> file: No such file or directory): RTLD_NOW [...] ```
> >>
> >> I suggest to set the CWD to a defined and safe default like "/".
> >>
> >> Furthermore I'd adjust the code in `eina_module_load()` to use
> >> `dlerror()` instead of guessing reasons for failed library loads.
> >>
> >> ## h) `_cb_stdio_in_read()`: potentially large memory allocation based
> >>        on untrusted user data
> >>
> >> The line `buf = malloc(head.size)` in this function uses the untrusted
> >> size specification provided by the unprivileged user to allocate a
> >> potentially large chunk of memory on the heap. On Linux this is mostly
> >> uncritical, because the kernel by default overcommits memory and only
> >> really allocates memory once it's accessed. On other OSs this could be
> >> used to hog memory in a root process, however.
> >>
> >> I suggest to implement a reasonable maximum message size and reject
> >> everything else.
> >>
> >> ## i) `ecore_file_app_installed()` can be tricked into returning bogus
> >> ## results
> >>
> >> Various calls to `ecore_file_app_installed()` are performed in the
> >> context of the setuid-root binary. This function performs a direct check
> >> for the existence of the given filename before checking the directories
> >> found in the PATH environment variable.
> >>
> >> Since the CWD is controlled by a potential attacker (see g)), the
> >> attacker can place arbitrary files named like the searched binaries in
> >> the CWD. As a result the `ecore_file_app_installed()` will returns bogus
> >> results. I couldn't find any way to exploit this fact in the context of
> >> the setuid-root binary, however.
> >>
> >> Like in g) I suggest to set the CWD to a safe default value.
> >> Furthermore I suggest *not* to check the CWD in
> >> `ecore_file_app_installed()` installed. If the CWD should be checked
> >> then the PATH environment variable should contain an entry for "."
> >> instead.
> >>
> >> # Security Vulnerability Process
> >>
> >> I'm posting these findings here publicly since the Enlightenment project
> >> does not document any preferred vulnerability report procedure and does
> >> not offer a means of coordinated disclosure. I asked on the
> >> Enlightenment freenode IRC channel about the best way to report security
> >> issues and I was pointed towards the mailing lists and the issue
> >> tracker.
> >>
> >>  From my point of view at least items a), b) and d) deserve a CVE
> >> assignment due to the severity of the issues. Even if to my knowledge
> >> the code in question wasn't yet part of an official release yet it might
> >> help the community to identify risks in their systems. Please tell me
> >> whether you want to assign CVEs on your end or whether I should do this.
> >>
> >> # The general design of the `enlightenment_system` setuid-root program
> >>
> >> I would like to share my thoughts on the general design of this
> >> setuid-root program:
> >>
> >> - the fact that all of the child processes are created through "/bin/sh"
> >>    is difficult for security and complicates the code a lot. By using a
> >>    mechanism based on the `execve()` system call all worries about shell
> >>    metacharacters would be gone in the first place.
> >> - the various "filter" logic that tries to detect bad paths and alike is
> >>    error prone in nature. It is very difficult to operate as root on user
> >>    controlled paths. Lower level system calls like `openat()` are a must
> >>    to avoid symlink attacks and other surprises. Child programs like
> >>    `mount` can then be pointed to the already open files in
> >>    /proc/self/fd/, for example. This is valid at least for Linux.
> >> - allowing to mount arbitrary block devices as is the case with the
> >>    "store-mount" command seems a bad idea to me. This should be limited
> >>    to removable block devices at best (this property can be determined
> >>    via sysfs on Linux).
> >> - even though the setuid-root specific code is "only" about 3.000 lines
> >>    of code in size, the plethora of framework libraries pulled in make
> >>    this a surprisingly complex program for a setuid-root utility. These
> >>    libraries are largely unaware that they're operating in a security
> >>    sensitive context which can create additional security issues (like
> >>    issue g) and i)).
> >> - the design of the program is already asynchronous in nature based on
> >>    the main loop. Furthermore there is a rather complex communication
> >>    channel established between the caller and the program based on a
> >>    special purpose binary protocol. Given this I wonder why a setuid-root
> >>    program has been chosen to realize these features in the first place.
> >>    With this level of complexity and feature set a D-Bus service with
> >>    polkit authentication on top would be the cleaner solution.
> >>
> >> Cheers
> >>
> >> Matthias
> >>
> >> -- 
> >> Matthias Gerstner <matthias.gerst...@suse.de>
> >> Dipl.-Wirtsch.-Inf. (FH), Security Engineer
> >> https://www.suse.com/security
> >> Phone: +49 911 740 53 290
> >> GPG Key ID: 0x14C405C971923553
> >>
> >> SUSE Software Solutions Germany GmbH
> >> HRB 36809, AG Nürnberg
> >> Geschäftsführer: Felix Imendörffer
> >>
> > 
> > 
> 
> 
> _______________________________________________
> enlightenment-devel mailing list
> enlightenment-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/enlightenment-devel


-- 
------------- Codito, ergo sum - "I code, therefore I am" --------------
Carsten Haitzler - ras...@rasterman.com



_______________________________________________
enlightenment-devel mailing list
enlightenment-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to