How * nix signals let you read the memory of other processes

There is a very old and rooted * nix thing called "signals" . The idea behind these primitives is very simple: to implement a software analog of interrupts. Different processes can send signals to each other and to themselves, knowing the process id (pid) of the recipient. The receiving process is free to either assign a signal handler function that will be automatically called when it is received, or ignore it using a special mask, or trust the default behavior. So far so good.



Default Behavior on Receiving a Signal ... What do these reassuring words mean? Not what you expected, sure. The wiki says that the default 28 signal handlers (there are others!) Are as follows: 2 are ignored, 4 cause the process to stop, 1 to continue, 11 to terminate, 10 to terminate with a memory dump. Now that's interesting! So, the situation is as follows: even if your program does not mention signals in any way in the source code, in fact it uses them, and in a very dramatic way.



From now on, we'll have to dig deeper. Who can send signals and to whom? The wiki says, "A process (or a user from a shell) with an effective UID other than 0 (superuser UID) can only send signals to processes with the same UID." So, if you run 100 programs, then any of them can easily kill all these 100 programs using the system API, even if all programs (except the killer) did not mention signals in any way in their source code. If you worked under the root account, then it doesn't matter at all who started certain processes, you can still easily end them. It is, of course, possible to find out the pid of a particular process and execute its "contract murder", but it is even easier to kill everyone who can be simply brute-force pid-s.



“Wait, wait, don't drive the horses. You mentioned that signals can be processed and ignored! " - I hear the voice of my reader. What will Vicki say? "For alternate handling of all signals except SIGKILL and SIGSTOP, a process can assign its own handler or ignore their occurrence by modifying its signal mask." We look at the default actions when receiving these signals and see: "Process termination", "Process termination". It turns out that we can do these two actions whenever sending SIGKILL and SIGSTOP signals to the victim is in principle possible. "The only exception is a process with pid 1 (init), which is allowed to ignore or handle any signals, including KILL and STOP."We may not even be able to kill one of the most important system processes as root, but in an amicable way, this requires additional research.



Well, the picture has become a little more positive, but it is still gloomy. If you start a process, then it is guaranteed that it can terminate and stop a bunch of other processes. If the very simple condition “the developers of the receiving process forgot to ignore or somehow handle one of the many other signals” is met, the maniac application will be able to cause the process to terminate with a memory dump or continue the process after stopping. If the developers of the receiving application have hung their handlers on some signals, you can try to interfere with the functioning of this application by sending signals to it. The latter is a topic for a separate discussion, because due to the asynchronous execution of handlers, races and undefined behavior are possible ...



“Abstract reasoning is very cool, but let's get closer to specifics,” a demanding reader will tell me. Ok, no problem! Any * nix user is familiar with a program like bash. This program has been developing for almost 30 years and has a whole mountain of possibilities. Let's fill her up for clarity and get some yummy from her memory!



I take my home Ubuntu 16.04.2 out of wide leg and run two copies of bash 4.3.46 on it. In one of them, I will execute a hypothetical command with secret data: export password = SECRET. Let's forget for a while about the file with the command history, in which the password would also be written. In the same window, type the ps command to find out the pid of this process - say, 3580.



Without closing the first window, let's go to the second. The ps command in it will give another pid of this bash instance - say, 5378. Just for clarity, it is from this second bash that we will send a signal to the first command kill -SIGFPE 3580. Yes, dear reader, this is complete absurdity: process 2 says nothing related to to process 1, that in this very process 1 an erroneous arithmetic operation occurred. The following window appears on the screen: The







desired abnormality occurred with the creation of a memory dump, that is, bash does not seem to process or ignore this signal. Googling where I look for the dump, I found a detailed answer ( one , two). In my Ubuntu, the situation is like this: if an application from the standard package crashes due to a signal other than SIGABRT, then the dump is transferred to the Apport program. This is just our case! This program assembles a file with diagnostic information and displays the window shown above. The official website proudly states: “Apport collects potentially sensitive data, such as core dumps, stack traces, and log files. They can contain passwords, credit card numbers, serial numbers, and other private material. " Well, well, I wonder where we have this file? Yep, /var/crash/_bin_bash.1000.crash. Let's pull its contents into the somedir folder: apport-unpack /var/crash/_bin_bash.1000.crash somedir. In addition to various uninteresting little things, there will be a coveted memory dump called CoreDump.



Here it is, the moment of truth! Let's search this file for the password string and see what we get interesting in response. Strings CoreDump command | grep password will remind the forgetful hacker that password is SECRET. Wonderful!



I did the same with my favorite text editor gedit, starting typing in the buffer and then reading it from the dump. No problem! At this point, Vicki whispered in her ear warningly: "Sometimes (for example, for programs executed on behalf of the superuser), a memory dump is not created for security reasons." Soooo, let's check ... When receiving a signal from the root bash, the second root bash crashed with the creation of a memory dump, but because of the permissions (-rw-r ----- with the owner of root) it is no longer as easy to read it as the previous ones owned by my user account. Well, if the hypothetical killer program managed to send a signal from the superuser UID, then it can touch such a dump.



The meticulous reader may remark, "It was very easy for you to find the data you were looking for in the sea of ​​garbage." It's true, but I'm sure: if you know what kind of fish you want to catch and where it swims, then finding it in the dump nets should be realistic almost always. For example, no one bothers to download a package with debugging information for a crashed program and find out the contents of the variables you are interested in in GDB by post-mortem debugging.



All this may look quite harmless, but in fact it is not. All the actions I described could easily be done by a program or script running in user mode, not to mention a more privileged access level. The bottom line is that a malicious executable thing can easily chop programs to the right and left, and often also freely read their entire memory. Here are the signals and bug reports! I am sure that on other * nix platforms and with other recipient programs the situation is similar, but of course I will not check it.



The objection may arise: the malware can simply use the debugging tools to pull interesting data from the application. It really is. Why, then, is this post? My point is this: the first thing that comes to mind when trying to stop data theft from applications is just the limitations on debugging tools. Surely antivirus tools catch the use of ptrace () in the first place - this is a very suspicious event. Signals are another matter entirely. One process sends another a standard signal - so what? At first glance, this is a completely normal event. But, as we have already seen, this can lead to an abnormal termination of the application, creating a core dump in some folder, from which it can be tried to pull it.



When I tried to open the vk.com login page and dump Firefox with the same fatal signal, it crashed, but called its dump handler. Dumps in the tricky minidump format are saved at ~ / .mozilla / firefox / Crash Reports / {pending or submitted} and require further investigation. Here's what you will find out if, in the settings window, click on "Learn more" opposite the checkmark (the text below used to hang at www.mozilla.org/ru/privacy/firefox/#crash-reporter ):







« Mozilla Firefox. , Firefox, , URL- , . , , . crash-stats.mozilla.com. . .»There is rarely something really tasty in URLs, but whether the dumps contain passwords or cookies is a good question!



On this mysterious and intriguing note, I will end. I received a signal that I forgot to explicitly process.



PS I wrote a simple program with such a signal handler SIGUSR1: print the string "1" on the screen, enter an endless loop. I was hoping that if you send the SIGUSR1 signal many times to this program, the handler will be called many times, causing a stack overflow. Unfortunately for me, the handler was only called once. Okay, let's write a similar handler for the SIGUSR2 signal and send two different signals in the hope that this will dump the victim ... Alas, it didn't help either: each handler was called only once. Overflowed, overflowed, but did not overflow!



PS 2. In the world of Windows there is a kind of signals - messages that can be sent to windows . It is very likely that they can also be used for fun and profit!



The original was published on my blog 05.05.17.



All Articles