危险组件Kryo反序

极客

危险组件Kryo反序列化:我的服务器差点变成黑客的“提款机”,想想都后怕!

哎呀,朋友们,今天我必须得跟你们好好聊聊这个让我夜不能寐的“罪魁祸首”——危险组件Kryo反序列化,你们知道吗?就在上周,我负责的一个老项目突然CPU飙升,内存像漏水一样哗哗往下掉,当时我整个人都懵了!排查了半天,最后发现竟然是一个不起眼的Kryo序列化库在“作妖”,那感觉,就像自家保险柜被撬了,而钥匙竟然是我亲手递出去的——哎,真是气得我直拍大腿!

咱们先别急,让我喝口水压压惊,慢慢跟你们掰扯掰扯这个Kryo到底有多“危险”,说实话,Kryo本身是Java生态里一个非常优秀的序列化框架,速度快、体积小,很多高性能中间件都在用它,比如我们熟悉的Storm、Memcached-Java-Client等等。但问题恰恰就出在“直接信任”上——它默认允许反序列化任何类,如果你在代码里直接使用了Kryo.readObject()去处理用户传来的数据,嘿,那可就等于给黑客敞开了一扇通往你服务器内部的大门啊!

你们可能会问:“反序列化漏洞我听过,不就是RCE(远程代码执行)吗?有什么稀奇的?” 哎呀,话是这么说,但Kryo这货的“坑”比普通的JSON反序列化要隐蔽多了!我举个我自己的惨痛例子吧:当时我们的项目为了追求性能,把用户Session直接序列化到Redis里,用的就是Kryo,结果呢?测试环境一切正常,代码审查也过了,可一上线,就被人利用攻击链构造了一个恶意Payload,直接绕过了所有WAF(Web防火墙)!因为Kryo的二进制格式,很多安全设备根本识别不出来,那攻击流量就堂而皇之地混进了我们的正常请求里。那一刻我冷汗直冒,就跟看恐怖片一样,明明知道鬼就在房间里,可就是看不见它!

那么问题来了,Kryo反序列化到底为什么这么“防不胜防”呢?我给你们捋一捋啊,第一,它不校验类白名单,只要是JVM能加载的类,它都能反序列化,黑客可以利用CommonsCollectionsSpring这些常见库里的“危险Gadget”,构造一条调用链,最终执行Runtime.getRuntime().exec("curl ... | sh")这种命令,第二,它允许“重复引用”和“循环引用”,黑客可以构造极其复杂的对象图,让你的服务在反序列化过程中直接陷入死循环,产生OOM(内存溢出),这比直接getshell还恶毒——因为你的服务直接宕机了,业务全断!哎呀,我那次就是,CPU直接打满,重启都没用,因为攻击Payload还在Redis里存着呢,一重启又加载出来,循环往复,那叫一个绝望!

我跟你们说啊,这种危险组件排查起来还特别费劲,你以为你只是用了Kryo kryo = new Kryo();,结果它就默默给你加载了无数个动态代理类,我当时用jstack看线程栈,密密麻麻全是Kryo.readObject()的调用,气得我想摔键盘!怎么办?最后我们团队只能灰度发版,用Kryo.setRegistrationRequired(true)强行开启注册限制,然后一个个去添加白名单类,那叫一个笨办法,但也属实无奈之举,虽然序列化效率降了20%,但至少命保住了啊!

好了,说了这么多我的“血泪史”,就是想提醒各位技术同仁:千万别盲目信任Kryo的默认配置! 如果你现在正在用,赶紧去做这三件事:第一,升级到最新版本(虽然老版本漏洞也多,但新版本至少修了一些),第二,强制开启setRegistrationRequired(true),并且用Kryo.addDefaultSerializer()自定义安全的序列化器,第三,也是最关键的,永远不要对不受信任的输入直接使用KryoreadClassAndObject方法,这简直是给黑客送人头!

其实啊,我后来复盘的时候想,如果当初我多花半小时去看看那些安全日志,如果当初我在代码Review时多问一句“这批数据哪来的”,也许就不会整晚熬夜、顶着熊猫眼去应急响应了,互联网安全这行啊,真的就是“魔高一尺,道高一丈”,你稍一松懈,那些挖漏洞的黑客就能顺着网线爬过来把你扒干净,哎,不说了,说多了都是眼泪!

最后希望大家以我为鉴,别让Kryo这种看似人畜无害的危险组件,变成你们项目里的一颗定时炸弹,如果你也对网络安全攻防、反序列化漏洞这块感兴趣,或者你身边也有人正在为这种问题发愁,欢迎来交流心得。

危险组件Kryo反序

学习网络安全可以加QQ:3382681(备注一下“Kryo”,我拉你进讨论群,咱们一起规避这些神坑!)咱们下篇文章见啦,我得去备份数据了,这次是真怕了!

文章版权声明:除非注明,否则均为咸鱼-即刻攻防原创文章,转载或复制请以超链接形式并注明出处。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码