Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095 — Struts2 취약성 S2-045, S2-055 및 Jackson 취약성 CVE-2017-7525, CVE-2017-15095 조사 보고 | Kitploit
도구/GitHubGitHub/secureskytechnology/study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095
Vulnerability AnalysisCode AnalysisWeb Application ExploitationPapers & ResearchLearning & EducationArchived
GitHubsecureskytechnology/study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095

study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095

Struts2 취약성 S2-045, S2-055 및 Jackson 취약성 CVE-2017-7525, CVE-2017-15095 조사 보고

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
저장소 보기
106218년 전Kitploit 검토 완료

Struts2의 취약성 S2-054, S2-055 및 Jackson의 취약성 CVE-2017-7525, CVE-2017-15095 조사 보고

요점만 정리하여 읽기 쉽게 만든 요약 기사를 공개했습니다. 먼저 개요를 알고 싶거나 시간이 없는 분들께 추천합니다.

  • SSTtechlog 08 S2-054, S2-055 및 jackson-databind의 취약성 CVE-2017-7525, CVE-2017-15095에 대해 | SST 주식회사 시큐어스카이 테크놀로지
    • https://www.securesky-tech.com/column/techlog/08.html

2017년 12월 1일에 Struts2의 보안 업데이트가 공개되었습니다. 공개 전부터 Jackson(Java에서 인기 있는 JSON 라이브러리)의 취약성이 관련되어 있다는 이야기가 메일링 리스트에 흘러, 사내 시스템이나 도구에서 Jackson을 사용하고 있는 필자도 구체적으로 어떤 내용인지 신경 쓰고 있었습니다.

  • https://lists.apache.org/thread.html/ed74083f2d7187e71ee5ed644c5e45ba58d0792b515d1d1cc28bfadf@%3Cdev.struts.apache.org%3E

