.NET高级代码审计(第二课) Json.Net反序列化漏洞

0X00 前言

Newtonsoft.Json,这是一个开源的Json.Net库,官方地址:Json.NET - Newtonsoft ,一个读写Json效率非常高的.Net库,在做开发的时候,很多数据交换都是以json格式传输的。而使用Json的时候,开发者很多时候会涉及到几个序列化对象的使用:DataContractJsonSerializer,JavaScriptSerializer 和 Json.NET即Newtonsoft.Json。大多数人都会选择性能以及通用性较好Json.NET,这个虽不是微软的类库,但却是一个开源的世界级的Json操作类库,从下面的性能对比就可以看到它的性能优点。

用它可轻松实现.Net中所有类型(对象,基本数据类型等)同Json之间的转换,在带来便捷的同时也隐藏了很大的安全隐患,在某些场景下开发者使用DeserializeObject方法序列化不安全的数据,就会造成反序列化漏洞从而实现远程RCE攻击,本文笔者从原理和代码审计的视角做了相关介绍和复现。

0X01 Json.Net序列化

在Newtonsoft.Json中使用JSONSerializer可以非常方便的实现.NET对象与Json之间的转化,JSONSerializer把.NET对象的属性名转化为Json数据中的Key,把对象的属性值转化为Json数据中的Value,如下Demo,定义TestClass对象

并有三个成员,Classname在序列化的过程中被忽略(JsonIgnore),此外实现了一个静态方法ClassMethod启动进程。 序列化过程通过创建对象实例分别给成员赋值,

用JsonConvert.SerializeObject得到序列化后的字符串

Json数据中并没有包含方法ClassMethod,因为它是静态方法,不参与实例化的过程,自然在testClass这个对象中不存在。这就是一个最简单的序列化Demo。为了尽量保证序列化过程不抛出异常,笔者引入 SerializeObject方法的第二个参数并实例化创建JsonSerializerSettings,下面列出属性

修改代码添加 TypeNameAssemblyFormatHandling.Full、TypeNameHandling.ALL

将代码改成这样后得到的testString变量值才是笔者想要的,打印的数据中带有完整的程序集名等信息。

0x02 Json.Net反序列化

2.1、反序列化用法

反序列过程就是将Json字符串转换为对象,通过创建一个新对象的方式调用JsonConvert.DeserializeObject方法实现的,传入两个参数,第一个参数需要被序列化的字符串、第二个参数设置序列化配置选项来指定JsonSerializer按照指定的类型名称处理,其中TypeNameHandling可选择的成员分为五种

默认情况下设置为TypeNameHandling.None,表示Json.NET在反序列化期间不读取或写入类型名称。具体代码可参考以下

2.2、攻击向量—ObjectDataProvider

漏洞的触发点也是在于TypeNameHandling这个枚举值,如果开发者设置为非空值、也就是对象(Objects) 、数组(Arrays) 、自动识别 (Auto) 、所有值(ALL) 的时候都会造成反序列化漏洞,为此官方文档里也标注了警告,当您的应用程序从外部源反序列化JSON时应谨慎使用TypeNameHandling。

笔者继续选择ObjectDataProvider类方便调用任意被引用类中的方法,具体有关此类的用法可以看一下《.NET高级代码审计(第一课)XmlSerializer反序列化漏洞》,首先来序列化TestClass 

指定TypeNameHandling.All、TypeNameAssemblyFormatHandling.Full后得到序列化后的Json数据

如何构造System.Diagnostics.Process序列化的Json字符串呢?笔者需要做的工作替换掉ObjectInstance的$type、MethodName的值以及MethodParameters的$type值,删除一些不需要的Member、最终得到的反序列话Json数据如下

再经过JsonConvert.DeserializeObject反序列化(注意一点指定TypeNameHandling的值一定不能是None),成功弹出计算器。

 

2.3、攻击向量—WindowsIdentity

WindowsIdentity类位于System.Security.Principal命名空间下。顾名思义,用于表示基于Windows认证的身份,认证是安全体系的第一道屏障肩负着守护着整个应用或者服务的第一道大门,此类定义了Windows身份一系列属性

