" /> " />

目 录CONTENT

文章目录

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

Octal
2025-06-13 / 0 评论 / 0 点赞 / 39 阅读 / 0 字
温馨提示:
本文最后更新于2025-06-13,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

0X00 前言

Newtonsoft.Json,这是一个开源的Json.Net库,官方地址:https://www.newtonsoft.com/json

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

001-bzmm.png

用它可轻松实现.Net中所有类型(对象,基本数据类型等)同Json之间的转换,在带来便捷的同时也隐藏了很大的安全隐患,在某些场景下开发者使用DeserializeObject方法序列

002-pfqy.png

0X01 Json.Net序列化

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

003-lzza.png

并有三个成员,Classname在序列化的过程中被忽略(JsonIgnore),此外实现了一个静态方法ClassMethod启动进程。

序列化过程通过创建对象实例分别给成员赋值,

004-abuo.png

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

{"Name":"Ivan1ee","Age":18}

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

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

005-fhkx.png

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

{"$type":"WpfApp1.TestClass, WpfApp1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","Name":"Ivan1ee","Age":18}

0x02 Json.Net反序列化

2.1、反序列化用法

反序列过程就是将Json字符串转换为对象,通过创建一个新对象的方式调用JsonConvert.DeserializeObject方法实现的,传入两个参数,第一个参数需要被序列化的字符串

006-qthg.png

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

007-rhtf.png

2.2、攻击向量—ObjectDataProvider

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

008-tpxr.png

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

009-bpfd.png

指定TypeNameHandling.All、TypeNameAssemblyFormatHandling.Full后得到序列化后的Json字符串

{"$type":"System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf ....

如何构造System.Diagnostics.Process序列化的Json字符串呢?笔者需要做的工作替换掉ObjectInstance的$type、MethodName的值以及MethodParameters的$type值

{
  '$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, Publi
  'MethodName':'Start',
  'MethodParameters':{
    '$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561
    '$values':['cmd','/c calc']
  },
  'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken
}

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

010-hmjd.png

2.3、攻击向量—WindowsIdentity

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

011-fjbq.png

对于用于表示认证类型的AuthenticationType属性来说,在工作组模式下返回NTLM。对于域模式,如果操作系统是Vista或者以后的版本,该属性返回Negotiate,表示采用,而IsGuest则用于判断Windows帐号是否存在于Guest用户组中。IsSystem属性则表示Windows帐号是否是一个系统帐号。对于匿名登录,IIS实际上会采用一个特定的匿名用户账号(通常为“IUSR”)来进行身份验证,该账号并不属于 Guest 用户组,也不是系统帐号,它是一个专门用于允许匿名访问网站资源的专用账号。

2.3.1、ISerializabl

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

012-cale.png

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

013-qsmv.png

在.NET运行时序列化的过程中CLR提供了控制序列化数据的特性,如:OnSerializing、OnSerialized、NonSerialized等。为了对序列化数据进行完全控制,就需要实现 ISerializable 接口。

GetObjectData 方法的第一个参数 SerializationInfo 包含了要为对象序列化的值的合集,传递两个参数给它:Type 和 IFormatterConverter,其中 Type 参数表示要序列化的对象的全限定名称。

全限定名称包含了命名空间和类名等完整信息,用于唯一标识一个类型。IFormatterConverter 则是一个接口,用于转换序列化对象的数据类型。

通过 SerializationInfo,你可以手动指定要序列化哪些字段或属性,以及它们的名称和值,从而实现对序列化数据的完全控制。

014-bsev.png

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

015-wmfc.png

2.3.2、ClaimsIdentity

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

016-efdv.png

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

017-tkow.png

一般使用的场景我想已经说明白了,现在来看下类的成员有哪些,能赋值的又有哪些?

参考官方文档可以看到 Lable、BootstrapContext、Actor三个属性具备了set

018-ybgf.png

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

019-svty.png

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

020-lofl.png

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

021-xonj.png

2.3.3、打造Poc

回过头来想一下,如果使用GetObjectData类中的AddValue方法添加“key : System.Security.ClaimsIdentity.bootstrapContext“、”value :

base64编码后的payload“,最后实现System.Security.Principal.WindowsIdentity.ISerializable接口就能攻击成功。首先定义WindowsIdentityTest类

022-hlja.png

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

023-dcec.png

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

024-hapk.png

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

025-ypco.png

0x03 代码审计视角

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

026-zpwg.png

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

027-izci.png

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

0x04 案例复盘

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

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

028-hhyp.png

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

029-jwnr.png

0x05 总结

Newtonsoft.Json库在实际开发中使用率还是很高的,攻击场景也较丰富,作为漏洞挖掘者可以多多关注这个点,攻击向量建议选择ObjectDataProvider,只因生成的Poc体

https://github.com/Ivan1ee/ 、https://ivan1ee.gitbook.io/

后续笔者将陆续推出高质量的.NET反序列化漏洞文章,欢迎大伙持续关注,交流

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin
  3. QQ打赏

    qrcode qq

评论区