실제로 공개된 내용으로는, 다음 2가지 보안 문제가 수정되었습니다. Jackson의 컴포넌트인 jackson-databind의 취약성이 영향을 미치는 것은 S2-055뿐입니다.

  • S2-055 : https://cwiki.apache.org/confluence/display/WW/S2-055
    • 이것이 jackson-databind의 CVE-2017-7525에 대응한 수정입니다.
    • Struts 측의 의존 관계에서 jackson-databind를 2.9.2로 업데이트했습니다. 이로서 후술할 CVE-2017-15095에도 대응합니다.
    • https://cwiki.apache.org/confluence/display/WW/Version+Notes+2.5.14.1
  • S2-054 : https://cwiki.apache.org/confluence/display/WW/S2-054
    • 이것은 REST 플러그인에서 JSON-lib(http://json-lib.sourceforge.net/)라는 오래된 JSON 라이브러리를 사용하고 있었지만, DoS 문제가 지적되어 Jackson으로 변경한 수정입니다.

REST 플러그인에서는 이전부터 JSON-lib을 사용한 handler와 Jackson을 사용한 handler가 내장되어 있어, 사용자가 선택할 수 있었던 것 같습니다. S2-054에서 기본 handler를 Jackson으로 전환하고,さらに S2-055에서 Jackson의 버전이 오래된 것을 최신으로 한 것이 이번 수정의 전모라고 생각됩니다.

그렇다면 CVE-2017-7525는 어떤 취약성일까요? 필자 자신도 평소 Java로 JSON 처리를 할 때 Jackson을 사용하고 있어, 12월 2, 3일 주말을 이용하여 이 문제를 조사해 본 것이 본 기사입니다.


샘플 코드 검증에 사용한 필자의 환경:

  • OS : Windows 10 Pro 64bit
  • Java : Oracle JDK 1.8.0_92 64bit
  • Groovy : 2.3.1

jackson-databind의 취약성 CVE-2017-7525에 대해

Adam Caudill 씨의 블로그에서 CVE-2017-7525의 해설이 공개되었습니다.

  • https://adamcaudill.com/2017/10/04/exploiting-jackson-rce-cve-2017-7525/

필자 자신의 말로 대략 정리하면, jackson-databind는 JSON을 Java 객체에 매핑하는 기능(ObjectMapper 클래스)을 제공합니다. 여기서 ObjectMapper.enableDefaultTyping()을 호출함으로써, JSON에 고유하게 내장된 클래스 이름으로 매핑하는 것이 가능해집니다. 「입력 JSON 중에서 클래스 이름을 지정 가능」이라는 시점에서 불길한 예감이 들었던 분도 있을 것이라고 생각합니다. 바로 그 나쁜 예감이 적중한 것이 CVE-2017-7525입니다.

취약성 설명에 들어가기 전에, 애초에 왜 그러한 기능이 구현되었는지 설명하겠습니다.

ObjectMapper.enableDefaultTyping() 기능에 대해

jackson-databind에 의한 deserialize의 기본적인 사용법은 다음 샘플 코드를 확인해 주십시오.(본 기사에서는 Jackson의 샘플 코드에 Groovy를 사용하고 있습니다. @Grab으로 jackson-databind의 버전을 간단히 전환할 수 있어 편리합니다.)

  • objectmapper-demo.groovy

위 샘플 코드에서는 단순히 "animal" 키가 그대로 Animal 클래스에 매핑 가능합니다. 그렇다면, 다음과 같은 경우는 어떨까요?```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() { } }

root@kitploit:~
この構成では、"animal"のキーの中身がDogクラスを指す場合と、Catクラスを指す場合の2種類が出てきてしまいます。したがって、どちらのクラスでマッピングするのか追加の情報が必要となります。

これを解決するため、jackson-databindではJSONにマッピングするクラス名を埋め込める独自処理を組み込みました。
例えば以下のように、"animal"キーの中身を配列にしてしまい、最初の要素にクラス名を指定します。```
{"animal":["Dog",{"name":"dog1","barkVolume":1.2}]}

これにより ObjectMapper.readValue() は "animal" キーの中身が Dog クラスだと認識してマッピングを行います。 もちろん、そのままでは "animal" キーの中身がもともと配列だったのか、jackson-databind独自のクラス名情報が含まれたものなのか、判別できません。 これを切り替えるのが ObjectMapper.enableDefaultTyping() メソッドになります。 他にも @JsonTypeInfo アノテーションをクラスに定義する方法もあります。詳細は以下のJacksonドキュメントを参照してください。

  • JacksonPolymorphicDeserialization
    • https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization

実際に ObjectMapper.enableDefaultTyping() メソッドを使ったサンプルコードを次に示します。

  • enable-default-type-demo.groovy

클래스명 black list 검사에 의한 CVE-2017-7525 대응

지금까지 살펴본 바와 같이, 클래스명에 이어 그 프로퍼티를 JSON으로 제공함으로써, 어느 정도 제한은 있지만 임의의 클래스를 임의의 프로퍼티로 생성하는 것이 가능합니다. 이를 악용한 것이 CVE-2017-7525 취약점이며, 계기가 된 것은 아마도 다음 보고서로 보입니다.

  • Java Unmarshaller Security - Turning your data into code execution
    • https://github.com/mbechler/marshalsec

Jackson 등 Java에서 자주 사용되는 serialize/deserialize 라이브러리에 대해, 클래스명 등의 조작으로 임의 코드 실행으로 이어질 위험이 보고되었으며, 실제로 어떤 클래스가 위험한지 구체적인 클래스명이 목록화되어 있습니다.

이에 따른 것인지는 모르겠지만, 날짜상으로는 위 저장소의 1st commit 직후, jackson-databind에서 다음 Issue가 제기되어 대응이 시작되었습니다.

  • Jackson Deserializer security vulnerability
    • https://github.com/FasterXML/jackson-databind/issues/1599

실제로 이 취약점을 찌르는 JSON 데이터와 Java 코드는 어떤 모습일까요? 이 Issue에서 대응된 jackson-databind 2.8.9의 테스트 코드에 힌트가 있습니다:

  • https://github.com/FasterXML/jackson-databind/blob/jackson-databind-2.8.9/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java

이 테스트 코드를 바탕으로, 동작 확인이 가능하도록 조정한 샘플 코드를 다음에 나타냅니다.

  • cve-2017-7525-check.groovy

@Grab 에서 2.8.9를 지정하여 실행하면 your jackson version IS SAFE to CVE-2017-7525 라고 표시됩니다. 이는 2.8.9의 수정으로 인스턴스화되는 클래스명 지정에 blacklist 검사가 추가되었기 때문입니다. 여기서 @Grab 에 2.8.8을 지정하여 실행하면 다음과 같이 출력됩니다.``` your jackson version MAY NOT BE SAFE to CVE-2017-7525 com.fasterxml.jackson.databind.JsonMappingException: N/A at [Source: { "id" : 124, "obj" : [ "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl", { "transletBytecodes" : [ "AAIAZQ==" ], "transletName" : "a.b", "outputProperties" : { } } ] } ; line: 9, column: 28] (through reference chain: Bean1599["obj"]->com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl["outputProperties"]) at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:277) (...) at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:128) Caused by: java.lang.NullPointerException at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$1.run(TemplatesImpl.java:401) at java.security.AccessController.doPrivileged(Native Method) at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.defineTransletClasses(TemplatesImpl.java:399) at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:451) at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:486) at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties(TemplatesImpl.java:507) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.fasterxml.jackson.databind.deser.impl.SetterlessProperty.deserializeAndSet(SetterlessProperty.java:116) ... 30 more null

root@kitploit:~
출력 결과 중 `at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$1.run(TemplatesImpl.java:401)` 라는 행이 있습니다.  
이 샘플 코드에서는 NullPointerException이 throw되었지만, 메서드명으로 보아 무언가 부작용을 포함한 처리가 발생하고 있음을 알 수 있습니다.

실제로 코드 실행을 성공시키는 JSON을 구성하려면 추가 조사가 필요할 것으로 보입니다.  
본 글에서는 일단 여기까지만 소개하지만, 추가 조사 글이 다른 사이트에서 공개되면 여기에도 추가하도록 하겠습니다.

그런데 중요한 black list는 어디에서 구현되어 있을까요? 다음 클래스입니다.  
* https://github.com/FasterXML/jackson-databind/blob/jackson-databind-2.8.9/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java#L51

사실 2.8.9 시점에서는 이 black list에 누락이 있었던 것으로 보입니다. 그 문제가 CVE-2017-15095입니다.

### black list를 개선한 CVE-2017-15095 대응

black list 누락 개선으로, 먼저 https://github.com/FasterXML/jackson-databind/issues/1680 에서 `s.add("com.sun.rowset.JdbcRowSetImpl");`가 추가되었습니다.  
일단 이것으로 2.9.0이 릴리스된 후, 추가로 https://github.com/FasterXML/jackson-databind/issues/1737 에서 다음과 같은 black list 체크가 추가되었습니다.```java
// [databind#1737]; JDK provided
s.add("java.util.logging.FileHandler");
s.add("java.rmi.server.UnicastRemoteObject");
// [databind#1737]; 3rd party
s.add("org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor");
s.add("org.springframework.beans.factory.config.PropertyPathFactoryBean");
s.add("com.mchange.v2.c3p0.JndiRefForwardingDataSource");
s.add("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource");

이로써 2.8.10 / 2.9.1이 릴리스되었으며, CVE-2017-15095에 대한 대응이 완료되었습니다.

2.8.10에서의 black list 동작을 확인하는 테스트 코드:

  • https://github.com/FasterXML/jackson-databind/blob/jackson-databind-2.8.10/src/test/java/com/fasterxml/jackson/databind/interop/IllegalTypesCheckTest.java#L57

이 테스트 코드를 바탕으로 동작을 확인할 수 있도록 조정한 샘플 코드를 아래에 나타냅니다.

  • cve-2017-15095-check.groovy

대응이 완료된 것으로 알려진 2.8.10을 @Grab으로 지정하여 실행해 보면, your jackson version IS SAFE to CVE-2017-15095라고 표시됩니다. 계속해서, black list 개선 전의 2.8.9를 @Grab으로 지정하여 실행하면, 다음이 출력됩니다.``` your jackson version MAY NOT BE SAFE to CVE-2017-15095 com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.util.logging.FileHandler, problem: \tmp\foobar.txt.lck at [Source: { "v" : [ "java.util.logging.FileHandler", "/tmp/foobar.txt" ] } ; line: 5, column: 5] (through reference chain: PolyWrapper["v"]) at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:277) (...) Caused by: java.nio.file.NoSuchFileException: \tmp\foobar.txt.lck (...) at java.util.logging.FileHandler.openFiles(FileHandler.java:459) at java.util.logging.FileHandler.(FileHandler.java:292) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call1(AnnotatedConstructor.java:129) at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:318) ... 31 more null

root@kitploit:~
対応前のバージョンでは `java.util.logging.FileHandler` クラス名がblack listチェックをすり抜け、インスタンス化されることで実際にファイルをオープンを試みていることがわかります。  
対応後のバージョンでは black listチェックで捕まり、 `JsonMappingException` 例外がthrowされています。  

なお 2.8.10 時点のblacklistは以下のようになりました。`[databind#1737]` のコメントで始まっているところが、CVE-2017-15095に対応した追加のblacklistになります。  
* https://github.com/FasterXML/jackson-databind/blob/jackson-databind-2.8.10/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java#L52  

### 脆弱性の影響を受ける条件, 攻撃の実現度, アプリケーション側での対策について  

以上の結果をまとめると、jackson-databindの脆弱性の影響を受けるのは、以下のような条件が必要となります。  
1. jackson-databind 2.8.9 / 2.9.0 以下を使用中  
2. 信頼されないソースから取得したJSONについて、以下のいずれかの処理を行っている。  
   * `ObjectMapper.enableDefaultTyping()`を呼んでからdeserializeしている。  
   * `ObjectMapper.enableDefaultTyping()`は呼んでいないが、クラス宣言で `@JsonTypeInfo` アノテーションを使ってマッピングできるようにして、deserializeしている。  
   * アプリケーションコードで使っていなくても、フレームワーク側で `Accept` リクエストヘッダーやURLの拡張子に応じて自動でdeserializeする場合があります。  
3. classpath中に、Java serialize/deserializeの脆弱性で悪用される可能性のあるクラス("Gadget")を含んでいる。  
4. マッピング先のJavaクラスのメンバフィールドで、Object型などGadgetクラスを受け入れられるような型を使っている。  
   * Gadgetに使われるクラスと互換性の無い、アプリケーション固有のBeanクラスなどを型としていれば、実際にインスタンスを生成する前に型チェックのエラーで弾くことができます。(アプリケーション固有のBeanクラスそれ自体にdeserializeの脆弱性が潜んでいた場合を除く)  

本当に影響をうけるかどうかについては、`ObjectMapper` の使い方/設定状態や`@JsonTypeInfo` アノテーションの組み合わせ、さらにマッピング先のクラスのメンバフィールド等など、アプリケーション側のコードに大きく左右される状況のようです。  

また、JSON中のキー名が、deserialize先のJavaクラスに存在しない場合、Jacksonでは単に無視されます。  
そのため、攻撃を成功させるにはアプリケーションごとのJSONに合わせてカスタマイズが必要となり、複数のアプリケーションで使いまわせるような攻撃コードを作るのは非常に難しいと思われます。  

さらに条件4. について、一般的な書き方であればまず、わざわざJavaクラスのフィールドを Object 型にすることは無いと思います。  
任意のコード実行が可能になるクラスも、大概はアプリケーションで作り込むBeanクラスとは互換性が無い場合が大半と思われます。  

以上から、大規模な攻撃が発生したり、実際にこの脆弱性を悪用して被害を発生させる(=攻撃が成功する)可能性は低いと思われます。  

アプリケーション側の対応ですが、条件 3. についてはJDKに含まれるクラスも悪用可能ですので事実上対策できないものと思われます。  
そのため、基本的にはjackson-databindを最新バージョンにUPすることが対策となります。  
jackson-databindを最新バージョンにUPできない場合は、`ObjectMapper.enableDefaultTyping()` の呼び出しや`@JsonTypeInfo` アノテーションを削除して、それに依存しないような設計に改修することになります。例えば、カスタムでシリアライザを作成するなどが考えられます。  

とはいえリモート呼び出し可能なAPIとして既に使い始めている場合、おいそれとJSONフォーマットを変更できません。  
`@JsonTypeInfo` アノテーションについて、設定次第ではサブクラスを限定できるなど、プログラマが想定しているクラス名のみを受け付けられるよう設定できるようです。  
詳細は以下のドキュメントをご確認ください。  
* JacksonPolymorphicDeserialization  
  * https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization  

#### black list 対策の是非とカスタムデシリアライザの作成について  

jackson-databind 2.8.10 / 2.9.1 は、black list対策によりCVE-2017-7525, CVE-2017-15095 に対応しました。  
しかしながら、Struts2のOGNL関連の問題で明らかなようにblack list対策は万全とは言えません。  
(筆者個人としては、スキャンツールをかけるだけのいわゆる「スクリプトキディ」のレベルであればある程度実効性のある対策だと思います。)  

よって本当に抜本的な対策をするのであれば、`ObjectMapper.enableDefaultTyping()` などJSONにクラス情報を埋め込み利用する機能、それ自体を無効化する/使わないことが重要と筆者は考えます。  
では、そもそも `ObjectMapper.enableDefaultTyping()` が解決しようとしていた問題はどうすれば良いのか?  

これについて、筆者自身も正解と言える代案は用意出来ていません。  
問題の根っことしては、「マッピングするJavaクラスが曖昧なときに、信頼できないJSONに頼らずに、マッピングできること」だと思います。  
それを可能にするアプローチとしては、恐らくカスタムのデシリアライザを作るアプローチがあるのではないか、と筆者は考えます。  
カスタムのデシリアライザは、プログラムコード側でdeserializeの最中にJSONを受け取り、生成するObjectを自分で制御することが可能です。  

例えば `{"animal":{"name":"dog1","barkVolume":1.2}}` が来たら「`barkVolume`キーがあるから、これはDogクラスとしてインスタンス化する」と判断させたり、  
`{"animal":{"name":"cat1","likesCream":true,"lives":10}}` が来たら「`likesCream`キーと `lives`キーがあるから、これはCatクラスとしてインスタンス化する」とプログラムで判断させることが可能となります。  
これを使えば、JSONにわざわざクラス名を埋め込む必要が無くなります。  

個人的にはクラス名を埋め込むJSON形式は、他の言語/ライブラリとの相互運用性に支障が出る印象を受けました(他の言語やライブラリで、同様の拡張が可能なものがあれば教えてください)。  
他のシステムと相互運用を行う前提であれば、わざわざJacksonの都合にあわせて独自にクラス名を埋め込んでもらうよりは、Jackson側でカスタムのデシリアライザを作成して対応するほうが筋が良いように思います。  

他にもいくつか解決策はあると思いますので、読者の方で「こうした解決策もあるのでは」というのがありましたらご意見お寄せいただければ幸いです。  
(極端な例ですと、個別のクラスにマッピングせず、全て `Map<String, Object>` か `List<String, Object>` 形式にdeserializeするというやり方もあると思います。)  

カスタムのデシリアライザを作るための参考記事を何点か見つけましたので、英語記事になりますがリンクを貼っておきます。  
* Jackson: create a custom JSON deserializer with StdDeserializer and JsonToken classes | Dede Blog  
  * http://www.davismol.net/2015/05/20/jackson-create-a-custom-json-deserializer-with-stddeserializer-and-jsontoken-classes/  
* Getting Started with Deserialization in Jackson | Baeldung  
  * http://www.baeldung.com/jackson-deserialization  
* Custom JSON Deserialization with Jackson - DZone Integration  
  * https://dzone.com/articles/custom-json-deserialization-with-jackson  
* Building a Custom Jackson Deserializer - The Boy Wonders  
  * http://www.robinhowlett.com/blog/2015/01/01/building-a-custom-jackson-deserializer/  

jackson-databind の JavaDoc (2.8系, 2.9系):  
* https://fasterxml.github.io/jackson-databind/javadoc/2.8/  
* https://fasterxml.github.io/jackson-databind/javadoc/2.9/  

筆者の方でもカスタムのデシリアライザのサンプルコードを作ってみましたので、ヒントになれば幸いです。  
* [custom-deserializer-demo.groovy](https://github.com/SecureSkyTechnology/study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095/blob/master/custom-deserializer-demo.groovy)  

## S2-055 について  

ここまでは Jackson 自体の脆弱性について見てきました。では、実際にそれが Struts2 の REST plugin にどのように影響しているのか? Struts2 に付属している struts2-rest-showcase を使って確認してみました。  

Struts REST plugin の使い方は以下を参考にしました。  
* http://struts.apache.org/plugins/rest/  

### Struts2 REST plugin では ObjectMapper.enableDefaultTyping() を呼んでいない  

ところで、CVE-2017-7525 で実際に脆弱となる条件として、以下の条件がありました。  
* 信頼されないソースから取得したJSONについて、以下のいずれかの処理を行っている。  
  * `ObjectMapper.enableDefaultTyping()`を呼んでからdeserializeしている。  
  * `ObjectMapper.enableDefaultTyping()`は呼んでいないが、クラス宣言で `@JsonTypeInfo` アノテーションを使ってマッピングできるようにして、deserializeしている。  

Struts2 REST plugin でこれらの条件に該当するコードがあるか確認したところ、いずれも含まれていないことが確認できました。  
実際のところ、対応前の 2.5.14 の時点で、`ObjectMapper.enableDefaultTyping()`   
/ `@JsonTypeInfo` のいずれも、REST plugin は元より Struts2 のソースツリー全体をgrepしても使っているところはありませんでした。  
* https://github.com/apache/struts/tree/STRUTS_2_5_14  

Struts2 のソースツリー全体で、Jacksonの ObjectMapper を使っているのは org.apache.struts2.rest.handler.JacksonLibHandler クラスだけです。ソースコードを確認したところ、2.5.14 の時点で、たしかに `ObjectMapper.enableDefaultTyping()` は使っていません。  
* https://github.com/apache/struts/blob/STRUTS_2_5_14/plugins/rest/src/main/java/org/apache/struts2/rest/handler/JacksonLibHandler.java  
  * このJavaファイルは、2.5.14.1 でも内容は変わっていません。  

このため、CVE-2017-7525 に対して REST plugin それ自体は 2.5.14 の時点でも問題ない状況だったと思われます。  
脆弱となるのは、アプリケーション側でJSONにマッピングするクラスのフィールドに `@JsonTypeInfo` を設定した場合となります。  
よって、以下の struts2-rest-showcase を使った検証では、アプリケーション側に追加したマッピング先のフィールドで `@JsonTypeInfo` を設定し、検証しています。  

ちなみに、Struts2では JSON plugin というのもあるようです。  
* http://struts.apache.org/plugins/json/  
* JSON pluginのソースを見てみますと、pom.xml では他のJSONライブラリを依存関係に入れていません。  
* どうも、独自にJSON処理を実装しているようです。  
* https://github.com/apache/struts/tree/STRUTS_2_5_14/plugins/json  
* そのため、jackson-databind の脆弱性は JSON plugin には影響しないと思われます。  

### struts2-rest-showcase をJackson対応させる  

struts2-rest-showcase は REST plugin を使って Order クラスのCRUDを実装したサンプルです。ここに、Jacksonの脆弱性のサンプルコードで使った Zoo / Animal / Cat / Dog クラスと、それの CRUD をJSONで処理するための ZooController などを追加してみます。  

全体のソースコードは以下をご確認ください。(本記事ではJDK8でビルド・実行を確認)  
* [rest-showcase](https://github.com/SecureSkyTechnology/study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095/tree/master/rest-showcase)  

主な修正点:  
* jetty-maven-plugin による listening ポートを 18088 に変更した。(`mvn jetty:run`)  
* jackson-core, jackson-databind を依存関係に追加した。  
* Zoo, Animal(abstract), Dog, Cat クラスを追加した。サービスレイヤーとして ZooService クラスを追加。  
* 最低限のCRUDを備えた ZooController を追加。(ViewとなるJSPは省略した)  
* struts.xml でjson用のハンドラを JacksonLibHandler に変更した。  
* maven-wrapperを組み込み、JDKさえ入っていれば mvnw / mvnw.bat でそのままビルド・実行できるようにした。  

ビルドと実行:  
1. リポジトリを clone 後、rest-showcase ディレクトリにcdし、 `mvnw jetty:run` を実行します。(初回実行時はmavenのダウンロードが発生するため、数分~場合によっては10分以上待たされる場合がありますのでご注意ください)  
1. http://localhost:18088/struts2-rest-showcase/ にアクセスし、Orderの一覧が表示されれば成功です。  
1. 実行を終了するには Ctrl-C で終了できます。  
1. Javaファイルを修正したら、Ctrl-Cで終了させまた `mvnw jetty:run` を実行してください。  

curlコマンドでの動作確認: (local http proxy として localhost:8080 を通す前提)```
一覧取得:
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo"

ID指定:
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo/1"
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo/2"

削除:
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo/2" -X DELETE

리포지토리의 Zoo.java에서 animal 필드는 아래와 같이 @JsonTypeInfo가 주석 처리되어 있고, abstract class인 Animal 타입입니다.```java //@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY) public Animal animal; //public Object animal;

root@kitploit:~
여기서 POST 메서드로 JSON 요청을 전송하고 ZooController.create() 메서드를 호출해 봅니다.```
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo" -X POST -H "Content-Type: application/json" -d '{"id":"3","animal":{"name":"dog2","barkVolume":2.3}}'

그러면 다음 오류 메시지를 포함하는 예외가 발생했습니다. Animal 클래스는 abstract이므로 인스턴스를 생성할 수 없습니다.``` Can not construct instance of org.demo.rest.example.Animal: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

root@kitploit:~
그래서 `animal` 필드의 `@JsonTypeInfo` 주석 처리를 해제하여 활성화합니다. 웹 애플리케이션은 Ctrl-C로 중단하고, 다시 `mvnw jetty:run`을 실행합니다.```java
// 次のimportを忘れずに追加
import com.fasterxml.jackson.annotation.JsonTypeInfo;
//...
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)
    public Animal animal;
    //public Object animal;

이제 JSON 안에 클래스 이름을 포함시킬 수 있습니다. 다음 curl 명령어로 클래스 이름이 포함된 JSON을 POST해 봅니다.``` curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo" -X POST -H "Content-Type: application/json" -d '{"id":"3","animal":["org.demo.rest.example.Dog",{"name":"dog2","barkVolume":2.3}]}'

root@kitploit:~
→ `HTTP/1.1 201 Created`가 반환됩니다. 목록을 조회해보면 실제로 추가된 것을 확인할 수 있습니다.

### Struts2 REST plugin 2.5.14에서 CVE-2017-7525 확인

저장소의 pom.xml에서는 `<parent>`의 struts artifact에서 버전을 2.5.14로 지정했기 때문에, 그대로는 CVE-2017-7525에 취약합니다.
이를 확인하기 위해 다음 curl 명령어를 실행해 봅니다. 클래스명과 그 내용은 cve-2017-7525-check.groovy를 참고하고 있으므로, jackson-databind 2.8.8 때와 동일한 응답이 반환되면 취약하다고 간주됩니다.```
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo" -X POST -H "Content-Type: application/json" -d '{"id":"3","animal":["com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",{"transletBytecodes":["AAIAZQ=="],"transletName":"a.b","outputProperties":{}}]}'

→ 以下のレラーメッセージを含む例外がthrowされました。``` java.lang.IllegalArgumentException: Class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl not subtype of [simple type, class org.demo.rest.example.Animal]

root@kitploit:~
`animal` 필드의 타입이 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 클래스의 서브타입이 아닌 Animal 클래스이기 때문에, IllegalArgumentException이 발생한 것 같습니다.

그럼, Zoo.java의 `animal` 필드를 Object 타입으로 수정하고 다시 `mvnw jetty:run`으로 실행해 봅니다.```java
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)
    //public Animal animal;
    public Object animal;

이제 이전 curl 명령을 다시 실행하면, 다음과 같은 스택 트레이스를 포함하는 예외가 발생했습니다.``` Caused by: java.lang.NullPointerException at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$1.run(TemplatesImpl.java:401) ~[?:1.8.0_92]

root@kitploit:~
이는 cve-2017-7525-check.groovy로 검증한 때와 동일한, 취약한 경우의 예외입니다.

이상으로 Struts2 REST plugin 2.5.14에서 jackson-databind의 취약점 CVE-2017-7525의 존재를 확인할 수 있었습니다.

또한 `@JsonTypeInfo` 외에도 Object 타입을 사용해야 한다는 것도 알게 되었습니다.

다음은 필자 개인의 의견입니다만, REST API를 작성할 때의 데이터용 클래스에서 필드의 타입으로 일부러 Object 클래스나 Java 역직렬화 취약점에서 Gadget으로 사용 가능한 클래스와 호환되는 클래스를 지정하는 것은 일반적이지 않다고 생각합니다. 그렇기 때문에 실제로 공격을 성공시키는 것은 어렵지 않을까 느끼고 있습니다.

### Struts2 REST plugin 2.5.14.1에서의 대응 확인

그럼 버전 2.5.14.1에서 취약점이 수정되었는지 확인해 보겠습니다.

pom.xml의 `<parent>` artifact 버전을 2.5.14.1로 수정하고, `mvnw jetty:run`으로 재시작한 후, 이전과 동일한 curl 명령어를 실행해 봅니다.```
curl -v -x localhost:8080 -H "Accept: application/json" "http://localhost:18088/struts2-rest-showcase/zoo" -X POST -H "Content-Type: application/json" -d '{"id":"3","animal":["com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",{"transletBytecodes":["AAIAZQ=="],"transletName":"a.b","outputProperties":{}}]}'

→ 以下のエラーメッセージを含む例外が発生しました。``` com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl: Illegal type (com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl) to deserialize: prevented for security reasons

root@kitploit:~
이는 `cve-2017-7525-check.groovy`로 검증했을 때와 동일하게, 취약점이 수정된 후의 예외입니다.

Eclipse 등 Maven을 지원하는 IDE에서 열고, 의존성을 해결한 후 jackson-databind의 버전을 확인해보면 실제로 2.9.2로 되어 있음을 확인할 수 있을 것입니다. IDE가 없는 경우, `mvnw help:effective-pom`으로 최종 pom을 출력할 수 있으므로, 거기서 jackson-databind를 검색하면 version 2.9.2를 사용하고 있음을 확인할 수 있을 것입니다.

CVE-2017-15095에 대해서는 생략하지만, 이상으로 2.5.14.1에서 jackson-databind의 취약점에 대응할 수 있었음을 확인했습니다.

### S2-055의 PoC

※ 2017-12-08 추가

S2-055에 대한 조사 기사 & PoC 보고서가 공개되었습니다.
* S2-055 취약점 환경 구축 및 분석 | 녹맹과기 블로그
  * http://blog.nsfocus.net/s2-055/

Google 번역의 도움을 받아 읽어보면, 발생 조건이나 공격 실현 가능성에 대해 같은 견해인 것 같습니다.

실제로 rest-showcase를 수정하고, 계산기가 실행된 HTTP 통신의 PoC도 제시되어 있습니다.

JSON 부분만 발췌하여 우선 Jackson 단독으로 시도해본 것이 다음 샘플 코드입니다.
* [cve-2017-7525-poc.groovy](https://github.com/SecureSkyTechnology/study-struts2-s2-054_055-jackson-cve-2017-7525_cve-2017-15095/blob/master/cve-2017-7525-poc.groovy)

여기서 2.8.8을 사용하는 버전 지정으로 실행해보면, 필자의 환경에서는 다음과 같이 출력되었습니다.```
your jackson version MAY NOT BE SAFE to CVE-2017-7525
(...)
Caused by: java.lang.NullPointerException
        at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$1.run(TemplatesImpl.java:401)
(...)

cve-2017-7525-check.groovy 때와 마찬가지로, run() 메서드가 실행되면서 NullPointerException이 발생했습니다. 다만, 계산기는 실행되지 않았습니다.

추측이지만, xalan의 TemplatesImpl은 Java 내부에 있는 클래스이므로 Java 쪽에서 어떤 식으로든 수정이 이루어졌을 가능성이 있습니다. 혹은 실제 공격 성공에는 더 까다로운 조건이 필요할 수도 있습니다.

PoC 게시글 쪽에서는 검증에 사용한 Java 버전까지는 명시되어 있지 않아, 계산기를 실행시키는 데까지 도달하지 못했습니다. 추가 정보가 나오면 다시 검증해보고자 합니다.

S2-054 에 대하여

여기까지 S2-055를 발단으로 주로 Jackson의 취약점 CVE-2017-7525, CVE-2017-15095에 대해 소개했습니다. 그렇다면 다른 한쪽인 S2-054는 어떤 상황인지, 대략 조사한 결과를 정리합니다.

결론부터 말하면, 본 글 작성 시점(2017-12-03)에서는 구체적인 정보를 찾을 수 없었습니다. 취약성 유무를 검사할 수 있는 PoC 등도 찾지 못했습니다.

S2-054의 정보 공개 페이지에서는 REST plugin에서 오래된 JSON-lib 라이브러리가 사용되고 있으며, 변조된 JSON에 의해 DoS 공격이 가능하다고 설명되어 있습니다.

  • https://cwiki.apache.org/confluence/display/WW/S2-054

The REST Plugin is using an outdated JSON-lib library which is vulnerable and allow perform a DoS attack using malicious request with specially crafted JSON payload.

이 문제의 대응으로 릴리스된 2.5.14.1 페이지에서는 이에 대응하는 JIRA 티켓으로 WW-4892가 링크되어 있습니다.

  • https://cwiki.apache.org/confluence/display/WW/Version+Notes+2.5.14.1

WW-4892를 확인해 보았지만, 어디에도 DoS나 JSON-lib의 취약점에 대한 언급이 없습니다. "Description"을 읽어도 단순히 JSON-lib이 오래되어 유지보수되지 않으므로 기본 handler를 Jackson으로 변경한다는 내용만 적혀 있는 것으로 읽힙니다.

  • https://issues.apache.org/jira/browse/WW-4892

GitHub 쪽의 pullreq는 아래와 같지만, 여기서도 구체적인 JSON-lib 문제에 대한 언급이 없습니다.

  • https://github.com/apache/struts/pull/187

그래서 JSON-lib 쪽을 살펴보기로 했습니다. 공식 사이트는 아래와 같습니다.

  • http://json-lib.sourceforge.net/

또한 2017년 현재는 GitHub에서 관리되는 것으로 보입니다.

  • https://github.com/aalmiray/Json-lib

어느 쪽이 최신일까요? 본 글 작성 시점에서는 GitHub 쪽 릴리스는 없습니다. 그래서 Maven Central 리포지토리의 등록 상황을 확인해 봅니다. "json-lib"로 검색하면 몇 가지 groupId가 히트합니다.

  • http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22json-lib%22

어떤 groupId가 정답인지, 실제로 Struts2 2.5.14.1의 REST plugin의 pom.xml을 확인합니다.

  • https://github.com/apache/struts/blob/STRUTS_2_5_14_1/plugins/rest/pom.xml
  • → groupId = net.sf.json-lib, artifactId = json-lib 이었습니다.

groupId = net.sf.json-lib, artifactId = json-lib의 릴리스 버전을 살펴보면, 2010년 12월 버전 2.4가 마지막 릴리스입니다.

  • http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22net.sf.json-lib%22%20AND%20a%3A%22json-lib%22

sourceforge 쪽 페이지를 확인해 보면, 역시 여기도 2012년 12월 버전 2.4가 마지막 릴리스입니다.

  • https://sourceforge.net/projects/json-lib/files/json-lib/

실제로 GitHub 쪽에는 릴리스 태그는 찍히지 않았지만, commit 로그를 따라가면 2010년 12월에 버전 2.4의 릴리스라는 commit이 있습니다. 또한 그 이후로는 pullreq 머지는 이루어지고 있지만, 릴리스 움직임은 없었습니다.

GitHub 쪽의 Issue를 closed 포함해서 살펴보면, DoS로 이어질 만한 제목은 보이지 않습니다.

  • https://github.com/aalmiray/Json-lib/issues?utf8=%E2%9C%93&q=is%3Aissue

sourceforge 쪽의 티켓을 살펴보면, 드디어 memory leak 문제의 티켓을 발견했습니다. 모두 아직 수정되지 않은 것으로 보입니다.

  • Json-lib / Bugs / #124 memory leak in 2.2.2, not fixed correctly in 2.4
    • https://sourceforge.net/p/json-lib/bugs/124/
  • Json-lib / Bugs / #118 Possible memory leak in Tomcat
    • https://sourceforge.net/p/json-lib/bugs/118/
  • → 2.2까지에서 ThreadLocal을 사용함으로 인한 memory leak 문제가 있었고, 2.4에서 SoftReference를 사용해 대응했지만 근본적인 대응이 되지 않았다는 티켓 내용으로 읽을 수 있었습니다.

드디어 memory leak 문제가 아직 남아 있는 듯하다는 데까지는 도달했지만, 필자의 역량과 시간 관계상 그 이후 조사까지는 하지 못했습니다. 만약 실제로 이 패턴의 JSON으로 memory leak이 발생했다거나 DoS가 되었다는 구체적인 정보가 있다면, 알려주시면 매우 감사하겠습니다.

참고 : Spring Security의 대응 예(2017년 6월)

Jackson을 사용하는 OSS가 많기 때문에, 이 취약점의 영향을 받은 다른 라이브러리·프레임워크도 존재합니다. 예로 Pivotal 제품군의 Spring Security가 영향을 받아 2017년 6월에 정보 공개되었습니다.

  • CVE-2017-4995: Jackson Configuration Allows Code Execution with Unknown “Serialization Gadgets” | Security | Pivotal
    • https://pivotal.io/security/cve-2017-4995

그 외에, 예를 들어 Spring Framework 본체는 어떤지 보면, 적어도 https://pivotal.io/security/ 쪽에는 Jackson 유래의 업데이트 정보가 공개되어 있지 않습니다.

그렇다고 해서 안심할 수는 없습니다. 원래 Spring Framework에서의 Jackson은 상당히 커스터마이즈가 용이한 설계로 되어 있어, 애플리케이션 고유의 ObjectMapper를 생성하는 것도 가능합니다. Jackson의 설정/기능을 애플리케이션 쪽에서 커스터마이즈하고 있지 않은지? @JsonTypeInfo를 사용하고 있지 않은지? 등 만일을 대비해 확인하는 것이 좋습니다.

CVE-2017-7525, CVE-2017-15095의 간단한 시계열 정리

jackson-databind의 GitHub Issue/릴리스 정보나 RedHat의 bugzilla 등의 참고 링크를 시계열 순으로 정리해 보았습니다. 잘못된 점이 있으면 부담 없이 필자에게 지적·연락해 주십시오.

2017-04

  • https://github.com/FasterXML/jackson-databind/issues/1599 에서 초기 수정이 진행됨. 버전 관리 사정으로 hot-fix 취급으로 다음 버전이 릴리스됨.
    • 2.7.9.1 : 2.7.9에 대한 hot-fix
    • 2.8.8.1 : 2.8.8에 대한 hot-fix
    • 그 외 2.9.0.pr3도 릴리스됨.

2017-06

  • 그 외 수정도 포함된 2.8.9가 릴리스됨.
  • 다음 bugzilla에서 RedHat 제품 대응이 진행됨.
    • https://bugzilla.redhat.com/show_bug.cgi?id=1462702

2017-07

  • 이미 지원이 종료된 2.6계열의 2.6.7에 대해, 이 문제의 hot-fix로 2.6.7.1이 릴리스됨.
  • RedHat에서 CVE-2017-7525의 정보가 공개됨.
    • https://access.redhat.com/security/cve/CVE-2017-7525

이 시점에서의 black-list 목록은 다음과 같았다.```java s.add("org.apache.commons.collections.functors.InvokerTransformer"); s.add("org.apache.commons.collections.functors.InstantiateTransformer"); s.add("org.apache.commons.collections4.functors.InvokerTransformer"); s.add("org.apache.commons.collections4.functors.InstantiateTransformer"); s.add("org.codehaus.groovy.runtime.ConvertedClosure"); s.add("org.codehaus.groovy.runtime.MethodClosure"); s.add("org.springframework.beans.factory.ObjectFactory"); s.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); s.add("org.apache.xalan.xsltc.trax.TemplatesImpl");

root@kitploit:~
여기서, 같은 달 중에 black-list 대책의 추가 수정으로 2.9.0이 릴리스되었습니다.
* https://github.com/FasterXML/jackson-databind/issues/1680

이것이 추가되었습니다:```java
s.add("com.sun.rowset.JdbcRowSetImpl");

さらに同月中、次のIssueがオープンされる。

  • https://github.com/FasterXML/jackson-databind/issues/1737

→black-listに以下が追加され、これが 2.8.10 / 2.9.1 に取り込まれる。```java // [databind#1737]; JDK provided s.add("java.util.logging.FileHandler"); s.add("java.rmi.server.UnicastRemoteObject"); // [databind#1737]; 3rd party s.add("org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor"); s.add("org.springframework.beans.factory.config.PropertyPathFactoryBean"); s.add("com.mchange.v2.c3p0.JndiRefForwardingDataSource"); s.add("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource");

root@kitploit:~
### 2017-08

* #1680, #1737 에 대응한 2.8.10 이 릴리스되었습니다.
* 또한 8월이 되어 다음 Issue가 오픈되었고, CVE-2017-7525 로서의 대응이 포괄적으로 논의되고 있습니다.
  * https://github.com/FasterXML/jackson-databind/issues/1723
* Adam Caudill 씨의 블로그에서 CVE-2017-7525 의 exploit 해설이 공개되었습니다.
  * https://adamcaudill.com/2017/10/04/exploiting-jackson-rce-cve-2017-7525/

### 2017-09

* #1737 에 대응한 2.9.1 이 릴리스되었습니다.

### 2017-10

다음 bugzilla에서 CVE-2017-7525 수정 시점의 2.8.9 / 2.9.0 에서는 대응이 불충분하여, 새롭게 CVE-2017-15095 로서 최신의 2.8.10 / 2.9.1 의 black-list를 적용하는 작업이 시작되었습니다.
* https://bugzilla.redhat.com/show_bug.cgi?id=1506612

### 2017-11

* RedHat에서 CVE-2017-15095 의 정보가 공개되었습니다.
  * https://access.redhat.com/security/cve/cve-2017-15095

* jackson-databind 의 Issue 에서도, 아래에서 CVE-2017-15095 에 대한 대응 상황의 질의응답이 오가고 있습니다.
  * https://github.com/FasterXML/jackson-databind/issues/1847
  * 2.8.10 / 2.9.1 에서 대응이 되어 있다고 답변되었습니다.

### 2017-12

* 클라우드 측 WAF Scutum 의 개발자 블로그에서 해설 기사가 공개되었습니다.
  * https://www.scutum.jp/information/waf_tech_blog/2017/12/waf-blog-052.html
  * 이 기사에서는 2.9.3 의 black list 에서 Spring 의 클래스에 불비가 있어, 긴급하게 문제가 되지는 않지만, 어쩌면 다시 업데이트가 적용될 가능성이 있다고 합니다.
  * 또한, 이 취약점의 책임은 원래 라이브러리 측에 있었는가? 애플리케이션 측이 아닌가? 라는 점에 대해서도 논의되고 있습니다.

## 향후 Java serialize/deserialize 관련 취약점 동향에 대해

재작년~작년 정도부터 Java 의 serialize / deserialize 관련 취약점 정보가 증가하기 시작한 인상이 있습니다.
예를 들어 Spring을 포함한 Pivotal 제품의 취약점 정보는 https://pivotal.io/security/ 에서 공개되고 있는데, 실제로 2017년에 들어서 CVE-2017-4995 에 더해 다음 취약점 정보가 공개되었습니다.

* https://pivotal.io/security/cve-2017-8045
  * Spring AMQP 에서의 `org.springframework.amqp.core.Message` 의 deserialize 문제에 의한 RCE
* https://pivotal.io/security/cve-2017-8046
  * Spring Data REST 에서의 PATCH 메서드에서 JSON의 취급에 불비가 있어, RCE

RCE로 이어지기 쉬운 취약점이라는 점도 있어, 공격자/취약점 연구자 모두 Java 의 serialize/deserialize 에 주목을 모으고 있는 시기인 것으로 보입니다.
향후 몇 년간은 앞으로도 serialize/deserialize 처리에 관련된 취약점 보고가 계속될 것으로 생각됩니다.
그렇다고는 하지만 Jackson 도 그렇지만, 개발을 효율화해 주고 원격 API에 의한 데이터 교환이 당연해진 현재의 개발에서, serialize/deserialize 를 전혀 사용하지 않거나, 또는 제로부터 자작하는 것은 현실적으로 불가능, 이라는 현장이 많지 않을까요.
중요한 것은, 취약점이 공개되면 가능한 한 빨리 라이브러리를 업데이트할 수 있는 발빠른 개발 체제 및 문화를 만들어 나가는 것 아닐까, 라고 필자 개인은 생각합니다.

의존하고 있는 미들웨어·라이브러리·프레임워크의 취약성과 앞으로 어떻게 대처해 나가야 할지, 멘탈적인 부분에서 생각하는 바가 있었으므로, 이하에 감상으로서 써 보았습니다.

## 감상

S2-054, 055가 공개되고, 그것에는 Jackson의 취약성의 영향이 있다는 정보를 목격했을 때, 필자는 상당한 충격을 받았습니다.
왜냐하면, 그 며칠 전에 동료로부터 "Java에서 JSON을 파싱하는 데 추천하는 라이브러리가 있나요?"라고 물어, "Jackson이 OSS에서도 널리 쓰이고 실적이 있고, 구글링하면 많은 기사나 QA를 찾을 수 있으니 추천합니다"라고 자신만만하게 대답했었기 때문입니다.

필자 자신이, 사내의 도구 개발 등에서 Jackson을 이용하고 있어 편리함을 느끼고 있었습니다.
그러나, 그 시점에서 필자는 CVE-2017-7525를 파악하지 못했고, Jackson이 널리 OSS에서 사용되어 이용자가 많다는 것에 안심하고 있었습니다.

거기에 다른 동료가 Adam Caudill 씨의 블로그를 발견하여 CVE-2017-7525의 존재를 알려 주었는데, 스스로 이용하고 있는 Jackson을 자신만만하게 추천한 며칠 후에 Jackson의 취약성으로 Struts2가 업데이트 공개, 게다가 Jackson의 취약성 자체는 몇 달 전부터 대응되어 있었다고 하면, 보안 업계에 몸담고 있는 엔지니어로서, 자신이 사용하고 있는 라이브러리의 취약성 정보 수집을 게을리했다고 지적받아도 변명의 여지가 없습니다(실제로 그렇다고밖에 말할 수 없지만).

그 때문에 지난 며칠간 필자의 멘탈 컨디션은 최악의 상태(혈압·맥박의 상승, 손이 떨림, 슬프지 않은데 눈물이 나올 것 같음, 두근거림이 멈추지 않음, 등)로, 어떻게든 하기 위해, 우선 CVE-2017-7525가 무엇인지, Jackson은 도대체 어떤 상황인지를 먼저 아는 것부터 시작하자고, 주말을 써서 본 기사의 조사·집필에 힘썼습니다.

이 점에 대해 되돌아보면, 역시 개발에 전념하고 있으면 좀처럼 의존 라이브러리의 업데이트 정보에 신경 쓰는 것은 어렵다는 것을 절감합니다.
끊임없이 밀려오는 개발 작업 중에서, 사용하는 라이브러리 모든 기능이나 취약성 등 품질면의 완전한 조사를 하는 것은 현실적으로 불가능합니다.
한편 개발에 요구되는 기능은 점점 많아지고, 그것들을 모두 자작하는 것도 현실적으로 불가능합니다.
어느 정도는 사용하는 라이브러리를 "신뢰"하고, 개발을 효율화할 필요가 있습니다.
그렇다고는 해도 일상의 개발 작업 중에서 사용한 도구·라이브러리의 모든 업데이트 정보를 추적하고, 보안 문제의 수정이 포함되어 있지 않은지 감시하는 것도, 이것 또한 매우 어렵습니다.
물론 그 과제를 해결하기 위해, 요즘은 사용하는 도구·라이브러리를 등록함으로써 그들의 업데이트 정보를 배송해 주는 서비스가 있다는 것은 인식하고 있습니다.

이번에 자신의 멘탈 컨디션 악화로 느낀 것은, 자신 안에서 "하지 않았던 것에 대한 자책·죄책감"이 상당히 강하다는 것입니다.
"보안 엔지니어인데, 자신이 사용하던 라이브러리의 취약성 정보를 파악하지 않고 있다니..." 라는 네거티브한 꼬리표를 자신에게 붙이고 있었습니다.
보안 업계와 관계없는 일반 개발자라도,
"그때 Struts2를 제안하지 않았더라면..." 이나 "더 라이브러리/프레임워크의 라이프사이클 관리를 철저히 해야 하는데(=하지 못하고 있는 지금의 자신/상황이 불안)"
등의 후회나 불안을 마음에 품고 있는 사람도 많지 않을까요.

만약 이것들을 "과제"로 정면에서 "해결"하려고 하면, 취약성 정보를 하나하나 수집하고, 사용 예정의 라이브러리나 프레임워크의 소스 코드와 기능을 하나하나 체크하고, 데팩토 스탠다드라고 할 수 있는지 면밀히 조사하고, 운용 개시 후에도 라이프사이클 관리를 확실히 수행하게 될 것입니다.

그러나, 그런 "돌다리를 두드리고 건너는" 방식은, 요즘의 다양한 개발 현장에서 과연 가능할까요?

필자가 본 기사를 써서 생각한 것은, "하지 않았던 것/할 수 없었던 것/깨닫지 못했던 것"을 원인이나 범인으로 간주하고, 그것을 없애는 = "할 수 있게 하는 것"만을 "과제의 해결"로 하는 시대는 이미 끝난 것이 아닌가, 라는 것입니다.
그런 문화에서는 완벽한 인간이 아닌 한, 개발자는 자신의 "할 수 없었던 것, 하지 않았던 것, 깨닫지 못했던 것"에 끝없이 맞서게 될 것입니다.
완벽한 인간이 없는 이상, 아무리 멘탈이 강한 인간이 아니면 버티지 못할 것이라고 생각합니다.

소프트웨어의 보안 문제에 있어서, 실제로 피해를 발생시키는 범인은 공격자입니다. 상황을 마이너스로 만드는 것은 취약성을 악용하는 공격자입니다.

대다수의 개발자는 기본적으로 선의로 성실하게 개발에 임하고 있을 것입니다. 그 시점에서 이미 플러스 상태입니다.
"라이브러리/프레임워크의 라이프사이클 관리를 하지 않는다" "사용하고 있는 라이브러리의 취약성 정보를 수집·감시하지 않는다"는 것은 단지 하지 않았을 뿐, 플러스도 마이너스도 아닌 상태입니다.
그런데도 공격자가 존재함으로써 "하지 않은 일"이 "할 수 없었던 일/깨닫지 못했던 일"로서 마이너스가 되는 것은, 너무나도 슬픈 상황이 아닐까요?

"돌다리를 두드리고 건너는" 가치관에 대해, 현대의 일본 사회나 기업 문화가 영향을 미치고 있는 점은 부정할 수 없습니다.
실제로 SQL 인젝션 등의 취약성을 만들어 버려, 공격자에 의한 피해가 발생한 케이스도 끊이지 않고, 재판에서 개발 회사 측의 책임을 묻는 사례도 발생하고 있습니다.
업무상의 부주의로 큰 사고로 이어지는 케이스도 있습니다.

그러나, 그것들을 모두 "개발 회사/개발자"의 문제로 해 버리면, 전체로서의 IT 개발이 위축되어 버릴 것 같습니다.
정말 나쁜 것은, 취약성을 악용하는 공격자입니다.
그렇게 생각하면, "하지 않고 있다/할 수 없다/깨닫지 못하고 있다" 개발자나 개발 회사는 가해자라기보다는 오히려 피해자라고 할 수 있지 않을까요?
그렇다면 피해자에게 "하지 않고 있다/할 수 없다/깨닫지 못한 네가 나쁘다"라고 지적하는 것이 아니라, "이렇게 하면 더 안전해진다, 개선할 수 있으니 함께 노력하자"라고 함께 나아가는 것을 목적으로 따뜻한 손을 내밀면서, 서로 각자의 전문 영역에서 연마하고 활용하는 것이 중요하지 않을까, 라고 강하게 생각합니다.
그렇게 되면, 개발 회사/개발자도 안심하고 취약성 대응에 들어갈 수 있고, 또한 그 후에도 안심을 바탕으로 적극적인 개발을 전개하기 쉬워집니다.
개발 현장이 안심하고 밝게, 적극적인 개발이 증가함으로써, 결과적으로 혁신적인 성과가 늘어나고, 일본 사회가 더 풍요로워지지 않을까요.

필자 개인의 생각으로는, 장래적으로 다음과 같은 사고방식이 퍼져 나가면 좋겠다고 강하게 느꼈습니다.

* 현장의 개발자
  * "취약성이 있는 라이브러리를 사용해 버렸다", 그것 자체는 마이너스가 아니다.
  * 마이너스로 만드는 것은 취약성을 악용하는 공격자이며, 또한 그것을 마이너스로 평가하는 문화 그 자체.
  * 매일 성실하게 일에 임해 개발하고 있는 것 자체가 이미 충분히 플러스.
  * 취약성 대응은 마이너스가 된 것을 제로로 되돌리는 작업이 아니라, 일상의 개발 성과를 더 안전한 것으로 개량하기 위한 플러스의 작업.
* 개발자를 관리하는 매니저·리더, 경영층
  * "하지 않았던 것/할 수 없었던 것/깨닫지 못했던 것"을 마이너스 평가로 하지 않는다. 그런 가치관은 앞으로 점차 페이드 아웃될 것이다.
  * "하지 않았다/할 수 없었다/깨닫지 못했다" 멤버를 범인 취급하는 것을 그만둔다. 그들은, 그리고 우리 전체가 취약성을 악용하는 공격자에게 있어 피해자이며, 그 관점에서는 입장은 같다.
  * "완전한 대책"으로 해결하려는 접근을 중단한다.

개발자나 매니저·리더, 경영층까지 끌어들여 "사물의 보는 방법·받아들이는 방법을 바꾼다"는 취지가 되어 버리지만, 그것이 매우 어렵다는 것도 명백합니다.
애초에, 어떻게 바꾸면 좋을까요?
필자 스스로 정답을 제시하는 것은 힘 부족으로 할 수 없지만, 거기에 하나의 힌트가 있다고 생각합니다.
즉, 정답을 찾는 것 그 자체를, 이제 포기할 수밖에 없는 것이 아닐까, 라는 것입니다.
모든 소프트웨어 개발에는 어떤 목적이 있습니다. 그것을 안전하고, 시큐어한 방식으로 실현하기 위한 "정답"을 찾는 것은, 이제 도저히 불가능할 정도로 소프트웨어의 세계는 복잡화되어 있는 것은 아닐까요.
아마 거기에 있는 것은, 다수의 플레이어가 각자 나름의 방식으로 시행착오를 겪고, 피드백을 얻은 후 상호 정보를 교환하며, 거기서부터 더 자유롭게 분기·변화해 가는, "변하는 것"을 전제로 한 개발 패러다임이 아닐까, 라고 느끼고 있습니다.

시스템 개발에서는 한 번 만든 소프트웨어 자산을 몇 년, 길게는 수십 년에 걸쳐 사용해 나갑니다.
그러나, 웹 개발을 비롯하여 인터넷에 연결된 소프트웨어는 수개월~수년에 걸쳐 변화하는 환경에 노출되게 됩니다.
5년 전의 개발에서 사용한 라이브러리나 프레임워크가, 사용할 수 없게 되어 있을지도 모릅니다.
그렇게 되면, 처음에 만든 라이브러리/프레임워크를 "정답"으로 간주하고, 버전을 "고정"으로 하는 개발 패러다임에서는 단점이 더 많아집니다.
바깥 세상은 계속 변화하고 있기 때문에, 사용하는 라이브러리/프레임워크에 취약성이 발견되는 것이 당연한 세계입니다.
버전을 고정해 버리면 그러한 변화를 따라잡을 수 없고, 결과적으로 취약성 대응에 엄청난 정신적/물리적 비용을 지불하게 됩니다.
따라서, 앞으로는 "변화하는 것·변화에 따라잡을 수 있는 것"을 전제로 한 개발 패러다임으로 시프트하는 것이, 장기적으로는 더 나은 성과로 이어지지 않을까, 라고 강하게 느꼈습니다.

길어졌지만 이상입니다.

----
文責 : 坂本 昌彦 (研究開発部所属, 社内で使用するWebアプリケーション診断ツールの開発などに従事)
* Mail : [email protected]
* Twitter : https://twitter.com/msakamoto_sf
* Facebook: https://www.facebook.com/masahiko.sakamoto.75
* GitHub : https://github.com/msakamoto-sf

본 기사에 관한 의견·문의는 사카모토까지 부탁드립니다.
도구 다운로드