对于用于表示认证类型的AuthenticationType属性来说,在工作组模式下返回NTLM。对于域模式,如果操作系统是Vista或者以后的版本,该属性返回Negotiate,表示采用SPNEGO认证协议。而对于之前的Windows版本,则该属性值为Kerberos。Groups属性返回WindowsIdentity对应的Windows帐号所在的用户组(User Group),而IsGuest则用于判断Windows帐号是否存在于Guest用户组中。IsSystem属性则表示Windows帐号是否是一个系统帐号。对于匿名登录,IIS实际上会采用一个预先指定的Windows帐号进行登录。而在这里,IsAnonymous属性就表示该WindowsIdentity对应的Windows帐号是否是匿名帐号。

2.3.1、ISerializable

跟踪定义得知继承于ClaimsIdentity类,并且实现了ISerializable接口

查看定义得知,只有一个方法GetObjectData

在.NET运行时序列化的过程中CLR提供了控制序列化数据的特性,如:OnSerializing、OnSerialized、NonSerialized等。为了对序列化数据进行完全控制,就需要实现Serialization.ISeralizable接口,这个接口只有一个方法,即 GetObjectData,第一个参数SerializationInfo包含了要为对象序列化的值的合集,传递两个参数给它:Type和IFormatterConverter,其中Type参数表示要序列化的对象全名(包括了程序集名、版本、公钥等),这点对于构造恶意的反序列化字符串至关重要

另一方面GetObjectData又调用SerializationInfo 类提供的AddValue多个重载方法来指定序列化的信息,AddValue添加的是一组<key,value> ;GetObjectData负责添加好所有必要的序列化信息。

2.3.2、ClaimsIdentity

ClaimsIdentity(声称标识)位于System.Security.Claims命名空间下,首先看下类的定义

其实就是一个个包含了claims构成的单元体,举个栗子:驾照中的“身份证号码:000000”是一个claim、持证人的“姓名: Ivan1ee”是另一个claim、这一组键值对构成了一个Identity,具有这些claims的Identity就是ClaimsIdentity,通常用在登录Cookie验证,如下代码

一般使用的场景我想已经说明白了,现在来看下类的成员有哪些,能赋值的又有哪些?参考官方文档可以看到 Lable、BootstrapContext、Actor三个属性具备了set

查阅文档可知,这几个属性的原始成员分别为actor、bootstrapContext、lable如下

ClaimsIdentity类初始化方法有两个重载,并且通过前文介绍的SerializationInfo来传入数据,最后用Deserialize反序列化数据。

追溯的过程有点像框架类的代码审计,跟踪到Deserialize方法体内,查找BootstrapContextKey才知道原来它还需要被外层base64解码后带入反序列化

2.3.3、打造Poc

回过头来想一下,如果使用GetObjectData类中的AddValue方法添加“key : System.Security.ClaimsIdentity.bootstrapContext“、”value :  base64编码后的payload“,最后实现System.Security.Principal.WindowsIdentity.ISerializable接口就能攻击成功。首先定义WindowsIdentityTest类

笔者用ysoserial生成反序列化Base64 Payload赋值给BootstrapContextKey,实现代码如下

到这步生成变量obj1的值就是一段poc,但还需改造一下,将$type值改为System.Security.Principal.WindowsIdentity完全限定名

最后改进后交给反序列化代码执行,抛出异常之前触发计算器,效果如下图

0x03 代码审计视角

从代码审计的角度其实很容易找到漏洞的污染点,通过前面几个小节的知识能发现需要满足一个关键条件非TypeNameHandling.None的枚举值都可以被反序列化,例如以下Json类

都设置成TypeNameHandling.All,攻击者只需要控制传入参数 _in便可轻松实现反序列化漏洞攻击。Github上很多的json类存在漏洞,例如下图

代码中改用了Auto这个值,只要不是None值在条件许可的情况下都可以触发漏洞,笔者相信肯定还有更多的漏洞污染点,需要大家在代码审计的过程中一起去发掘。

0x04 案例复盘

最后再通过下面案例来复盘整个过程,全程展示在VS里调试里通过反序列化漏洞弹出计算器。

1. 输入http://localhost:5651/Default Post加载value值

2. 通过JsonConvert.DeserializeObject 反序列化 ,并弹出计算器

最后附上动图

0x05 总结

Newtonsoft.Json库在实际开发中使用率还是很高的,攻击场景也较丰富,作为漏洞挖掘者可以多多关注这个点,攻击向量建议选择ObjectDataProvider,只因生成的Poc体积相对较小。最后.NET反序列化系列课程笔者会同步到 Ivan1ee (Ivan Lee) · GitHub 、前言 - .NET高级代码审计 ,后续笔者将陆续推出高质量的.NET反序列化漏洞文章,请大伙持续关注。

 

