How to bypass the check of setuid - c

I am now taking a security class. And the professor asked us to exploit a program to gain higher privilege.
This is the code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFSIZE 512
int main(int argc, char *argv[]) {
struct stat buf;
char cmd[BUFSIZE];
FILE *f = NULL;
if (argv[1] == NULL) {
fprintf(stderr, "Please provide an argument\n");
exit(1);
}
if (stat(argv[1], &buf)) {
fprintf(stderr, "Can't stat the file\n");
exit(1);
}
if (buf.st_gid != getegid()) {
fprintf(stderr, "The file must be owned by group %d\n", getegid());
exit(1);
}
sleep(1);
if ((f = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "Cannot open command file\n");
return 1;
}
while (fgets(cmd, BUFSIZE, f)) {
if ((cmd[0] == '\n') || (cmd[0] == 0x7f)) {
fprintf(stderr, "Found empty line, quitting!\n");
return 2;
}
system(cmd);
}
printf("Done!\n");
return 0;
}
The particular situation is:
There is a binary code for this program for us to run. And the SUID of the binary code is set to higher level(here I assume level2). However, buf.st_gid != getegid() will always check if the file we specified is owned by root. Most important thing is, I only have level1 privilege. There is only one file owned by level2 I can use which is provided by our professor. And I certainly tried to pass in that filename. But nothing happens. I don't know if there is anyway I can exploit this program.(To execute some commands during runtime using the privilege of level2)

Related

S_ISREG returns 0

So I want to test if a file given is regular or not.
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main(int argc, char **argv)
{
// Input check.
if (argc != 2) {
fprintf(stdout,"Format: %s <filename.txt>\n", argv[0]);
return -1;
}
// Make sure the file is a regular file.
int fd;
if ((fd = open(argv[1], O_RDONLY) == -1)) {
fprintf(stdout, "%s", strerror(errno));
return -1;
}
struct stat st;
if ((fstat(fd, &st) == -1)) {
fprintf(stdout, "%s\n", strerror(errno));
return -1;
}
if (!(S_ISREG(st.st_mode))) {
fprintf(stdout, "Error, invalid file\n");
return -1;
}
close(fd);
return 0;
}
I run: .\a in.txt
I don't know what exactly is going on, but when I'm trying to test if the file is regular (last if statement), it fails. I tested to see if fstat fails, but it doesn't.
Here's the problem:
if ((fd = open(argv[1], O_RDONLY) == -1)) {
The equality operator == has higher precedence than the assignment operator =. So the above parses as:
if (fd = (open(argv[1], O_RDONLY) == -1)) {
Which assigns to fd the result of the comparison which will be either 0 or 1. These values both happen to be valid open file descriptors for stdin and stdout so the fstat call is successful and gets you the status of one of these streams.
You'll need to adjust the parenthesis to do the assignment first:
if ((fd = open(argv[1], O_RDONLY)) == -1) {
Also, it looks like you have other if statements that have a redundant set of parenthesis that you can remove. You want to avoid this because those extra parenthesis can silence warnings about exactly what you did.

open()'s "mode" argument won't set the correct permissions for the file

My intention was to open two files, where the second one would be brand new, with the same permissions as the first file. So to test my code I changed the first file permissions to "777". Then I proceeded to run my program. And to my surprise, the permission of the newborn file2 were wrong! They where set to 755. Even weirder is when I set the first file to "111" and tried again, the result now was "1204".
Can someone explain to me this weird behavior?
Here's my code
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *args[]) {
struct stat stats;
int fd1, fd2;
fd1 = open("testfile.txt", O_RDONLY);
/* Error check*/
if (fd1 == -1) {
/* Error handling */
perror("Opening");
printf("Unable to open file: %s\n", "testfile.txt");
printf("ERROR: %s\n", strerror(errno));
return 1;
}
if(fstat(fd1, &stats) == -1)
{
printf("Error while getting stats: %s\n", strerror(errno));
exit(-1);
}
//Receives the output file as a main argument . . .
if (argc > 1)
{
//(stats.st_mode = Gets the mask of the first file)
fd2 = open(args[1], O_WRONLY|O_CREAT, stats.st_mode);
/* Error check*/
if (fd2 == -1) {
/* Error handling */
perror("Opening");
printf("Unable to open file: %s\n",args[1]);
printf("ERROR: %s\n", strerror(errno));
return 1;
}
}
//. . . if it doesn't it creates a standard one warning you about it
else
{
fd2 = open("Nope.txt", O_WRONLY|O_CREAT, stats.st_mode);
/* Error check*/
if (fd2 == -1) {
/* Error handling */
perror("Opening");
printf("Unable to open file: %s\n",args[1]);
printf("ERROR: %s\n", strerror(errno));
return 1;
}
printf("Standard file created\n");
}
close(fd1);
close(fd2);
return 0;
}
I tried to make it as tidy as I could :)
From open(2) man page on the part about O_CREATE:
The effective mode is modified by the process's umask in the usual way: in the absence of a default ACL, the mode of the created file is (mode & ~umask).
If you type umask in bash you can see what value is used and which bits get cleared from the mode you provide.

C copying files using only system calls, infinite runtime

So I am working on a simple program in C but have been stuck on the copying portion. The program takes two filenames on the command line as arguments and copies the first to the second by using system calls. If the second file exists it asks the user if they want to overwrite, if not it creates it. However, my program when the user choices overwrite goes on infinitely.
Here is my code:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd1, fd2;
char buffer[1024];
long int n;
char c;
int num;
if (argc != 3) {
printf("%d\n",argc);
printf("Error, you need to give 2 arguments. Such that [File to copy] [File to create].\n");
exit(1);
}
if (access(argv[1], F_OK) < 0) {
printf("File %s either does not exist or cannot be accessed.\n", argv[1]);
exit(1);
} else {
printf("file %s exists\n", argv[1]);
}
if (access(argv[2], F_OK) < 0) {
printf("File %s does not exist, but one will be created.\n", argv[1]);
fd2=open(argv[2],O_CREAT|O_WRONLY|O_TRUNC, 0700);
} else {
printf("file %s exists\n", argv[2]);
printf("Would you like to overwrite %s? (Type 1 for yes or 0 for no)\n", argv[2]);
scanf("%d%c", &num, &c); // use c to capture \n
if (num == 1) {
fd2=open(argv[2],O_CREAT|O_WRONLY|O_TRUNC, 0700);
} else {
if (num == 0) {
printf("Ok, the file will not be copied and the program will now exit.\n");
exit(1);
} else {
printf("I do not recognize this response, program will now be terminated.\n");
}
}
}
printf("step\n");
while ((n1 = read(fd1, buffer, 1024)) > 0) {
printf("step\n");
if(write(fd2, buffer, n1) != n1){
printf("step\n");
perror("Error writing file.");
printf("step\n");
exit(3);
}
printf("stepss\n");
}
close(fd1);
close(fd2);
}
The printf("step") is for debugging, but it only prints one. Meaning the program freezes up by the while loop. I can use stat(), open(), read(), write(), close(), and access(). Any ideas on what is wrong or how it can be done better would be appreciated!
Any ideas on what is wrong
Your fd1 never been assigned, so read(fd1, ...) returns an error.
Check return value of read and printf("%m\n") will print the details.
$ ./a.out a b
file a exists
file b exists
Would you like to overwrite b? (Type 1 for yes or 0 for no)
1
step
Bad file descriptor

