Setting up SUID in C language is not enough - c

For pedagogical purposes, I want to set up a basic command injection in C. I have the following code :
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv) {
char cat[] = "cat ";
char *command;
size_t commandLength;
commandLength = strlen(cat) + strlen(argv[1]) + 1;
command = (char *) malloc(commandLength);
strncpy(command, cat, commandLength);
strncat(command, argv[1], (commandLength - strlen(cat)) );
system(command);
return (0);
}
I compile it, set the binary as owned by root and set the SUID to 1, as follows :
gcc injectionos.c -o injectionos
sudo chown root:root injectionos
sudo chmod +s injectionos
I obtain the following result :
ls -la
total 40
drwxr-xr-x 2 olive olive 4096 Jan 6 13:17 .
drwxr-xr-x 3 olive olive 4096 Jan 6 12:15 ..
-rwsr-sr-x 1 root root 16824 Jan 6 13:17 injectionos
-rw-r--r-- 1 olive olive 415 Jan 6 13:17 injectionos.c
-rwx------ 1 root root 9 Jan 6 12:43 titi.txt
-rw-r--r-- 1 olive olive 9 Jan 6 12:16 toto.txt`
So, basically, with the SUID set to 1, i should be able to open both toto.txt and titi.txt files by performing the following injection :
./injectionos "toto.txt;cat titi.txt"
But executing this command, I got a permission denied when accessing titi.txt. Finally, when I add a setuid(geteuid()); in my code, the injection is working and I can access to titi.txt file.
Given that injectionos is ran as root and titi.txt belong to root, I supposed that it was enough, but apparently no. What am I missing here?

The privileges are being dropped by /bin/sh executed as part of the system() call. See the man page for bash and the -p option
If the shell is started with the effective user (group) id not equal
to the real user (group) id, and the -p option is not supplied, no
startup files are read, shell functions are not inherited from the
environment, the SHELLOPTS, BASHOPTS, CDPATH, and GLOBIGNORE
variables, if they appear in the environment, are ignored, and the
effective user id is set to the real user id. If the -p option is
supplied at invocation, the startup behavior is the same, but the
effective user id is not reset.
Well, technically debian uses dash by default, but it does the same thing.
So the default behavior of the shell has been adjusted to mitigate this injection at least somewhat.

Related

Pipes executes command only once

I'm new on StackOverflow and learning about pipes in C. I'm trying to make a shell interpeter that allows introducing 2 commands linked by pipe like
/bin/ls -l | /bin/grep a
I have a main which have a function that reads a command, then main calls a function that searchs for a "|" and if it finds it, the function makes 2 commands from it, then that function calls another one which executes the commands. My problem is that it just works once. If I introduce a command with pipes it executes it perfectly, but if I do it again it doesn't work, like the directory had change. It doesn't matter that the first command has pipes or not, the second command (if it has pipes) wont execute correctly. I think it's because of the pipes, the code of the function that execute the pipes:
void execute_pipes(char *** command1, char *** command2){
pid_t son;
int tube2[2];
pipe(tube2);
son=fork();
if(son==0){//son1
dup2(tube2[1], STDOUT_FILENO);
close(tube2[0]);
if(execv((*command1)[0],*command1)==-1){
printf("Error");
exit(0);
}
}else{//father
son=fork();
if(son==0){//son2
dup2(tube2[0], STDIN_FILENO);
close(tube2[1]);
if(execv((*command2)[0],*command2)==-1){
printf("Error");
exit(0);
}
}else{//father
close(tube2[0]);
close(tube2[1]);
waitpid(son, NULL, 0);
}
}
}
I'm sure that the parameters are ok, ending with a NULL parameter (if not I guess it wouldnt execute one time). In each new read, a new pipe is created. I guess that the last pipe won't affect this new one but I don't know...
An example of this:
/home/user/Desktop$ /bin/ls -l | /bin/grep -
-rw-rw-r-- 1 user user 435 dec 18 02:33 filename
drwxrwsr-x 9 user user 4096 dec 20 19:23 filename
-rwxrwxr-x 1 user user 14464 dec 20 20:29 filename
-rw-rw-r-- 1 user user 3580430 dec 5 03:24 filename
-rw-rw-r-- 1 user user 6833 dec 20 20:05 filename
-rw-rw-r-- 1 user user 6772 dec 20 18:48 filename
-rw-rw-r-- 1 user user 1226 dec 19 21:48 filename
-rwxrwxr-x 1 user user 8704 dec 18 16:23 filename
-rw-rw-r-- 1 user user 33673847 oct 17 20:50 filename
/home/user/Desktop$ /bin/ls -l | /bin/grep -
-rwxr-xr-x 1 root root 126584 feb 18 2016 /bin/ls
/home/user/Desktop$
NOTE: I've changed my username for 'user' and the names of the files for 'filename'.
Thanks whoever who reads this.
Summarising: my code only executes a command correctly one time, the second time it doesn't work properly and I don't know why, but I guess the problem are the pipes.
I can't comment yet, so I'm posting it as an answer. Welcome to SO. It will be easier for people to help you if you provide a working example, which I think you can. Here is a guide How to create a Minimal, Complete, and Verifiable example
Unfortunately I can't get your code to work. Maybe looking at an example implementation of a lightweight shell will help you. Here is the source code for xv6 shell. Search for PIPE keyword.
xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix
Version 6 (v6).
Oke, I solved the problem, it was a problem of the initialization of the parameters. It was in a function I didn't write because I supposed it worked.
The only doubt I have now is why was even able to execute...
Thanks everyone for answering me .

Format file size in Unix in readable format

When I do ls -l I get
-rw-r--r-- 1 jboss admin **26644936** Sep 1 21:23 MyBig.war
How do I print it as below
-rw-r--r-- 1 jboss admin **26,644,936** Sep 1 21:23 MyBig.war
The proper way to format ls output is to specify BLOCK_SIZE.
Saying:
BLOCK_SIZE="'1" ls -l
would achieve your desired result.
Quoting from the above link:
Some GNU programs (at least df, du, and ls) display sizes in “blocks”.
You can adjust the block size and method of display to make sizes
easier to read.
A block size specification preceded by ‘'’ causes output sizes to be
displayed with thousands separators.
Using sed:
$ ls_output='-rw-r--r-- 1 jboss admin 26644936 Sep 1 21:23 MyBig.war'
$ echo $ls_output | sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta'
-rw-r--r-- 1 jboss admin 26,644,936 Sep 1 21:23 MyBig.war
Above sed command repeatedly replace the last 4 digits #### with #,###.
-e :a: Make a label named a for t command.
ta: Jump to a if substitution was successful.

copy content of a file

When you do this:
cp file1 file2
(file2 already exists)
What actually happens behind the scene?
1) Does the content of file1 actually get copied to file2?
2) Or is a new file created with the name file2 (overriding the old one) which has same content of file1?
1) Since you're using "cp", I assume the OS is Linux.
2) On Linux, a "file" is referenced by "inodes". Here are two example files:
$ ls -li 1 2
245728 -rw-r--r-- 1 paulsm users 8 Aug 14 14:52 1
245729 -rw-r--r-- 1 paulsm users 8 Aug 14 14:52 2
$ cat 1
Hello 1
$ cat 2
Hello 2
3) Here is the result after "cp"
$ cp 1 2
$ ls -li 1 2
245728 -rw-r--r-- 1 paulsm users 8 Aug 14 14:52 1
245729 -rw-r--r-- 1 paulsm users 8 Aug 14 14:55 2
$ cat 2
Hello 1
You see:
a) the contents of "1" completely replace "2"
b) there is no "new file" - the inode for "2" remains unchanged from before the copy
c) the file date is changed along with the file contents
'Hope that helps .. PSM
Usually the first. Both an index-entry as well as the file's data are written.
Yet it would help to know on what (file-)system you are (guessing linux flavour).
You would probably be aware if you were creating a junction point or symbolic/hard LINK.
Think of it like this:
Hardlink is a pointer/name, that points to a data; i.e. it's just an alternative filename; it has same inode number as the file it was created from.
Copy obviously, copy of the data; point to a different direction that file it was copyed from; has different inode number.
Also difference is in system calls, but that`s somewhat deep-diving into issue

