声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!
本专题文章导航
红日 新书上线了
红日安全推出的适合新手入门系列教程专刊上线啦!
红日安全专注新手入门教程。转眼间红日安全又成长了一岁,在这一年里,团队成长了很多很多。目前团队所有的文章对外都是公开的,希望可以给新手一个填补空白的地方。2020年,团队开始新的方向,和一些安全培训厂商进行合作,希望做出更多安全作品。感谢团队每一位成员为团队的辛勤付出,希望每一个人在团队里面有更大的作为。也感谢一直在后面默默支持我们的红粉,也祝愿你们在新的一年学到更多的干货!
1.1.5 PHP安全缺陷
1.1.5.1 PHP 常见安全配置
1.1.5.1.1 php的安全模式
php的安全模式是个非常重要的内嵌的安全机制,能够控制一些php中的函数,
比如system(),
同时把很多文件操作函数进行了权限控制,也不允许对某些关键文件的文件,
比如/etc/passwd,
但是默认的php.ini是没有打开安全模式的,我们把它打开:
safe_mode = on
1.1.5.1.2 php脚本能访问的目录
使用open_basedir选项能够控制PHP脚本只能访问指定的目录,这样能够避免
PHP脚本访问
不应该访问的文件,一定程度上限制了phpshell的危害,我们一般可以设
置为只能访问网站目录:
open_basedir = D:/usr/www
1.1.5.2 关闭危险函数
如果打开了安全模式,那么函数禁止是可以不需要的,但是我们为了安全还是考
虑进去。比如,
我们觉得不希望执行包括system()等在那的能够执行命令的php函数,或
者能够查看php信息的
phpinfo()等函数,那么我们就可以禁止它们:
disable_functions =
system,passthru,exec,shell_exec,popen,phpinfo,escapeshellarg,escapesh
ellcmd,proc_close,proc_open,dl,show_source,get_cfg_var
如果你要禁止任何文件和目录的操作,那么可以关闭很多文件操作
disable_functions =
chdir,chroot,dir,getcwd,opendir,readdir,scandir,fopen,unlink,delete,c
opy,mkdir,
rmdir,rename,file,file_get_contents,fputs,fwrite,chgrp,chmod,chown
以上只是列了部分不叫常用的文件处理函数,你也可以把上面执行命令函数
和这个函数结合,
就能够**大部分的phpshell了。
1.1.5.3 关闭注册全局变量
在PHP中提交的变量,包括使用POST或者GET提交的变量,都将自动注册为
全局变量,能够直接访问
这是对服务器非常不安全的,所以我们不能让它注册为全局变量,就把注册
全局变量选项关闭:
register_globals = Off
当然,如果这样设置了,那么获取对应变量的时候就要采用合理方式,比如
获取GET提交的变量var,
那么就要用$_GET['var']来进行获取,这个php程序员要注意。
1.1.5.4 magic_quotes_gpc 来防止 SQL 注入
SQL注入是非常危险的问题,小则网站后台被入侵,重则整个服务器沦陷,所以一定要小心。php.ini中有一个设置:magic_quotes_gpc = Off这个默认是关闭的,如果它打开后将自动把用户提交对sql的查询进行转换,比如把 ' 转为 \'等,这对防止sql注射有重大作用。所以我们推荐设置为:magic_quotes_gpc = On
1.1.6 代码审计入门
1.1.6.1 代码审计工具
所有的代码审计工具只能辅助我们进行漏洞发现,由于现在代码审计工具逻辑漏
洞是无法发现,只能靠人工进行源代码审计。审计套路网上也比较多。大概方法
分为以下几种:
工具+人工法审计
主要是根据代码审计工具进行函数遍历正则匹配,另加上人工对漏洞敏感度进行
查找。
代码通读法
主要是根据代码整体逻辑进行漏洞挖掘,这样耗费时间比较大,但是可以挖到比
较有价值的漏洞。还有一些商业化工具,这里暂且不讲。讲一下本漏洞经常用的
几款比较免费功能强大的工具。
SeayPHP
使用方法:直接新建项目,把源代码拖到里面即可。
下载地址:https://pan.baidu.com/s/1i3Jw2uL#list/path=%2F
具体使用方法:
https://jingyan.baidu.com/article/54b6b9c0f77e0c2d583b47b1.html
Cobra
程序界面
操作方法:直接安装即可使用,如果安装麻烦,程序也给出docker安装方式。
直接利用docker安装即可。
审计效果:
附加说明:一般工具都会有一些规则,作为一名安全研究人员,你可以多看看一
些开源工具的源码。另外此工具也有一些规则,你可以看一下工具的规则,来提
高代码审计。另外我们的函数都是采取工具截取,给大家进行演示。
我们随便摘取一个,方法是漏洞利用的正则。
Rips
使用截图:
使用起来比较简单,直接上传源代码即可。作为一个静态审计工具,在审计能力
方面我们发现这款工具还是可以。也可以定位到相关函数。
下载地址:https://sourceforge.net/projects/rips-scanner/files/
额外说明:大家可以利用此工具进行源码审计,然后利用手工进行函数查找,漏洞复现等操作。
Burp
Burp本身就是一个神奇。不仅可以配合源代码审计,也可以进行Web渗透测试等操作。此工具团队已经进行详细说明,大家可以参团队博客进行参考使用。
BadCode
工具截图:
此工具主要是利用subline3插件进行漏洞查找和利用。
下载地址:https://github.com/pwnsdx/BadCode
Ÿ VulHint subline3插件
安全规则:
l Linux系统
大家在渗透测试工程中,避免会遇到Linux系统,如果遇到了Linux系统,而且系统也不可以外连,也没有权限进行下载。那这个时候该怎么办?
如果遇到这种情况,我们可以利用Linux系统本身的查找命令进行源代码审计。加入我们有入侵了一个Linux系统,在相关目录里面我们发现了一套源码。然后利用此命令进行代码审计。发现可疑函数进行渗透测试。
Ÿ XSS漏洞
grep -Ri "echo" .
grep -Ri "\$_" . | grep "echo"
grep -Ri "\$_GET" . | grep "echo"
grep -Ri "\$_POST" . | grep "echo"
grep -Ri "\$_REQUEST" . | grep "echo"
发现好多函数里面都用echo这个命令。通过这个显示符,我们可以去查看一些
配置函数。
Ÿ 命令执行
grep -Ri "shell_exec(" .
grep -Ri "system(" .
grep -Ri "exec(" .
grep -Ri "popen(" .
grep -Ri "passthru(" .
grep -Ri "proc_open(" .
grep -Ri "pcntl_exec(" .
看到这些函数应该就可以去尝试一下漏洞
Ÿ 代码注入
grep -Ri "eval(" .
grep -Ri "assert(" .
grep -Ri "preg_replace" . | grep "/e"
grep -Ri "create_function(" .
Ÿ SQL注入
grep -Ri "\$sql" .
grep -Ri "\$sql" . | grep "\$_"
Ÿ 信息泄露
grep -Ri "phpinfo" .
Ÿ Debug调式
grep -Ri "debug" .
grep -Ri "\$_GET['debug']" .
grep -Ri "\$_GET['test']" .
Ÿ RFI/LFI
grep -Ri "file_include" .
grep -Ri "include(" .
grep -Ri "require(" .
grep -Ri "require(\$file)" .
grep -Ri "include_once(" .
grep -Ri "require_once(" .
grep -Ri "require_once(" . | grep "\$_"
Ÿ Misc
grep -Ri "header(" . | grep "\$_"
grep -Ri '$_SERVER["HTTP_USER_AGENT"]' .
Ÿ 文件类型
grep -Ri file_get_contents .
对于Linux系统源代码审计,方法多种多样,只是小编经常采用这用方式来进行
源代码审计。对整体代码逻辑性非常锻炼人。
1.1.7 代码审记函数理解
我们根据cobra规则,我们总结了一些PHP常见漏洞威胁函数。
其它工具的规则你可以进行查看。比如我们去查看一下Badcode里面的一些检查
规则,发现也类似。
所以先学习代码审计,你可以先学习一下PHP常见威胁函数有哪些,然后在查看
这些函数是如何调用的,最后整体进行漏洞挖掘。工具进行测试,也可以手工加
工具进行测试。最后结果漏洞发现,不管采用哪种方式,你都可以达到一个上升。
Ÿ 注入敏感函数
selectfrom 、mysql_connect、mysql_query、mysql_fetch_*、update、insert、
delete
(案例1)
为了讲解这些常用函数,临时写了一个HRvul小靶场进行漏洞练习。参考了部分
安全靶场。
首先看注入源码部分
<?php
if(isset($_GET['Submit'])){
// Retrieve data
$conn=mysql_connect("localhost","root","root",3306);
mysql_select_db("hrexp",$conn);
inject_checking(strtolower($_SERVER['QUERY_STRING']));
$id = $_GET['id'];
$getid = "SELECT * FROM users WHERE id = $id";
print_r($getid);
$result = mysql_query($getid) or die('<pre>' . mysql_error() .
'</pre>' );
echo "<br/>";
print_r(mysql_fetch_array($result));
对query_string传过来字符进行检查inject_checking函数,我们在看一下这个函数有哪些操作
<?php
function inject_check($str) { //防注入函数开始
$check=preg_match('/select|order|insert|update|eval|document|d
elete|injection|jection|link|and|or|from|union|into|load_file|
outfile|<script>/',$str);
if($check){
echo
"<script>alert('Filtered!!!');window.history.go(-1);</script>"
;
exit();
}else{
return $str;
}
}
对常见函数都进行检查,我们看看能不能进行绕过,可以直接对常见函数进行编码既可以绕过。
(2)案例2
<?php
$dbuser = "root";
$dbpwd = "";
$db = "test";
//这里是mysql的密码
$conn = mysql_connect("localhost",$dbuser,$dbpwd) or
die("error");
mysql_select_db($db,$conn);
$id = $_GET['id'];
$query = "select * from test where id =$id";
$result = mysql_query($query) or die(mysql_error());
print_r(mysql_fetch_array($result));
这里我们用到了mysql_query这个函数。由于$id参数在传参过程中没有进行修
饰,直接传给了MySQL。造成了注入。
(3)案例3
Simple Down 简单下载系统 v5.4
从源码状态来看,应该没过滤直接mysql_query或者过滤不严格。
文章地址:http://wooyun.jozxing.cc/static/bugs/wooyun-2013-037204.html
Ÿ 宽字节注入
setnamesgbk、character_set_client=gbkmysql_set_charset('gbk')、iconv
宽字符是指两个字节宽度的编码技术,如UNICODE、GBK、BIG5等。当MYSQL数
据库数据在处理和存储过程中,涉及到的字符集相关信息包括:
Ÿ character_set_client:客户端发送过来的SQL语句编码,也就是PHP发送
的SQL查询语句编码字符集。
character_set_connection:MySQL服务器接收客户端SQL查询语句后,在实
施真正查询之前SQL查询语句编码字符集。
Ÿ character_set_database:数据库缺省编码字符集。
Ÿ character_set_filesystem:文件系统编码字符集。
Ÿ character_set_results:SQL语句执行结果编码字符集。
Ÿ character_set_server:服务器缺省编码字符集。
Ÿ character_set_system:系统缺省编码字符集。
Ÿ character_sets_dir:字符集存放目录,一般不要修改。
宽字节对转义字符的影响发生在character_set_client=gbk的情况,也就是说,
如果客户端发送的数据字符集是gbk,则可能会吃掉转义字符\,从而导致转义
消毒失败。
例子3就是一个存在宽字符注入漏洞的PHP程序。
<?php
$name=$_GET['name'];
$name=addslashes($name);
$conn=mysql_connect('localhost','root','root');
if($conn==null){exit("connect error !<br>");}
mysql_query("SET NAMES 'gbk'",$conn);
mysql_select_db("hongri",$conn);
$sql="select * from hongrisec where name='".$name."'";
$result=mysql_query($sql,$conn);
while($val=mysql_fetch_row($result)){
print_r($val);
print("<br>");
}
?>
产生漏洞代码我们已经利用黄色底纹已经标出来,主要数据库编码采用了gbk编码,导致了宽字节注入。POC如下
http://127.0.0.1/hongri/hongrisec.php?name=a%df' or 1=1; %20%23
其原理是mysql_query("SETNAMES'gbk'",$conn)语句将编码字符集修改为gbk,
此时,%df\'对应的编码就是%df%5c’,即字“運’”,这样单引号之前的转义符号“\”就被吃调了,从而转义失败。一个汉字就把单引号吃掉了,导致过
滤失败,注入我们还可以继续进行。
二次编码注入
urldecode、rawurldecode
seay代码审计书中也描述过二次编码注入,主要是利用工具采用正则进行编码
寻找,然后寻找漏洞。
源码下载:https://pan.baidu.com/s/1c1mLCru
漏洞文章分析:https://www.waitalone.cn/php-code-audit-4.html
代码分析:
<?php
$a=addslashes($_GET['p']);
$b=urldecode($a);
echo '$a='.$a;
echo '$b='.$b;
?>
主要是urldecode这个函数导致问题。Addslashes函数主要是对单引号、双引号加反斜线进行注释。而urlencode这个函数又进行编码了,导致漏洞产生。比如我们进行编码,%2527,在这里%25编码的结果是%,如果程序使用urldecode函数,会直接和27数字连接上,形成单引号导致绕过addslashes函数,形成注入。
MySQL二次注入讲解
在浏览文章的时候,发现一个图片比较好,就给大家放过来更容易理解二次注入。
原文文章:
http://www.beesfun.com/2017/03/28/MySQL%E6%B3%A8%E5%85%A5%E7%B3%BB%E5
%88%97%E4%B9%8B%E4%BA%8C%E6%AC%A1%E6%B3%A8%E5%85%A5-%E4%B8%89/
来段代码讲解一下,比较典型代表了二次注入。
Hrconfig.php
<?php
mysql_connect('localhost', 'root', 'root');
mysql_select_db('hongrisec');
mysql_set_charset('utf-8');
if (!get_magic_quotes_gpc()){
//主要判断GPC是否打开,如果GPC打开就利用GPC过来单引号、双引号等特
殊字符。如果没开启直接去调用addslashes_deep函数,这个函数在下方已经
定义。
if (!empty($_GET)){
$_GET = addslashes_deep($_GET);
}
if (!empty($_POST)){
$_POST = addslashes_deep($_POST);
}
$_COOKIE = addslashes_deep($_COOKIE);
$_REQUEST = addslashes_deep($_REQUEST);
}
function addslashes_deep($value){
if (empty($value)){
return $value;
}else {
return is_array($value) ? array_map('addslashes_deep',
$value): addslashes($value);
}
}
?>
Hrreg.php
<?php
include "config.php";
if(!empty($_POST['submit'])){
//判断submit发送过来的值
$username = $_POST['username'];
$password = $_POST['password'];
$email = $_POST['email'];
$sql = "INSERT INTO `hongrisec`.`users` (`id`, `username`,
`password`, `email`)
VALUE (NULL, '$username', '$password', '$email');";
$row = mysql_query($sql);
//把我们输入的值插入到数据库的users表里面,看到没有经过任何过滤。
if ($row){
echo "注册成功";
} else {
echo "注册失败";
}
}
?>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<body align="center">
<form action="" method="POST">
<h4><b> username:</b><input type="text"
name="username"><br/></h4>
<h4><b> password:</b><input type="text"
name="password"><br/></h4>
<h4> <b>email:</b><input type="text" name="email"><br/></h4>
<input type="submit" name="submit" value="submit">
</form>
</body>
二次注入的利用就是把第一次存储到数据库里面的值在此调用然后进行二次利
用。Search.php
<?php
include "hrconfig.php";
if(!empty($_POST['submit'])){
$email = $_POST['email'];
//这里对email也进行了查询
$sql = "select * from users where email='{$email}'";
$row = mysql_query($sql);
if ($row){
$rows = mysql_fetch_array($row);
$username = $rows['username'];
//这里主要对$username又重新进行调用,所以产生了漏洞
$sql = "select * from users where username='$username'";
$row = mysql_query($sql) or die(mysql_error());
if ($row){
$rows = mysql_fetch_array($row);
echo $rows['username']."<br/>";
echo $rows['password']."<br/>";
echo $rows['email']."<br/>";
}
}
}
?>
<body align="center">
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<form action="" method="POST">
<h3><b>search email</b><input type="text"
name="email"><br/></h3>
<input type="submit" name="submit" value="ok">
</form></body>
代码已经为大家分析完毕。现在直接进行操作。首先在输入表里面输入字段。
我们已经在输入把单引号已经输入进去,我们查看一下数据库。
然后我们重新注册一个语句。利用MySQL语句进行注册
插入数据库语句。然后在利用查找语句进行查找邮箱语句。
发现已经查找成功。
Ÿ 文件包含
include、include_once 、require、require_once
本地包含漏洞
常见漏洞代码
<?php
if (isset($_GET[page])) {
//这句语句是核心语句,主要利用GET语句进行包含。我们可以构造一些语句
进行绕过,不过现在这样语句已经非常少了。
include $_GET[page];
} else {
include "home.PHP";
}
?>
案例2
1.包含文件上传的图片
<?php
if ($_GET[page]) {
include($_GET[page]);
} else {
include "show.php";
} //利用page=/uploads/xxx.jpg
用户控制$_GET[page]里面内容 包含一个带有恶意代码的上传图片。getshell
(其中要找到上传图片的绝得路径)
2.包含文件上传的第二种
<?php
if ($_GET[page]) {
include("./action/".$_GET[page]);
} else {
include "./action/show.php";
} //利用page=../uploads/xxx.jpg 跳出当前目录
3.第三种包含上传
<?php
if ($_GET[page]) {
include("./action/".$_GET[page].".php");
} else {
include "./action/show.php";
} // 可以看出会在后面追加一个.php后缀 ../uploads/xxx.jpg%00截断后
面的.php
需要开启(magic_quotes_gpc=off,php小于5.3.4)
Ÿ 文件上传
move_upload_file
直接看代码讲解:
<?php
if($_FILES['userfile']['type'] != "image/gif") #这里对上传的文件
类型进行判断,如果不是image/gif类型便返回错误。
{
echo "Sorry, we only allow uploading GIF images";
exit;
}
$uploaddir = 'uploads/';
$uploadfile=$uploaddir.basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'],
$uploadfile))
//move_uploaded_file主要是把文件移动到一个位置
{
echo "File is valid, and was successfully uploaded.\n";
} else {
echo "File uploading failed.\n";
}
?>
主要是对图片类型进行判断,如果不是图片类型就返回错误。上传内容也比较多。
后续看我们完整版本报告。
任意文件删除
unlink、session_destory
代码注入 :
eval assert、preg_replace(/e)、 call_user_func、call_user_func_array、
array_map
展示界面:
然后直接进行代码讲解
<?php
include "hrconfig.php";
if(!empty($_POST['submit'])){
$username = $_POST['username'];
$password = $_POST['password'];
$email = $_POST['email'];
@eval("\$var1=$username;");
//这里直接进行了代码执行操作。我们直接输入phpinfo语句。
echo $var1;
@eval("\$var2=$password;");
echo $var2;
}
?>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<body align="center">
<h1>PHP Code Injection</h1>
<form action="" method="POST">
<h3><b> username:</b><input type="text"
name="username"><br/></h3>
<h3><b> password:</b><input type="text"
name="password"><br/></h3>
<h3> <b>email:</b><input type="text" name="email"><br/></h3>
<input type="submit" name="submit" value="submit">
</form>
</body>
直接当成代码执行。
命令执行
system、exec、shell_exec、passthru 、pctnl_exec、popen、proc_exec、``
命令讲解
<?php
$cmd=$_GET['hongri'];
echo shell_exec($cmd);
?>
直接当成命令执行。Hongri的值如果可以控制,我们就可以执行我们想执行的命令。
变量覆盖
extract、parse_str、$$
函数演示
实例演示wooyun:frcms (wooyun 2014-073244)
他会把你从GET、POST、COOKIE 中的变量注册为全局变量,因此我们直接通过
GET 参数提交$insLockfile 变量即可绕过
反序列化
Unserialize、serialize()
代码讲解:
当在php中创建了一个对象后,可以通过serialize()把这个对象转变成一个字符串,保存对象的值方便之后的传递与使用。测试代码如下;
<?php
class hongrisec{
var $test = '123';
}
$class1 = new hongrisec;
$class1_ser = serialize($class1);
print_r($class1_ser);
?>
然后我们查看一下反序列化之后的值
O:9:"hongrisec":1:{s:4:"test";s:3:"123";}
这里的O代表存储的是对象(object),假如你给serialize()传入的是一个数
组,那它会变成字母a。9表示对象的名称有9个字符。"hongrisec"表示对象的
名称。1表示有一个值。{s:4:"test";s:3:"123";}中,s表示字符串,4表示该
字符串的长度,"test"为字符串的名称,之后的类似。
unserialize()
与serialize() 对应的,unserialize()可以从已存储的表示中创建PHP的值,
单就本次所关心的环境而言,可以从序列化后的结果中恢复对象(object)。
<?php
class hongrisec{
var $test = '123';
}
$class2 = 'O:9:"hongrisec":1:{s:4:"test";s:3:"123";}';
print_r($class2);
echo "</br>";
$class2_unser = unserialize($class2);
print_r($class2_ser);
?>
反序列之后,就还原成serialize之后的。反序列化漏洞就在序列化的时候插入
我们需要插入代码。造成漏洞利用化,但是利用漏洞有一定的限制。具体漏洞讲
解请参考完整报告。
随机数
rand、mt_rand
将在完整报告中更详细介绍。
你怎么看待红日发布的这款新专刊?你认为它有爆红的潜质吗?
欢迎在评论区留言分享你的观点和看法