一个ctf的web的题,包括了php方面知识。
第一题
下面我们就对其进行分析
show_source() 函数对文件进行语法高亮显示。
然后下面有一个include,这里说一下,引用文件的方法有两种:require 及 include。两种方式提供不同的使用弹性。
这里的include也就是引用
require 的使用方法如
require("MyRequireFile.php");
。这个函数通常放在 PHP 程序的最前面,PHP 程序在执行前,就会先读入 require 所指定引入的文件,使它变成 PHP 程序网页的一部份。常用的函数,亦可以这个方法将它引入网页中。
include 使用方法如
include("MyIncludeFile.php");
。这个函数一般是放在流程控制的处理部分中。PHP 程序网页在读到 include 的文件时,才将它读进来。这种方式,可以把程序执行时的流程简单化。他们两个的用途是完全一样的,不一定非得哪个放在最前面哪个放在中间。他们最根本的区别在于错误处理的方式不一样。
- require一个文件存在错误的话,那么程序就会中断执行了,并显示致命错误
- include一个文件存在错误的话,那么程序不会中端,而是继续执行,并显示一个警告错误。
这个题是传参get 提交,满足条件就有 flag1 flag2
首先创建了两个GET变量a和b,然后进行了三次判断,
第一次判断,同时满足a=0和a为真才能显示出flag1这个变量。
因为php语言中“==”为弱类型判断,会自动把判断转为同类型再比较,
故php中可以得到“ab” == 0 为真,所以我们在URL栏手动传参a=ab:
此时就只出了一半
然后进行b的构造
第二次进行判断 使用is_numeric函数,is_numeric() 函数用于检测变量是否为数字或数字字符串。
因此我们要返回false,避免退出程序。从而进行第三步if判断
如果b> 1234输出flag 我们构造“ ?a=a&b=1244m“ 这个后面的字母是防止第二步数字或者生成数字字符串。然后就返回flag了
第二题
原题代码:
<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
$a = $_GET['a'];
$b = $_GET['b'];
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
$key1 = 1;
}else{
die("Emmm...再想想");
}
}else{
die("Emmm...");
}
$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
}else{
die("no hack");
}
}else{
die("no");
}
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}
?> Emmm...
枯燥的同时也是学习的积累,每个函数都有独特的用处,大致意思以下:
从 $_GET中获取名为c的参数值,并将其解码为php数组。这里使用了(array)强制类型转换和 json_decode() 函数来实现。
$c=(array)json_decode(@$_GET['c']);
检查$c是否为数组,$c[“m”] 是否为一个大于2022的数值,以及$c[“n”]是否为一个包含两个元素且第一个元素为数组的数组,如果这些条件都满足,就将$key1和$key2设置为true,否则将结束脚本,并输出相应的错误信息。
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
}else{
die("no hack");
}
}else{
die("no");
}
如果$key1和$key2都为真,则包含一个名为Hgfks.php的文件,并输出一个$flag变量的消息。我们要的就是这个。
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}
根据这段代码的分析,它的主要作用是验证用户提交的参数值,并且只有在这些参数符合一定的条件时才会输出一个包含$flag变量的消息。具体来说,如果c参数为一个非空数组,m值为一个大于2022的数值,n值为一个包含两个元素且第一个元素为数组的数组,同时$c[“n”]不包含字符串”DGGJ”,那么将输出一个包含$falg变量的消息。否则将输出一些错误信息并结束脚本的执行。
开头还是一个highlight_file()函数,是php当中的一个内置函数,他表示输出一个突出显示PHP语法的文件。
找到flag ,还是include函数(程序不会中断,而是继续执行,并显示一个警告错误。)
前面调用die:(die是一种用于输出一条消息并退出当前脚本的函数,该函数是exit()函数的别名,die函数的语法是“die(status)”,其参数status规定在退出脚本之前写入的消息或状态号)
if….else 语句在条件为 true 时执行代码,在条件为 false 时执行另一段代码。
符合条件输出,不符合die函数执行一条“Emmm…再想想“消息。
分析第一个条件
(isset($a) && intval($a) > 6000000 && strlen($a) <= 3)
- Isset: 检测变量是否已设置并且非 NULL. 大于6000000 Intval: 强制类型转换,转换为整数类型 ; strlen:计算字符串的长度; 这句话判断a是非空的,转换为整数类型,且满足小于等于3
/?a=1e9
第二个
还是判断b非空, 有个函数是substr()函数;它用于返回字符串的一部分,但是应该返回多少呢,(-6,6) 真的无语,是后六位,还是MD5值,为8b184b,使用脚本,使用工具他不香吗,得到b的值为53724
脚本:
import hashlib
n = 0
while True:
m = hashlib.md5(str(n).encode('utf-8')).hexdigest()
if m[-6:] == '8b184b':
print(f"n = {n}, md5(n) = {m}")
break
n += 1
?a=1e9&b=53724
第三个
构造c,这确定是新手?
第一个判断
(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022)
json_decode:该函数主要用来将数组和对象,转换为json格式;输入的时候?
PHP is_array() 函数用来判断一个变量是否是数组,其语法格式如下:
如果参数是数组,就返回 true,否则返回 false。
is_numeric:检测字符串是否只由数字组成,如果字符串中只包括数字,就返回Ture,否则返回False
综上c是个json格式的参数,非空且不是数值类型(前面有个感叹号),且m键对应的值不为数字的同时需要满足大于2022
c的m键值后面还要做比较,因此不能直接简单构造一个is_numreric为false的字符串。例如,’123a’和一个整数作比较时,它会转换成123;’a123’和整数作比较时,由于第一个位置是a,非整数,php则规定其值为0,即变成了0和123比较
“m”:”2023a”
接下来是n:
有且仅有两个元素的数组,第一个值为数组,此处易于实现;第二个值要满足array_search(“DGGJ”, $c[“n”])返回为真,同时c[“n”]中又不能出现”DGGJ”。此处利用array_search函数在比较两者是否相等时是使用的弱类型比较。又由于”DGGJ”是既非数字字符串又非先导数字字符串的字符串,其在与数字进行比较时会转化为数字0。从而令c[‘n’]的第二个值为0。
“n”:[[6,6],0]}
综上
第三题
源码
<?php
highlight_file('index.php');
include("flag.php");
$id=$_POST['id'];
$json=json_decode($_GET['json'],true);
if ($id=="wllmNB"&&$json['x']=="wllm")
{echo $flag;}
?>
本题详细请移步:https://www.hlsiyy.love/get-post/
第四题
<?php
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里,或者直接运行根目录getflag
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (!preg_match("/[b-zA-Z_@#%^&*:{}\-\+<>\"|`;\[\]]/",$ctfshow)){
system($ctfshow);
}else{
echo("????????");
}
}
?>
- 还是if判断 isset检测非空,使用POST请求if (isset($_POST[‘ctf_show’])) { …} 判断是否存在名为ctf_show的POST参数。
- 变量$ctfshow 将ctf_show参数的值保存到$ctfshow变量中。
- 紧接着下面有一个preg_match检测:字符串的规则匹配。如果找到一个匹配,preg_match() 函数返回 1,否则返回 0,进行验证,匹配的内容就是代码里面的,如果包含则输出?????反之执行输出命令system($ctf_show);
- 但是这里为啥是:ctf_show=/???/?a??64%09/??a? 我不理解,谁能来教教老弟
So?利用通配符调用Linux系统命令来查看flag
- 在Linux系统中可以使用 ? * 等字符来正则匹配字母,星号(*)可以用来代替0个及以上任意字符,问号(?)可以用来代替1个任意字符,比如 : /???/??? => /bin/cat
然后我们按照题目描述:我们需要构成这个拿到服务器flag :ctf_show=/bin/base /flag,因此才使用上面的通配符,防止过滤
举个例子比如:ls命令 通配符的话 :/???/[:lower:]s ; /???/[l]s
参考https://blog.csdn.net/weixin_42197841/article/details/116152434