Bash scripting ls with spaces in the argument, when the argument is coming from an array

I have the following code:
entries=("Wet\ Picnic" "Arizona\ Jones" "Bikeboy")
for arg in "${entries[#]}"; do ls -lh $arg.* ; done
It gives me 4 errors and 1 success. I'd really like it to give me 3 successes.
How do I handle the fact that the arguments to ls contain spaces (I've obviously tried escaping them as that's how it is currently), but to no avail.
The console output is currently.
ls: Wet: No such file or directory
ls: Picnic: No such file or directory
ls: Arizona: No such file or directory
ls: Jones: No such file or directory
-rw-r--r-- 1 root root 0 Jun 27 17:55 Bikeboy.png
SO it's clearly splitting on the space. Even though it's escaped.
You're escaping in the wrong spot. Try this example:
entries=("Wet Picnic" "Arizona Jones" "Bikeboy")
for arg in "${entries[#]}"; do ls -lh "$arg".* ; done
It should work fine. Here's a complete example, including the "People's Stage" you mentioned in the comments:
$ ls
Arizona Jones.png People's Stage.png example*
Bikeboy.png Wet Picnic.png
$ cat example
#!/bin/bash
entries=("Wet Picnic" "Arizona Jones" "Bikeboy" "People's Stage")
for arg in "${entries[#]}"; do ls -lh "$arg".* ; done
$ ./example
-rw-r--r-- 1 carl staff 0B Jun 27 10:00 Wet Picnic.png
-rw-r--r-- 1 carl staff 0B Jun 27 10:00 Arizona Jones.png
-rw-r--r-- 1 carl staff 0B Jun 27 10:01 Bikeboy.png
-rw-r--r-- 1 carl staff 0B Jun 27 10:11 People's Stage.png

How to print file details in C without using ls -ln

After I found a file on the disk , I now need to print out all its details , for example :
-rwxr-xr-x 1 1000 1000 8296 2010-01-06 22:29 ./Documents/exer4
-rwxr-xr-x 1 1000 1000 8517 2009-12-30 11:30 ./Documents/os/exer4
lrwxrwxrwx 1 1000 1000 8 2010-01-10 13:10 ./Documents/cs/2012/exer4 -> ../a.out
I need to print a file details without using ls -ln .Any idea how to do that ?
Thanks
You want the stat() function.
Here's a web page that documents *NIX file functions including stat():
http://rabbit.eng.miami.edu/info/functions/unixio.html
You can use this function:
int lstat(const char *path, struct stat *buf);
This works for me from the shell.
ls -l | grep -v "\->"
It simply filters out any line that has a -> in it.
Note however, that if you have any files/directories that for some reason have -> in their names, they will also be filtered out. Having said that, I've never seen that, nor would it be a good idea.

Resources