phar反序列化及一些场景 旨在对phar
反序列化做一个归纳总结
介绍 什么是phar伪协议?
属于php伪协议的一种,phar协议的作用就是归档
简单来说phar就是php压缩文档。它可以把多个文件归档到同一个文件中,而且不经过解压就能被 php 访问并执行
phar 文件包在生成时会以序列化的形式存储用户自定义的 meta-data
phar文件的格式 翻阅手册可以知道,phar
由四个部分组成,分别是stub、manifest describing the contents、 the file contents、 [optional] a signature for verifying Phar integrity (phar file format only)
,以下是对详细的介绍:
stub
:phar
文件标识,格式为 xxx<?php xxx; __HALT_COMPILER();?>
manifest
:压缩文件的属性等信息,以序列化存储
contents
:压缩文件的内容
signature
:签名,放在文件末尾
注意:
文件标识,必须以__HALT_COMPILER();?>
结尾,但前面的内容没有限制,也就是说我们可以轻易伪造一个图片文件或者pdf
文件来绕过一些上传限制
我们所需的攻击利用点meta-data
序列化信息也在这第二部分中
结合之前的测试,除了上传文件外,文件写入也是可以利用的点,但是需要注意,文件写入很多时候并不是完全可控,但是只要写入最后面的内容可控即可,像那些加了\n
的是不可以利用的
相关函数 网上其实很多,但是不全而且存在错误,经测试都是闹着玩
写个脚本自己跑一下
首先获取到所有的php
内置函数
1 2 3 4 5 6 7 8 9 10 <?php $a = array_values(get_defined_functions())[0 ];$fp = fopen("aaa.txt" ,"a" );foreach ($a as $value ){ $c = $value ."\n" ; fwrite($fp ,$c ); } fclose($fp );
自动化测试
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 import requests content = '' '<?php namespace app\controller; use app\BaseController; class Index extends BaseController { public function test() { function_location } } ' '' path = "E:/phpstudy/directory/phpstudy_pro/WWW\www.tp6.com/app/controller/Index.php" address = "http://127.0.0.1:89/index.php/index/test" def write_file(file_name, contents): with open(file_name, "w" ) as f: f.write(contents) f.close() def func1(func1): str1 = "function('phar://test.phar/test.txt');" str1 = str1.replace("function" , func1) str2 = content.replace("function_location" , str1) write_file(path, str2) try : rsp = requests.get(address, timeout=2 ) except: return False if "api-ms-win-crt-environment-l1-1-0.dll" in str(rsp.text): return True else : return False def func2(func2): str1 = "function('phar://test.phar/test.txt','phar://aaaa.txt');" str1 = str1.replace("function" , func2) str2 = content.replace("function_location" , str1) write_file(path, str2) try : rsp = requests.get(address, timeout=2 ) except: return False if "api-ms-win-crt-environment-l1-1-0.dll" in str(rsp.text): return True else : return False def func3(func3): str1 = "function('phar://test.phar/test.txt','phar://aaaa.txt','phar://aaaa.txt');" str1 = str1.replace("function" , func3) str2 = content.replace("function_location" , str1) write_file(path, str2) try : rsp = requests.get(address, timeout=2 ) except: return False if "api-ms-win-crt-environment-l1-1-0.dll" in str(rsp.text): return True else : return False def func4(func4): str1 = "function('phar://test.phar/test.txt','phar://aaaa.txt','phar://aaaa.txt','phar://aaaa.txt');" str1 = str1.replace("function" , func4) str2 = content.replace("function_location" , str1) write_file(path, str2) try : rsp = requests.get(address, timeout=2 ) except: return False if "api-ms-win-crt-environment-l1-1-0.dll" in str(rsp.text): return True else : return False with open("aaa.txt" , "r" ) as f1: functions = f1.readlines() f1.close() result_list = [] i = 1 for func in functions: func_text = func.replace("\n" , "" ) print (str(i) + "==>" + func_text) if func1(func_text): func_text = func_text + "->1" result_list.append(func_text) i = i + 1 elif func2(func_text): func_text = func_text + "->2" result_list.append(func_text) i = i + 1 elif func3(func_text): func_text = func_text + "->3" result_list.append(func_text) i = i + 1 elif func4(func_text): func_text = func_text + "->4" result_list.append(func_text) i = i + 1 else : i = i + 1 pass with open("result.txt" , "a" ) as f2: for result in result_list: result_f = result + "\n" f2.write(result_f) f2.close() print (result_list)
得出简单的可以触发反序列化的函数如下
测试结果,目前测试到的总共是65
个
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 56 57 58 59 60 61 62 63 64 65 spl_autoload->1 sha1_file->1 md5_file->1 hash_file->2-2 hash_hmac_file->3-2 hash_update_file getimagesize->1 unlink->1 highlight_file->1 show_source->1 php_strip_whitespace->1 parse_ini_file->1 readfile->1 rmdir->1 fopen->2 mkdir->1 rename->2 copy->2 file->1 file_get_contents->1 file_put_contents->2 get_meta_tags->1 opendir->1 dir->1 scandir->1 fileatime->1 filectime->1 filegroup->1 fileinode->1 filemtime->1 fileowner->1 fileperms->1 filesize->1 filetype->1 finfo_file->2-2 file_exists->1 is_writable->1 is_writeable->1 is_readable->1 is_executable->1 is_file->1 is_dir->1 is_link->1 stat->1 lstat->1 touch->1 readgzfile->1 gzopen->2 gzfile->1 xmlwriter_open_uri->1 mime_content_type->1 imagecreatefrompng->1 imagecreatefromwebp->1 imagecreatefromgif->1 imagecreatefromjpeg->1 imagecreatefromwbmp->1 imagecreatefromxbm->1 imagecreatefromxpm->1 imagecreatefromgd->1 imagecreatefromgd2->1 imagecreatefrombmp->1 imageloadfont->1 exif_read_data->1 exif_thumbnail->1 exif_imagetype->1
这里有一部分是后续手动加上去的
比如hash_hmac_file->3-2
,表示需要三个参数,第二个参数触发phar
其他 zip
这里一开始还是比较奇怪的,open
有些类似fopen
,为什么不能触发呢
1 2 3 $zip = new ZipArchive();$res = $zip ->open('c.zip' );$zip ->extractTo('phar://test.phar/test' );
Postgres
1 2 3 <?php $pdo = new PDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s" , "127.0.0.1" , "postgres" , "sx" , "123456" ));@$pdo ->pgsqlCopyFromFile('aa' , 'phar://test.phar/aa' );
mysql
这里可以发散一下重装漏洞,配合mysql
的任意读取,不知道能不能行,暂未测试
1 2 3 4 5 6 7 8 9 10 11 <?php class A { public $s = '' ; public function __wakeup ( ) { system($this ->s); } } $m = mysqli_init();mysqli_options($m , MYSQLI_OPT_LOCAL_INFILE, true ); $s = mysqli_real_connect($m , 'localhost' , 'root' , '123456' , 'easyweb' , 3306 );$p = mysqli_query($m , 'LOAD DATA LOCAL INFILE \'phar://test.phar/test\' INTO TABLE a LINES TERMINATED BY \'\r\n\' IGNORE 1 LINES;' );
扩展 常规场景及利用不在多说
Trick 过滤开头是phar
协议的绕过,还有就是用其他协议封装,不一定支持,php
协议还好
大写PHAR
,这个协议是不分大小写的
compress.bzip2://phar://
compress.zlib://phar:///
php://filter/resource=phar://
无文件上传 针对场景
windows
无法完整的上传文件
反序列化漏洞未授权访问,但是上传需要权限
见《基于无上传的Phar
反序列化的利用》
其他 又遇到如下的情况
在loadFromFile
中触发反序列化
但是前面有个realpath
windows
首先明确的是,这个函数类似于file_exists
在windows
下不受不存在的路径的限制
在linux
下受不存在的路径的限制
结合phar
反序列化的小特点
只要路径中存在pahr
文件的路径,在文件之后,无论添加什么字符都可以触发反序列化,包括但不限于../
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php namespace app \controller ;use app \BaseController ;class Index extends BaseController { public function test ( ) { if (@realpath("phar://test.phar/test.txt/../../../test.phar" )){ file_get_contents("phar://test.phar/test.txt/../../../test.phar" ); } } }
虽然不再像之前那样不报错了
但是并不影响反序列化的触发
end