shc
shc copied to clipboard
Use Pipes instead of arguments to pass script to bash
I've been looking into #7 and I think the real problem here is not that command arguments are being leaked, but that SHC is using command arguments to pass sensitive information (the script) to the shell.
Instead of something of the form:
$ bash -c "script goes here"
Something of the form:
script="script goes here"
echo $script | bash
Would be more secure.
I'd make the PR myself but I have no idea how to express that in C.
nice idea, but unfortunately I don't have much time to spare on it.
nice idea i'll may be add it to my new feature "Hardening" but its not needed for the moment
@intika Note that you have to take care of positional arguments. Passing the arguments after -s flag works for dash, bash, ksh, zsh, csh, tcsh
Example:
echo 'echo $1 and $2' | bash -s first second
You reminded me that i have to add all those shells to the if condition testing the parent process
Should be fine with the current sh and /bin/sh... as all thoses bashes should link to that... any way if you need to add them in v4... here is a list expect, busybox, sh, dash, bash, ksh, zsh, csh, tcsh
Started implementing this but i am out of time :(, i won't finish it as i don't need it... but here is what i did if anyone is willing to fix this issue:
First thing first this require something like this because exec family does not support piping
/********************************************************
* This program runs "ls -al | grep arg1 > arg2 " *
* This uses the strategy that parent does redirection *
* before forking, so child does not need to do it. *
********************************************************/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc,char**argv)
{
if (argc < 3) {
fprintf(stderr, "usage: lsgrep arg1 arg2\n");
exit(1);
}
// Parent saves stdin/stdout
int tempin = dup(0);
int tempout = dup(1);
int fdpipe[2];
pipe(fdpipe); // pipe must be created in parent so both children have access
// Strategy: parent does the redirection before fork()
dup2(fdpipe[1],1); // Now stdout is a copy of the read end of pipe
close(fdpipe[1]); // Having made a copy, the original is no longer needed
// fork for "ls"
int ret= fork();
if(ret==0) {
// This is child process.
close(fdpipe[0]); // In child (for ls) the writing end of pipe is not needed
char * args[3];
args[0]="ls";
args[1]="-al";
args[2]=NULL;
execvp(args[0], args);
// If we reach here, there is an error in execvp
perror("execvp");
exit(1);
}
// We are still in parent process after first fork.
// We are to set things up for "grep"
dup2(fdpipe[0], 0); // Set stdin to a copy of the reading end of pipe
close(fdpipe[0]); // The original is no longer needed, now a copy is made
//create outfile
int fd=open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600);
if (fd < 0){
perror("open failed");
exit(1);
}
dup2(fd,1); // redirect stdout to a copy of fd
close(fd); // original fd no longer needed
// fork for grep
ret= fork();
if(ret==0) {
char * args[3];
args[0]="grep";
args[1]=argv[1];
args[2]=NULL;
execvp(args[0], args);
// error in execvp
perror("execvp failed");
exit(1);
}
// Restore stdin/stdout
dup2(tempin,0);
dup2(tempout,1);
// Parent waits for grep process to complete
waitpid(ret,NULL);
printf("All done!!\n");
}
Implementation: Under the generated x.c code: this will replace the line execvp(shll, varg);
char * usedArg;
sprintf(usedArg, "exec '%s' \"$@\"", varg[0]);
if (strcmp(varg[2], usedArg) == 0) {
//First exec
execvp(shll, varg);
}
else {
//Second exec
//Pipe is supported with dash, bash, ksh, zsh, csh, tcsh
if (varg[4]) { //if there are param
if ((strstr(shll, "bash") != NULL) ||
(strstr(shll, "dash") != NULL) ||
(strstr(shll, "ksh") != NULL) ||
(strstr(shll, "zsh") != NULL) ||
(strstr(shll, "csh") != NULL) ||
(strstr(shll, "tcsh") != NULL)
) {
//Pipe with params
//
// TODO ******************************
// pipExec(shll, varg, true);
// ***********************************
//
}
else {
execvp(shll, varg);
}
} else {
//Pipe without params
//
// TODO ******************************
// pipExec(shll, varg, false);
// ***********************************
//
//Infos about state on second exec
//shll = /bin/bash
//varg0 = ./my-app-name
//varg1 = -c
//varg2 = ... #!/bin/bash ... ;
//varg3 = ./my-app-name
//varg3 = NULL
}
}
due to the buggy -H flag, I wrote a simple alternate tool using pipe method mentioned in this thread. if someone is interested, check this out:
https://github.com/liberize/sshc
Wouldn't it even be better to write to stdin directly from c?