命令注入这个攻击因为它的危害过大,且一般web应用并不会提供这个功能给用户,所以直接的命令注入实例并不好找,dvwa这以比较常见的代ping ip为例开始了实战体验.
0x00. 回顾
小学的时候我们都做过一类文字填空: 这个星期天,我应该去___________________?
常见的回答如下:
好同学 小张: 我应该去”吃鸡”.
学霸 阿汤:我应该去”图书馆学习”.
学渣 小明: 我应该去”实验室玩?nope,我应该去实验室学技术”.
引入这个例子,大家能感受到命令注入的一个核心思想了么? 就是转折 ,那么命令注入就是在web上注入系统命令,回顾一下命令注入三部曲:
此处是否调用的是系统命令 (ping tracert pwd ls等等)
输入的参数是否可修改控制 (比如有些只能点击ping某些地址)
是否可以跟小明同学一样转折
0x01. 命令注入实战 首先是low难度的. 应该没有做什么防护,我随手一试拼接个&就get了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?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>" ; } ?>
stristr() 函数搜索字符串在另一字符串中的第一次出现,并返回剩余部分 ,phpuname的s参数返回system信息
上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 <?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>" ; } ?>
可以看到把&& 跟; 过滤了,但是我直接&就可以了呀…(naive…
然后做了个拼接. 而且这样的话.其实我看看可以不可以重复法绕过…唔
hard难度:
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 <?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>" ; } ?>
毙掉了很多常见语句.因为看到%没有被过滤,试试转码? 想一下不对…
咦,然后直接输入 localhost | dir 就出现dir的结果了,说好的 | 被过滤为空了呢?什么情况..??? (|这里作为管道用法)
最后贴impossible的代码:
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 36 37 <?php if ( isset ( $_POST [ 'Submit' ] ) ) { checkToken ( $_REQUEST [ 'user_token' ], $_SESSION [ 'session_token' ], 'index.php' ); $target = $_REQUEST [ 'ip' ]; $target = stripslashes ( $target ); $octet = explode ( "." , $target ); if ( ( is_numeric ( $octet [0 ] ) ) && ( is_numeric ( $octet [1 ] ) ) && ( is_numeric ( $octet [2 ] ) ) && ( is_numeric ( $octet [3 ] ) ) && ( sizeof ( $octet ) == 4 ) ) { $target = $octet [0 ] . '.' . $octet [1 ] . '.' . $octet [2 ] . '.' . $octet [3 ]; if ( stristr ( php_uname ( 's' ), 'Windows NT' ) ) { $cmd = shell_exec ( 'ping ' . $target ); } else { $cmd = shell_exec ( 'ping -c 4 ' . $target ); } echo "<pre>{$cmd} </pre>" ; } else { echo '<pre>ERROR: You have entered an invalid IP.</pre>' ; } } generateSessionToken ();?>
可以看到首先user_token先行…其次正则分割,一个个判断数字.然后再拼接起来.(丧心病狂啊…
0x02. how to fix?
真的要向impossible这样一个个去分割么? 那如果是不同的命令,怎么办呢?
在安全中,一般公认最小信任度原则. 那显然白名单是更合适的.有时候白+黑效果更好.
比如黑名单过滤了whoami (大名鼎鼎的命令
那么,可以利用双引号/单引号/括号或者组合拳来绕过
命令执行了不显示结果怎么办?
延时注入 : win下ping localhost -n 5 >nul . Linux下 直接sleep 5.
远程请求 : win下的ping/telnet命令. Linux下的wget/curl命令
当下比较流行的是nslookup 这种方式~~ 后面会单独说