<think>好的,我现在需要详细分析用户的问题,关于反序列化漏洞的原理及防御方法。用户提到了PHP和Java的反序列化漏洞,还引用了四篇资料,我需要结合这些资料来构建答案。 首先,理解反序列化漏洞的基本概念。根据引用[1],PHP反序列化漏洞是因为反序列化过程中执行了恶意代码。而引用[2]提到,关键在于攻击者构造恶意序列化字符串,触发魔术方法或执行非法操作。引用[3]说明Java的反序列化使用ObjectInputStream的readObject方法,这可能存在类似风险。引用[4]则概括了防御方式,作者有实战经验,可能提供有效方法。 接下来,要分步骤解释原理。需要说明序列化和反序列化的过程,以及漏洞如何产生。比如在PHP中,当反序列化一个对象时,会自动调用__wakeup()或__destruct()等方法,如果这些方法中有危险操作,攻击者可能利用。Java中类似,比如重写readObject方法执行代码。需要结合引用中的例子,如引用[2]提到的魔术方法,引用[3]的Java反序列化机制。 然后,防御方法部分,需要整理各个资料的建议。引用[1]提到输入验证、禁用危险函数、最小化权限等。引用[4]可能补充使用安全库、加密签名、日志监控等。需要将这些点系统化,比如输入检查、安全配置、代码审计等。 还要确保回答结构清晰,分点说明,符合用户要求的层次结构。同时,生成的相关问题需要围绕原理、防御、具体语言(PHP/Java)、实际案例等方面,帮助用户进一步学习。 需要验证LaTeX格式是否正确,虽然这个问题可能不涉及数学公式,但如果有变量或代码,需正确使用格式。例如,类名或方法名如__wakeup()可能需要代码块标记,但根据用户示例,代码块用三个反引号包围,所以正确应用。 最后,确保引用标识正确添加到每个引用观点的末尾,例如[^1]、[^2]等,并且自然融入内容中,不打断阅读流程。</think>### 反序列化漏洞原理及防御方法 #### 一、反序列化漏洞原理 1. **序列化与反序列化定义** - **序列化**:将对象转换为可存储或传输的字节序列,例如PHP的`serialize()`或Java的`ObjectOutputStream`。 - **反序列化**:将字节序列恢复为对象,例如PHP的`unserialize()`或Java的`ObjectInputStream.readObject()`[^3][^4]。 2. **漏洞触发条件** - **危险魔术方法(PHP)**:反序列化时会自动调用`__wakeup()`、`__destruct()`等方法。若这些方法包含敏感操作(如文件操作、命令执行),攻击者可构造恶意数据触发它们[^1]。 - **Java反序列化链**:通过重写`readObject()`或利用第三方库(如Apache Commons Collections)的漏洞方法链,执行任意代码[^3]。 - **未验证输入**:直接反序列化用户输入的未经验证数据,导致恶意载荷被执行。 3. **攻击示例(PHP)** ```php class Example { public $data; function __destruct() { system($this->data); // 攻击者可控制$data执行命令 } } // 恶意序列化数据:{"data": "rm -rf /"} ``` 攻击者提交构造的序列化字符串后,反序列化会触发`__destruct()`并执行系统命令。 --- #### 二、防御方法 1. **输入验证与过滤** - 避免直接反序列化用户输入,若必须处理,需严格校验数据来源及内容格式。 2. **禁用危险方法** - 在PHP中限制或替换包含敏感操作的魔术方法(如`__wakeup()`)。 - 在Java中禁止使用存在已知漏洞的库(如旧版Apache Commons Collections)。 3. **最小化权限** 运行反序列化操作的进程应限制权限,避免以root或管理员身份执行。 4. **使用安全替代方案** - 替换原生反序列化方法,例如采用JSON或XML等更安全的格式传输数据[^4]。 - 在Java中使用`SerializationKiller`等工具拦截恶意载荷。 5. **签名与加密** 对序列化数据添加数字签名(如HMAC),确保数据未被篡改。 6. **日志与监控** 记录反序列化操作日志,监控异常行为(如频繁反序列化失败)。 --- #### 三、案例分析 - **PHP反序列化漏洞**:攻击者通过构造包含`system()`命令的序列化字符串,触发`__destruct()`删除服务器文件[^2]。 - **Java反序列化漏洞(Log4j)**:利用反序列化链加载远程恶意类,导致远程代码执行(RCE)。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dot.Net安全矩阵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值