1

So, a user had exceeded her nproc limit. Due to this, both SSH and su are unable to login as the target user. However, there is no accessible shell which is owned by the user. How can this be solved without root permission?

Thanks

  • User can run kill <pid> or pkill <process name> on processes that was run by her to send a SIGTERM ( terminate) signal. If process refuses to terminate, -9 flag would forcefully shutdown a process. Though without a root permission user can't kill system processes or processes that was started by other users. – ddnomad Mar 20 '17 at 8:25
  • @ddnomad please note that nproc limit has exceeded - new process can't be started not even kill. – Fries Mar 20 '17 at 9:12
  • I have posted my solution. However, still looking for a better way as my way requires you have to other user's credential. – Fries Mar 20 '17 at 9:21
  • kill is a bash builtin, so I think some situations would allow you to run it even after exceeding this limit. As a last ditch effort, you could always exec kill -9 pid, but that kills your shell, too – Fox Mar 20 '17 at 15:30
  • @Fox Due to this, we can't do both SSH and su... I should have mentioned it. The problem is modified to reflect this. Thanks for your response! – Fries Mar 20 '17 at 18:12
1

As I see nproc and "ulimit" tag, I'll add 2 points to this ulimit mecanism:

I'd expect your sysadmin to have put a soft-limit a bit lower than the hard-limit, exactly to avoid the case you're on. That way, you can increase your soft-value to launch su from another account and use that to do a kill.

Also, keep in mind that the ulimits are set by PAM (grep -r pam_limits.so /etc/pam.d/), so if you have an authentication method / program that doesn't use this PAM module, you can avoid this ulimit and generate a kill.

Aside from these 2 solutions that require an action before the issue arise, there's indeed no way to send a signal to an existing program without having the correct access (same user, root, setuid or capability CAP_KILL).

0

If you have other user's credential and the target user's credential can be used to change the file system somehow, you may compile the following program, set the s bit and use other user's credential to execute it.

NOTE: the executable must be owned by the target user in order to be able to seteuid to the target user's uid.

The first argument of the program is the target user's uid.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>

int main(int argc, char**argv) {
    if(seteuid(atoi(argv[1])) != 0) {
        printf("seteuid failed... %d", errno);
    }
    if(kill(-1, SIGTERM) != 0) {
    printf("kill failed...%d", errno);
    }

    return 0;
}
  • 1
    I doubt this way would work as other non-root user has no permission to kill processes of a former user. Have you tested it? – ddnomad Mar 20 '17 at 9:27
  • Yes, this was used to solve the mess that was going on. It works because of the s bit. By using the s bit, the program will be able to seteuid to the target user id. Maybe I should state it more clearly that the file must be owned by the target user. – Fries Mar 20 '17 at 10:09
  • Hm, then it's an interesting thing. Thanks for sharing! – ddnomad Mar 20 '17 at 10:14
  • ddnomad, You are welcome! Still looking for a better solution though since this requires you to have other user's credential. Thanks ddnomad! – Fries Mar 20 '17 at 18:09
  • I consulted relative who is quite good in sysadmin stuff and he said there is no other way (sadly) – ddnomad Mar 20 '17 at 18:44
0

If nproc has been reached, the kernel isn't going to allow any other processes to run with that user's credentials, as you learned. This means no remote session over SSH, no su or sudo, not even a program setuid to their account.

That leaves you with either root, or the user if they happen to still have a working interactive session. Since you excluded root, you'll have to hope your user still has a shell running. If so, there should be enough functionality built into their shell to kill off some of the offending processes. You'll need to leverage shell builtins to iterate through /proc.

For example, if I wanted to kill off all of my bash shells, I could do something like this, which only uses Bash shell builtins:

pushd /proc && for pid in *; do
    test "$pid" = "self" && continue        # skip /proc/self
    test -d "$pid" || continue              # skip if not a directory
    test -O "$pid" || continue              # skip if we don't own it
    read cmd < "${pid}/cmdline"
    case "$cmd" in
        *bash*) kill -9 $pid ;;             # or whatever you want to do to it
    esac
done && popd

This does suffer from a couple limitations, so it isn't foolproof, but it should be useful in most cases.

  1. If there are a lot of processing running, the * expansion at the top of the loop may result in a strings that exceeds the maximum command length. If you run into this, you could pare down the wildcard expansion with multiple runs using 1*, 2*, {1..3}*, or something like that.

  2. /proc/${pid}/cmdline contains a NULL-delimited list, of which the actual command is just one item. I'm not aware of a way to cleanly parse such a string using just Bash builtins. Since strings are normally terminated with a NULL, read is only going to get the first item, which may or may not be the command name. An alternative approach would be to iterate over /proc/${pid}/stat or /proc/${pid}/status.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.