setuid on an executable doesn't seem to work - linux

I wrote a small C utility called killSPR to kill the following processes on my RHEL box. The idea is for anyone who logs into this linux box to be able to use this utility to kill the below mentioned processes (which doesn't work - explained below).
cadmn#rhel /tmp > ps -eaf | grep -v grep | grep " SPR "
cadmn 5822 5821 99 17:19 ? 00:33:13 SPR 4 cadmn
cadmn 10466 10465 99 17:25 ? 00:26:34 SPR 4 cadmn
cadmn 13431 13430 99 17:32 ? 00:19:55 SPR 4 cadmn
cadmn 17320 17319 99 17:39 ? 00:13:04 SPR 4 cadmn
cadmn 20589 20588 99 16:50 ? 01:01:30 SPR 4 cadmn
cadmn 22084 22083 99 17:45 ? 00:06:34 SPR 4 cadmn
cadmn#rhel /tmp >
This utility is owned by the user cadmn (under which these processes run) and has the setuid flag set on it (shown below).
cadmn#rhel /tmp > ls -l killSPR
-rwsr-xr-x 1 cadmn cusers 9925 Dec 17 17:51 killSPR
cadmn#rhel /tmp >
The C code is given below:
/*
* Program Name: killSPR.c
* Description: A simple program that kills all SPR processes that
* run as user cadmn
*/
#include <stdio.h>
int main()
{
char *input;
printf("Before you proceed, find out under which ID I'm running. Hit enter when you are done...");
fgets(input, 2, stdin);
const char *killCmd = "kill -9 $(ps -eaf | grep -v grep | grep \" SPR \" | awk '{print $2}')";
system(killCmd);
return 0;
}
A user (pmn) different from cadmn tries to kill the above-mentioned processes with this utility and fails (shown below):
pmn#rhel /tmp > ./killSPR
Before you proceed, find out under which ID I'm running. Hit enter when you are done...
sh: line 0: kill: (5822) - Operation not permitted
sh: line 0: kill: (10466) - Operation not permitted
sh: line 0: kill: (13431) - Operation not permitted
sh: line 0: kill: (17320) - Operation not permitted
sh: line 0: kill: (20589) - Operation not permitted
sh: line 0: kill: (22084) - Operation not permitted
pmn#rhel /tmp >
While the user waits to hit enter above, the process killSPR is inspected and is seen to be running as the user cadmn (shown below) despite which killSPR is unable to terminate the processes.
cadmn#rhel /tmp > ps -eaf | grep -v grep | grep killSPR
cadmn 24851 22918 0 17:51 pts/36 00:00:00 ./killSPR
cadmn#rhel /tmp >
BTW, none of the main partitions have any nosuid on them
pmn#rhel /tmp > mount | grep nosuid
pmn#rhel /tmp >
The setuid flag on the executable doesn't seem to have the desired effect. What am I missing here? Have I misunderstood how setuid works?

First and foremost, setuid bit simply allows a script to set the uid. The script still needs to call setuid() or setreuid() to run in the the real uid or effective uid respectively. Without calling setuid() or setreuid(), the script will still run as the user who invoked the script.
Avoid system and exec as they drop privileges for security reason. You can use kill() to kill the processes.
Check These out.
http://linux.die.net/man/2/setuid
http://man7.org/linux/man-pages/man2/setreuid.2.html
http://man7.org/linux/man-pages/man2/kill.2.html

You should replace your system call with exec call. Manual for system say's it drops privileges when run from suid program.
The reason is explained in man system:
Do not use system() from a program with set-user-ID or set-group-ID
privileges, because strange values for some environment variables might
be used to subvert system integrity. Use the exec(3) family of func‐
tions instead, but not execlp(3) or execvp(3). system() will not, in
fact, work properly from programs with set-user-ID or set-group-ID
privileges on systems on which /bin/sh is bash version 2, since bash 2
drops privileges on startup. (Debian uses a modified bash which does
not do this when invoked as sh.)
If you replace system with exec you will need to be able to use shell syntax unless you call /bin/sh -c <shell command>, this is what is system actually doing.

Check out this link on making a shell script a daemon:
Best way to make a shell script daemon?
You might also want to google some 'linux script to service', I found a couple of links on this subject.
The idea is that you wrap a shell script that has some basic stuff in it that allows a user to control a program run as another user by calling a 'service' type script instead. For example, you could wrap up /usr/var/myservice/SPRkiller as a 'service' script that could then just be called as such from any user: service SPRkiller start, then SPRkiller would run, kill the appropriate services (assuming the SPR 'program' is run as a non-root user).
This is what it sounds like you are trying to achieve. Running a program (shell script/C program/whatever) carries the same user restrictions on it no matter what (except for escalation bugs/hacks).
On a side note, you seem to have a slight misunderstanding of user rights on Linux/Unix as well as what certain commands and functions do. If a user does not have permissions to do a certain action (like kill the process of another user), then calling setuid on the program you want to kill (or on kill itself) will have no effect because the user does not have permission to another users 'space' without super user rights. So even if you're in a shell script or a C program and called the same system command, you will get the same effect.
http://www.linux.com/learn/ is a great resource, and here's a link for file permissions
hope that helps

Related

Run command as another user in Linux with `su - user c` creates a duplicate process

I want to run a process by a service as root user, because the daemon may have its own user.
But when I run it with system("su - root c ./testbin"), the system shows two processes (I check this via ps aux | grep testbin):
su - root c ./testbin
and
./testbin
How to achieve a single process?
The "su" process can not be avoided, but you can get it to end before your testbin does.
Your original question using "sleep" looks like this:
(su - root -c "sleep 120" &) ; ps aux | grep sleep
If you execute that line multiple times you will see multiple "su" processes as a result of the grep.
Backgrounding the subprocess allows the su process to end, like this:
(su - root -c "sleep 120 &" &) ; ps aux | grep sleep
When you execute that line multiple times you can see that the "su" processes disappear from the list but that the sleep commands continue.
Note that the ampersand inside the double quotes is for the sub process and that the ampersand just before the parentheses is for the 'su' command which is required to perform your question in a single line and speed up testing this case.
I checked if an equivalent of 'execv' exists for the command line, but this does not seem to be the case. Also, 'su' is a process that runs with the permissions of the caller and the subprocess of su runs with the permissions of the process forked by 'su'. It seems logical to me that you can not replace the 'su' process with its child as 'execv' does in 'C' for security reasons.
ps aux | grep testbin | grep -v grep
I think you just see your "grep" process. Use the command above.

Unable to write on /dev/* files

I'm writing a basic char device driver for Linux kernel.
For this, the code flow I have considered is as follows:
alloc_chrdev_region() -> to use dynamic allocation of major number
class_create() -> to create device class in sysfs
device_creat() -> to create device under /dev/
cdv_init() -> to initialize char device structure
cdev_add() -> to add my device structure in kernel
I have added read, write, open, release methods in code.
When I try to read device file under /dev/ my read method is called.
But when I try to write on /dev/ file using echo it gives error
"bash: /dev/scull: Permission denied"
I have checked permissions of file using ls -l, and I have permissions to read or write on this file.
This problem occurs for every device driver module I have written. It works well in on another machine.
I'm working on ubuntu 15.10, custom compiled kernel 4.3.0
the result of ls -l /dev/scull:
crw------- 1 root root 247, 0 Dec 30 18:06 /dev/scull
the exact command I used to open the file
$ sudo echo 54 > /dev/scull
the source code for the open implementation
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){
pr_alert("Device Written\n");
return 0;
}
Behavior I'm seeking here is, I should be able to see 'Device Written' in dmesg ouput?
I assume that you are normally not root on your bash shell. Then this command line
sudo echo 54 > /dev/scull
does not what you think. The command is executed in two steps:
The bash setups the output redirection, i.e., it tries to open /dev/scull with the current user privileges.
The command sudo echo 54 is executed whereas stdout is connected to the file.
As you have no write-permissions as non-root user, the first step fails and the bash reports
"bash: /dev/scull: Permission denied"
You must already be root to setup the output redirection. Thus execute
sudo -i
which gives you an interactive shell with root privileges. The you can execute
echo 54 > /dev/scull
within that root shell.
I know the thread is too old to answer but just in case if someone wants to know alternative method without switching to root user, here is the solution:
sudo bash -c 'echo "54" > /dev/my_dev'
I wanted to note that on your system only root (file owner) has read / write permissions. Your (normal) user account has not! So another (fast) solution would be to give all users read / write permissions.
Probably this is not the safest solution! Only do this in your test environment!
sudo chmod a+rw /dev/scull
But now you test your module with your user account (without sudo)
echo "hello, world!" > /dev/scull
cat < /dev/scull
You can do so while going root with the command
sudo su
and then going into the /dev folder and enter your command (to save data into /dev/scull).
cd /dev
echo 54 > scull

Cannot strace sudo; reports that effective uid is nonzero

command:
bigxu#bigxu-ThinkPad-T410 ~/work/lean $ sudo ls
content_shell.pak leanote libgcrypt.so.11 libnotify.so.4 __MACOSX resources
icudtl.dat leanote.png libnode.so locales natives_blob.bin snapshot_blob.bin
most time it is right.but sometimes it is very slow.
so i strace it.
command:
bigxu#bigxu-ThinkPad-T410 ~/work/lean $ strace sudo ls
execve("/usr/bin/sudo", ["sudo", "ls"], [/* 66 vars */]) = 0
brk(0) = 0x7f2b3c423000
fcntl(0, F_GETFD) = 0
fcntl(1, F_GETFD) = 0
fcntl(2, F_GETFD) = 0
......
......
......
write(2, "sudo: effective uid is not 0, is"..., 140sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges?
) = 140
exit_group(1) = ?
+++ exited with 1 +++
other information:
bigxu-ThinkPad-T410 lean # ls /etc/sudoers -alht
-r--r----- 1 root root 745 2月 11 2014 /etc/sudoers
bigxu-ThinkPad-T410 lean # ls /usr/bin/sudo -alht
-rwsr-xr-x 1 root root 152K 12月 14 21:13 /usr/bin/sudo
bigxu-ThinkPad-T410 lean # df `which sudo`
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sdb1 67153528 7502092 56217148 12%
For security reasons, the setuid bit and ptrace (used to run binaries under a debugger) cannot both be honored at the same time. Failure to enforce this restriction in the past led to CVE-2001-1384.
Consequently, any operating system designed with an eye to security will either stop honoring ptrace on exec of a setuid binary, or fail to honor the setuid bit when ptrace is in use.
On Linux, consider using Sysdig instead -- which, being able to only view but not modify behavior, does not run the same risks.
How to trace sudo
$ sudo strace -u <username> sudo -k <command>
sudo runs strace as root.
strace runs sudo as <username> passed via the -u option.
sudo drops cached credentials from the previous sudo with -k option (for asking the password again) and runs <command>.
The second sudo is the tracee (the process being traced).
For automatically putting the current user in the place of <username>, use $(id -u -n).
Why sudo does not work with strace
In addition to this answer by Charles, here is what execve() manual page says:
If the set-user-ID bit is set on the program file referred to by pathname, then the effective user ID of the calling process is changed to that of the owner of the program file. Similarly, when the set-group-ID bit of the program file is set the effective group ID of the calling process is set to the group of the program file.
The aforementioned transformations of the effective IDs are not performed (i.e., the set-user-ID and set-group-ID bits are ignored) if any of the following is true:
the no_new_privs attribute is set for the calling thread (see prctl(2));
the underlying filesystem is mounted nosuid (the MS_NOSUID flag for mount(2)); or
the calling process is being ptraced.
The capabilities of the program file (see capabilities(7)) are also ignored if any of the above are true.
The permissions for tracing a process, inspecting or modifying its memory, are described in subsection Ptrace access mode checking in section NOTES of ptrace(2) manual page. I've commented about this in this answer.

