题目:
<?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...
源码分析
highlight_file(__FILE__);
这行代码将当前 PHP 文件的内容高亮显示。通常用于代码审计或教学
$key1 = 0;
$key2 = 0;
这里定义了两个变量 $key1 和 $key2 并初始化为 0。这两个变量将用于后续的条件检查
$a = $_GET['a'];
$b = $_GET['b'];
从 URL 的 GET 参数中获取 a 和 b 的值。
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
...
}else{
die("Emmm...");
}
如果 $a 存在,其整数值大于 6000000,且长度小于等于 3,则进入第一个条件块。否则,输出 "Emmm..." 并终止脚本。
isset()
isset()
是一个语言结构,用于检测变量是否已设置并且非 NULL。如果变量存在并且值不是 NULL,则返回 TRUE,否则返回 FALSE。
intval()
intval()
函数用于获取变量的整数值。它尝试将变量转换为整数类型。如果变量是字符串,它会解析字符串直到遇到一个非数字的字符为止,并返回解析到的数字。如果变量是一个布尔值,则返回 1(对于 TRUE)或 0(对于 FALSE)。如果变量是 NULL,则返回 0。如果变量不存在,则返回 0。
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
$key1 = 1;
}else{
die("Emmm...再想想");
}
如果 $b 存在,并且其 MD5 哈希值的最后 6 个字符是 '8b184b',则将 $key1 设置为 1。否则,输出 "Emmm...再想想" 并终止脚本。
substr(string $string, int $start, int $length = null): string
$string 是要从中提取子串的原始字符串。
$start 是子串开始的位置。如果是正数,则从字符串的开头开始计数;如果是负数,则从字符串的结尾开始计数。
$length 是可选参数,表示子串的长度。如果省略或设置为 null,则返回从 $start 开始到字符串末尾的所有字符。
在表达式 substr(md5($b), -6, 6) 中:
md5($b) 首先计算变量 $b 的 MD5 哈希值。MD5 是一种常用的哈希算法,它接受一个字符串作为输入,并返回一个固定长度的 32 字符长的十六进制数字字符串。
substr(md5($b), -6, 6) 然后从 MD5 哈希值的最后一个字符开始,向前取 6 个字符作为子串。这里 -6 作为 $start 参数意味着从字符串末尾向前数第 6 个位置开始。然而,$length 参数也被设置为 6,这意味着即使从倒数第 6 个位置开始,实际上最多也只能取到字符串的末尾,因为不可能从某个位置开始取超过该位置到字符串末尾的字符数。
因此,这个表达式实际上是从 MD5 哈希值的最后 6 个字符中取子串。由于 $length 是 6,且 $start 也是从倒数第 6 个位置开始,所以实际上它直接返回了 MD5 哈希值的最后 6 个字符。
$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
...
}else{
die("no");
}
首先,尝试将 GET 参数 c 解码为 JSON 数组。然后,检查 $c 是否为数组,$c["m"] 是否为非数字,并且 $c["m"] 的值是否大于 2022。如果所有条件都满足,则进入条件块。否则,输出 "no" 并终止脚本
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
...
}else{
die("no hack");
}
检查 $c["n"] 是否为数组,其长度是否为 2,以及 $c["n"][0] 是否也为数组。如果满足这些条件,则进入条件块。否则,输出 "no hack" 并终止脚本。
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
首先,使用 array_search 函数查找 $c["n"] 中是否存在 "DGGJ"。如果找不到,输出 "no..." 并终止脚本。然后,遍历 $c["n"],如果找到值为 "DGGJ" 的元素,输出 "no......" 并终止脚本。如果所有这些检查都通过,将 $key2 设置为 1。
$d === false ? die("no...") : NULL;
这行代码做了以下事情:
检查变量 $d
是否严格等于(===
)false
。这里的严格等于不仅比较值,还比较变量的类型。如果 $d
严格等于 false
,则执行 die("no...")
。这会导致脚本终止,并输出字符串 "no..."。如果 $d
不严格等于 false
,则执行 NULL
。在这种情况下,实际上什么都不会发生,因为 NULL
在这里是一个无操作(no-op)。
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
foreach($c["n"] as $key=>$val):
这行代码遍历 $c["n"] 数组。对于数组中的每个元素,$key 会被赋值为当前元素的键,$val 会被赋值为当前元素的值。
$val==="DGGJ"?die("no......"):NULL;:
对于数组中的每个 $val 值,代码检查它是否严格等于(===)字符串 "DGGJ"。
如果 $val 严格等于 "DGGJ",则执行 die("no......")。这会导致脚本立即终止,并输出字符串 "no......"。
如果 $val 不严格等于 "DGGJ",则执行 NULL。这实际上是一个无操作(no-op),因为 NULL 在这里不会产生任何效果。
$key2 = 1;:
这行代码在 foreach 循环外部,将变量 $key2 的值设置为 1。
综上所述,这段代码的主要目的是检查 $c["n"] 数组中的每个值,如果找到任何值严格等于 "DGGJ",则立即终止脚本并输出 "no......"。如果数组中没有值严格等于 "DGGJ",则脚本会继续执行,并将 $key2 设置为 1。
需要注意的是,尽管在 foreach 循环中使用了 $key 变量,但在这段代码中并没有对 $key 进行任何操作或使用。如果 $key 的值对于后续代码不重要,那么它可能是不必要的。此外,$key2 的赋值是在循环之外进行的,这意味着无论循环中的情况如何,$key2 的值最终都会是 1。
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}
如果 $key1 和 $key2 都为 1,则包含 "Hgfks.php" 文件,并输出 "You're right" 和 $flag 变量的值。
CTF_Web:php弱类型绕过与md5碰撞 - 简书 (jianshu.com)
playload构造:
a要大于6000000,且a的长度要小于等于3,科学计数法a=9e9(9*10^9)
bMD5加密后字符串的后六位等于“8b184b”
直接上脚本
import hashlib def md5_suffix_match(number, suffix): """检查给定数字的MD5哈希值是否以指定的后缀结尾""" md5_hash = hashlib.md5(str(number).encode('utf-8')).hexdigest() return md5_hash[-6:] == suffix def find_number_with_md5_suffix(start, end, suffix): """在指定范围内查找具有指定MD5后缀的数字""" for number in range(start, end + 1): if md5_suffix_match(number, suffix): return number return None # 如果没有找到匹配项,则返回None # 设置你想要的后六位 suffix = '8b184b' # 设置搜索范围(可以根据需要调整) start = 1 # 开始搜索的数字 end = 10**9 # 结束搜索的数字(可以根据需要设置更大的值,但要注意时间限制) # 尝试找到符合条件的数字 result = find_number_with_md5_suffix(start, end, suffix) if result is not None: print(f"Found number: {result}, with MD5 suffix: {suffix}") else: print("No number found with the specified MD5 suffix in the given range.")
跑出来53724
c要是json格式的字符串,而且key为m的value不能为数字而且要大于数字2022
当字符串与数字相比会把字符串强制转换成整型
如"123abc"转换成123
"abc"会转换成0
跟==和===一样的
所以m为"9999abc"
n是一个包含二个元素的数组
先假设c=[[1],[2]]因为之前的下面这段代码
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
互相矛盾如果有DGGJ不行
只能从array_search()下手
在 PHP 中,array_search()
函数用于在数组中搜索一个特定的值,并返回对应的键名。如果找不到该值,则返回 false
。这个函数特别适用于关联数组,但也可以用于索引数组。
函数的语法如下:
php
array_search(value, array, strict) |
value
:必需。指定要搜索的值。array
:必需。指定要搜索的数组。strict
:可选。如果设置为true
,则使用严格比较(类型和值都必须匹配)。默认是false
,表示使用非严格比较。
这里默认false,在 PHP 中,array_search()
函数使用弱比较(非严格比较)时,会尝试将不同类型的值进行类型转换以便进行比较。这种类型转换在某些情况下可能会导致意料之外的结果。
当你使用 array_search()
函数并传入一个整数(如 0
)和一个包含字符串的数组时,PHP 的弱比较机制会尝试将整数和字符串之间进行类型转换和比较。由于字符串 'DGGJ'
在转换为数字时会被解释为 0
(因为字符串以非数字字符开头,所以其数字值被认为是 0
),整数 0
和字符串 'DGGJ'
在弱比较下被认为是相等的。
这种行为是由于 PHP 的类型转换规则导致的。在 PHP 中,当使用 ==
运算符进行比较时,如果操作数类型不同,PHP 会尝试将它们转换为相同的类型,然后再进行比较。对于字符串和数字之间的比较,PHP 会尝试将字符串转换为一个数字。如果字符串以一个或多个有效的数字字符开头,那么这些数字字符将被解析为数字值。如果字符串以非数字字符开头,那么整个字符串将被解析为 0
。
因此,在 array_search(0, array('DGGJ'))
的情况下,'DGGJ'
字符串被转换为数字 0
,然后 0
和 0
被认为是相等的,所以函数返回了 'DGGJ'
在数组中的键名(如果存在的话)。
所以n=[[1],0]
c={"m":"9999abc","n":[[1],0]}
所以playload
/?a=1e9&b=53724&c={"m":"9999abc","n":[[1],0]}