ctfshow web 入门 php特性

web 89

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 15:38:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


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

preg_match当检测的变量是数组的时候会报错并返回0。而intval函数当传入的变量也是数组的时候,会返回1

// intval 转换数组类型时 不关心数组中的内容 只判断数组中有没有元素

// 空数组 返回 0

// 非空数组 返回 1

?num[]=1

web90

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 16:06:11
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


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($var,$base),其中var必填,base可选,这里base=0,则表示根据var开始的数字决定使用的进制: 0x或0X开头使用十六进制,0开头使用八进制,否则使用十进制。 这里===表示类型和数值必须相等,我们可以使用4476的八进制或十六进制绕过检测。 paylod:num=010574或num=0x117c

可以使用科学计数法进行绕过 ?num=4476.0 /** intval('4476.0')=4476 小数点
intval('+4476.0')=4476 正负号 intval('4476e0')=4476 科学计数法 intval('0x117c')=4476 16进制 intval('010574')=4476 8进制 intval(' 010574')=4476 8进制+空格 **/

有关这个函数intval(str,int)的讲解: 关于他的使用,当你使用 intval($_GET['num'], 0) 时,PHP 将对字符串 4476# 进行整数转换,PHP 的 intval 函数会从字符串的开始部分提取有效数值,直到遇到非数字字符就停止了,所以当你传入4476的时候,只要在其后面加入特殊符号的url编码值即可绕过 示例:?num=4476%23 ?num=4476%09 等等

web94

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 16:46:19
# @link: https://ctfer.com

*/

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

num不能等于4476

会正则匹配a-z,不能出现字母,基本十六进制绕过就行不通了

strpos函数会检测匹配的字符在检测的字符串中出现的位置,第零个字符出现就返回0,题目要求为0的话就会返回no,no,no显然开头不能有零,但是字符串里其他地方必须有个零

综上,可以用八进制绕过/?num=+010754(前面的0和十六进制的0x是一个意思)

解释一下为什么呢=可以绕过,strpos匹配的时候+会占一位,所以实际0被匹配的时候是第二位了

image

也可以用小数/?num=4476.0

也可以用换行?num=4476%0a0

也可以在最前面加空格?num=%204476

web95

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 16:53:59
# @link: https://ctfer.com

*/

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

跟上题相比多了个.的绕过,小数绕过行不通了,还多了个弱比较相等,

用+010574可以绕过(因为弱比较不会识别八进制),还可以用%2010574(strpos函数会将%20自动算作一位)

web96

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 19:21:24
# @link: https://ctfer.com

*/


highlight_file(__FILE__);

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


}

拼接不行fl''ag.php

image

通配不行f?ag.php

image

转义也不行flag.php%00

image

看来只能老老实实的用目录遍历写了

./flag.php

/var/www/html/flag.php

当然php伪协议也是可以的,file协议也可以

php://filter/read=covert.base64-encode/resource=flag.php

file:///var/www/html/flag.php

web98

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 21:39:27
# @link: https://ctfer.com

*/

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

?>

这里涉及到php的三元运算符XXXX?XX:xx,如果XXXX成立,冒号前的为真,反之冒号后的为假

$_GET?$_GET=&$_POST:'flag';首先判断是否存在get参数,存在的话就将 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">$_GET</font>** 赋值为 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">$_POST</font>**引用**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">&</font>** 表示引用,即两者指向同一内存地址,修改其中一个会影响另一个),不存在的话

就是flag,但是貌似没啥意义

<font style="color:rgb(64, 64, 64);">$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';</font>首先判断GET传参的flag参数的值是否为'flag',如果是的话就把将 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">$_GET</font>** 赋值为 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">$_COOKIE</font>**引用,不存在的话就是flag,但是貌似没啥意义

<font style="color:rgb(64, 64, 64);">$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';</font>首先判断GET传参的flag参数的值是否为'flag',如果是的话就把将 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">$_GET</font>** 赋值为 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">$_SERVER</font>**引用,不存在的话就是flag,但是貌似没啥意义

<font style="color:rgb(64, 64, 64);">highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);</font>首先判断GET传参的HTTP_FLAG参数的值是否为'flag',是的话就输出flag,不是的话就输出当前php文件源码

解题思路

分析上面的代码可以看出来,只要有输入的get参数就将get方法改变为post方法(修改了get方法的地址),而第二三行代码没啥用,我们用不到,直接看第四行,如果get参数HTTP_FLAG的值为flag,就读取文件,也就是输出flag。所以思路就有了,我们通过get随便传一个参数并赋值,然后我们通过post请求传HTTP_FLAG参数并赋值为flag即可获得flag。

image

web99

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 22:36:12
# @link: https://ctfer.com

*/

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

?>

代码审计可以得到,创建一个对象赋值为空数组,然后从i=36开始一直到0x36d(877)

在每次循环中,使用 rand(1, $i) 函数生成一个 1 到 $i 之间的随机数(也就是 1~877),并将其添加到 $allow 数组的最后一个元素

检查是否设置了名为 n 的 GET 参数,并且确保它的值在 $allow 数组中。如果 $_GET['n'] 的值在 $allow 数组中,将 $_POST['content'] 的内容写入文件 $_GET['n'] 中。

接下来我们先看一个关于 in_array 函数的小测试:

<?php
$allow = [1,2,3];
$a = '2.php'
echo in_array($a,$allow)
?>

**结论:in_array() 函数存在弱比较的漏洞,如果没有设置第三个参数,in_array() 函数在比较时默认是弱类型比较,这意味着它会进行自动类型转换。例如数组中的元素是整数,而搜索的值是字符串,PHP 会尝试将字符串转换为整数来进行比较。比如上面字符串类型的 1.php 就自动转换为了整数 1,也就符合在数组中的条件。**

关于 in_array() 函数的参数和用法:

image
因此我们的文件名的数字只要符合在 1~877 之间,理论上来说就都可以写入一句话木马。

保险起见我们使用大概率会出现的,比如 1 ,多试几次。

写入一句话木马

get:

:::info
?n=1.php

:::

image

post:

:::info
?content=

:::

image

页面为空白说明成功写入

image

image

posted @ 2025-09-19 20:39  朱迪Judy  阅读(12)  评论(0)    收藏  举报