加密shell脚本
neurobin-shc作为一款通用的SHELL脚本编译器,将换用户输入的SHELL脚本转换为C语言代码,编译链接形成一份剥离符号的可执行程序。shc并不是类似与cc的编译器工具,它只是简单的编码和加密SHELL脚本,生成C语言代码,同时增加比如脚本过期检测,等其他特性。其使用系统的编译器和相关工具生成剥离符号的可执行程序,程序与脚本执行效果相同。
需要注意的是其编译的二进制文件取决于SHELL解释器,shc产生的并非完全独立与系统的二进制程序,其处理后的可执行程序只能在编译主机相同的环境中运行
0.使用
shc支持很多选项,开启特定功能,比如用户可以使用-e指定脚本的有效执行时间,-m定制过期后的技术支持联系信息,-D选择在执行时显示调试信息,-v展示编译过程的参数信息,-S将root执行的脚本设置为setuid,-U使得程序防止strace,ptrace追踪,-r允许生成的二进制文件,能够在相同版本的操作系统运行
下面假设用户需要加密的脚本文件test.sh,使用shc -rf test.sh加密脚本,默认生成可执行文件test.sh.x和C语言文件test.sh.x.c
cat test.sh
#!/bin/bash
echo "hello,world
test.sh test.sh.x test.sh.x.c
test.sh.x: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=044499892c9c33d7c91f45feb1413e41dd1e2a27, for GNU/Linux 3.2.0, stripped
test.sh.x.c文件内容如下所示
#if 0
shc Version 4.0.3, Generic Shell Script Compiler
GNU GPL Version 3 Md Jahidul Hamid <jahidulhamid@yahoo.com>
shc -rf test.sh
#endif
static char data [] =
#define tst1_z 22
#define tst1 ((&data[1]))
"\030\115\076\307\352\251\122\334\257\345\123\205\106\331\310\126"
"\306\365\222\136\257\375\074\131\151\322\311\234"
#define tst2_z 19
#define tst2 ((&data[29]))
"\373\001\053\034\243\243\327\252\343\047\144\311\002\062\041\060"
"\005\045\255\050\026\056"
#define shll_z 10
#define shll ((&data[50]))
"\026\324\303\131\264\245\233\155\211\041\365"
#define chk2_z 19
#define chk2 ((&data[63]))
"\343\134\027\267\010\164\350\146\006\173\011\157\012\014\065\350"
"\070\374\246\344\244\152"
#define lsto_z 1
#define lsto ((&data[83]))
"\241"
#define chk1_z 22
#define chk1 ((&data[84]))
"\263\160\137\140\034\005\137\221\361\364\205\373\062\214\141\366"
"\004\177\354\243\302\204\026"
#define date_z 1
#define date ((&data[107]))
"\212"
#define msg2_z 19
#define msg2 ((&data[111]))
"\124\074\340\040\252\304\020\035\177\177\014\140\114\351\344\073"
"\344\317\367\045\356\177"
#define rlax_z 1
#define rlax ((&data[130]))
"\255"
#define opts_z 1
#define opts ((&data[131]))
"\234"
#define pswd_z 256
#define pswd ((&data[152]))
"\357\272\107\322\026\261\100\334\314\320\337\023\346\020\111\025"
"\233\351\027\357\262\242\314\056\113\200\220\263\311\116\231\164"
"\001\342\261\062\254\213\257\162\322\374\160\203\243\005\063\236"
"\235\220\174\120\063\111\176\177\311\017\062\223\135\314\010\137"
"\256\271\221\132\105\101\315\030\076\076\233\341\103\317\177\341"
"\137\374\061\223\105\257\023\017\276\106\243\034\022\253\173\301"
"\145\015\034\252\117\352\302\215\050\136\157\153\055\356\114\215"
"\353\175\040\061\055\064\100\354\172\344\010\215\217\204\116\364"
"\222\153\237\341\125\141\157\175\277\336\351\355\315\066\172\270"
"\264\233\351\341\317\052\316\112\016\327\327\236\133\046\223\355"
"\221\062\317\347\224\076\144\124\034\116\101\352\204\273\242\071"
"\126\214\032\046\267\351\160\306\300\110\144\034\157\367\011\001"
"\052\330\350\276\027\115\023\063\233\124\036\040\020\300\131\147"
"\115\164\215\004\135\376\312\036\107\057\072\266\047\103\267\121"
"\034\240\020\064\355\044\150\210\170\206\250\210\106\001\357\224"
"\165\175\230\323\174\143\361\303\222\053\172\272\157\062\013\214"
"\322\034\300\277\100\050\110\271\256\361\102\365\363\062\211\151"
"\257\042\074\054\046\370\232"
#define text_z 34
#define text ((&data[418]))
"\252\356\135\373\375\114\265\121\361\372\222\156\226\243\055\231"
"\035\244\362\272\374\214\031\277\114\012\305\343\323\073\203\042"
"\047\167\354\114\031\202\220\354\201\105\037\314"
#define msg1_z 65
#define msg1 ((&data[461]))
"\303\057\210\326\026\230\210\113\110\226\277\361\215\017\204\015"
"\040\353\156\317\067\270\136\021\057\307\176\277\035\021\052\261"
"\320\264\037\020\256\021\054\242\230\360\304\367\224\310\357\365"
"\010\152\041\053\367\163\225\221\162\211\202\264\264\313\370\056"
"\022\052\234\065\246\133\025\037\054\063\011\104\043\057\074\276"
"\000\022"
#define inlo_z 3
#define inlo ((&data[537]))
"\002\114\021"
#define xecc_z 15
#define xecc ((&data[542]))
"\304\135\155\330\141\311\253\016\333\076\154\063\324\263\057\111"
"\254\103\344\051"/* End of data[] */;
#define hide_z 4096
#define SETUID 0 /* Define as 1 to call setuid(0) at start of script */
#define DEBUGEXEC 0 /* Define as 1 to debug execvp calls */
#define TRACEABLE 1 /* Define as 1 to enable ptrace the executable */
#define HARDENING 0 /* Define as 1 to disable ptrace/dump the executable */
#define BUSYBOXON 0 /* Define as 1 to enable work with busybox */
#define MMAP2 0 /* Define as 1 to use syscall mmap2 */
#if HARDENING
static const char * shc_x[] = {
"/*",
" * Copyright 2019 - Intika <intika@librefox.org>",
" * Replace ******** with secret read from fd 21",
" * Also change arguments location of sub commands (sh script commands)",
" * gcc -Wall -fpic -shared -o shc_secret.so shc_secret.c -ldl",
" */",
"",
"#define _GNU_SOURCE /* needed to get RTLD_NEXT defined in dlfcn.h */",
"#define PLACEHOLDER \"********\"",
"#include <dlfcn.h>",
"#include <stdlib.h>",
"#include <string.h>",
"#include <unistd.h>",
"#include <stdio.h>",
"#include <signal.h>",
"",
"static char secret[128000]; //max size",
"typedef int (*pfi)(int, char **, char **);",
"static pfi real_main;",
"",
"// copy argv to new location",
"char **copyargs(int argc, char** argv){",
" char **newargv = malloc((argc+1)*sizeof(*argv));",
" char *from,*to;",
" int i,len;",
"",
" for(i = 0; i<argc; i++){",
" from = argv[i];",
" len = strlen(from)+1;",
" to = malloc(len);",
" memcpy(to,from,len);",
" // zap old argv space",
" memset(from,'\\0',len);",
" newargv[i] = to;",
" argv[i] = 0;",
" }",
" newargv[argc] = 0;",
" return newargv;",
"}",
"",
"static int mymain(int argc, char** argv, char** env) {",
" //fprintf(stderr, if (["localhost", "127.0.0.1"].indexOf(window.location.hostname) != -1) {
document.getElementById('disqus_thread').innerHTML = 'Disqus comments not available by default when the website is previewed locally.';
return;
} \"Inject main argc = %d\\n\", argc);",
" return real_main(argc, copyargs(argc,argv), env);",
"}",
"",
"int __libc_start_main(int (*main) (int, char**, char**),",
" int argc,",
" char **argv,",
" void (*init) (void),",
" void (*fini)(void),",
" void (*rtld_fini)(void),",
" void (*stack_end)){",
" static int (*real___libc_start_main)() = NULL;",
" int n;",
"",
" if (!real___libc_start_main) {",
" real___libc_start_main = dlsym(RTLD_NEXT, \"__libc_start_main\");",
" if (!real___libc_start_main) abort();",
" }",
"",
" n = read(21, secret, sizeof(secret));",
" if (n > 0) {",
" int i;",
"",
" if (secret[n - 1] == '\\n') secret[--n] = '\\0';",
" for (i = 1; i < argc; i++)",
" if (strcmp(argv[i], PLACEHOLDER) == 0)",
" argv[i] = secret;",
" }",
"",
" real_main = main;",
"",
" return real___libc_start_main(mymain, argc, argv, init, fini, rtld_fini, stack_end);",
"}",
"",
0};
#endif /* HARDENING */
/* rtc.c */
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/* 'Alleged RC4' */
static unsigned char stte[256], indx, jndx, kndx;
/*
* Reset arc4 stte.
*/
void stte_0(void)
{
indx = jndx = kndx = 0;
do {
stte[indx] = indx;
} while (++indx);
}
/*
* Set key. Can be used more than once.
*/
void key(void * str, int len)
{
unsigned char tmp, * ptr = (unsigned char *)str;
while (len > 0) {
do {
tmp = stte[indx];
kndx += tmp;
kndx += ptr[(int)indx % len];
stte[indx] = stte[kndx];
stte[kndx] = tmp;
} while (++indx);
ptr += 256;
len -= 256;
}
}
/*
* Crypt data.
*/
void arc4(void * str, int len)
{
unsigned char tmp, * ptr = (unsigned char *)str;
while (len > 0) {
indx++;
tmp = stte[indx];
jndx += tmp;
stte[indx] = stte[jndx];
stte[jndx] = tmp;
tmp += stte[indx];
*ptr ^= stte[tmp];
ptr++;
len--;
}
}
/* End of ARC4 */
#if HARDENING
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/prctl.h>
#define PR_SET_PTRACER 0x59616d61
/* Seccomp Sandboxing Init */
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
#define ArchField offsetof(struct seccomp_data, arch)
#define Allow(syscall) \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_##syscall, 0, 1), \
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
struct sock_filter filter[] = {
/* validate arch */
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ArchField),
BPF_JUMP( BPF_JMP+BPF_JEQ+BPF_K, AUDIT_ARCH_X86_64, 1, 0),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
/* load syscall */
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),
/* list of allowed syscalls */
Allow(exit_group), /* exits a process */
Allow(brk), /* for malloc(), inside libc */
#if MMAP2
Allow(mmap2), /* also for malloc() */
#else
Allow(mmap), /* also for malloc() */
#endif
Allow(munmap), /* for free(), inside libc */
/* and if we don't match above, die */
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
};
struct sock_fprog filterprog = {
.len = sizeof(filter)/sizeof(filter[0]),
.filter = filter
};
/* Seccomp Sandboxing - Set up the restricted environment */
void seccomp_hardening() {
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
perror("Could not start seccomp:");
exit(1);
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &filterprog) == -1) {
perror("Could not start seccomp:");
exit(1);
}
}
/* End Seccomp Sandboxing Init */
void shc_x_file() {
FILE *fp;
int line = 0;
if ((fp = fopen("/tmp/shc_x.c", "w")) == NULL ) {exit(1); exit(1);}
for (line = 0; shc_x[line]; line++) fprintf(fp, "%s\n", shc_x[line]);
fflush(fp);fclose(fp);
}
int make() {
char * cc, * cflags, * ldflags;
char cmd[4096];
cc = getenv("CC");
if (!cc) cc = "cc";
sprintf(cmd, "%s %s -o %s %s", cc, "-Wall -fpic -shared", "/tmp/shc_x.so", "/tmp/shc_x.c -ldl");
if (system(cmd)) {remove("/tmp/shc_x.c"); return -1;}
remove("/tmp/shc_x.c"); return 0;
}
void arc4_hardrun(void * str, int len) {
//Decode locally
char tmp2[len];
char tmp3[len+1024];
memcpy(tmp2, str, len);
unsigned char tmp, * ptr = (unsigned char *)tmp2;
int lentmp = len;
int pid, status;
pid = fork();
shc_x_file();
if (make()) {exit(1);}
setenv("LD_PRELOAD","/tmp/shc_x.so",1);
if(pid==0) {
//Start tracing to protect from dump & trace
if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
kill(getpid(), SIGKILL);
_exit(1);
}
//Decode Bash
while (len > 0) {
indx++;
tmp = stte[indx];
jndx += tmp;
stte[indx] = stte[jndx];
stte[jndx] = tmp;
tmp += stte[indx];
*ptr ^= stte[tmp];
ptr++;
len--;
}
//Do the magic
sprintf(tmp3, "%s %s", "'********' 21<<<", tmp2);
//Exec bash script //fork execl with 'sh -c'
system(tmp2);
//Empty script variable
memcpy(tmp2, str, lentmp);
//Clean temp
remove("/tmp/shc_x.so");
//Sinal to detach ptrace
ptrace(PTRACE_DETACH, 0, 0, 0);
exit(0);
}
else {wait(&status);}
/* Seccomp Sandboxing - Start */
seccomp_hardening();
exit(0);
}
#endif /* HARDENING */
/*
* Key with file invariants.
*/
int key_with_file(char * file)
{
struct stat statf[1];
struct stat control[1];
if (stat(file, statf) < 0)
return -1;
/* Turn on stable fields */
memset(control, 0, sizeof(control));
control->st_ino = statf->st_ino;
control->st_dev = statf->st_dev;
control->st_rdev = statf->st_rdev;
control->st_uid = statf->st_uid;
control->st_gid = statf->st_gid;
control->st_size = statf->st_size;
control->st_mtime = statf->st_mtime;
control->st_ctime = statf->st_ctime;
key(control, sizeof(control));
return 0;
}
#if DEBUGEXEC
void debugexec(char * sh11, int argc, char ** argv)
{
int i;
fprintf(stderr, "shll=%s\n", sh11 ? sh11 : "<null>");
fprintf(stderr, "argc=%d\n", argc);
if (!argv) {
fprintf(stderr, "argv=<null>\n");
} else {
for (i = 0; i <= argc ; i++)
fprintf(stderr, "argv[%d]=%.60s\n", i, argv[i] ? argv[i] : "<null>");
}
}
#endif /* DEBUGEXEC */
void rmarg(char ** argv, char * arg)
{
for (; argv && *argv && *argv != arg; argv++);
for (; argv && *argv; argv++)
*argv = argv[1];
}
void chkenv_end(void);
int chkenv(int argc)
{
char buff[512];
unsigned long mask, m;
int l, a, c;
char * string;
extern char ** environ;
mask = (unsigned long)getpid();
stte_0();
key(&chkenv, (void*)&chkenv_end - (void*)&chkenv);
key(&data, sizeof(data));
key(&mask, sizeof(mask));
arc4(&mask, sizeof(mask));
sprintf(buff, "x%lx", mask);
string = getenv(buff);
#if DEBUGEXEC
fprintf(stderr, "getenv(%s)=%s\n", buff, string ? string : "<null>");
#endif
l = strlen(buff);
if (!string) {
/* 1st */
sprintf(&buff[l], "=%lu %d", mask, argc);
putenv(strdup(buff));
return 0;
}
c = sscanf(string, "%lu %d%c", &m, &a, buff);
if (c == 2 && m == mask) {
/* 3rd */
rmarg(environ, &string[-l - 1]);
return 1 + (argc - a);
}
return -1;
}
void chkenv_end(void){}
#if HARDENING
static void gets_process_name(const pid_t pid, char * name) {
char procfile[BUFSIZ];
sprintf(procfile, "/proc/%d/cmdline", pid);
FILE* f = fopen(procfile, "r");
if (f) {
size_t size;
size = fread(name, sizeof (char), sizeof (procfile), f);
if (size > 0) {
if ('\n' == name[size - 1])
name[size - 1] = '\0';
}
fclose(f);
}
}
void hardening() {
prctl(PR_SET_DUMPABLE, 0);
prctl(PR_SET_PTRACER, -1);
int pid = getppid();
char name[256] = {0};
gets_process_name(pid, name);
if ( (strcmp(name, "bash") != 0)
&& (strcmp(name, "/bin/bash") != 0)
&& (strcmp(name, "sh") != 0)
&& (strcmp(name, "/bin/sh") != 0)
&& (strcmp(name, "sudo") != 0)
&& (strcmp(name, "/bin/sudo") != 0)
&& (strcmp(name, "/usr/bin/sudo") != 0)
&& (strcmp(name, "gksudo") != 0)
&& (strcmp(name, "/bin/gksudo") != 0)
&& (strcmp(name, "/usr/bin/gksudo") != 0)
&& (strcmp(name, "kdesu") != 0)
&& (strcmp(name, "/bin/kdesu") != 0)
&& (strcmp(name, "/usr/bin/kdesu") != 0)
)
{
printf("Operation not permitted\n");
kill(getpid(), SIGKILL);
exit(1);
}
}
#endif /* HARDENING */
#if !TRACEABLE
#define _LINUX_SOURCE_COMPAT
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#if !defined(PT_ATTACHEXC) /* New replacement for PT_ATTACH */
#if !defined(PTRACE_ATTACH) && defined(PT_ATTACH)
#define PT_ATTACHEXC PT_ATTACH
#elif defined(PTRACE_ATTACH)
#define PT_ATTACHEXC PTRACE_ATTACH
#endif
#endif
void untraceable(char * argv0)
{
char proc[80];
int pid, mine;
switch(pid = fork()) {
case 0:
pid = getppid();
/* For problematic SunOS ptrace */
#if defined(__FreeBSD__)
sprintf(proc, "/proc/%d/mem", (int)pid);
#else
sprintf(proc, "/proc/%d/as", (int)pid);
#endif
close(0);
mine = !open(proc, O_RDWR|O_EXCL);
if (!mine && errno != EBUSY)
mine = !ptrace(PT_ATTACHEXC, pid, 0, 0);
if (mine) {
kill(pid, SIGCONT);
} else {
perror(argv0);
kill(pid, SIGKILL);
}
_exit(mine);
case -1:
break;
default:
if (pid == waitpid(pid, 0, 0))
return;
}
perror(argv0);
_exit(1);
}
#endif /* !TRACEABLE */
char * xsh(int argc, char ** argv)
{
char * scrpt;
int ret, i, j;
char ** varg;
char * me = argv[0];
if (me == NULL) { me = getenv("_"); }
if (me == 0) { fprintf(stderr, "E: neither argv[0] nor $_ works."); exit(1); }
ret = chkenv(argc);
stte_0();
key(pswd, pswd_z);
arc4(msg1, msg1_z);
arc4(date, date_z);
if (date[0] && (atoll(date)<time(NULL)))
return msg1;
arc4(shll, shll_z);
arc4(inlo, inlo_z);
arc4(xecc, xecc_z);
arc4(lsto, lsto_z);
arc4(tst1, tst1_z);
key(tst1, tst1_z);
arc4(chk1, chk1_z);
if ((chk1_z != tst1_z) || memcmp(tst1, chk1, tst1_z))
return tst1;
arc4(msg2, msg2_z);
if (ret < 0)
return msg2;
varg = (char **)calloc(argc + 10, sizeof(char *));
if (!varg)
return 0;
if (ret) {
arc4(rlax, rlax_z);
if (!rlax[0] && key_with_file(shll))
return shll;
arc4(opts, opts_z);
#if HARDENING
arc4_hardrun(text, text_z);
exit(0);
/* Seccomp Sandboxing - Start */
seccomp_hardening();
#endif
arc4(text, text_z);
arc4(tst2, tst2_z);
key(tst2, tst2_z);
arc4(chk2, chk2_z);
if ((chk2_z != tst2_z) || memcmp(tst2, chk2, tst2_z))
return tst2;
/* Prepend hide_z spaces to script text to hide it. */
scrpt = malloc(hide_z + text_z);
if (!scrpt)
return 0;
memset(scrpt, (int) ' ', hide_z);
memcpy(&scrpt[hide_z], text, text_z);
} else { /* Reexecute */
if (*xecc) {
scrpt = malloc(512);
if (!scrpt)
return 0;
sprintf(scrpt, xecc, me);
} else {
scrpt = me;
}
}
j = 0;
#if BUSYBOXON
varg[j++] = "busybox";
varg[j++] = "sh";
#else
varg[j++] = argv[0]; /* My own name at execution */
#endif
if (ret && *opts)
varg[j++] = opts; /* Options on 1st line of code */
if (*inlo)
varg[j++] = inlo; /* Option introducing inline code */
varg[j++] = scrpt; /* The script itself */
if (*lsto)
varg[j++] = lsto; /* Option meaning last option */
i = (ret > 1) ? ret : 0; /* Args numbering correction */
while (i < argc)
varg[j++] = argv[i++]; /* Main run-time arguments */
varg[j] = 0; /* NULL terminated array */
#if DEBUGEXEC
debugexec(shll, j, varg);
#endif
execvp(shll, varg);
return shll;
}
int main(int argc, char ** argv)
{
#if SETUID
setuid(0);
#endif
#if DEBUGEXEC
debugexec("main", argc, argv);
#endif
#if HARDENING
hardening();
#endif
#if !TRACEABLE
untraceable(argv[0]);
#endif
argv[1] = xsh(argc, argv);
fprintf(stderr, "%s%s%s: %s\n", argv[0],
errno ? ": " : "",
errno ? strerror(errno) : "",
argv[1] ? argv[1] : "<null>"
);
return 1;
}
1.原理
分析test.sh.x.c源码,SETUID|DEBUGEXEC|TRACEABLE|HARDENING|BUSYBOXON|MMAP2宏控制相关的安全功能
反调试
TRACEABLE宏控制使用开启反调试功能。untraceable函数实现反调试功能核心逻辑,其中/proc/<pid>/as主要存在于Solaris/SunOS系统中表示目标进程的地址空间,freebsd13以后的版本默认不挂载procfs,需要手动挂载(也就是freebsd13以后版本,不存在/proc/%d/mem文件)——其实在freebsd系统下反调试功能是存在bug的,首先因为open返回最小可用文件描述符,mine被赋值为1,因此不会进行ptrace调用,使得自进程trace父进程,达到反调试的目的,其次因为即使调用成功子进程并没有等待父进程结束而自己退出,导致目标进程的调试状态会内核被清除,此时其他调试器可以附加调试。
调试模式
调试模式受DEBUGEXEC宏控制,挑用debugexec函数,打印当前可执行程序及参数,帮助用户理解脚本文件的执行过程
加固
程序开始的时刻调用hardening函数,函数主要完成以下工作:
-
设置进程的dumpable属性,该属性决定当进程因接收到某些信号如段错误而触发core dump行为时,是否允许生成核心转储文件。需要注意的是,当dumpable为0时,进程不能被其他进程使用ptrace附加调试,同时其/proc/[pid]目录下的文件权限也会有所限制
-
设置进程的ptracer属性,此选项仅在Yama Linux安全模块(LSM)启用且处于模式1(“受限 ptrace”,可通过/proc/sys/kernel/yama/ptrace_scope查看)时生效, 查看系统启动的安全模块/sys/kernel/security/lsm,???prctl(PR_SET_PTRACER, -1)???
-
检查当前进程的名称是否是属于允许的shell或特权提升工具在执行
-
xsh作为程序的核心入口函数,首先获取程序的执行路径argv[0](在许多Unix系统里,环境变量 _ 通常会保存上一个运行的命令路径),初始化rc4算法的状态转移表stte。使用key函数混淆初始化状态,使用arc4算法对mask加密的同时,将其加入环境变量,在xsh函数主体中解密相关的运行参数(相关运行时的参数,程序,报错信息都加密在data数据结构中),调用execvp重新加载进程,再次检查运行环境获取第一次运行的环境变量,判断当前环境的一致性,最后运行arc4_hardrun函数编译预制的shc_x内容成动态库,设置环境变量LD_PRELOAD,该库劫持__libc_start_main,从21文件描述符中读取密钥,替换站位参数,最后原始参数内容继续执行
其实程序中的seccomp_hardening函数实现对进程沙箱基本的限制功能(1.禁止进程后续获取其他权限 2.设置系统调用的白名单),但是这些作者可能在后续才会加入
其他选项
MMAP2设置进程在安全加固的函数中是否运行mmap2系统调用(映射更大内存页),BUSYBOXON宏判断是否运行进程使用busybox作为shell的执行环境