PHP TRICKS

发布于 2022-07-29  314 次阅读


一些知识

  1. $_GET['a']此类传参, _GET['a']变量(或者a变量)的值为传入值,是一个数,也可传递数组,如a[0]=1&a[1]=2
  2. $_GET此类传参, _GET变量的值为一个数组,如a=1&b=2,则传入Array ( [a] => 1 [b] => 2 )
  3. $_POST['a']此类传参, _POST['a']变量(或者a变量)的值为传入值,是一个数,也可传递数组,如a[0]=1&a[1]=2
  4. $_POST此类传参, _POST变量的值为一个数组,如a=1&b=2,则传入Array ( [a] => 1 [b] => 2 )
  5. 数组不能直接用echo打印,要用print_r($_POST);但print_r不能打印变量

1

 <?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
} 
  • pregmatch()函数无法处理数组
  • 关于正则匹配:此函数只有1和0两个返回值,如果要查询总匹配次数要pregmatchall()函数,"/xxx/"为模式分隔符,无含义,/XXX/i表示忽略大小写,\bXXX\b表示完全匹配,比如cat和cate不匹配
  • 传参?num[20]=a

2

 <?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){  
        die("no no no!");
    }
    if(intval($num,0)===4476){ 
        echo $flag;
    }else{
        echo intval($num,0);
    }
} 
  • 关于intval(),intval(a,b)将a视为b进制下的数,若b为0或为空时,b的值却决于a,若a包含0x则b=16,若a以0开头,则b=8,否则b=10,当b=10时a自动取整数部分,同时在此函数下1===+1
  • 数不能是4476,经过intval()处理后为4476,可以用4476的8进制或16进制或小数

3

 <?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(!strpos($num, "0")){ 
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
} 
  • strpos(a,b)查找a在b中第一次出现的位置,无论是第一位还是找不到都返回false
  • 过滤了8进制和16进制,同时必须有0,考虑小数加0

4

 <?php
include("flag.php");
highlight_file(__FILE__);

if (isset($_POST['a']) and isset($_POST['b'])) {
    if ($_POST['a'] != $_POST['b'])
        if (md5($_POST['a']) === md5($_POST['b']))
            echo $flag;
    else
        print 'Wrong.';
} 
  • md5()函数不能处理数组,数组返回null
  • post a[]=1&b[]=2

5

 <?php
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2)){
        echo $flag;
    }
} 
  • 需要同时get和post
  • 地址栏get,hackbar post

6

 <?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){
    $v1 = $_POST['v1'];
    $v3 = $_GET['v3'];
    parse_str($v1,$v2);
    if($v2['flag']==md5($v3)){
        echo $flag;
    }
} 
  • parse_str(a,b),将a中的x=y放入b数组中,b[y]=x,
  • 查得admin的md5值为21232f297a57a5a743894a0e4a801fc3,传入admin与flag=21232f297a57a5a743894a0e4a801fc3(不需要加括号)即可

7

 <?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE)  {
    die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
    echo $flag;
} 
  • strrev()函数将字符串反转,strrev()与正则匹配类似
  • ereg()函数匹配相当于设置白名单,可以被%00截断
  • 16进制转换后为877,要求输入全为字母,可以?c=a%00778

8-1

 <?php
error_reporting(0);
highlight_file(__FILE__);
class SYC
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}
call_user_func($_POST['vanzy']); 
  • call_user_func(),一共有4种回调方法(注意单引号)
  1. call_user_func('函数名','传入参数')回调函数
  2. call_user_func('类名'::'函数名')回调类中的静态方法(注意函数不加括号)
  3. call_user_func(array(类名,'say_hello'))数值数组回调
  4. $myobject = new myclass(); call_user_func(array($myobject, '函数'))对象回调
  • 这里使用静态方法回调,call_user_func(SYC::getFlag)(注意此echo打印到页面代码中而不是页面上)(也可以数值数组回调)

8-2

 <?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}
if(strripos($_POST['ctfshow'], ":")>-1){
    die("private function");
}
call_user_func($_POST['ctfshow']); 
  • sql可以字符串拼接 是因为 sql语句本身是一个字符串,而php中除了部分函数,不能将传递的字符串当代码执行
  • strripos(a,b)查找a在b中最后一次出现的位置,所以过滤了:,故使用8中的数值数组回调,所以传入ctfshow[0]=ctfshow&ctfshow[1]=getflag(必须从0(数组第一位)开始传,默认从第一位开始读,第一位为空出问题)

