Allowing a non-root user to drop cache - linux

I am carrying out performance tests on a system where I need to ensure I am reading data from the disk, and that it is not just cached (say from earlier tests). I read here that I can drop cache with the command
echo 3 | sudo tee /proc/sys/vm/drop_caches
However, note that even though my account is an admin account (login peter), it still requires my password. I want to be able to run this in a batch script without the requirement to input a password (as this is obviously manual)
More research led me to the sudoers file. My plan was to place the above command into a one line script called dropCache, and edit sudoers so that I could run it without entering a password. So I added the line
ALL ALL=(ALL)NOPASSWD:/home/peter/dropCache
at the end of my sudoers file (using visudo). With my admin account, if I run
sudo -l
I get
(ALL) NOPASSWD: /home/peter/dropCache
However, if I run my dropCache script I still get asked for my password
./dropCache
[sudo] password for peter:
Any help with this would be much appreciated. I am running Ubuntu 12.04
Thanks
Peter

What I did when I needed this was I wrote a small C program, changed the owner of the compiled file to root, and set the setuid bit.
Here is the source code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
extern void sync(void);
int main(void) {
if (geteuid() != 0) {
fprintf(stderr, "flush-cache: Not root\n");
exit(EXIT_FAILURE);
}
printf("Flushing page cache, dentries and inodes...\n");
// First: the traditional three sync calls. Perhaps not needed?
// For security reasons, system("sync") is not a good idea.
sync();
sync();
sync();
FILE* f;
f = fopen("/proc/sys/vm/drop_caches", "w");
if (f == NULL) {
fprintf(stderr, "flush-cache: Couldn't open /proc/sys/vm/drop_caches\n");
exit(EXIT_FAILURE);
}
if (fprintf(f, "3\n") != 2) {
fprintf(stderr, "flush-cache: Couldn't write 3 to /proc/sys/vm/drop_caches\n");
exit(EXIT_FAILURE);
}
fclose(f);
printf("Done flushing.\n");
return 0;
}

I tried this on Ubuntu 21.04 and it works:
Step 1: create a small script like below (assuming the name to be clear_drop_cacahes.sh):
#!/bin/sh
# Run a sync to reduce dirty caches
sync
# Tell the OS to clear caches
echo 3 > /proc/sys/vm/drop_caches
Step 2: make the script executable: chmod 744 clear_drop_caches.sh
Step 3: need to do this with someone with sudo right:
sudo visudo
Add the following line (to the end of the file)
ALL ALL=(ALL) NOPASSWD:/path-to-the-small-script/clear_drop_caches.sh
Step 4: now any user can run the drop caches script like below:
sudo /path-to-the-small-script/clear_drop_caches.sh
I am not sure about the security of doing something like this.

Related

Why the set-uid operation can't be used in an excutable file?

When learning the system security in the ubuntu 20.04 on the VMware, I tried the set-uid operation and found the fllowing question:
With the excutable file catcall compiled by the source code caltcall.c:
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char* argv[]){
char *cat = "/bin/cat";
if(argc < 2){
printf("please type a file name.\n");
return 1;
}
char *command = malloc(strlen(cat) + strlen(argv[1] + 2));
sprintf(command, "%s %s",cat, argv[1]);
system(command);
return 0;
}
I complete the set-uid operation through the following codes:
$ sudo chown root catcall
$ sudo chmod 4755 catcall
When excuting the excutable file catcall, I thought I can see the content of the file /etc/shadow, for the 'catcall' has been set to the Set-Uid programme.
But the operation is denied when trying to access the etc/shadow:
/bin/cat: /etc/shadow: Permission denied
Why did the set-uid operation failed?
Each process actually has two user ids, a real uid and an effective uid, see Difference between Real User ID, Effective User ID and Saved User ID. Normally they are equal. Setting the setuid bit causes the effective uid to be set to the owner of the binary upon exec, but the real uid remains unchanged, and will normally still be the uid of the user who actually ran the program.
So when your program starts, the effective uid will be root and the real uid will be dim_shimmer. Now you have chosen to try to start your program via system(), which executes the specified command using the shell. However, many shells have a security feature where they compare the real and effective uid, and if they are not equal, they set the effective uid equal to the real uid. (For instance, this avoids a disaster in case the sysadmin accidentally does chmod +s /bin/sh). I suspect that's what's happening here. So by the time your cat command runs, your real and effective uid are both dim_shimmer again, and that user does not have permission to read /etc/shadow.
If you run your cat command using execl instead (or one of its relatives), so as to bypass the shell, then I suspect you'll find that it works.

Allow non-root user to process.setuid to certain other user

