Accessing command line arguments from KAuth scope

Questions and Answers about all things *OS (macOS, iOS, tvOS, watchOS)

Accessing command line arguments from KAuth scope

Postby TheDarkKnight » Mon Apr 11, 2016 2:38 pm

With a kernel extension monitoring the file scope, if a user executes a command from the Terminal is it possible to retrieve the arguments supplied to the process and if so, how can this be done?

If it's not possible from the file scope, can it be retrieved via the VNode scope?
TheDarkKnight
 
Posts: 26
Joined: Wed Dec 16, 2015 10:30 am

Re: Accessing command line arguments from KAuth scope

Postby morpheus » Wed Apr 13, 2016 12:34 pm

as Apple states in their https://developer.apple.com/library/mac/technotes/tn2127/_index.html#//apple_ref/doc/uid/DTS10003591-CH1-SUBSECTION10, arg1 is supposed to give you the full path. From the sources of XNU 3247 I see

/*
* Call out to allow 3rd party notification of exec.
* Ignore result of kauth_authorize_fileop call.
*/
if (error == 0 && kauth_authorize_fileop_has_listeners()) {
kauth_authorize_fileop(vfs_context_ucred(imgp->ip_vfs_context),
KAUTH_FILEOP_EXEC,
(uintptr_t)ndp->ni_vp, 0);
}

which actually shows the first argument is NULL. So the arguments - not even the first - aren't passed.

Edit: the pathname is embedded in kern_authorization.c, kauth_authorize_fileop(), but you certainly lose the pointer to imgp.

You'd need to get a handle to imgp, from which you can obtain all. You'd probably be better off with the MACF hook (much more powerful), unless you're asking this for an actual commercial product, and not a hack-type one.

There are workarounds - you could have your kauth hook signal to user mode and have a user mode process collect the arguments, possibly killing the fledgling process if its args are bad.
morpheus
Site Admin
 
Posts: 532
Joined: Thu Apr 11, 2013 6:24 pm

Re: Accessing command line arguments from KAuth scope

Postby TheDarkKnight » Wed Apr 13, 2016 2:07 pm

Thank you for replying.

unless you're asking this for an actual commercial product, and not a hack-type one


Unfortunately, it is for a commercial product.

kauth hook signal to user mode and have a user mode process collect the arguments


So I assume sysctl is the best way forward here?
Last edited by TheDarkKnight on Wed Apr 13, 2016 2:42 pm, edited 1 time in total.
TheDarkKnight
 
Posts: 26
Joined: Wed Dec 16, 2015 10:30 am

Re: Accessing command line arguments from KAuth scope

Postby morpheus » Wed Apr 13, 2016 2:15 pm

Absolutely. sysctl (...KERN_PROCARGS and KERN_PROCARGS2..) exist solely for that. That's how ps does that. That's how procexp does that, too.
morpheus
Site Admin
 
Posts: 532
Joined: Thu Apr 11, 2013 6:24 pm

Re: Accessing command line arguments from KAuth scope

Postby TheDarkKnight » Wed Apr 13, 2016 2:42 pm

Thanks again, it's much appreciated :)
TheDarkKnight
 
Posts: 26
Joined: Wed Dec 16, 2015 10:30 am

Re: Accessing command line arguments from KAuth scope

Postby TheDarkKnight » Wed Apr 13, 2016 4:15 pm

I'm not sure what the difference is between KERN_PROCARGS and KERN_PROCARGS2 (thanks Apple for not adding comments in the header for these!), but they both return the values I need. However, the output also includes all the environment variables. For example:

"/usr/bin/top"
"top"
"-n"
"10"
"TERM_PROGRAM=Apple_Terminal"
"TERM=xterm"
"SHELL=/bin/bash"
"TMPDIR=/var/folders/gm/8gt8tlp10hv20c2k6msbxmrw0000gp/T/"
"Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.QMZLnHUHIf/Render"
"TERM_PROGRAM_VERSION=361.1"
"TERM_SESSION_ID=D0B72334-6399-4B4E-A509-DA4DCE525699"
etc...


As it's possible to create a file with a name containing an equals sign, how can we differentiate between the user arguments and the environment variables?