9

 <?php
   include "flag.php";
   error_reporting(0);
   highlight_file(__FILE__);
   $_403 = "Access Denied";
   $_200 = "Welcome Admin";
   if ($_SERVER["REQUEST_METHOD"] != "POST")
   {
         die("BugsBunnyCTF is here :p…");
   }
   if ( !isset($_POST["flag"]) )
   {
         die($_403);
   }
   foreach ($_GET as $key => $value)
   {
         $$key = $$value;
   }
   foreach ($_POST as $key => $value)
   {
         $$key = $value;
   }
   if ( $_POST["flag"] !== $flag )
   {
         die($_403);
   }
   echo "This is your flag : ". $flag . "\n";
   die($_200);
?> BugsBunnyCTF is here :p…
  • $SERVER["REQUEST_METHOD"] != "xxx"判定请求方式,
  • $$a,是指把$a的值作为$的变量名称
  • if ( !isset($ -POST["flag"]) )将flag值给覆盖了,推演foreach函数,发现此函数的作用是,将a[x]=y中x与y作为两个变量名并且使之相等,flag值被覆盖打印出来没有,考虑将利用foreach函数将flag值传递给403或者200,get ?_200=flag,post flag=a(此处应传入数组,方法见上)

10

 <?php
   include "flag.php";
   error_reporting(0);
    highlight_file(__FILE__);
    extract($_GET);
   if (isset($gift))
  {
       $content = trim(file_get_contents($flag));
       if ($gift == $content)
      {
            echo $flag;
      }
       else
      {
           echo 'Oh..';
      }
  } 
  • extract()函数,依次创建许多变量,把数组中的key当作变量名,值当作变量值
  • trim()(同文件包含trim)移除两端空白字符
  • 如果直接修改变量相等,content=trim(file_get_contents($flag)); 会将content覆盖,所以把flag值设置为null,使此条语句失效,所以?flag=&gift=(此应为正确解答,但实际结果为只需传?gift= 即可,两者一起传反而不行)

unr

 <?php
include("./flag.php");

class one {
    public $object;

    public function MeMeMe() {
        array_walk($this, function($fn, $prev){
            if ($fn[0] === "CTF" && $prev === "twotwo") {
                global $talk;
                echo "$talk"."</br>";
                global $flag;
                echo $flag;
            }
        });
    }

    public function __destruct() {
        @$this->object->add();
    }

    public function __toString() {
        return $this->object->string;
    }
}

class second {
    protected $filename;

    protected function addMe() {
        return "Wow you have True".$this->filename;
    }

    public function __call($func, $args) {
        call_user_func([$this, $func."Me"], $args);
    }
}

class third {
    private $string;

    public function __construct($string) {
        $this->string = $string;
    }

    public function __get($name) {
        $var = $this->$name;
        $var[$name]();
    }
}

if (isset($_POST["hack"])) {
    $a=unserialize($_POST['hack']);
    throw new Exception("You are Failed");
} else {
    highlight_file(__FILE__);
} 
  • 函数serialize()和unserialize()是序列化和反序列,将变量转换为可保存或传输的字符串的过程;反序列化就是在适当的时候把这个字符串再转化成原来的变量使用

  • throw new Exception()相当于echo,用于报错

  • array_walk()函数有不超过3个参数,array_walk(数组名(必须),函数名(必须),额外传入参数),要求对应的函数有相等的传参接口,且对应顺序如下(奇奇怪怪的对应顺序):

    a=array({1=>q,2=>w);
    function b($one,$two,$three)
    {
      echo "$one $two $three
    "; } array_walk($a,"b","c");//数组中的1(key)对应函数的two,数组中的q(key的value)对应函数的one,c对应three
  • 上述函数的一个奇怪的用法(已理解)

    ";echo $prev."
    ";}); }//$this指针在未被指向具体属性的时候就会遍历所有属性,此处只有一个属性,所有$this就是指向$key,同时$this自己表示对象(此处应实验验证) } $a=new one(); $a->aa(); ?> 输出: value key
    • 先列出魔术方法
    本题目中有这些魔术方法可以利用(总结见task6报告)
    public function __destruct()//退出one类时触发
    public function __toString()//任意类中对象当做字符串的时候会被调用。
    public function __call($func, $args)//任意类中调用了不存在的函数时触发
    public function __construct($string)//进入third类中时被调用。
    public function __get($name)//任意类中从不可访问或不存在的属性读取数据时被调用
  • 找开头结尾

    (网上的)首部 --> one::destruct() --> second::call() --> second::addMe() --> one::toString() --> third::get() --> one::MeMeMe() --> 尾部(疑问:mememe()是public的为什么不直接实例化one的mememe函数)

  • 不可见字符?gc利用?系统学习task6反序列化漏洞后重新做这道题

届ける言葉を今は育ててる
最后更新于 2024-02-07