如何从 ps 中隐藏 execl() 参数?
How to hide execl() arguments from ps?
如何在execl()
之后hide/change一个进程参数?或者我们如何 hide/change 使用 system()
/ execl()
的子进程的参数?
正在处理 SHC(此应用程序的目的是将 bash 脚本编译成二进制文件)我正在使用 execl()
函数来执行 sh 脚本;问题是 execl()
参数暴露给 ps
;这个问题的目的是让SHC更可靠一点,解决一些用户反馈的问题。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc,char* argv[]){
int runThis;
//Create child process
if(fork() == 0){
printf("I'm the child\n");
//runThis = system("echo test; sleep 30");
runThis = execl("/bin/sh", "sh", "-c", "echo test; sleep 30", (char *) 0);
exit(0);
} else {
printf("I'm the parent.\n");
}
printf("Continue main\n");
return 0;
}
当运行这段代码,sh -c echo test; sleep 30
暴露给ps
解决方案尝试1:成功但不可靠
隐藏带有 ld_preload
的命令参数可以用这个 solution 或使用 setenv("LD_PRELOAD","myLib.so",1);
来完成(dlopen()
不适用于 execl()
),这个解决方案确实需要将库加载到我们的应用程序。
解法尝试2:半成功
用 ld --wrap=symbol
包装 __libc_start_main
,这适用于父级,但代码在 execl()
/ system()
之后不包装
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
int __real___libc_start_main(int (*main) (int, char **, char **), int argc, char **ubp_av, void (*init) (void), void (*fini)(void), void (*rtld_fini)(void), void (*stack_end));
int __wrap___libc_start_main(int (*main) (int, char **, char **), int argc, char **ubp_av, void (*init) (void), void (*fini)(void), void (*rtld_fini)(void), void (*stack_end)) {
printf("Main called\n");
//ubp_av[1] = "test";
int result = __real___libc_start_main(main, argc, ubp_av, init, fini, rtld_fini, stack_end);
return result;
}
构建命令:(wrap.c
是上面的代码,example.c
是第一个代码示例)
gcc -c example.c -o 1.o;
gcc -c wrap.c -o 2.o;
gcc -Wl,-wrap,__libc_start_main -Wl,-wrap=__libc_start_main 1.o 2.o -o myapp
解法尝试3:半成功
类似于尝试 2,它包含 link 在构建时尝试 1 的代码...但这不适用于 execl()
- 将库构建为 libfoo 或其他名称
gcc -Wall -O2 -fpic -shared -Wl,-soname,libfoo.so -ldl -o libfoo.so wrap.c
(wrap.c 是尝试 1 中的代码)
- 安装它
sudo ln -s /path/libfoo.so /usr/lib64/libfoo.so
- link这
gcc example.c -o myapp -L.. -lfoo
解决方案尝试4:相关但在这里没有用
之后的子参数
备选方案:
Bash 内容可以通过管道传输,因此对 ps
隐藏
script="script goes here"
echo $script | bash
缓解方案:
这不是一个完美的解决方案,但它会回答这个问题,这段代码会在 /tmp build 下创建一个 shc_x.c
,然后用环境变量预加载它。
shc_x.c,将 bash sh 内容替换为 ******** 参数并更改子命令参数的位置,从而将它们从 [=42= 中隐藏起来] 还有。
shc_x.c:(此文件是用第二个代码生成的)
/*
* 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,'[=11=]',len);
newargv[i] = to;
argv[i] = 0;
}
newargv[argc] = 0;
return newargv;
}
static int mymain(int argc, char** argv, char** env) {
//fprintf(stderr, "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] = '[=11=]';
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);
}
在主 c 应用程序上:
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, \"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};
#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>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/prctl.h>
#define PR_SET_PTRACER 0x59616d61
#include <stddef.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
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;
}
int main(int argc, char ** argv)
{
shc_x_file();
if (make()) {exit(1);}
setenv("LD_PRELOAD","/tmp/shc_x.so",1);
// rest of the code execl etc...
}
注意: arguments 总是可以通过多种方式恢复,这段代码只是让逆向变得有点复杂。
如何在execl()
之后hide/change一个进程参数?或者我们如何 hide/change 使用 system()
/ execl()
的子进程的参数?
正在处理 SHC(此应用程序的目的是将 bash 脚本编译成二进制文件)我正在使用 execl()
函数来执行 sh 脚本;问题是 execl()
参数暴露给 ps
;这个问题的目的是让SHC更可靠一点,解决一些用户反馈的问题。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc,char* argv[]){
int runThis;
//Create child process
if(fork() == 0){
printf("I'm the child\n");
//runThis = system("echo test; sleep 30");
runThis = execl("/bin/sh", "sh", "-c", "echo test; sleep 30", (char *) 0);
exit(0);
} else {
printf("I'm the parent.\n");
}
printf("Continue main\n");
return 0;
}
当运行这段代码,sh -c echo test; sleep 30
暴露给ps
解决方案尝试1:成功但不可靠
隐藏带有 ld_preload
的命令参数可以用这个 solution 或使用 setenv("LD_PRELOAD","myLib.so",1);
来完成(dlopen()
不适用于 execl()
),这个解决方案确实需要将库加载到我们的应用程序。
解法尝试2:半成功
用 ld --wrap=symbol
包装 __libc_start_main
,这适用于父级,但代码在 execl()
/ system()
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
int __real___libc_start_main(int (*main) (int, char **, char **), int argc, char **ubp_av, void (*init) (void), void (*fini)(void), void (*rtld_fini)(void), void (*stack_end));
int __wrap___libc_start_main(int (*main) (int, char **, char **), int argc, char **ubp_av, void (*init) (void), void (*fini)(void), void (*rtld_fini)(void), void (*stack_end)) {
printf("Main called\n");
//ubp_av[1] = "test";
int result = __real___libc_start_main(main, argc, ubp_av, init, fini, rtld_fini, stack_end);
return result;
}
构建命令:(wrap.c
是上面的代码,example.c
是第一个代码示例)
gcc -c example.c -o 1.o;
gcc -c wrap.c -o 2.o;
gcc -Wl,-wrap,__libc_start_main -Wl,-wrap=__libc_start_main 1.o 2.o -o myapp
解法尝试3:半成功
类似于尝试 2,它包含 link 在构建时尝试 1 的代码...但这不适用于 execl()
- 将库构建为 libfoo 或其他名称
gcc -Wall -O2 -fpic -shared -Wl,-soname,libfoo.so -ldl -o libfoo.so wrap.c
(wrap.c 是尝试 1 中的代码) - 安装它
sudo ln -s /path/libfoo.so /usr/lib64/libfoo.so
- link这
gcc example.c -o myapp -L.. -lfoo
解决方案尝试4:相关但在这里没有用
之后的子参数备选方案:
Bash 内容可以通过管道传输,因此对 ps
隐藏script="script goes here"
echo $script | bash
缓解方案:
这不是一个完美的解决方案,但它会回答这个问题,这段代码会在 /tmp build 下创建一个 shc_x.c
,然后用环境变量预加载它。
shc_x.c,将 bash sh 内容替换为 ******** 参数并更改子命令参数的位置,从而将它们从 [=42= 中隐藏起来] 还有。
shc_x.c:(此文件是用第二个代码生成的)
/*
* 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,'[=11=]',len);
newargv[i] = to;
argv[i] = 0;
}
newargv[argc] = 0;
return newargv;
}
static int mymain(int argc, char** argv, char** env) {
//fprintf(stderr, "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] = '[=11=]';
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);
}
在主 c 应用程序上:
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, \"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};
#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>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/prctl.h>
#define PR_SET_PTRACER 0x59616d61
#include <stddef.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
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;
}
int main(int argc, char ** argv)
{
shc_x_file();
if (make()) {exit(1);}
setenv("LD_PRELOAD","/tmp/shc_x.so",1);
// rest of the code execl etc...
}
注意: arguments 总是可以通过多种方式恢复,这段代码只是让逆向变得有点复杂。