网络编程
位置:首页>> 网络编程>> php编程>> php bugs代码审计基础详解

php bugs代码审计基础详解

作者:w350809090  发布时间:2023-06-02 13:49:52 

标签:php,bugs,代码审计

变量覆盖漏洞

<?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 bugs代码审计基础详解

绕过过滤空白字符

<?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!

php bugs代码审计基础详解

但是它的第二个要求为数字必须为整数,否则输出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!! ";  
}

导致我们输入字符串也会报错

php bugs代码审计基础详解

这里我们用到%00来绕过is_numeric函数的判断。

php bugs代码审计基础详解

根据报错,再来看看$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!";
    }

所以第三步要满足的条件为,它必须为回文数即从左往右和从右往左读取都要相同的数值,所以我们构造如下

php bugs代码审计基础详解

以上三个条件都满足后,接下来看看最后一个条件

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 bugs代码审计基础详解

多重加密

<?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得数据。

我们来看看演示,值直接进行查询是有结果得

php bugs代码审计基础详解

使用group by语句分组查询也是正常显示

php bugs代码审计基础详解

但是当我们在group by语句后添加WITH ROLLUP,可以看到效果如下

php bugs代码审计基础详解

但是我们只需要其中第二列得数据,所以使用limit 1读取1条数据并使用offset去除一行数据得到我们需要得第二行

php bugs代码审计基础详解

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

0
投稿

猜你喜欢

  • 各人觉得这些LOGO的设计都很好,简洁,明了,大方。特整理出来与大家分享,希望能吸取设计经验。asp之家祝愿各位09年身体健康,万事如意,网
  • 传统行业里,缺做互联网资深的人;互联网行业里,缺玩传统业务资深的人。于是会造成很多问题,比如两边难沟通,在传统行业者心目中,网络营销e-Ma
  • 对象Javascript 根本上是和对象相关的。数组是对象。函数是对象。对象是对象。那什么是对象呢?对象是名-值对的集合。名是字符串,值可以
  • 搞前端应该对语义化并不陌生,每天都在说语义化,可什么是语义化,语义化究竟能给我们带来什么好处?参加web标准交流会的时候我向各位同学提出了我
  • jquery基本入门 第一天:选择器相关 1.html()与.text() .html()取得第一个匹配元素的html内容。会带有标签,.t
  • 问题:连续或者单个窗体,如何打印当前显示的记录?当前窗体还有对应的子窗体,也要一起打印出来我在一个窗体里有一个单号,大子窗体里有几组数据,我
  • 在翻译这篇文章时我想起一件事情,去年有个朋友在网上非常兴致勃勃的和我说:“我弄了一个很酷的网站,去玩玩吧!真的不错哦!”,然后他把网址发给我
  • 和朋友讨论时,我提到过一个观点,所有框架层设计中,最核心的是导航设计。最近更看到有国外同行提出“80%的可用性是导航!”因为良好的导航可以保
  • 有时候,我们需要替换指定标签外的内容,而保留标签里面的内容不替换。比如当我们要在浏览器中显示出编辑器显示的原始格式时、需要将普通换行符“\n
  • 我们现在使用的验证手段都是以验证码为主,让用户根据图片输入验证字符,这种方法的安全度尚可,但会给用户带来一些不便和困扰,比如这个雅虎的验证码
  •  我们可以先建立一个包含文件名,文件标题的待检索文件的数据库,然后,用ADO方式来访问它,并建立记录集对象。具体代码和说明见下:
  • 如何剔除HTML标识?这里没有使用正则表达式来去除,当然大家用正则"<.+?>"也可以实现这个功能只留下干净
  • PNG格式以支持透明和无损,且相对大小适中,已成为现在网页中图片运用的主流。有些时候我们在制作网页时使用PNG格式图片,用IE浏览器查看却无
  • 假如不使用INSTEAD OF触发器或可更新分区视图而是通过视图来修改数据,那么再修改之前,请考虑下列准则:◆如果在视图定义中使用了 WIT
  • 本文主要介绍了一个获得当前数据库对象依赖关系的实用算法,具体示例请大家参考下文:create   function&nb
  • 1 什么是prototype       JavaScript中对象的prototype属性,可以返
  • SQL Server数据库查询优化的常用方法总结:本文中,abigale代表查询字符串,ada代表数据表名,alice代表字段名。技巧一:问
  • 有时候要用Javascript输常用的字符,比如每个页面都要有的脚注。这里提供一个转换脚本:将HTML自动转为JS代码<script&
  • 1、现象a.用localhost访问,正常b.用IP地址访问,则出现403错误2、分析a.怀疑是ACL问题,设置Everyone为完全控制,
  • [前言:]ASP.NET是微软提供的最新的开发基于Web的应用程序的技术。它提供了大量的比传统ASP脚本技术的好处,包括:1)通过把UI表现
手机版 网络编程 asp之家 www.aspxhome.com