[鹤城杯 2021]Middle magic
目录
[鹤城杯 2021]Middle magic
题目:
|
|
分析代码,要获得 flag 需要让代码执行到: echo "success:" . $flag;
大体上可以分成三层:
第一层:
|
|
preg_replace()
会把 aaa 中符合正则匹配的部分替换成后面的内容- 正则表达式:
/^(.*)level(.*)$/
会在 aaa 字符串的开头和结尾中匹配是否包含字符串 “level”^
用于匹配字符串的开头、$
用于匹配字符串的结尾.*
用于匹配任意字符任意次,不包括换行符(关键信息) 加上括号(.*)
表示为捕获组
${1}<!-- filtered -->${2}
作为替换的字符串,${1}
和${2}
分别引用正则表达式中的第一个和第二个捕获组- 在 aaa 字符串匹配是否包含字符串 “pass_the_level_1#”
示例:
1 2 3
<?php echo preg_replace('/^(.*)level(.*)$/', '${1}<!-- filtered -->${2}', "This is a high-level example."); ?>
输出的结果:
1
This is a high-<!-- filtered --> example.
乍一看这两条代码有点冲突:
|
|
由于 .*
不匹配换行符,可以借此来绕过正则匹配替换
第一层 Bypass :
GET 方式提交:aaa=%0apass_the_level_1%23
(%0a
为换行符、%23
为井字符)
第二层:
|
|
- 检查是否通过 POST 请求提交了名为 ‘admin’ 和 ‘root_pwd’ 的非空数据
- ‘admin’ 和 ‘root_pwd’ 的值不能相同,这里的比较用的是弱类型比较
- ‘admin’ 和 ‘root_pwd’ 的值经过 sha1 加密后必须相等,这里的比较用的是强类型比较
满足条件二和条件三有两种方法:
- sha1 碰撞
- 数组绕过
sha1 碰撞:
以下两个字符串不相同但经 sha1 加密后的值相同
1 2 3 4 5 6 7 8
<?php $a = urldecode("%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1"); $b = urldecode("%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1"); var_dump($a == $b); var_dump(sha1($a)===sha1($b)); ?>
运行结果:
1 2
bool(false) bool(true)
数组绕过:
sha1()
在处理数组时会报错并返回 “NULL”,而NULL===NULL
的结果为真:
1 2 3 4 5
<?php $a = array(1); @var_dump(sha1($a)); var_dump(NULL === NULL); ?>
运行结果:
1 2
NULL bool(true)
第二层 Bypass :
POST 方式提交:admin[]=1&root_pwd[]=2
第三层:
|
|
-
检查是否通过 POST 请求提交了名为 ’level_3’ 的非空数据
-
使用
json_decode()
将 JSON 格式的字符串转换为 PHP 对象。json_decode()
用于解码 JSON 字符串,返回一个对象或者关联数组,具体取决于第二个参数(默认返回对象) -
对象 level_3 的 result 属性的值与 result.php 里的 result 变量的值相等,这里的比较为弱类型比较
示例:
1 2 3 4 5
<?php $a = '{"a":1,"b":2,"c":3}'; var_dump(json_decode($a)); var_dump(json_decode($a, true)); ?>
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
object(stdClass)#1 (3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) } array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) }
由于不知道 result.php 里的 result 变量的内容,盲猜为未知字符串,可以用 0 来绕过
第二层 Bypass :
POST 方式提交:level_3={"result":0}
由于弱类型比较的特性,当字符串与数字比较时,会将字符串转化成数字,所以数字 0 等于空字符串、任何非数字开头的字符串、任何以 0 开头的字符串
示例:
1 2 3 4 5 6
<?php var_dump(0 == ''); var_dump(0 == '0'); var_dump(0 == '0a'); var_dump(0 == 'he110 w0rd!'); ?>
运行结果:
1 2 3 4
bool(true) bool(true) bool(true) bool(true)
或
欲将心事付瑶琴。知音少,弦断有谁听?