How does Set-user-id bit work on Linux?

I have the following "root-file" with the following contents:
$ cat root-file
#!/bin/bash
echo $EUID
id
Following are the permissions for this file:
$ ls -l root-file
-rwsr-sr-x 1 root root 15 Nov 18 02:20 root-file
Since the set-user-id bit is set for this file, I would expect that on executing this
file, the effective uid would be displayed as 0 even when a non-root user executes it (since set-user-id bit causes the process to be executed with the effective user-id of the owner of the file, which in this case is root). However, instead I get the following output on executing "root-file" from a non-root shell.
$ ./root-file
1000
uid=1000(chanakya) gid=1000(chanakya) groups=1000(chanakya),4(adm),20(dialout),24(cdrom),46(plugdev),105(lpadmin),119(admin),122(sambashare)
This file/or script is not being executed with effective user-id 0. Why is that so?
you cannot use setuid on shell scripts...
if you absolutely need to use setuid checkout http://isptools.sourceforge.net/suid-wrap.html
Normally something like this could also be established using some custom sudo configuration...

How to redirect output of an already running process [duplicate]

This question already has answers here:
Redirect STDERR / STDOUT of a process AFTER it's been started, using command line?
(8 answers)
Closed 6 years ago.
Normally I would start a command like
longcommand &;
I know you can redirect it by doing something like
longcommand > /dev/null;
for instance to get rid of the output or
longcommand 2>&1 > output.log
to capture output.
But I sometimes forget, and was wondering if there is a way to capture or redirect after the fact.
longcommand
ctrl-z
bg 2>&1 > /dev/null
or something like that so I can continue using the terminal without messages popping up on the terminal.
See Redirecting Output from a Running Process.
Firstly I run the command cat > foo1 in one session and test that data from stdin is copied to the file. Then in another session I redirect the output.
Firstly find the PID of the process:
$ ps aux | grep cat
rjc 6760 0.0 0.0 1580 376 pts/5 S+ 15:31 0:00 cat
Now check the file handles it has open:
$ ls -l /proc/6760/fd
total 3
lrwx—— 1 rjc rjc 64 Feb 27 15:32 0 -> /dev/pts/5
l-wx—— 1 rjc rjc 64 Feb 27 15:32 1 -> /tmp/foo1
lrwx—— 1 rjc rjc 64 Feb 27 15:32 2 -> /dev/pts/5
Now run GDB:
$ gdb -p 6760 /bin/cat
GNU gdb 6.4.90-debian
[license stuff snipped]
Attaching to program: /bin/cat, process 6760
[snip other stuff that's not interesting now]
(gdb) p close(1)
$1 = 0
(gdb) p creat("/tmp/foo3", 0600)
$2 = 1
(gdb) q
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from program: /bin/cat, process 6760
The p command in GDB will print the value of an expression, an expression can be a function to call, it can be a system call… So I execute a close() system call and pass file handle 1, then I execute a creat() system call to open a new file. The result of the creat() was 1 which means that it replaced the previous file handle. If I wanted to use the same file for stdout and stderr or if I wanted to replace a file handle with some other number then I would need to call the dup2() system call to achieve that result.
For this example I chose to use creat() instead of open() because there are fewer parameter. The C macros for the flags are not usable from GDB (it doesn’t use C headers) so I would have to read header files to discover this – it’s not that hard to do so but would take more time. Note that 0600 is the octal permission for the owner having read/write access and the group and others having no access. It would also work to use 0 for that parameter and run chmod on the file later on.
After that I verify the result:
ls -l /proc/6760/fd/
total 3
lrwx—— 1 rjc rjc 64 2008-02-27 15:32 0 -> /dev/pts/5
l-wx—— 1 rjc rjc 64 2008-02-27 15:32 1 -> /tmp/foo3 <====
lrwx—— 1 rjc rjc 64 2008-02-27 15:32 2 -> /dev/pts/5
Typing more data in to cat results in the file /tmp/foo3 being appended to.
If you want to close the original session you need to close all file handles for it, open a new device that can be the controlling tty, and then call setsid().
You can also do it using reredirect (https://github.com/jerome-pouiller/reredirect/).
The command bellow redirects the outputs (standard and error) of the process PID to FILE:
reredirect -m FILE PID
The README of reredirect also explains other interesting features: how to restore the original state of the process, how to redirect to another command or to redirect only stdout or stderr.
The tool also provides relink, a script allowing to redirect the outputs to the current terminal:
relink PID
relink PID | grep usefull_content
(reredirect seems to have same features than Dupx described in another answer but, it does not depend on Gdb).
Dupx
Dupx is a simple *nix utility to redirect standard output/input/error of an already running process.
Motivation
I've often found myself in a situation where a process I started on a remote system via SSH takes much longer than I had anticipated. I need to break the SSH connection, but if I do so, the process will die if it tries to write something on stdout/error of a broken pipe. I wish I could suspend the process with ^Z and then do a
bg %1 >/tmp/stdout 2>/tmp/stderr
Unfortunately this will not work (in shells I know).
http://www.isi.edu/~yuri/dupx/
Screen
If process is running in a screen session you can use screen's log command to log the output of that window to a file:
Switch to the script's window, C-a H to log.
Now you can :
$ tail -f screenlog.2 | grep whatever
From screen's man page:
log [on|off]
Start/stop writing output of the current window to a file "screenlog.n" in the window's default directory, where n is the number of the current window. This filename can be changed with the 'logfile' command. If no parameter is given, the state of logging is toggled. The session log is appended to the previous contents of the file if it already exists. The current contents and the contents of the scrollback history are not included in the session log. Default is 'off'.
I'm sure tmux has something similar as well.
I collected some information on the internet and prepared the script that requires no external tool: See my response here. Hope it's helpful.

Resources