0


Go程序中调用shell命令安全吗?

大家好,今天和大家分享一个项目中遇到的问题~
在实际项目中,因业务功能的原因在编写程序中经常会调用第三方命令或系统命令来完成相应的功能,不由在想这种方式真的安全吗?
由于这种疑问让我产生了浓烈的兴趣,本次将分析的经过记录了下来。接下来小伙伴们跟我一起研究学习吧~

首先,下面代码块中使用了Go语言的

exec.Command

的函数进行执行

zip

命令压缩本地文件。其中

-rP

参数代表递归压缩并进行加密,密码abcd1234

很大部分场景中,在做业务功能时有很多类似的做法。例如进行执行加密、解密过程、获取系统某些信息等,习惯性调用系统命令。为了演示,在执行

zip

命令之前,加了命令

sleep 60

等待,只是为了让大家更好的看到效果。
下面通过go build进行编译,接着让编译后的命令运行起来。

$ go build -o zipcommand main.go
$ ./zipcommand

用户态层面

接触过Linux的小伙伴肯定首先想到使用

ps

命令来看进程的上下文信息。

图中,先将

zipcommand

进程PID找到,然后使用

pstree

将该进程树打印,发现go中

exec.Command

函数在调用shell命令时会

fork()

一个子进程执行。 将第一个子进程

3648095

展开

COMMAND

时,发现shell与我们在go代码中传入shell命令是一致的,这样就将命令裸露到系统层面上了。 图中第二个子进程

3648096

是因为使用了

&&

拼接符,又利用

fork()

子进程方式先执行

sleep

后在执行

zip

命令。
还可以通过

/proc/pid/cmdline

方式读取

COMMAND

进程启动命令。

注:这里大家可以详细查看linux进程

fork()

机制的材料,有助于加深理解。golang封装的

exec.Command

这种方式最终也是操作系统的系统调用真正在做处理。

系统调用层面

下面我在想,现在已经知道程序中调用shell命令会暴露在系统层面,那程序在

exec.Command()

传入shell命令后会被篡改吗?
大概思路是使用

ptrace()

系统调用方式,先拿到程序中传入的shell命令变量内存地址,进行尝试修改变量值。

上面go程序中的内存地址不能与c语言直接处理,下面举例先全部使用c语言代码演示。
代码中将

cmd

内存地址直接输出,让

ptrace()

尝试修改内存地址,达到篡改进程调用的目的。通过

gcc cmd.c -o zipcommand

进行编译。

下面将

zipcommand

命令运行,然后拿到

cmd

变量的内存地址。
ptrace程序关键代码

主要是通过系统调用

ptrace()

方法,先

attach

到指定进程上,在基于拿到的内存地址,进行修改后让进程继续运行。

本次所用到

ptrace()

的类型如下:

PTRACE_ATTACH

ptrace()

attach

到一个指定的进程,使其成为当前进程跟踪的子进程。

PTRACE_PEEKDATA

向内存区域中写入一个WORD,内存地址为addr。

PTRACE_DETACH

进程在每次系统调用之后暂停。

通过

gcc ptrace.c -o ptrace

进行编译。
下面尝试使用

ptrace

zipcommand

变量内存地址尝试篡改。

我们可以看到

zipcommand

中的

cmd

变量值已经被篡改成功。
ptrace.c详细代码

#include<stdio.h>#include<stdlib.h>#include<sys/ptrace.h>#include<sys/wait.h>#include<unistd.h>#include<sys/reg.h>#include<sys/syscall.h>intmain(int argc,char* argv[]){pid_t attack_pid;char cmd[]="echo helloword";if(argc !=2){printf("Usage: %s <pid to be traced>\n",
               argv[0]);exit(1);}
    attack_pid =atoi(argv[1]);if(ptrace(PTRACE_ATTACH, attack_pid,NULL,NULL)<0){printf("attach failed\n");return0;}//读数据printf("stack_arg %d\n",ptrace(PTRACE_PEEKDATA , attack_pid,(void*)0x7ffc41334450,NULL));//修改数据putdata(attack_pid,(void*)0x7ffc41334450, cmd,44);ptrace(PTRACE_DETACH, attack_pid,NULL,NULL);waitpid(attack_pid,NULL, WUNTRACED);return0;}voidputdata(pid_t child,long addr,char*str,int len){char*laddr;int long_size =sizeof(long);int i, j;union u {long val;char chars[long_size];} data;

    i =0;
    j = len / long_size;
    laddr = str;while(i < j){memcpy(data.chars, laddr, long_size);//每次写入一个字ptrace(PTRACE_POKEDATA, child, addr + i *8, data.val);++i;
        laddr += long_size;}
    j = len % long_size;if(j !=0){memcpy(data.chars, laddr, j);ptrace(PTRACE_POKEDATA, child, addr + i *8, data.val);}}

上面通过两个层面,说明了在程序中调用shell命令的面临的风险。 为了让程序更加安全,应减少采用调用shell命令这种方式。

技术文章持续更新,请大家多多关注呀~~

搜索微信公众号【 帽儿山的枪手 】,关注我


本文转载自: https://blog.csdn.net/qq_29455595/article/details/138344363
版权归原作者 帽儿山的枪手 所有, 如有侵权,请联系我们删除。

“Go程序中调用shell命令安全吗?”的评论:

还没有评论