I run a Node.js process as a non-root user with very limited rights (limited_user).
However, inside this application, there is a potentially attackable part which I (among other security precautions) would like to run as an even less privileged user. I'd like to do that by putting this part of the program to a different file and execute it with childProc.execSync("node my_dangerous_subprocess.js");
Inside that file, process.setuid('very_limited_user'); is used to switch to a different user.
That works, as long as I run the main process as root/sudo. However, I don't want that. The process should be run by limited_user - without sudo rights, if possible.
So, running on Ubuntu 16.04 LS, what can I do to allow a certain non-root user (limited_user) to switch to a certain other user (very_limited_user) via process.setuid?
Node.js version used is 6.10.2.
You can not only use the sudo command to run some program as administrator but the sudo command can also be used to run some program as certain user.
I don't know if it is possible to configure sudo in a way that a certain command can be executed as certain user without asking for a password.
So what you would do is:
childProc.execSync("sudo -u very_limited_user node my_dangerous_subprocess.js");
I myself would not do this because I'm not aware of the sudo configuration files (and I'd fear to destroy those files in a way that sudo does not work any more) but I can program C well enough.
I'd write the following program:
#include <stdio.h>
#include <unistd.h>
int main()
{
int i;
i=geteuid();
setreuid(i,i);
i=getegid();
setregid(i,i);
execlp("node","node","my_dangerous_subprocess.js",NULL);
fprintf(stderr,"Could not execute node!\n");
return 1;
}
(Note that "node" appears twice as argument of execlp().)
I'd compile the program and change the user ID, the group ID and the flags of the resulting executable file (in this example the C source file is named "my_dangerous_part.c"):
gcc -o my_dangerous_part.bin my_dangerous_part.c
sudo chown less_privileged_user:less_privileged_group my_dangerous_part.bin
sudo chmod 6755 my_dangerous_part.bin
Then I could run the program like this:
childProc.execSync("/directory_containing_the_file/my_dangerous_part.bin");

Updating environment variables in Bash

I have one long-running script which does some work with AWS.
I have another script which puts environment variables for AWS authentication but that is only valid for 15 mins.
Now I can't change the long running script so is there any way that I can have a cron job or anything else which can update the environment variables in the shell where long script is running?
Elaborating the comment:
Assumptions
The long running script cannot be modified.
The long running script will call an executable file that can be modified (for the sake of the example, lets assume that the executable file is /usr/local/bin/callable).
You've permissions to rename /usr/local/bin/callable and create a new file under that file path and name.
Either the long running script is running as root, or the /usr/local/bin/callable must be able to perform privilege escalation with the setuid bit set.
You'll need gdb installed.
You'll need to have gcc installed if the long running script isn't running as root.
Risks
If this is a critical system and security is a moderate to major concern, do not use any of the following procedures.
Although unlikely to happen, but attaching to a running process and injecting calls to it may cause unexpected or undefined behaviours. If this is a critical system doing some critical procedures, do not use any of the following procedures.
Generally, all these procedures are a bad idea, but they represent one possible solution. But...
Use at your own risk.
Procedures (for long running script running as root)
bash# mv /usr/local/bin/callable /usr/local/bin/callable.orig
bash# cat > /usr/local/bin/callable << EOF
> #!/bin/bash
>
> echo -e "attach ${PPID}\ncall setenv(\"VAR_NAME\", \"some_value\", 1)\ndetach" | /usr/bin/gdb >& /dev/null
>
> /usr/local/bin/callable.orig
>
> EOF
bash# chmod 755 /usr/local/bin/callable
Procedures (for long running script NOT running as root)
bash# mv /usr/local/bin/callable /usr/local/bin/callable.orig
bash# cat > /usr/local/bin/callable.c << EOF
> #include <stdio.h>
> #include <sys/types.h>
> #include <unistd.h>
> #include <stdlib.h>
> int main(void) {
> char inject[128]; /* You may want to increase this size, based on your environment variables that will affect the size of the string */
> uid_t save_uid = getuid();
> gid_t save_gid = getgid();
> sprintf(inject, "echo -e \"attach %u\ncall setenv(\\\"VAR_NAME\\\", \\\"some_value\\\", 1)\ndetach\" | /usr/bin/gdb >& /dev/null", getppid());
> setreuid(0, 0);
> setregid(0, 0);
> system(inject);
> setregid(save_gid, save_gid);
> setreuid(save_uid, save_uid);
> system("/usr/local/bin/callable.orig");
> return 0;
> }
> EOF
bash# gcc -o /usr/local/bin/callable /usr/local/bin/callable.c
bash# rm -f /usr/local/bin/callable.c
bash# chown root:long_running_script_exclusive_group /usr/local/bin/callable
bash# chmod 4750 /usr/local/bin/callable
Bonus
Instead of intercepting, you can, as you stated, use a cronjob to attach to the process with gdb (this will, at least, avoid you to intecept the long running script with another script and, in the worst case, the need to create a setuid binary to do it). You will, however, need to know or fetch the PID of the long running script shell process (as it is changing for each time it is called). It is also prone to failure, due to syncing problems (the script may not be running when the crontab triggers).
References
Changing environment variable of a running process
Is there a way to change another process's environment variables?