In the case of ps, it clearly shows only the user arguments: -

Code: Select all
ps aux -O args | grep top
root     19100   1.8  0.0  2501280   6528 s000  S+   Mon05pm  29:57.24 top -n 10    19100 top -n 10   s000  S+    29:57.24 top -n 10
TheDarkKnight
 
Posts: 26
Joined: Wed Dec 16, 2015 10:30 am

Re: Accessing command line arguments from KAuth scope

Postby Siguza » Wed Apr 13, 2016 7:46 pm

KERN_PROCARGS2 gives you argc (4 bytes at the beginning of the buffer), KERN_PROCARGS doesn't.
Handy for telling apart arguments and environment variables.

However, I found the structure of the returned data to be rather awkward.
i.e. I wrote the following program:

Code: Select all
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sysctl.h>
#include <sys/syslimits.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int mode = KERN_PROCARGS; // change to KERN_PROCARGS2 at will
    int name[] = {CTL_KERN, mode, getpid()};
    size_t len = ARG_MAX;
    char *buf = malloc(len);
    if(buf == NULL)
    {
        fprintf(stderr, "Failed to allocate buffer\n");
        exit(1);
    }
    if(sysctl(name, 3, buf, &len, NULL, 0))
    {
        fprintf(stderr, "sysctl(): %s\n", strerror(errno));
        exit(1);
    }
    printf("len  = %lu\n", len);
    if(mode == KERN_PROCARGS2)
    {
        printf("argc = %i\n", *((int*)buf));
        buf += sizeof(int);
        len -= sizeof(int);
    }
    printf("argv:\n");
    for(char *end = buf + len; buf < end; buf += strlen(buf) + 1)
    {
        printf("%s\n", buf);
    }
}

Which yields the following output when run as "./test a b c":

Code: Select all
len  = 1280
argv:
./test

