shc icon indicating copy to clipboard operation
shc copied to clipboard

Use Pipes instead of arguments to pass script to bash

Open TJesionowski opened this issue 7 years ago • 8 comments

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.

TJesionowski avatar Jun 04 '18 18:06 TJesionowski

nice idea, but unfortunately I don't have much time to spare on it.

neurobin avatar Oct 20 '18 14:10 neurobin

nice idea i'll may be add it to my new feature "Hardening" but its not needed for the moment

intika avatar Nov 12 '18 17:11 intika

@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

neurobin avatar Nov 12 '18 18:11 neurobin

You reminded me that i have to add all those shells to the if condition testing the parent process

intika avatar Nov 12 '18 18:11 intika

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

intika avatar Nov 12 '18 19:11 intika

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   
        }     
    }

intika avatar Feb 26 '19 11:02 intika

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

liberize avatar Jan 01 '21 04:01 liberize

Wouldn't it even be better to write to stdin directly from c?

lesleyxyz avatar May 10 '23 14:05 lesleyxyz