How to check if two device files are equal in C?

I want to check if two device files are equal in C, without accessing the underlying devices.
Can this be done using stat and lstat?
To determine if two device files are the same, call stat on each of them, and check that they're both the same type of device (block or character) and that their .st_rdev members are equal.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(int arc, char **argv)
{
struct stat s1, s2;
char *err;
if (stat(argv[1], &s1) < 0) {
err = strerror(errno);
fprintf(stderr, "Can't stat %s: %s\n", argv[1], err);
exit(1);
}
if (stat(argv[2], &s2) < 0) {
err = strerror(errno);
fprintf(stderr, "Can't stat %s: %s\n", argv[2], err);
exit(1);
}
if (S_ISCHR(s1.st_mode) && S_ISCHR(s2.st_mode) && s1.st_rdev == s2.st_rdev) {
printf("Same char device\n");
exit(0);
}
if (S_ISBLK(s1.st_mode) && S_ISBLK(s2.st_mode) && s1.st_rdev == s2.st_rdev) {
printf("Same block device\n");
exit(0);
}
printf("devices do not match\n");
exit(1);
}
Have you looked at stat structure? You can find the size of a file, and many many things. .st_dev should works in your case.

Determine programmatically if a program is running

In C, how can I find out programmatically if a process is already running on Linux/Ubuntu to avoid having it start twice? I'm looking for something similar to pidof.
You can walk the pid entries in /proc and check for your process in either the cmdline file or perform a readlink on the exe link (The following uses the first method).
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
pid_t proc_find(const char* name)
{
DIR* dir;
struct dirent* ent;
char* endptr;
char buf[512];
if (!(dir = opendir("/proc"))) {
perror("can't open /proc");
return -1;
}
while((ent = readdir(dir)) != NULL) {
/* if endptr is not a null character, the directory is not
* entirely numeric, so ignore it */
long lpid = strtol(ent->d_name, &endptr, 10);
if (*endptr != '\0') {
continue;
}
/* try to open the cmdline file */
snprintf(buf, sizeof(buf), "/proc/%ld/cmdline", lpid);
FILE* fp = fopen(buf, "r");
if (fp) {
if (fgets(buf, sizeof(buf), fp) != NULL) {
/* check the first token in the file, the program name */
char* first = strtok(buf, " ");
if (!strcmp(first, name)) {
fclose(fp);
closedir(dir);
return (pid_t)lpid;
}
}
fclose(fp);
}
}
closedir(dir);
return -1;
}
int main(int argc, char* argv[])
{
if (argc == 1) {
fprintf("usage: %s name1 name2 ...\n", argv[0]);
return 1;
}
int i;
for(int i = 1; i < argc; ++i) {
pid_t pid = proc_find(argv[i]);
if (pid == -1) {
printf("%s: not found\n", argv[i]);
} else {
printf("%s: %d\n", argv[i], pid);
}
}
return 0;
}
This is the same as the code posted by John Ledbetter . It is good to refer to the file named stat in /proc/pid/ directory than cmdline since the former gives process states and process name. The cmdline file gives complete arguments with which the process is started. So that fails in some cases. Any way the idea given by John is good. Here I posted the modified code of John. I was looking for the code in c in Linux to check dhcp is running or not . With this code, I am able to do that. I hope it may be useful for someone like me.
#include <sys/types.h>
#include <dirent.h>
#include<unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
pid_t proc_find(const char* name)
{
DIR* dir;
struct dirent* ent;
char buf[512];
long pid;
char pname[100] = {0,};
char state;
FILE *fp=NULL;
if (!(dir = opendir("/proc"))) {
perror("can't open /proc");
return -1;
}
while((ent = readdir(dir)) != NULL) {
long lpid = atol(ent->d_name);
if(lpid < 0)
continue;
snprintf(buf, sizeof(buf), "/proc/%ld/stat", lpid);
fp = fopen(buf, "r");
if (fp) {
if ( (fscanf(fp, "%ld (%[^)]) %c", &pid, pname, &state)) != 3 ){
printf("fscanf failed \n");
fclose(fp);
closedir(dir);
return -1;
}
if (!strcmp(pname, name)) {
fclose(fp);
closedir(dir);
return (pid_t)lpid;
}
fclose(fp);
}
}
closedir(dir);
return -1;
}
int main(int argc, char* argv[])
{
int i;
if (argc == 1) {
printf("usage: %s name1 name2 ...\n", argv[0]);
return 1;
}
for( i = 1; i < argc; ++i) {
pid_t pid = proc_find(argv[i]);
if (pid == -1) {
printf("%s: not found\n", argv[i]);
} else {
printf("%s: %d\n", argv[i], pid);
}
}
return 0;
}
There are ways to avoid /proc usage (and there might be good reasons to do so, e.g. /proc might not be installed at all, and/or it might have been symlinked to something deceptive, or that pid has been hidden in /proc). Granted, the below method doesn't look that good, I wish there were a proper API for that!
Anyway, section 1.9 of a 1997 Unix programming FAQ says:
Use kill() with 0 for the signal number. There are four possible results from this call:
kill() returns 0
This implies that a process exists with the given PID, and the
system would allow you to send signals to it. It is
system-dependent whether the process could be a zombie.
kill() returns -1, errno == ESRCH
Either no process exists with the given PID, or security
enhancements are causing the system to deny its existence. (On
some systems, the process could be a zombie.)
kill() returns -1, errno == EPERM
The system would not allow you to kill the specified process.
This means that either the process exists (again, it could be a
zombie) or draconian security enhancements are present (e.g. your
process is not allowed to send signals to anybody).
kill() returns -1, with some other value of errno
You are in trouble!
The most-used technique is to assume that success or failure with EPERM
implies that the process exists, and any other error implies that it
doesn't.
pidof works by walking over the /proc filesystem. In C, you could do something similar by enumerating /proc; opening /proc/X/cmdline for every X where X is a list of one or more decimal numbers. I don't know if you have any portability requirements but bear that in mind if you are to rely on the availability of /proc.
This problem is more commonly solved on UNIX-like systems by wrapping the start-up of the program and maintaining a PID file. See /etc/init.d/* for classic examples of this approach. You will need to be careful to ensure that the code which reads of writes the PID file does so in a safe manner (atomically). If your target OS has a more capable init (such as systemd), you may be able to out source this work to that.

Resources