php bugs代码审计基础详解
作者:w350809090 发布时间:2023-06-02 13:49:52
变量覆盖漏洞
<?php
$flag='xxx';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag)); //将读取$flag内容并去除左右空白后保存到$content
if($shiyan==$content)
{
echo'ctf{xxx}';
}
else
{
echo'Oh.no';
}
}
?>
重要点为$shiyan==$content
只要满足这个条件就可以获取flag。
首先extract()
函数的作用为从数组将变量导入到当前符号表,也就是说我们如果构造
xxx.com/index.php?$shiyan=1
则会生成一个名字为$shiyan
的变量,值为1。
然后通过isset
函数来判断刚生成的$shiyan
变量是否为null,如果为null就进入判断。
$content
变量则是通过file_get_contents
函数和trim
函数来读取文件,但是此时它所读取的文件$flag
值为xxx,此时这个目录是不存在的,所以它的值为空。
所以我们此时要做的就是将$shiyan
的值变为空即可。
所以构造链接xxx.com/index.php?$shiyan=&flag=1
即可获得ctf{xxx}
绕过过滤空白字符
<?php
$info = "";
$req = [];
$flag="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
ini_set("display_error", false); //为一个配置选项设置值
error_reporting(0); //关闭所有PHP错误报告
if(!isset($_GET['number'])){
header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); //HTTP头显示hint 26966dc52e85af40f59b4fe73d8c323a.txt
die("have a fun!!"); //die — 等同于 exit()
}
foreach([$_GET, $_POST] as $global_var) { //foreach 语法结构提供了遍历数组的简单方式
foreach($global_var as $key => $value) {
$value = trim($value); //trim — 去除字符串首尾处的空白字符(或者其他字符)
is_string($value) && $req[$key] = addslashes($value); // is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串
}
}
function is_palindrome_number($number) {
$number = strval($number); //strval — 获取变量的字符串值
$i = 0;
$j = strlen($number) - 1; //strlen — 获取字符串长度
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}
if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串
{
$info="sorry, you cann't input a number!";
}
elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值
{
$info = "number must be equal to it's integer!! ";
}
else
{
$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"]));
if($value1!=$value2){
$info="no, this is not a palindrome number!";
}
else
{
if(is_palindrome_number($req["number"])){
$info = "nice! {$value1} is a palindrome number!";
}
else
{
$info=$flag;
}
}
}
echo $info;
根据代码判断,它需要满足多个条件才可以执行$info=$flag;
之后echo出来的才是flag。
if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串
{
$info="sorry, you cann't input a number!";
}
先来看看第一个条件,它要求number参数传入的内容不能为数字,否则返回sorry, you cann't input a number!
但是它的第二个要求为数字必须为整数,否则输出number must be equal to it's integer!!
elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值
{
$info = "number must be equal to it's integer!! ";
}
导致我们输入字符串也会报错
这里我们用到%00来绕过is_numeric
函数的判断。
根据报错,再来看看$value1
,它是$req["number"]
的整数值,$value2
则为反转之后的$req["number"]
的整数值。
$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"]));
if($value1!=$value2){
$info="no, this is not a palindrome number!";
}
所以第三步要满足的条件为,它必须为回文数即从左往右和从右往左读取都要相同的数值,所以我们构造如下
以上三个条件都满足后,接下来看看最后一个条件
if(is_palindrome_number($req["number"])){
$info = "nice! {$value1} is a palindrome number!";
}
else
{
$info=$flag;
}
这里调用了is_palindrome_number()
函数,我们看看函数内容
function is_palindrome_number($number) {
$number = strval($number); //strval — 获取变量的字符串值
$i = 0;
$j = strlen($number) - 1; //strlen — 获取字符串长度
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}
可以看到这里函数的作用是判断数字是否是对称的,我们的要求是让它执行return false
来执行$info=$flag;
所以这里想到的是在数字前加字符串+
字符串+
在is_numeric
中是被无视的,也就是说+100与100相等。
所以我们构造%00%2b454即可
多重加密
<?php
include 'common.php';
$requset = array_merge($_GET, $_POST, $_SESSION, $_COOKIE);
//把一个或多个数组合并为一个数组
class db
{
public $where;
function __wakeup()
{
if(!empty($this->where))
{
$this->select($this->where);
}
}
function select($where)
{
$sql = mysql_query('select * from user where '.$where);
//函数执行一条 MySQL 查询。
return @mysql_fetch_array($sql);
//从结果集中取得一行作为关联数组,或数字数组,或二者兼有返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false
}
}
if(isset($requset['token']))
//测试变量是否已经配置。若变量已存在则返回 true 值。其它情形返回 false 值。
{
$login = unserialize(gzuncompress(base64_decode($requset['token'])));
//gzuncompress:进行字符串压缩
//unserialize: 将已序列化的字符串还原回 PHP 的值
$db = new db();
$row = $db->select('user=\''.mysql_real_escape_string($login['user']).'\'');
//mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。
if($login['user'] === 'ichunqiu')
{
echo $flag;
}else if($row['pass'] !== $login['pass']){
echo 'unserialize injection!!';
}else{
echo "(╯‵□′)╯︵┴─┴ ";
}
}else{
header('Location: index.php?error=1');
}
?>
因题目中并没有给出数据库配置文件,所以直接看题,在题目中重点部分为
if(isset($requset['token']))
//测试变量是否已经配置。若变量已存在则返回 true 值。其它情形返回 false 值。
{
$login = unserialize(gzuncompress(base64_decode($requset['token'])));
//gzuncompress:进行字符串压缩
//unserialize: 将已序列化的字符串还原回 PHP 的值
$db = new db();
$row = $db->select('user=\''.mysql_real_escape_string($login['user']).'\'');
//mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。
if($login['user'] === 'ichunqiu')
{
echo $flag;
}else if($row['pass'] !== $login['pass']){
echo 'unserialize injection!!';
}else{
echo "(╯‵□′)╯︵┴─┴ ";
}
}else{
header('Location: index.php?error=1');
}
条件1为判断是否存在token
参数,如果存在,那么就将token
得值进行反序列化和解压缩后的值进行base64解密。
解密完成后会带入上面写得db
类中得select
方法查询。
接着就是需要注意得重点,if($login['user'] === 'ichunqiu')
即需要传入的user值为ichunqiu
。
所以我们逆推出来需要做得就是先将user设定值为ichunqiu,随后进行base64加密得到值,但是我们刚才说到它在传值过程中进行了反序列话和解压缩,所以我们也需要进行压缩和序列化,分别用
gzcompress
来压缩gzuncompress
解压缩。
serialize
来序列化unserialize
反序列化。
最终得到如下代码,并得到token的值为eJxLtDK0qs60MrBOAuJaAB5uBBQ=
,提交token即可echo $flag
<?php
$arr = array(['user'] === 'ichunqiu');
$token = base64_encode(gzcompress(serialize($arr)));
print_r($token);
?>
WITH ROLLUP注入
<?php
error_reporting(0);
if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {
echo '<form action="" method="post">'."<br/>";
echo '<input name="uname" type="text"/>'."<br/>";
echo '<input name="pwd" type="text"/>'."<br/>";
echo '<input type="submit" />'."<br/>";
echo '</form>'."<br/>";
echo '<!--source: source.txt-->'."<br/>";
die;
}
function AttackFilter($StrKey,$StrValue,$ArrReq){
if (is_array($StrValue)){
//检测变量是否是数组
$StrValue=implode($StrValue);
//返回由数组元素组合成的字符串
}
if (preg_match("/".$ArrReq."/is",$StrValue)==1){
//匹配成功一次后就会停止匹配
print "水可载舟,亦可赛艇!";
exit();
}
}
$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
foreach($_POST as $key=>$value){
//遍历数组
AttackFilter($key,$value,$filter);
}
$con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
if (!$con){
die('Could not connect: ' . mysql_error());
}
$db="XXXXXX";
mysql_select_db($db, $con);
//设置活动的 MySQL 数据库
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
$query = mysql_query($sql);
//执行一条 MySQL 查询
if (mysql_num_rows($query) == 1) {
//返回结果集中行的数目
$key = mysql_fetch_array($query);
//返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false
if($key['pwd'] == $_POST['pwd']) {
print "CTF{XXXXXX}";
}else{
print "亦可赛艇!";
}
}else{
print "一颗赛艇!";
}
mysql_close($con);
?>
第四题我们先来看看flag输出得条件
if($key['pwd'] == $_POST['pwd']) {
print "CTF{XXXXXX}";
}else{
print "亦可赛艇!";
}
要满足post中提交得pwd与$key = mysql_fetch_array($query);
数据库中读取到得pwd相等,所以考点在于注入。
但是在AttackFilter
函数和$filter
中已经限制了sql注入得关键字,所以没办法直接进行注入。
其实在报错得过程中已经进行了提示亦可赛艇!,谐音为因缺思汀也就是WITH ROLLUP
绕过注入
WITH ROLLUP
是对group by
分组后得结果进行进一步得汇总,如果按照列名进行分组,因为列得属性不同,所以会生成一条值null得新数据,如果查询结果时单一得情况下会生成一条列为null得数据。
我们来看看演示,值直接进行查询是有结果得
使用group by
语句分组查询也是正常显示
但是当我们在group by
语句后添加WITH ROLLUP
,可以看到效果如下
但是我们只需要其中第二列得数据,所以使用limit 1
读取1条数据并使用offset
去除一行数据得到我们需要得第二行
pwd得值被设置为了null,所以此题我们可以通过提交admin' GROUP BY pwd WITH ROLLUP LIMIT 1 OFFSET 1-- -
来达到$key['pwd'] == $_POST['pwd']
得条件并获取flag。
erge截断
<?php
$flag = "flag";
if (isset ($_GET['password']))
{
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo '<p>You password must be alphanumeric</p>';
}
else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
{
if (strpos ($_GET['password'], '*-*') !== FALSE) //strpos — 查找字符串首次出现的位置
{
die('Flag: ' . $flag);
}
else
{
echo('<p>*-* have not been found</p>');
}
}
else
{
echo '<p>Invalid password</p>';
}
}
?>
先来梳理流程 首先条件一用ereg
函数来写死get参数password得值必须是数字大小写字母,否则输出You password must be alphanumeric
。
第二个条件为strlen($_GET['password']) < 8 && $_GET['password'] > 9999999
也就是必须长度小于8但是值又要大于9999999。
所以我们需要用科学计数法来绕过这里得限制1e7
为10得7次方10000000。
第三个条件为strpos ($_GET['password'], '*-*') !== FALSE
在值中必须存在*-*
如果满足此条件,就没办法满足条件一,所以这里可以采用%00
截断法来进行绕过,因为ereg
函数遇到%00后就不会继续进行判断
?password=1e7%00*-*
即可满足全部条件,执行die('Flag: ' . $flag);
来获取flag
strcmp比较字符串
<?php
$flag = "flag";
if (isset($_GET['a'])) {
if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小于 str2 返回 < 0; 如果 str1大于 str2返回 > 0;如果两者相等,返回 0。
//比较两个字符串(区分大小写)
die('Flag: '.$flag);
else
print 'No';
}
?>
这题得考点在于strcmp
函数,它的作用在于两个字符串相比较,如果两者相等就会==0
。
此函数是用来处理字符串参数的,如果提交的值是数组的话会返回个null
在判断中使用的是==
等值符,如果类型不相同的情况下会转换为同类型进行比较,所以null
==0
,执行die('Flag: '.$flag);
所以我们提交一个数组类型的值即可?a[]=1
。
sha()函数比较绕过
<?php
$flag = "flag";
if (isset($_GET['name']) and isset($_GET['password']))
{
if ($_GET['name'] == $_GET['password'])
echo '<p>Your password can not be your name!</p>';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: '.$flag);
else
echo '<p>Invalid password.</p>';
}
else
echo '<p>Login first!</p>';
?>
这题的考点在于sha1($_GET['name']) === sha1($_GET['password'])
他这里使用的是===
等同符,他要求两边值得类型相同,才会去比较值,否则会直接返回false
首先来看条件一$_GET['name'] == $_GET['password']
name要与password不相等才会执行else if
但是else if
又要求===
,所以我们可以利用sha1
函数不能处理数组得机制来绕过
即?name[]=1&password[]=2
既满足了name与password不相等,也满足了因sha1
无法处理数组,导致返回值为false=false
所以会执行die('Flag: '.$flag);
来源:https://blog.csdn.net/w350809090/article/details/127186942
猜你喜欢
- 本文实例为大家分享了TensorFlow实现简单线性回归的具体代码,供大家参考,具体内容如下简单的一元线性回归一元线性回归公式:其中x是特征
- getDatagetData方法主要是获取方法的元数据metadata。getData实现上借助metaMap和noop两个内部方法。met
- 前言在最一开始,我的B站收藏一直是存放在默认收藏夹中,但是随着视频收藏的越来越多,没有分类的视频放在一起,想在众多视频中找到想要的视频非常困
- 获取不带扩展名的文件的名称:import osprintos.path.splitext("path_to_file")
- 谈到用户界面交互总少不了事件,前面一系列文章介绍的鼠标光标、坐标、弹出式提示框等实现的底层其实都是事件处理,只不过matplotlib或其他
- 在SQL SERVER下跟踪sql采用事件探查器,而在mysql下如何跟踪sql呢? 其实方法很简单,开启mysql的日志log功能,通过查
- 本文介绍Python中的线程同步对象,主要涉及 thread 和 threading 模块。threading 模块提供的线程同步原语包括:
- object.OpenTextFile(filename[, iomode[, create[, format]]]) 参数 object
- Python虚拟机注:本篇是根据教程学习记录的笔记,部分内容与教程是相同的,因为转载需要填链接,但是没有,所以填的原创,如果侵权会直接删除。
- 前端部分(Vue + Vant)引入Vant、使用Vant中的Uploader组件上传文件(支持手机拍照)import Vue from &
- 初学Python,在网上看到Python图片转字符画的教程,我也来尝试下。 首先我们要用到Python的PIL库的Image模块,PIL(P
- 导语在CSDN学习的过程中,遇到了爆火的文章是关于刮刮卡的!大家猜猜看是谁写的?我看这文章都特别火,我也感觉挺好玩的,那就寻思用 Pytho
- 关于浅拷贝和深拷贝想必大家在学习中遇到很多次,这也是面试中常常被问到的问题,借由这个时间,整理一下浅拷贝和深拷贝的关系先从一个简单的例子入手
- 本文利用python opencv进行图像的边缘检测,一般要经过如下几个步骤:1、去噪如cv2.GaussianBlur()等函数;2、计算
- 本文实例讲解了Python中除法使用的注意事项,是非常重要的技巧,对于Python程序设计来说有很好的借鉴价值。具体分析如下:现来看如下示例
- 一、定义新的自动求导函数在底层,每个原始的自动求导运算实际上是两个在Tensor上运行的函数。其中,forward函数计算从输入Tensor
- 一.前言众所周知:没有对象怎么办?那就new一个!那么在JS中,当我们new一个对象的时候,这个new关键字内部都干了什么呢?现在我们就来剖
- 用js判断复选框是否选中及选中个数,以前经常有朋友提到复选框的选中问题,这段代码可以帮你解决这个问题,而且它还可以判断你选择了多少个复选框,
- asp时间加减运算 和转换问题 a=2007-07-24 2:23:15 b=2005-06-25 2:23:15 问题1 如何将a转换成2
- 1、开头:#!/usr/bin/python和# -*- coding: utf-8 -*-的作用 – 指定#!/usr/bin/pytho