Raw capture capabilities (CAP_NET_RAW, CAP_NET_ADMIN) not working outside /usr/bin and friends for packet capture program using libpcap

TL;DR: Why are cap_net_raw, cap_net_admin capabilities only working in /usr/bin (or /usr/sbin), but not other places? Can this be configured someplace?
I'm having problems assigning capabilities to my C program utilizing libpcap in Ubuntu 14.04. Even after assigning capabilities using setcap(8) and checking it using getcap(8), I still get a permission error. It seems capabilities only work for executables in \usr\bin and friends.
My program test.c looks as follows:
#include <stdio.h>
#include <pcap.h>
int main(int argc, char **argv) {
if (argc != 2) {
printf("Specify interface \n");
return -1;
}
char errbuf[PCAP_ERRBUF_SIZE];
struct pcap* pcap = pcap_open_live(argv[1], BUFSIZ, 1, 0, errbuf);
if (pcap == NULL) {
printf("%s\n", errbuf);
return -1;
}
return 0;
}
and is compiled with
gcc test.c -lpcap
generating a.out executable. I set capabilities:
sudo setcap cap_net_raw,cap_net_admin=eip ./a.out
And check to see that it looks right:
getcap a.out
which gives me
a.out = cap_net_admin,cap_net_raw+eip
Running a.out gives me:
./a.out eth0
eth0: You don't have permission to capture on that device (socket: Operation not permitted)
Running with sudo works as expected (program prints nothing and exits).
Here's the interesting part: If I move a.out to /usr/bin (and reapply the capabilities), it works. Vice versa: taking the capability-enabled /usr/bin/dumpcap from wireshark (which works fine for users in the wireshark group) and moving it out of /usr/bin, say to my home dir, reapplying the same capabilities, it doesn't work. Moving it back, it works.
SO: Why are these capabilities only working in /usr/bin (and /usr/sbin), but not other places? Can this be configured someplace?
This might be because your home directory is mounted with nosuid, which seems to prevent capabilities working. Ubuntu encrypts the home directory, and mounts that with ecryptfs as nosuid.
Binaries with capabilities work for me in /usr/, and /home/, but not my home directory.
The only reference I could find to nosuid defeating capabilities is this link: http://www.gossamer-threads.com/lists/linux/kernel/1860853#1860853. I would love to find an authoritative source.

In Linux, does the location of an executable affect how the setuid bit is interpreted?

In a Linux system, does the permissions of the directory in which a setuid program resides affect how the kernel launches the process? The reason I ask is that when I compiled an identical setuid program in two different directories, it only actually assumed the user permissions in one directory. I compiled it in /tmp and /home/flag03 where flag03 is the user account that I am attempting to gain access to. When executed from /tmp it did not escalate privileges as expected, but it worked under /home.
Some background on the problem:
I'm working on level 03 of exploit-exercises.com/nebula. The exercise requires that you gain access to a flag03 user account. The exercise is setup so that the flag03 user is periodically running a cronjob which will allow you to execute a script in a specific directory. My plan was to write a simple bash script which will compile a setuid program that itself launches a bash shell, and then set the setuid bit with chmod +s. The idea is that when the setuid program is compiled, it is compiled by user flag03 via the cronjob. Once this newly compiled program is executed, it will launch a shell as user flag03, and that is the goal.
Here is the simple setuid program (l3.c, based on levels 1 + 2):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char **argv, char **envp)
{
uid_t uid;
gid_t gid;
uid = geteuid();
gid = getegid();
setresuid(uid,uid,uid);
setresgid(gid,gid,gid);
printf("uid: %d\n", getuid());
printf("gid: %d\n", getgid());
system("/bin/bash");
return 0;
}
In order for this to work, a bash script compiles the program as user flag03 and then chmods the setuid bit.
#!/bin/bash
#gcc -o /tmp/l3 /tmp/l3.c
#chmod +s,a+rwx /tmp/l3
gcc -o /home/flag03/l3 /tmp/l3.c
chmod +s,a+rwx /home/flag03/l3
The executable generated in /tmp does not escalate privileges as expected, but the one generated in /home/flag03 works as expected.
NOTE I just created a new bash script to move the version of the setuid program that was compiled in /tmp to /home/flag03, and then reset the setuid bit. When executed from there, that version worked as well. So it appears to me that the permissions of the directory in which the setuid program resides has some kind of impact with how the process is launch. Maybe this is related to /tmp being a somewhat "special" directory?
Thanks for any interest in this long-winded question!
If the filesystem is mounted with the nosuid option, the suid bit will be ignored when executing files located there. As I understand it, /tmp is usually a separate filesystem (often tmpfs) mounted with the nosuid option. The motivation for this configuration is preventing a compromised account that has no writable storage except /tmp (e.g. nobody) from being able to produce suid binaries, which may be used in certain elaborate multi-step attacks to elevate privilege.

Resources