./test
a
b
c
TERM_PROGRAM=Apple_Terminal
SHELL=/bin/bash
TERM=xterm-256color
TMPDIR=/var/folders/hs/pd35cjc17mn_qvrw4qzd08j40000gn/T/
Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.hnEmfIPsne/Render
TERM_PROGRAM_VERSION=361.1
TERM_SESSION_ID=40DAEB8E-4FAA-4D9C-858F-EF2A628C9F5B
USER=herp
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.25mKNXq6tb/Listeners
__CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0
PATH=/Users/herp/local/gcc/win64/bin:/Users/herp/local/gcc/linux/bin:/Users/herp/local/twlan/bin:/Users/herp/local/zephir/bin:/Users/herp/local/mysql/bin:/Users/herp/local/bin:/Users/herp/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/git/bin:/usr/local/go/bin
PWD=/Users/herp/Desktop
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home
XPC_FLAGS=0x0
PS1=\[\e[0;1;37m\]\h:\W \u$ \[\e[0m\]
PS2=\[\e[0;1;37m\]> \[\e[0m\]
XPC_SERVICE_NAME=0
SHLVL=1
HOME=/Users/herp
OLDPATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/git/bin:/usr/local/go/bin
LOGNAME=herp
DISPLAY=/private/tmp/com.apple.launchd.GrBlJQ5s4K/org.macosforge.xquartz:0
_=./test
OLDPWD=/Users/herp

















































































































��



./test






When run as "$PWD/test a b c", it prints:

Code: Select all
len  = 1364
argv:
/Users/derp/Desktop/test







/Users/derp/Desktop/test
a
b
c
TERM_PROGRAM=Apple_Terminal
SHELL=/bin/bash
TERM=xterm-256color
TMPDIR=/var/folders/hs/pd35cjc17mn_qvrw4qzd08j40000gn/T/
Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.hnEmfIPsne/Render
TERM_PROGRAM_VERSION=361.1
TERM_SESSION_ID=40DAEB8E-4FAA-4D9C-858F-EF2A628C9F5B
USER=derp
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.25mKNXq6tb/Listeners
__CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0
PATH=/Users/derp/local/gcc/win64/bin:/Users/derp/local/gcc/linux/bin:/Users/derp/local/twlan/bin:/Users/derp/local/zephir/bin:/Users/derp/local/mysql/bin:/Users/derp/local/bin:/Users/derp/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/git/bin:/usr/local/go/bin
PWD=/Users/derp/Desktop
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home
XPC_FLAGS=0x0
PS1=\[\e[0;1;37m\]\h:\W \u$ \[\e[0m\]
PS2=\[\e[0;1;37m\]> \[\e[0m\]
XPC_SERVICE_NAME=0
SHLVL=1
HOME=/Users/derp
OLDPATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/git/bin:/usr/local/go/bin
LOGNAME=derp
DISPLAY=/private/tmp/com.apple.launchd.GrBlJQ5s4K/org.macosforge.xquartz:0
_=/Users/derp/Desktop/test
OLDPWD=/Users/derp





















































































































��



/Users/derp/Desktop/test








Is there a struct somewhere that I haven't found, or do I just have to skip the first 8*ceil((strlen(buf) + 1) / 8d) bytes and read until I hit an empty string?
User avatar
Siguza
Unicorn
 
Posts: 159
Joined: Thu Jan 28, 2016 10:38 am

Re: Accessing command line arguments from KAuth scope

Postby TheDarkKnight » Thu Apr 14, 2016 8:56 am

Thanks Siguza for pointing out the difference between the KERN_PROCARGS and KERN_PROCARGS2; that's just what I was looking for.
I haven't yet been able to find any such struct for the data and simply ignore the empty strings.

I notice your code uses the pre-defined ARG_MAX variable, whereas I retrieve that from sysctl, which returns the same value.

Code: Select all
    int argmax;
    int mib[] = {CTL_KERN, KERN_ARGMAX};
    size = sizeof(argmax);
    if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1)
    {
        printf("Failed to find size of args\n");
    }


Does anyone know if there's any reason not to simply use the define as it certainly creates much cleaner code?
TheDarkKnight
 
Posts: 26
Joined: Wed Dec 16, 2015 10:30 am

Re: Accessing command line arguments from KAuth scope

Postby Siguza » Thu Apr 14, 2016 1:14 pm

ARG_MAX can change with new OS versions, at which point the macro will give you a deprecated value, whereas the sysctl call will give you the correct one regardless of which header your program was built against.
The only time it changed so far was in XNU-517, where the limit went from 64K to 256K.
So for quick & dirty stuff like the thing I posted though, ARG_MAX should be enough, but for stuff that is supposed to last longer, you should probably go with sysctl.
User avatar
Siguza
Unicorn
 
Posts: 159
Joined: Thu Jan 28, 2016 10:38 am

Re: Accessing command line arguments from KAuth scope

Postby morpheus » Thu Apr 14, 2016 1:50 pm

Wow I'm away for a day and people are discussing stuff! Good times have come to this forum!

DK, Siguza: From following this, I can suggest:

when the argv is returned it is returned as an array of pointers. that array is terminated by a pointer to a NULL (which is at argv[argc]). Right after that will come envp.

Think of it like this: (from dyldStartup.s)

Code: Select all
 *      |  apple[0]   |
 *      +-------------+
 *      |      0      |
 *      +-------------+
 *      |    env[n]   |
 *      +-------------+
 *             :
 *             :
 *      +-------------+
 *      |    env[0]   |
 *      +-------------+
 *      |      0      |
 *      +-------------+
 *      | arg[argc-1] |
 *      +-------------+
 *             :
 *             :
 *      +-------------+
 *      |    arg[0]   |
 *      +-------------+
 *      |     argc    |
 *      +-------------+


So there are two ways you can do it:

- Without argc, read the argv[] until you hit a null string
- with argc, read argc first (cast to int32!) then read for (a = 0 ; a < argc; a++) { read sizeof(void *); }

and that way you get just the args, without the envp.

DK: What's a good email to get you at? I want to share with you a specific preview of the KAuth chapter from MOXiI 2 - Kauth was a glaring omission in the 1st ed, and I need to make sure I have it fully covered this time.
morpheus
Site Admin
 
Posts: 532
Joined: Thu Apr 11, 2013 6:24 pm

Next

Return to Questions and Answers

Who is online

Users browsing this forum: No registered users and 1 guest