DVWA详解:CommandInjection篇(全)
前言:
"Command Injection"中文翻译意为"命令注入",原理其实和"SQL注入"没什么区别,当然,可能有读者不知道什么是"SQL注入",不过这完全没关系,毕竟DVWA的主要作用还是科普性质,其入面的漏洞案例都是非常的简单,是很容易入门及学习的;
关于"命令注入",只要你会"SHELL(LINUX)"或"CMD(WINDOW)",是很容易理解这一漏洞的;其原理不过是利用了"命令行中一些特殊字符的特定功能",产生这种漏洞的原因是"程序在设计过程中,没有严格过滤用户的输入内容"而已;
"命令注入"能写的内容实在不多,实际上也没什么好写的,写这篇博文的目的,主要原因是博主想在这段比闲的时间内,补完DVWA这个坑而已;但目前来看,要完成这个系列还有很长的路要走,即使写完当前这篇博文,DVWA的完成度估计也只有40%左右;这篇博文也将会是一篇水博,只是博主用于撑撑博客博文的数量的......
特别提示:如果你在实验过程中,直接复制本博客任意博文中的配置/代码,"空格"字符的前面将可能产生不可见的字符"M-BM-",从而造成文件不可用,这是字符编码的问题~若必需使用复制粘贴方式,请务必手动替换掉配置项中的所有空格!!!或尝试使用以下命令过滤掉所有"M-BM-"字符~
1 2 3 4 |
sed -i 's/\xc2\xa0/ /g' [文件名称] # LINUX下可使用以下命令查看不可见字符的情况 cat -A [文件名称] |
环境需求:
测试需要部署DVWA环境,如果你会DOCKER,可以使用博主提供的文件快速部署一个可用的DVWA环境:下载"DVWA-2.3.tar.gz"压缩包,通过DOCKER-COMPOSE快速部署起DVWA的2.3版本:百度网盘(提取码:7s4q)--> [ 文件名称:DVWA/DVWA-2.3.tar.gz ];
注意,这个版本的DVWA是不完善的,有能力的话,你应该至GITHUB上使用官方的最新源代码自己去构建测试环境;不完善并不是博主的原因,这是当时官方的源代码就存在问题(一些新增加的漏洞案例,在代码逻辑上有问题,不影响本篇),官方不时会修复这些问题;在博主写这篇博文时,官方又修复了一些...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 注:在每次使用"docker-compose down"完全清除容器后, # 再次运行需要使用"./Readme.sh"文件(有配置文件复制至容器); $ tar -zxf DVWA-2.3.tar.gz $ cd DVWA $ ./Readme.sh # 其它命令 $ docker-compose down # 删除DVWA服务(再次创建服务需要使用"Readme.sh"脚本) $ docker-compose stop # 停止DVWA服务(在不删除DVWA容器时用本命令停止即可) $ docker-compose start # 启动DVWA服务(在不删除DVWA容器时用本命令启动即可) # ================================================================================================= # 其它补充: # 你可能在博文的截图中看到博主使用了"dvwa.domain.local"的域名,这是博主在浏览器的主机上使用了自定义的HOSTS解释; # > LINUX :你可以在"/etc/hosts"文件中自定义DNS解释 # > WINDOW :你可以在"c:\windows\system32\drivers\etc\hosts"文件中自定义DNS解释 |
使用博主的代码搭建DVWA环境的大概截图过程如下:
漏洞原理:
漏洞的原理,前言中已经简单提及过了,就是利用了"命令行中一些特殊字符的特定功能"而已;例如"|"符号,在LINUX的SHELL命令行环境中,这被称为"管道符",它的作用是"把前一个命令原本要输出到屏幕的信息当作后一个命令的标准输入";类似以上的有特殊功能的"符号",在命令行上有很多,要了解这些"符号",你可以去学习"BASH/CMD",这些符号所实现的特殊功能都差不多;
当然,只是有命令行中的"符号"有特殊功能,这不足以产生"命令注入"漏洞;要产生"命令注入"漏洞,关键还是程序在设计过程中,调用了编程语言的"系统命令的执行功能函数";对于PHP编程语言,这个功能函数是"shell_exec()"(相当于GO语言中"os/exec"模块中的"exec.Command()"函数;相当于PYTHON中"subprocess"的"subprocess.run()"函数),所有的编程语言都有类似的功能函数;在使用这些功能函数时,如果直接面向用户,却又忽略了"用户输入的可能性",不对"用户输入的数据"的作"合理的检查或过滤",就会产生"命令注入"漏洞;
LOW:
上面说了"命令注入"漏洞产生的原理,现在分析LOW等级下的PHP源码,可以发现其使用了PHP编程语言中的"shell_exec()"功能函数,但对于"用户的输入",其未对"输入数据"作任何合理性的检查,只是将"用户输入"简单的拼接在"ping"/"ping -c"之后,在这样的情况下,产生命令注入漏洞是必然的;见下面是LOW等级的源代码解读:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// 1、判断POST请求中是否存在'Submit'这个键,判断成功才继续执行下一步操作; // 2、将"$_REQUEST[ 'ip' ]"存入"$target"变量; // 3、判断当前服务器的操作系统类型,再决定如何去在服务器上执行"ping"操作; // 4、最后将执行结果存入"$cmd"变量然后返回执行结果; // // 产生漏洞原因: // > 从源代码中可以看到,在LOW难度下,代码对用户侧的输入采用了完全信任的态度, // 简单的将"用户输入的内容"存入至"$target"变量中,然后与字符串"ping"或 // "ping -c 4"进行字符串拼接,最后直接调用"shell_exec()"函数在服务器上执行 // 对应命令; // > "shell_exec()"函数是PHP中的一个内置功能函数,能直接在操作系统上执行命令; <?php if( isset( $_POST[ 'Submit' ] ) ) { $target = $_REQUEST[ 'ip' ]; if( stristr( php_uname( 's' ), 'Windows NT' ) ) { $cmd = shell_exec( 'ping ' . $target ); } else { $cmd = shell_exec( 'ping -c 4 ' . $target ); } echo "<pre>{$cmd}</pre>"; } ?> |
在浏览器中,访问"Command Injection"页面,在正常情况下,用户应该输入具体的某一IP地址或域名(如:127.0.0.1),这是程序设计这一功能时的原始目的,见下图;
然而,实际上,在用户使用这一功能的过程中,我们并不能限制用户"实际输入了什么";于是,我们在原来的输入的"127.0.0.1"的基础上,增加了" ; whoami "("whoami"命令是LINUX中用于"查询当前用户的用户名的命令"),并执行"Submit"操作,其返回结果中包含了"www-data","whoami"命令被意外的执行了,但这并不是该功能所设计的原始目的,于是"命令注入"漏洞就这样产生了;
上面是从WEB页面角度看到的结果,如果从服务器操作系统的角度,调用了PHP的"shell_exec()"函数,就相当于在服务器上执行了相关的命令,见下图:
MEDIUM:
MEDIUM等级下,从源代码中可以看到,程序的设计人员并不相信"用户的输入",对"用户的输入"作了一定的过滤,其设计了一个有关"符号"的黑名单,对"&&"与";"字符进行了过滤操作(删除对应字符);然而,命令行中有特殊功能的"符号"并不只有这两个,相关的"符号"被过滤了,不使用就是了,我们使用其它的一些"有特殊功能的符号"即可绕过过滤规则;其实MEDIUM等级的设计,是模拟开发者在开发过程中,对可能的"意外输入数据"考虑不足的情况;见下源码解释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// 1、判断POST请求中是否存在'Submit'这个键,判断成功才继续执行下一步操作; // 2、将"$_REQUEST[ 'ip' ]"存入"$target"变量; // 3、设计了"$substitutions"黑名单(包含了"&&"与";"符号); // 4、使用"str_replace()"删除"$target"中特殊字符(依据"$substitutions"黑名单) // 5、判断当前服务器的操作系统类型,再决定如何去在服务器上执行"ping"操作; // 6、最后将执行结果存入"$cmd"变量然后返回执行结果; // // 产生漏洞原因: // > 黑名单规则不完善,考虑不足; // > "shell_exec()"函数是PHP中的一个内置功能函数,能直接在操作系统上执行命令; <?php if( isset( $_POST[ 'Submit' ] ) ) { $target = $_REQUEST[ 'ip' ]; $substitutions = array( '&&' => '', ';' => '', ); $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); if( stristr( php_uname( 's' ), 'Windows NT' ) ) { $cmd = shell_exec( 'ping ' . $target ); } else { $cmd = shell_exec( 'ping -c 4 ' . $target ); } echo "<pre>{$cmd}</pre>"; } ?> |
在MEDIUM等级下输入" || whoami ",可以返回"whoami"命令的执行结果,其利用的就是命令行中的"||"这个特殊符号;"||"在命令行中表示的是"或"操作,表示如果"如果前一命令执行失败"则"执行'||'之后的命令";
等同在LINUX下执行了"ping -c 4 || whoami"命令(为什么返回的是"root"而不是"www-data"见LOW解释):
HIGH:
HIGH等级在MEDIUM等级的基础上,完善了黑名中对字符的检测过滤规则;但模拟了程序员在"编写规则时产生了疏忽",在编写针对"|"符号的规则时,多输入了一个空格(写成"| ");实际上,在命令行中,在"特殊字符"后面接额外的命令,两者之间的"空格"不是必须的(留空格的做法只是让命令看上去更舒适,并方便看出命令的作用),于是,我们可以利用这一疏忽,绕过"黑名单"的过滤机制;详见下面的源码解读:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// 1、判断POST请求中是否存在'Submit'这个键,判断成功才继续执行下一步操作; // 2、将"$_REQUEST[ 'ip' ]"存入"$target"变量; // 3、设计了"$substitutions"黑名单(更完善的黑名单); // 4、使用"str_replace()"删除"$target"中特殊字符(依据"$substitutions"黑名单) // 5、判断当前服务器的操作系统类型,再决定如何去在服务器上执行"ping"操作; // 6、最后将执行结果存入"$cmd"变量然后返回执行结果; // // 产生漏洞原因: // > 模拟开发人员失误,本来应写为" '|' => ''; "的代码,因为大意,误写为 // " '| ' => ''; "(多了一个空格); // > "shell_exec()"函数是PHP中的一个内置功能函数,能直接在操作系统上执行命令; <?php if( isset( $_POST[ 'Submit' ] ) ) { $target = trim($_REQUEST[ 'ip' ]); $substitutions = array( '&' => '', ';' => '', '| ' => '', '-' => '', '$' => '', '(' => '', ')' => '', '`' => '', '||' => '', ); $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); if( stristr( php_uname( 's' ), 'Windows NT' ) ) { $cmd = shell_exec( 'ping ' . $target ); } else { $cmd = shell_exec( 'ping -c 4 ' . $target ); } echo "<pre>{$cmd}</pre>"; } ?> |
符号"|"在命令行中被称为"管道符",作用是"作用是将前一个命令的Stdout作为后一个命令的Stdin";HIGH难度下,本处利用了"黑名单"的规则编写失误,与"|"管道符的运行机制,成功的执行了"whoami"命令;实际上"whoami XXX"这样的命令是无法执行成功的,但"|"管道符的一些运行原理,使其忽略了前面"ping -c 4 127.0.0.1"命令传递过来的内容:
等同在LINUX下执行了"ping -c 4 127.0.0.1 |whoami"命令(为什么返回的是"root"而不是"www-data"见LOW解释),注意"|"与"whoami"之间并没有空格:
结:
至此,"Command Injection"篇已经解释完成了,"命令注入"漏洞也确实没什么特别值得说的,也就这样吧~~~
DVWA详解:CommandInjection篇(全):等您坐沙发呢!