DEV Community

Tepid Angler
Tepid Angler

Posted on Edited on

Using 'scl' of scl-utils to abuse sudo on CentOS/RHEL

What:
In an effort to make it easier for unprivleged users on a shared system to use 'scl' an admin (reasonably) might
allow sudo privs with no password for 'scl'. What's expected is for the unprivledged user to be able to execute
the 'scl' binary to see a multitude of different information regarding the binary and whatever collections an
admin may have downloaded for the developers to use on the system. Because the binary passes arguments through 'exec()'
to achieve this, an attacker can leverage the 'exec()' function to pop a shell while abusing sudo to escalate privileges
by simply adding bash as the 'COMMAND' argument in the scl syntax like so 'sudo scl enable rh-ruby25 bash'.

Who:
It's important to mention that this vulnerability doesn't affect all environments. For instance, if your users are protected by passwords
this adds an extra layer of security against the vulnerability as the attacker would have to then find the password.
Another example, if you have scl installed by default but don't have any packages in the '/opt/rh/' directory then
scl will not have any collections to pull from which results in the 'scl' binary itself failing and then exiting.
As far as who would be affected, any system that has scl-utils downloaded, has a collection installed, and that allows users to
call the 'scl' binary using sudo.

How:
The driving force behind this abuse stems from the 'args.c' file which holds the source code for how to handle arguments.
While reviewing 'args.c' I noticed they call the 'exec()' function which ultimately starts a new process using the arguments
we gave the 'scl' binary on the command line. This is where our root shell comes from: since there it doesn't drop privileges
to that of the person who called the sudo, we're left with a process that is now running as root.
Lines 331-359 are as follows:

 else if (!strcmp(argv[1], "run") || !strcmp(argv[1], "enable")) {
        int i, i2;

        args->action = ACTION_COMMAND;

        for (i = 2; i < argc; i++) {
            if (!strcmp(argv[i], "--exec") || !strcmp(argv[i], "-x")) {
                args->exec_flag = true;
                break;
            }
        }

        /* Remove -x / --exec from argv */
        if (args->exec_flag) {
            for (i2 = i; i2 < argc - 1; i2++) {
                argv[i2] = argv[i2 + 1];
            }
            argc--;
        }

        if (argc < 4) {
            ret = EINPUT;
            goto fail;
        }
        ret = parse_run_args(argc, argv, args);
        if (ret != EOK) {
            goto fail;
        }
Enter fullscreen mode Exit fullscreen mode

Line 356: ret = parse_run_args(argc, argv, args); is the line that passes our arguments to the 'exec()' function.
To mitigate this vulnerability I have written a patch that checks the 'SUDO_GID' and 'SUDO_UID' environment variables
and then drops the privileges of the process to the user who called the sudo. This stops our attacker from escalating
his privileges allowing the system users to continue using the 'scl' binary the same way they have been before.

Below I have provided a link to the bug I submitted through bugzilla, which does provide a patch to mitigate this type
of attack:

https://bugzilla.redhat.com/show_bug.cgi?id=1694875

Closing:

I should mention that I did reach out to a vulnerability disclosure program, Zerodium, and was told that this isn't
considered a privilege escalation exploit. Redhat also advised it's a feature. I should also note that even if you don't meet the conditions to escalate privilege's you can still use this binary to escape jails. I also provided the patch (SEE BUGZILLA LINK) to the upstream. With all of this being said I felt it was safe to release this information to the world specifically because until the patch
has been pushed by upstream systems may still be infected and now they will at least have a patch available until
they can apply the patch to a release. Since there has been no CVE issued at this time I feel this issue is best
covered by CWE-264 with a severity of 41.6.

GitHub logo Tepidangler / SCL-patch

A patch for the args.c file in the scl binary source

SCL-patch

A patch for the args.c file in the scl binary source

Applying the patch

  • If you already have scl compiled on your host you can delete the binary using

# rm -rf path/to/scl/binary

Steps

  1. Create a temporary working space in the /tmp directory and change to that directory

$ mkdir /tmp/scl-patch && cd /tmp/scl-patch

  1. Clone scl-utils repo from github

$ git clone https://github.com/sclorg/scl-utils.git

  1. Clone the patch for args.c from this repo

$ git clone https://github.com/Tepidangler/SCL-patch.git

  1. Change to the src directory in scl-utils/ $ cd scl-utils/src/

  2. Patch the args.c file

$ patch < /tmp/scl-patch/SCL-patch/args.patch

  1. Compile the source code

$ cmake . && make






Shoutout to:

@TJnull & @lewellyn

Feel free to follow them on Twitter

Top comments (0)