
Struts2の脆弱性S2-045, S2-055 および Jackson の脆弱性 CVE-2017-7525, CVE-2017-15095 の調査報告
We have published a summarized article that condenses the key points for easier reading. Recommended for those who want to get an overview first or don't have much time.
A security update for Struts2 was released on December 1, 2017. Before its release, discussions circulated on the mailing list regarding a related vulnerability in Jackson (a popular JSON library for Java). As the author uses Jackson in internal systems and tools, they were also curious about the specific details.
The actual release addressed the following two security issues. Only S2-055 is affected by the vulnerability in jackson-databind, which is a component of Jackson.
It appears that the REST plugin previously included handlers for both JSON-lib and Jackson, allowing users to choose between them. The full scope of this fix likely involves switching the default handler to Jackson in S2-054, and then updating Jackson to the latest version in S2-055.
So, what exactly is CVE-2017-7525? Since the author typically uses Jackson for JSON processing in Java, this article documents an investigation into this issue conducted over the weekend of December 2nd and 3rd.
Author's environment used for sample code verification:
Adam Caudill's blog features an explanation of CVE-2017-7525.
In the author's own words, jackson-databind provides functionality (ObjectMapper class) to map JSON to Java objects.
By calling ObjectMapper.enableDefaultTyping(), it becomes possible to map using class names embedded directly within the JSON.
Some may have felt a sense of foreboding at the ability to specify class names from the input JSON, and indeed, that bad feeling proved accurate with CVE-2017-7525.
Before explaining the vulnerability, we will first describe why such a feature was implemented in the first place.
Please refer to the following sample code for the basic usage of deserialization with jackson-databind. (This article uses Groovy for Jackson sample code. It is convenient that you can easily switch the jackson-databind version using @Grab.)
In the above sample code, the "animal" key is simply mapped directly to the Animal class. So, how would it work in a case like the following?```java class Zoo { Animal animal; }
abstract class Animal { String name; protected Animal() { } }
class Dog extends Animal { double barkVolume; Dog() { } }
class Cat extends Animal { boolean likesCream; int lives; Cat() { } }
With this configuration, the value of the "animal" key can refer to either the Dog class or the Cat class, resulting in two possible types. Therefore, additional information is required to specify which class should be used for mapping.
To address this, jackson-databind includes a built-in feature that allows embedding the target class name within the JSON.
For example, as shown below, you can convert the value of the "animal" key into an array and specify the class name as the first element.```
{"animal":["Dog",{"name":"dog1","barkVolume":1.2}]}
これにより ObjectMapper.readValue() は "animal" キーの中身が Dog クラスだと認識してマッピングを行います。
もちろん、そのままでは "animal" キーの中身がもともと配列だったのか、jackson-databind独自のクラス名情報が含まれたものなのか、判別できません。
これを切り替えるのが ObjectMapper.enableDefaultTyping() メソッドになります。
他にも @JsonTypeInfo アノテーションをクラスに定義する方法もあります。詳細は以下のJacksonドキュメントを参照してください。
実際に ObjectMapper.enableDefaultTyping() メソッドを使ったサンプルコードを次に示します。
以上見てきたように、クラス名に続いてそのプロパティをJSONで与えることで、ある程度制限はあるものの、任意のクラスを任意のプロパティで生成することが可能となります。 これを悪用したのがCVE-2017-7525の脆弱性で、きっかけとなったのは恐らく次のレポートと思われます。
JacksonなどJavaでよく使われているserialize/deserializeライブラリについて、クラス名などの操作で任意コード実行につながる危険性がレポートされており、実際にどのようなクラスが危険か具体的なクラス名がリストアップされています。
これを受けてのものか分かりませんが、日付的には上記リポジトリの1st commitの直後に、jackson-databind で以下のIssueが立てられ、対応が始まりました。
実際にこの脆弱性を突くようなJSONデータとJavaコードはどのようなものでしょうか? このIssueで対応された jackson-databind 2.8.9 のテストコードにヒントがあります :
このテストコードを元に、動作確認できるよう調整したサンプルコードを次に示します。