Closed jvoisin closed 7 years ago
That's my doing, so that I can get the name of the script being run by an interpreter. If you know of a better way to get that information I'm happy to change it.
I don't know either, sorry :/
Maybe a ghetto-mitigation would be to check that /proc/self/cmdline
is starting with file /proc/self/exe
?
ckuethe wrote:
That's my doing, so that I can get the name of the script being run by an interpreter. If you know of a better way to get that information I'm happy to change it.
When I was posed with the same issue while working on onion-grater (note that this is not the upstream repo, which seems to not be publicly accessible at the moment, whatever) I could only find that AppArmor confined scripts have a safe way to make a PID to script path mapping. Of course, using that approach limits you to AppArmor confined scripts only, which was fine for my case, but clearly isn't for your. Any way, here's the code: https://github.com/Whonix/onion-grater/blob/master/usr/lib/onion-grater#L169
If you find a general purpose solution, I'm all ears! :)
jvoisin wrote:
Maybe a ghetto-mitigation would be to check that
/proc/self/cmdline
is starting withfile /proc/self/exe
?
I don't get how that changes anything. The application then just has to add that part in the beginning, followed by the application it wants to spoof (the "script"), right?
An application could spoof its script, but not its interepreter name; hence the term "ghetto-mitigation" :D
Ah, now I got what you meant. But it still sucks! :) Using that trick, when you add an exception to opensnitch
for a script s interpreted by an interpreter i, then all malicious scripts interpreted by i can spoof as script s and get the same exception. Hardly an improvement, imho.
Also, /proc/self/exe
is in the malicious script's control since there is such a thing as exec()
. :)
exec()
will destroy the process context completely (am I wrong?), what could an attacker do with this?
I'm not sure I'd ever whitelist an application entirely, especially not an interpreter.
exe may not even be found in comm or cmdline. Clearly it's hard to get a trustworthy snapshot of arguments, process name, etc. :(
$ ls -l /proc/1769/exe
lrwxrwxrwx 1 ckuethe ckuethe 0 Apr 20 11:09 /proc/1769/exe -> /usr/bin/python2.7
Which is expected given the first line of the script:
#!/usr/bin/env python2.7
However ps
says:
ckuethe 1769 0.4 0.4 733044 70888 ? Sl 11:07 0:11 python2 /path/to/whateverscript
or
1769 ? 00:00:10 whateverscript
jvoisin wrote:
exec()
will destroy the process context completely (am I wrong?), what could an attacker do with this?
My exec()
remark was just a general reminder that /po,roc/self/exe
can change when calling exec()
which the attacker can do. It's not necessary to workaround your proposed mitigation.
Let's just make it clear that your mitigation won't work: so reading /proc/self/cmdline
will read the data from the memory address of argv
(speaking C). Try putting the following in your main()
function and then check what /proc/self/cmdline
becomes:
memcpy(argv[0], "/usr/bin/python3\0/path/to/trusted\0", 41);
I.e. you can set it however you like. Of course, to not overwrite important memory, call the program with atkeast 41 characters as the first argument. :)
So let's say we have an opensnitch
exception for some path /path/to/trusted
A local attacker can then run a Python script that writes /usr/bin/python\0/path/to/trusted\0
to os.ARGV
(actually that doesn't work, but you can achieve the same with a simple C module that can be imported). Then the current opensnitch
code will think that the script /path/to/trusted
is running, and set that as the path
in the Application
class and hence the attacker's code will run with the exception made for /path/to/trusted
. Furthermore /proc/self/exe
will be /usr/bin/python
(the attacker is running a Python script!), which is a prefix in the forged /proc/self/cmdline
, so your mitigation is not effective. Ok?
ckuethe:/proc/25147$ cat comm
/sbin/init
ckuethe:/proc/25147$ cat cmdline
/sbin/initckuethe:/proc/25147$ ps -p 25147
PID TTY TIME CMD
25147 pts/4 00:00:08 /sbin/init
ckuethe:/proc/25147$ ps wwax | grep 25147
25147 pts/4 Sl+ 0:08 /sbin/init
25917 pts/6 S+ 0:00 grep --color=auto 25147
Which is actually /usr/bin/python2 /usr/local/bin/ptpython
. the python setproctitle
module makes such manipulation dead easy and we don't have a good countermeasure for it.
Non-malicious programs do this too. The MySQL client will censor its own argv in an attempt to reduce exposure of the password if you give it on the command line.
ckuethe wrote:
I'm not sure I'd ever whitelist an application entirely [...]
I think full application granularity is the best we can except from average users. If that isn't your intended audience: fair enough! :)
[...] especially not an interpreter.
Of course! That's why you have this pid-to-script-path workaround in the Applications
class in the first place. It's absolutely needed, and...
exe may not even be found in comm or cmdline. Clearly it's hard to get a trustworthy snapshot of arguments, process name, etc. :(
... it's unfortunate that the Linux kernel doesn't provide anything to help us. :/ It'd be great if Linux recorded argv
each time ecec()
is called, before the code has a chance to manipulate it (imagine /proc/self/cmdline.safe
). That would be such a "trustworthy snapshot of arguments" you are talking about. In fact, this would probably be possible to implement in a kernel module right now. And eventually proposing it to be added into mainline Linux as an option since some people might want to keep it the old way, like MySQL users that like to pass critically sensitive information on the command line... *eyeroll*
Hm. This actually sounds like a great idea, or am I missing something obvious? If not, I believe it would offer us the perfect solution for opensnitch
, onion-grater
and other applications that use the application path of a running process as the basis for identifying what conceptual application is being executed.
I was just thinking about tossing together a quick kernel module to snapshot that information at exec() time.
Or maybe abuse apparmor by creating a profile to audit everything's command line arguments and not interfere with any other operation (unless overridden by a more specific policy)
A kernel module sounds like a great idea (and, surprisingly, the easiest way to go). It would be amazing to get it merged upstead.
Until this, I would recommend to stick with /proc/self/exe
, that can't be modified (can it?).
I can look at making sure that exe, comm, and cmdline are all exposed to the UI, maybe with a tooltip to warn the user that a process can manipulate this information.
"Here are the process information, they might be wrong, it's up to you to trust them" doesn't sound like a good UX design to me :D
ckuethe wrote:
I was just thinking about tossing together a quick kernel module to snapshot that information at exec() time.
Same here, it looks like the kernel hacking project I've been looking for for some time (i.e. something I feel motivated about). :)
Or maybe abuse apparmor by creating a profile to audit everything's command line arguments and not interfere with any other operation (unless overridden by a more specific policy)
When I was faced with this problem, that's what I did, i.e. I created "empty" profiles that
I can look at making sure that exe, comm, and cmdline are all exposed to the UI, maybe with a tooltip to warn the user that a process can manipulate this information.
I don't think this will change anything from the user's PoV. They still won't get any help to make the right decision, because the information they get cannot be trusted (except exe).
jvoisin wrote:
A kernel module sounds like a great idea (and, surprisingly, the easiest way to go). It would be amazing to get it merged upstead.
I'll start looking into it now! This will be useful for onion-grater
too. :)
Until this, I would recommend to stick with /proc/self/exe
You mean, to remove the workaround (based on comm and cmdline) for interpreted applications? If so I think I agree, even if that cripples opensnitch
quite a bit. :/
that can't be modified (can it?).
I believe it is. I'm cloning Linux' Git right now, will have a look once it's done.
You mean, to remove the workaround (based on comm and cmdline) for interpreted applications?
Yes I do :)
I had a look at the kernel module idea for a few hours today:
First, it seems entries to /proc/pid/
cannot be added via a module so it requires building a new kernel, but I definitely could be wrong.
Second, I played around with kprobes
and it was easy to hook into a particular syscall (sys_execve
), bit less easy to find the information we want. Well, it's easy to to find the path to the binary, but not the command line. Refactoring the crazy code used for /proc/pid/cmdline
should make it possible.
Any way, it definitely seems like more work than I initially thought, so it's not clear if I'll have the time to work on this. :/
Guys ... IDEA! Maybe we can use ftrace
kernel feature like they do in order to intercept calls to exec
? :)
Simone Margaritelli:
Guys ... IDEA! Maybe we can use
ftrace
kernel feature like they do in order to intercept calls toexec
? :)
While this is an improvement, I think there are some remaining problems, some fixable, some not. A fixable issue is that you also have to intercept sys_execveat
calls since they too can modify argv
. But an unfixable, fundamental limitation of this approach is that processes started before ProcMon
(and, hence, opensnitch
) can still spoof their command-lines and get away with it.
So, for a safe solution I believe we need a kernel-level fix. :/
I think that it's an acceptable trade-off.
@fred-a-kemp, @jvoisin and @ckuethe IMHO even before that we'll need to:
Split the project into opensnitchd
, opensnitch-ui
and opensnitch-ruleman
:
opensnitchd
will be a C++ daemon, running as root with the main logic. It'll fix this.opensnitch-ui
python (?) UI running as normal user, getting the daemon messages. Will fix thisopensnitch-ruleman
python (?) UI for rule editing.Questions:
What is the best IPC method in this case? I mean, if the daemon just creates a unix socket readable and writable by any user, any third party malicious software could access it and simply ACCEPT
every packet ... dbus? No idea how it works honestly.
What's the best way in your opinion to keep all the involved/interested developers in sync and let them communicate without using github issues? Mailing list? Slack? Pigeons?
Nooooo IRC no please XD
Then whatever fits :D
@fred-a-kemp @ckuethe @jvoisin guys I just created a private Telegram channel for devs, where can I send you guys the invitation link?
First.last@gmail
@ckuethe no such info on your profile man :)
there is now :)
sent ;)
Simone Margaritelli:
@fred-a-kemp, @jvoisin and @ckuethe IMHO even before that we'll need to:
Split the project into
opensnitchd
,opensnitch-ui
andopensnitch-ruleman
:
opensnitchd
will be a C++ daemon, running as root with the main logic.
Sorry for the hate, but why C++? Sure, if you'll restrict yourself to some "safe" subset of C++, with a stack-first memory allocation approach, employing modern features like smart pointers to get memory safety if dynamic memory allocation is needed, etc. then I guess it is ok. That said, I do not really see what benefit we'd get from moving away from Python.
BTW, I assume you intend to put the ProcMon
functionality into the opensnitchd
daemon. Since other projects might be interested in only the ProcMon
bits you may want to split it into another daemon (perhaps openinformantd
? :P), dedicated to keeping track of process information.
Questions:
- What is the best IPC method in this case? I mean, if the daemon just creates a unix socket readable and writable by any user, any third party malicious software could access it and simply
ACCEPT
every packet ... dbus? No idea how it works honestly.
Make the socket only accessible by members of the opensnitch
group. If you go the DBus way you can define security policies with the same restrictions (see dbus-daemon(1)
, search for "
- What's the best way in your opinion to keep all the involved/interested developers in sync and let them communicate without using github issues? Mailing list? Slack? Pigeons?
Personally I find issue/ticket/bug trackers the ideal place for development discussion. :)
Sorry for the hate, but why C++? Sure, if you'll restrict yourself to some "safe" subset of C++, with a stack-first memory allocation approach, employing modern features like smart pointers to get memory safety if dynamic memory allocation is needed, etc. then I guess it is ok. That said, I do not really see what benefit we'd get from moving away from Python.
LOL What's wrong with you guys and C++? :D JK, dunno since handling the connections is not very performance centric maybe you're right, we can keep it in python.
BTW, I assume you intend to put the
ProcMon
functionality into theopensnitchd
daemon. Since other projects might be interested in only theProcMon
bits you may want to split it into another daemon (perhapsopeninformantd
? :P), dedicated to keeping track of process information.
Why split it into two daemons? Maybe create a proper python library/module for reusability, but I'd rather have 1 daemon and 1 UI process.
Make the socket only accessible by members of the
opensnitch
group. If you go the DBus way you can define security policies with the same restrictions (seedbus-daemon(1)
, search for "").
Will take a look, tnx.
@evilsocket Would you mind inviting me too? :) My email address is in my git commits Author field
@adisbladis sure! it's the one bound to your gpg key id I guess, right?
Currently, opensnitch is using
/proc/self/cmdline
and/proc/self/comm
, but they can easily be manipulated by a malicious application, and thus shouldn't be trusted.