diff --git a/generate_shellcode.sh b/generate_shellcode.sh index ba52653..f758da7 100755 --- a/generate_shellcode.sh +++ b/generate_shellcode.sh @@ -23,7 +23,7 @@ echo '#ifndef G_VERSION_H' > "$tmpfile" echo '#define G_VERSION_H' >> "$tmpfile" for I in "$codedir"/*.sh do - printf '#define %s "%s"\n' "$(basename "$I" | tr [:lower:] [:upper:] | tr '.' '_')" "$(minimize "$I" | to_cstr)" >> "$tmpfile" + printf '#define %s "%s\\n"\n' "$(basename "$I" | tr [:lower:] [:upper:] | tr '.' '_')" "$(minimize "$I" | to_cstr)" >> "$tmpfile" done echo "#endif" >> "$tmpfile" diff --git a/include/debashify.hpp b/include/debashify.hpp index 5a8a174..1e9a7ba 100644 --- a/include/debashify.hpp +++ b/include/debashify.hpp @@ -3,8 +3,25 @@ #include "struc.hpp" -bool r_debashify(_obj* o); +#include +typedef struct debashify_params { + bool need_random_string=false; + bool need_random_tmpfile=false; + bool need_array_create=false; + bool need_array_set=false; + bool need_array_get=false; + bool need_map_create=false; + bool need_map_set=false; + bool need_map_get=false; + // map of detected arrays + // bool value: is associative + std::map arrays; +} debashify_params; + +bool r_debashify(_obj* o, debashify_params* params); + +void debashify(_obj* o, debashify_params* params); void debashify(shmain* sh); #endif //DEBASHIFY_HPP diff --git a/include/exec.hpp b/include/exec.hpp new file mode 100644 index 0000000..109f2b3 --- /dev/null +++ b/include/exec.hpp @@ -0,0 +1,12 @@ +#ifndef EXEC_HPP +#define EXEC_HPP + +#include "options.hpp" + + +void parse_exec(FILE* fd, const char* in, uint32_t size, std::string const& filename=""); +inline void parse_exec(FILE* fd, std::string const& in, std::string const& filename="") { parse_exec(fd, in.c_str(), in.size(), filename); } + +int exec_process(std::string const& runtime, std::vector const& args, std::string const& filecontents, std::string const& file); + +#endif //EXEC_HPP diff --git a/include/parse.hpp b/include/parse.hpp index edfcbc9..6f9651c 100644 --- a/include/parse.hpp +++ b/include/parse.hpp @@ -26,6 +26,13 @@ // bash specific #define ARRAY_ARG_END " \t\n;#()&|<>]" +// macros +#define PARSE_ERROR(str, i) ztd::format_error(str, "", in, i) + +// globals + +extern bool g_bash; + extern const std::vector posix_cmdvar; extern const std::vector bash_cmdvar; @@ -38,6 +45,12 @@ inline shmain* parse(std::string const& file) { return parse_text(import_file(fi // ** unit parsers ** // /* util parsers */ +bool word_eq(const char* word, const char* in, uint32_t size, uint32_t start, const char* end_set=NULL); +std::pair get_word(const char* in, uint32_t size, uint32_t start, const char* end_set); +uint32_t skip_chars(const char* in, uint32_t size, uint32_t start, const char* set); +uint32_t skip_until(const char* in, uint32_t size, uint32_t start, const char* set); +uint32_t skip_unread(const char* in, uint32_t size, uint32_t start); + // list std::pair parse_list_until(const char* in, uint32_t size, uint32_t start, char end_c, const char* expecting=NULL); std::pair parse_list_until(const char* in, uint32_t size, uint32_t start, std::string const& end_word); diff --git a/include/resolve.hpp b/include/resolve.hpp index 7ce5d26..d93a3e6 100644 --- a/include/resolve.hpp +++ b/include/resolve.hpp @@ -5,9 +5,15 @@ extern std::vector included; +std::vector> do_include_raw(condlist* cmd, std::string const& filename, std::string* ex_dir=nullptr); +std::pair do_resolve_raw(condlist* cmd, std::string const& filename, std::string* ex_dir=nullptr); + bool add_include(std::string const& file); -void resolve(_obj* sh, shmain* parent); +void resolve(_obj* sh, std::string* filename); void resolve(shmain* sh); +std::string _pre_cd(std::string const& filename); +void _cd(std::string const& dir); + #endif //RESOLVE_HPP diff --git a/include/struc.hpp b/include/struc.hpp index 27340ba..7ffec33 100644 --- a/include/struc.hpp +++ b/include/struc.hpp @@ -269,6 +269,8 @@ public: list(condlist* in) { type=_obj::_list; this->add(in); } ~list() { for(auto it: cls) delete it; } + void clear() { for(auto it: cls) delete it; cls.resize(0); } + std::vector cls; inline void add(condlist* in) { cls.push_back(in); } @@ -340,8 +342,6 @@ public: if(lst!=nullptr) delete lst; } - bool is_dev_file() { return filename.substr(0,5) == "/dev/"; } - void concat(shmain* in); std::string filename; diff --git a/include/util.hpp b/include/util.hpp index c183e40..53c6f4c 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -22,6 +22,8 @@ std::string cut_last(std::string const& in, char c); std::string basename(std::string const& in); std::string dirname(std::string const& in); +inline bool is_dev_file(std::string const& filename) { return filename.substr(0,5) == "/dev/"; } + std::string indent(int n); bool is_among(std::string const& in, std::vector const& values); diff --git a/src/debashify.cpp b/src/debashify.cpp index d4f9393..a142893 100644 --- a/src/debashify.cpp +++ b/src/debashify.cpp @@ -11,21 +11,6 @@ #include "g_shellcode.h" -typedef struct debashify_params { - bool need_random_string=false; - bool need_random_tmpfile=false; - bool need_array_create=false; - bool need_array_set=false; - bool need_array_get=false; - bool need_map_create=false; - bool need_map_set=false; - bool need_map_get=false; - // map of detected arrays - // bool value: is associative - std::map arrays; -} debashify_params; - - /* [[ ]] debashifying: [[ EXPRESSION && EXPRESSION ]] separated into two parts @@ -841,6 +826,12 @@ bool r_debashify(_obj* o, debashify_params* params) } return true; } + +void debashify(_obj* o, debashify_params* params) +{ + recurse(r_debashify, o, params); +} + // void debashify(shmain* sh) { diff --git a/src/exec.cpp b/src/exec.cpp new file mode 100644 index 0000000..88048b4 --- /dev/null +++ b/src/exec.cpp @@ -0,0 +1,289 @@ +#include "exec.hpp" + +#include +#include +#include +#include +#include + +#include "g_shellcode.h" + +#include "util.hpp" +#include "parse.hpp" +#include "debashify.hpp" +#include "resolve.hpp" +#include "recursive.hpp" + +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +std::vector do_include_exec(condlist* cmd, std::string const& filename, FILE* fd) +{ + std::vector ret; + + std::string dir; + auto incs=do_include_raw(cmd, filename, &dir); + + for(auto it: incs) + { + parse_exec(fd, it.second, it.first); + } + // cd back + _cd(dir); + + return ret; +} + +// if first is nullptr: is a string +std::vector do_resolve_exec(condlist* cmd, std::string const& filename, FILE* fd) +{ + std::vector ret; + + std::pair p; + try + { + // get + std::string dir; + p=do_resolve_raw(cmd, filename, &dir); + // do parse + parse_exec(fd, p.second, filename); + // cd back + _cd(dir); + } + catch(ztd::format_error& e) + { + throw ztd::format_error(e.what(), '`'+p.first+'`', e.data(), e.where()); + } + + return ret; +} + +// -- OBJECT CALLS -- + +bool resolve_condlist_exec(condlist* in, std::string const& filename, FILE* fd) +{ + cmd* tc = in->first_cmd(); + if(tc == nullptr) + return false; + + std::string const& strcmd=tc->arg_string(0); + + if(g_include && strcmd == "%include") + { + do_include_exec(in, filename, fd); + return true; + } + else if(g_resolve && strcmd == "%resolve") + { + do_resolve_exec(in, filename, fd); + return true; + } + return false; +} + + +bool resolve_exec(condlist* in, std::string const& filename, FILE* fd) +{ + if(!resolve_condlist_exec(in, filename, fd)) + { + resolve(in, (std::string*) &filename); + return false; + } + return true; +} + +char byte_to_char(uint8_t in) +{ + uint8_t t = in&0b00111111; // equiv to %64 + if(t < 26) + return t+'a'; + if(t < 52) + return (t-26)+'A'; + if(t < 62) + return (t-52)+'0'; + if(t == 62) + return '-'; + return '_'; +} + +std::string gettmpdir() +{ + std::string tmpdir; + char* tbuf = getenv("TMPDIR"); + if(tbuf != NULL) + tmpdir = tbuf; + if(tmpdir == "") + tmpdir = "/tmp"; + return tmpdir; +} + +// random string of size 20 +std::string random_string() +{ + // get system random seed + FILE* f = fopen("/dev/urandom", "r"); + if(!f) + throw std::runtime_error("Cannot open stream to /dev/urandom"); + uint8_t buffer[20]; + size_t r = fread(buffer, 20, 1, f); + fclose(f); + if(r<=0) + throw std::runtime_error("Cannot read from /dev/urandom"); + + std::string ret; + for(uint8_t i=0; i<20; i++) + ret += byte_to_char(buffer[i]); + + return ret; +} + +void parse_exec(FILE* fd, const char* in, uint32_t size, std::string const& filename) +{ + uint32_t i=skip_unread(in, size, 0); +#ifndef NO_PARSE_CATCH + try + { +#endif + ; + debashify_params debash_params; + list* t_lst=new list; + if(t_lst == nullptr) + throw std::runtime_error("Alloc error"); + while(iadd(pp.first); + if(g_resolve || g_include) + { + if(resolve_exec(t_lst->cls[0], filename, fd)) + { + t_lst->clear(); + continue; + } + } + if(options["debashify"]) + debashify(t_lst, &debash_params); + + + std::string gen=t_lst->generate(0); + t_lst->clear(); + + fprintf(fd, "%s", gen.c_str()); + + if(i < size) + { + if(in[i] == '#') + ; // skip here + else if(is_in(in[i], COMMAND_SEPARATOR)) + i++; // skip on next char + else if(is_in(in[i], CONTROL_END)) + throw PARSE_ERROR(strf("Unexpected token: '%c'", in[i]), i); + + i = skip_unread(in, size, i); + } + } + delete t_lst; +#ifndef NO_PARSE_CATCH +} + catch(ztd::format_error& e) + { + throw ztd::format_error(e.what(), e.where(), in, filename); + } +#endif +} + +pid_t forkexec(const char* bin, char *const args[]) +{ + pid_t child_pid; + // int tfd = dup(STDIN_FILENO); + // std::cout << tfd << std::endl; + if((child_pid = vfork()) == -1) + { + throw std::runtime_error("fork() failed"); + } + if (child_pid == 0) // child process + { + // char buf[1000] = {0}; + // read(STDIN_FILENO, buf, 1000); + // std::cout << std::string(buf) << std::endl; + // std::cout << dup2(tfd, STDIN_FILENO) << std::endl; + setpgid(child_pid, child_pid); //Needed so negative PIDs can kill children of /bin/sh + execv(bin, args); + throw std::runtime_error("execv() failed"); + } + else // main process + { + return child_pid; + } +} + +int wait_pid(pid_t pid) +{ + int stat; + while (waitpid(pid, &stat, 0) == -1) + { + if (errno != EINTR) + { + stat = -1; + break; + } + } + + return WEXITSTATUS(stat); +} + +int exec_process(std::string const& runtime, std::vector const& args, std::string const& filecontents, std::string const& file) +{ + std::vector strargs = split(runtime, " \t"); + std::vector runargs; + + std::string fifopath=gettmpdir(); + fifopath+="/lxshfiforun_"; + fifopath+=random_string(); + + if(mkfifo(fifopath.c_str(), 0700)<0) + throw std::runtime_error("Cannot create fifo "+fifopath); + + for(uint32_t i=0; i 1) // not exec: parse options on args - { args=options.process(args); - } + + if(!is_exec && options['e']) + throw std::runtime_error("Option -e must be before file"); + + if(shebang_is_bin) // enable debashify option + options["debashify"].activated=true; oneshot_opt_process(argv[0]); get_opts(); + } // parse g_origin=file; if(!add_include(file)) continue; - tsh = parse_text(filecontents, file); - if(shebang_is_bin) // resolve lxsh shebang to sh - { - options["debashify"].activated=true; - tsh->shebang="#!/bin/sh"; - } - /* mid processing */ - // resolve/include - if(g_include || g_resolve) - { - resolve(tsh); - } - - // concatenate to main - sh->concat(tsh); - delete tsh; - tsh = nullptr; - - // is exec: break and exec if(is_exec) - break; + { + if(options["debashify"]) + { + shebang = "#!/bin/sh"; + } + if(options["debashify"] || basename(shebang) == "bash") + { + g_bash = true; + } + args.erase(args.begin()); + return exec_process(shebang.substr(2), args, filecontents, file); + } + else + { + tsh = parse_text(filecontents, file); + if(shebang_is_bin) // resolve lxsh shebang to sh + tsh->shebang="#!/bin/sh"; + + /* mid processing */ + // resolve/include + if(g_include || g_resolve) + resolve(tsh); + + // concatenate to main + sh->concat(tsh); + delete tsh; + tsh = nullptr; + } } // end of argument parse if(options["debashify"]) @@ -191,11 +207,6 @@ int main(int argc, char* argv[]) list_fcts(sh, re_fct_exclude); else if(options["list-cmd"]) list_cmds(sh, regex_null); - // execute - else if(is_exec) - { - ret = execute(sh, args); - } // output else if(options['o']) // file output { diff --git a/src/parse.cpp b/src/parse.cpp index 3706457..4d58df4 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -15,8 +15,6 @@ bool g_bash=false; // macro -#define PARSE_ERROR(str, i) ztd::format_error(str, "", in, i) - // constants const std::vector posix_cmdvar = { "export", "unset", "local", "read", "getopts" }; const std::vector bash_cmdvar = { "readonly", "declare", "typeset" }; @@ -65,7 +63,7 @@ bool valid_name(std::string const& str) // string utils -bool word_eq(const char* word, const char* in, uint32_t size, uint32_t start, const char* end_set=NULL) +bool word_eq(const char* word, const char* in, uint32_t size, uint32_t start, const char* end_set) { uint32_t wordsize=strlen(word); if(wordsize > size-start) diff --git a/src/resolve.cpp b/src/resolve.cpp index 9a2d0be..59a47a6 100644 --- a/src/resolve.cpp +++ b/src/resolve.cpp @@ -34,17 +34,18 @@ bool add_include(std::string const& file) if(it == truepath) return false; } + // std::cout << truepath << std::endl; included.push_back(truepath); return true; } // returns path to old dir -std::string _pre_cd(shmain* parent) +std::string _pre_cd(std::string const& filename) { - if(parent->is_dev_file() || parent->filename == "") + if(filename == "" || is_dev_file(filename)) return ""; std::string dir=pwd(); - std::string cddir=dirname(parent->filename); + std::string cddir=dirname(filename); if(chdir(cddir.c_str()) != 0) throw std::runtime_error("Cannot cd to '"+cddir+"'"); return dir; @@ -59,7 +60,7 @@ void _cd(std::string const& dir) // -- COMMANDS -- // return [] -std::vector> do_include_raw(condlist* cmd, shmain* parent, std::string* ex_dir=nullptr) +std::vector> do_include_raw(condlist* cmd, std::string const& filename, std::string* ex_dir) { std::vector> ret; @@ -77,7 +78,7 @@ std::vector> do_include_raw(condlist* cmd, s std::string dir; if(g_cd && !opts['C']) { - dir=_pre_cd(parent); + dir=_pre_cd(filename); if(ex_dir!=nullptr) *ex_dir=dir; } @@ -104,31 +105,8 @@ std::vector> do_include_raw(condlist* cmd, s return ret; } -std::vector do_include_parse(condlist* cmd, shmain* parent) -{ - std::vector ret; - - std::string dir; - auto incs=do_include_raw(cmd, parent, &dir); - - for(auto it: incs) - { - shmain* sh=parse_text(it.second, it.first); - resolve(sh); - // get the cls - ret.insert(ret.end(), sh->lst->cls.begin(), sh->lst->cls.end()); - // safety and cleanup - sh->lst->cls.resize(0); - delete sh; - } - // cd back - _cd(dir); - - return ret; -} - // -std::pair do_resolve_raw(condlist* cmd, shmain* parent, std::string* ex_dir=nullptr) +std::pair do_resolve_raw(condlist* cmd, std::string const& filename, std::string* ex_dir) { std::pair ret; @@ -146,7 +124,7 @@ std::pair do_resolve_raw(condlist* cmd, shmain* parent std::string dir; if(g_cd && !opts['C']) { - dir=_pre_cd(parent); + dir=_pre_cd(filename); if(ex_dir!=nullptr) *ex_dir=dir; } @@ -175,8 +153,31 @@ std::pair do_resolve_raw(condlist* cmd, shmain* parent return ret; } +std::vector do_include_parse(condlist* cmd, std::string const& filename) +{ + std::vector ret; + + std::string dir; + auto incs=do_include_raw(cmd, filename, &dir); + + for(auto it: incs) + { + shmain* sh=parse_text(it.second, it.first); + resolve(sh); + // get the cls + ret.insert(ret.end(), sh->lst->cls.begin(), sh->lst->cls.end()); + // safety and cleanup + sh->lst->cls.resize(0); + delete sh; + } + // cd back + _cd(dir); + + return ret; +} + // if first is nullptr: is a string -std::vector do_resolve_parse(condlist* cmd, shmain* parent) +std::vector do_resolve_parse(condlist* cmd, std::string const& filename) { std::vector ret; @@ -185,7 +186,7 @@ std::vector do_resolve_parse(condlist* cmd, shmain* parent) { // get std::string dir; - p=do_resolve_raw(cmd, parent, &dir); + p=do_resolve_raw(cmd, filename, &dir); // do parse shmain* sh = parse_text(p.second); resolve(sh); @@ -207,7 +208,7 @@ std::vector do_resolve_parse(condlist* cmd, shmain* parent) // -- OBJECT CALLS -- -std::pair< std::vector , bool > resolve_condlist(condlist* in, shmain* parent) +std::pair< std::vector , bool > resolve_condlist(condlist* in, std::string const& filename) { cmd* tc = in->first_cmd(); if(tc == nullptr) @@ -216,14 +217,14 @@ std::pair< std::vector , bool > resolve_condlist(condlist* in, shmain std::string const& strcmd=tc->arg_string(0); if(g_include && strcmd == "%include") - return std::make_pair(do_include_parse(in, parent), true); + return std::make_pair(do_include_parse(in, filename), true); else if(g_resolve && strcmd == "%resolve") - return std::make_pair(do_resolve_parse(in, parent), true); + return std::make_pair(do_resolve_parse(in, filename), true); else return std::make_pair(std::vector(), false); } -std::pair< std::vector , bool > resolve_arg(arg* in, shmain* parent, bool forcequote=false) +std::pair< std::vector , bool > resolve_arg(arg* in, std::string const& filename, bool forcequote=false) { std::vector ret; if(in == nullptr) @@ -249,12 +250,12 @@ std::pair< std::vector , bool > resolve_arg(arg* in, shmain* parent, bool std::string fulltext; if(g_include && strcmd == "%include") { - for(auto it: do_include_raw(tc, parent) ) + for(auto it: do_include_raw(tc, filename) ) fulltext += it.second; } else if(g_resolve && strcmd == "%resolve") { - fulltext = do_resolve_raw(tc, parent).second; + fulltext = do_resolve_raw(tc, filename).second; } else // skip continue; @@ -323,7 +324,7 @@ std::pair< std::vector , bool > resolve_arg(arg* in, shmain* parent, bool // -- RECURSIVE CALL -- -bool r_resolve(_obj* o, shmain* parent) +bool r_resolve(_obj* o, std::string* filename) { switch(o->type) { @@ -336,7 +337,7 @@ bool r_resolve(_obj* o, shmain* parent) auto t = dynamic_cast(o); for(uint32_t i=0 ; icls.size() ; i++) { - auto r=resolve_condlist(t->cls[i], parent); + auto r=resolve_condlist(t->cls[i], *filename); if(r.second) { // add new cls after current @@ -349,7 +350,7 @@ bool r_resolve(_obj* o, shmain* parent) } else { - resolve(t->cls[i], parent); + resolve(t->cls[i], filename); } } return false; @@ -359,7 +360,7 @@ bool r_resolve(_obj* o, shmain* parent) auto t = dynamic_cast(o); for(uint32_t i=0 ; isize() ; i++) { - auto r=resolve_arg(t->args[i], parent); + auto r=resolve_arg(t->args[i], *filename); if(r.first.size()>0) { // add new args @@ -371,7 +372,7 @@ bool r_resolve(_obj* o, shmain* parent) } else { - resolve(t->args[i], parent); + resolve(t->args[i], filename); } } return false; @@ -381,12 +382,12 @@ bool r_resolve(_obj* o, shmain* parent) auto t = dynamic_cast(o); for(auto it: t->var_assigns) // var assigns { - resolve_arg(it.second, parent, true); // force quoted - resolve(it.second, parent); + resolve_arg(it.second, *filename, true); // force quoted + resolve(it.second, filename); } for(auto it: t->redirs) - resolve(it, parent); - resolve(t->args, parent); + resolve(it, filename); + resolve(t->args, filename); return false; }; break; case _obj::block_case : @@ -394,15 +395,15 @@ bool r_resolve(_obj* o, shmain* parent) auto t = dynamic_cast(o); for(auto sc: t->cases) { - resolve_arg(t->carg, parent, true); // force quoted - resolve(t->carg, parent); + resolve_arg(t->carg, *filename, true); // force quoted + resolve(t->carg, filename); for(auto it: sc.first) { - resolve_arg(it, parent, true); // force quoted - resolve(it, parent); + resolve_arg(it, *filename, true); // force quoted + resolve(it, filename); } - resolve(sc.second, parent); + resolve(sc.second, filename); } }; break; default: break; @@ -411,12 +412,12 @@ bool r_resolve(_obj* o, shmain* parent) } // recursive call of resolve -void resolve(_obj* in, shmain* parent) +void resolve(_obj* in, std::string* filename) { - recurse(r_resolve, in, parent); + recurse(r_resolve, in, filename); } void resolve(shmain* sh) { - recurse(r_resolve, sh, sh); + recurse(r_resolve, sh, &sh->filename); }