2017. 5. 7. 18:29 :: 취약점

안녕하세요. Message 입니다.

오늘 포스팅 주제는 Java와 관련된 CVE에서 자주 등장하는 ysoserial 도구에 대한 내용입니다.

CVE-2015-4852 취약점 포스팅에 포함시키려 하였으나, 생각보다 분량이 많아져서 따로 작성하게 되었네요.

현재 이슈되고 있는 내용은 아니지만, 제가 모르는 내용이 많아서 배울것이 많은 주제였습니다. 

주요 골자는 아래 웹사이트에 있는 내용을 참고하였고, 필요한 개념과 디버깅 부분을 추가하여 포스팅을 진행하겠습니다.

 

OSINT :: Understanding the ysoserial's CommonsCollections1 exploit

http://opensources.info/understanding-the-ysoserials-commonscollections1-exploit/

 

 

 

  0x00  Ready

 

 1. Y SO SERIAL? - ysoserial.jar

 ysoserial은 해외 연구원들이 프레임워크상에서의 RCE 발생과 관련된 연구 결과를 입증하기 위해 제작한 개념증명 도구입니다.

 이 툴을 이용하면 Java 프로그램에서 임의의 사용자 명령을 실행할 수 있게 해주는 페이로드를 생성할 수 있습니다.

 Java의 직렬화(Serialization)/역직렬화(Deserialization)에서 발생하는 취약점을 다루다 보니 이름에 serial이 붙은걸로 추정되지만

 Github 대문에 조커와 비슷한 이미지가 있는걸로 보아 우리가 생각하는그 대사를 노린게 분명해보입니다 ㅎㅎ

 URL : https://github.com/frohoff/ysoserial

 

 

 

 2. Gadget Chain

 ysoserial은 직렬화/역직열화 과정에서 Commons-Collections 라이브러리의 InvokerTransformer를 악용합니다.

 만약 서버의 Java Class Path에 InvokerTransformer가 필수 가젯으로 포함된 응용 프로그램이 운영되고 있을 경우 

 공격자는 ysoserial을 이용하여 얻은 가젯체인(gadget chain)을 직렬화하여 서버로 전달할 방법을 고안할 것입니다.

 (여기서 가젯체인이라는 용어가 생소하지만, OSINT에서는 Method Sequence로 설명하고 있습니다.

 시퀀스의 각 메소드를 Gadget이라 하고, 사용자의 명령(ls, wget 등..)이나 InvokerTranformer등을 하나의 Gadget으로 봅니다.)

 데이터가 서버로 성공적으로 전달되어 역직렬화 되는 과정이 진행된다면, 결과적으로 Runtime.exec() 메소드를 호출하게 됩니다.

 아래 그림은 ysoserial의 gadget chain을 표현한 그림입니다. 지금은 이해가 안되더라도 한번 쓰-윽 살펴보고 넘어갑니다.

 

 

 

 3. 필요 배경지식

 ysoserial 도구는 아래의 클래스들과 디자인 패턴등을 이용하여 페이로드를 생성합니다.

 만약 이들에 대한 이해도가 부족하다면 ysoserial 페이로드가 실행되는 매커니즘을 완전히 이해하기 힘들 수 있습니다.

 1) JDK : ① AnnotationInvocationHandler  ② Proxy  ③ Map  ④ Override  ⑤ InvocationHandler  ⑥ Runtime

 2) Commons Collections : ① LazyMap  ② Transformer  ③ ChainedTransformer  ④ InvokerTransformer

 3) ETC.. : ① Java Serialization and Deserialization  ② ObjectInputStream - readObject()

 

 

 

   0x01  Payload only Execute

 

 1. RCE 예제 실행

 OSINT의 첫번째 예제를 실행시켜보겠습니다.

 해당 코드는 CommonsCollection1 옵션으로 생성한 페이로드의 핵심 부분이며, 여기에 20~30%만 덧붙이면 온전한 페이로드가 됩니다.

 CommonsCollection1 옵션은 ysoserial을 실행시키면 가장 앞에 있는 페이로드 생성 옵션입니다. (아래 그림은 ver.0.0.2 기준)

 해당 옵션은 CVE-2015-4852 취약점에서 사용되는 페이로드이기도 합니다.

 

 

 Commons-Collection 3.2.1 버전을 다운받아 라이브러리에 추가시킨 뒤 이클립스로 실행시켜보았습니다.

 아직 동작원리는 파악되지 않지만, String배열에 할당해준 사용자 명령어("calc.exe")가 정상 실행되는군요.

 다시 한번 상기해보면 ysoserial 도구는 Runtime.exec() 메소드 호출을 통해 InvokerTransformer를 악용하여 RCE를 발생시키는 도구이므로,

 아래의 코드 역시 결국 Runtime.exec() 메소드를 호출하는 구조일겁니다.

 

 

 위의 코드를 이해하려면 getMethod() + invoke() 메소드가 어떤 기능을 수행하는지 알아야합니다.

 아래 코드는 String.class에서 getMoethod 메소드로 length() 메소드 객체를 얻어와 invoke()로 실행시키는 간단한 예제입니다.

 invoke() 메소드는 클래스의 정보를 얻어와 동적으로 메소드를 실행시킬 수 있는 자바의 리플렉션(Reflection) 기능의 핵심입니다.

 리플렉션에 대한 자세한 설명은 아래쪽에서 하겠습니다. 일단 예제의 실행 결과는 length 메소드 실행 결과와 동일한 "9" 입니다.

 

 

 

 2. 예제 분석

 아래 사진은 위의 소스코드를 좀더 가독성 있게 정리한 결과입니다. 조금더 보기 수월해졌나요?

 일단 Transformer형 배열에 ConstantTransformer, InvokerTransformer를 할당하고 ChainedTransformer 객체에 인자로 넣어주고 있습니다.

 ConstantTransformer는 말그대로 상수 객체를 반환하며, InvokerTransformer는 invoke() 메소드를 이용한 결과값을 객체로 반환합니다.

 어떻게 invoke() 메소드를 이용하여 객체를 반환하는지는 아래에서 상세하게 보겠습니다.

 

 

 이어지는 코드에서는 LazyMap의 decorate 메소드를 호출하여 ChainedTransformer를 인자로 넣어준 뒤 LazyMap.get("문자열")을 호출합니다.

 LazyMap의 API 설명을 살펴보면, Map의 객체를 decorator 패턴으로 확장/생성할 수 있도록 설계된 메소드입니다.

 get 호출시 넘긴 key 값과 매칭되는 value값이 없으면 ChainedTransformer(factory)가 해당 인자를 이용하여 새로운 객체를 생성합니다.

 이때 ChainedTransformer안에 있는 Transformer들이 가지고 있는 값들(Runtime.class, getMethod, invoke...)을 이용합니다.

 

 

 실제로 get메 소드를 호출하며 넣어준 임의의 key값인 "message"는 매칭되는 value가 없으므로

 ChainedTransformer안에 들어 있는 4개의 Transformer들의 trnasform 메소드가 객체를 생성하기 위해 연쇄적으로 호출됩니다.

 아래 소스코드를 보면 map.cotainsKey(key) == false 일때 factory.transform 메소드가 실행됨을 알 수 있습니다.

 

 

 transform 함수는 메소드의 반환값을 다음 실행되는 transform의 인자로 넘기게 되는데

 Java의 decorator 디자인 패턴과 동일하게 아래와 같이 4단계로 확장되는 방식입니다.

 디자인 패턴까지 상세하게 다룰 수 없으므로, 관련된 블로그를 소개합니다 → 데코레이터 패턴 :: http://jdm.kr/blog/78

 ①  Runtime.class  // → ConstantTransformer

 ②  Runtime.class.getMethod("getRuntime", new Class[0])  // → InvokerTransformer

 ③  Runtime.class.getMethod("getRuntime", new Class[0]).invoke(new Class[]{ Object.class, Object.class},new Object[0]))

 ④  Runtime.class.getMethod("getRuntime", new Class[0]).invoke(new Class[]{ Object.class, Object.class},new Object[0])).exec("calc.exe");

 

 

 실제로 이렇게 일렬로 쭈욱 만들어진 코드를 실행해보면 계산기(calc.exe)가 진짜(?) 실행됨을 알 수 있습니다.

 여기 까지 진햄함으로서 우리는 LazyMap에 ChainedTransformer를 인자로 넣어주고 get 함수를 호출하면

 RCE가 발생할 조건이 성립됨을 알았습니다. 관건은 타겟 서버 LazyMap의 get 함수를 어떻게 호출시키느냐 일겁니다.

 그나마 희소식은 여기까지 잘 이해했다면 ysoserial gadget chain을 핵심부분을 이해했다고 볼 수 있습니다.

 

 

 

 

 

 

  0x02  Putting it all together

 

 1. CommonsCollections1 페이로드

 CommonsCollections1 옵션으로 생성한 페이로드 + 직렬화/역직렬화까지 포함한 풀버전(?)을 살펴보겠습니다.

 생각보다 길지만, 직렬화/역직렬화를 위한 파일입출력을 제외하면 위에서 분석한 예제의 내용이 상당수 포함되어 있습니다.

 

 

 

 

 2. Main

 main에서는 위에서 공부한 내용이 포함된 객체(evilObject)를 생성합니다.

 해당 객체에 대한 자세한 내용은 아래에서 다룹니다. 

 

 

 이후에 serializeToByteArray, derializeFromByteArray 메소드를 이용하여 evilObject 객체의 직렬화/역직렬화를 수행합니다.

 역직렬화를 하기 위한 deserializeFromByteArray 내부의 ObjectInputStream.readObject 메소드에서 RCE가 발생하게됩니다.

 

 

 getEvilObject의 상단 코드는 바로 위에서 본 내용입니다. 다만 RCE를 발생시키는 lazymap.get() 메소드가 없습니다.

 표면적으로는 없어졌지만, 다른 메소드를 타고 타고 넘어가다보면 결국 어디선가는 나올겁니다.

 lazymap.get() 대신 새롭게 추가된 코드를 살펴보면 AnnotationInvocationHandler, Constructor, InvocationHandler, Proxy 등이 보입니다.

 라인수는 얼마 안되지만 각종 디자인 패턴과 캐스팅이 많이 포함되어 있기 때문에 처음에는 무슨 내용인지 파악하기 힘들 수 있습니다.

 

 

 

 

 3. secondInvocationHandler :: 첫번째 Handler

 

 ① 리플렉션(Java Reflection)을 이용한 객체 생성

 새롭게 추가된 코드의 첫부분은 클래스의 이름 "sun.reflect.annotation.AnnotationInvocationHandler"을 이용하여 

 secondInvocationHandler 객체를 생성하는 부분이 나오는데, 이를 이해하기 위해서는 리플렉션(Java Reflection) 개념에 대해 알아야합니다.

 리플렉션은 동적인 방식을 이용하여 Java의 유연성을 높이는 기능이며, 프레임워크에서 자주 사용되는 방식입니다.

 Java는 아래의 java.lang.reflect 패키지를 통해 리플렉션 기능을 제공하고 있습니다.

 

 

 리플렉션에서 제공하는 동적 기능은 클래스의 이름만으로 해당 클래스의 다양한 정보를 얻어오고,

 중간에 보았던 invoke() 메소드 등을 활용하여 상황에 따른 메소드를 호출할 수 있는 기능등을 말합니다.

 이러한 동적 기능을 이용하게 되면, 클래스의 변경이 자주 일어나는 코드에서 일일이 소스코드를 수정해서 배포하지 않아도 되며

 상황에 따라 입력으로 받은 메소드 이름을 이용하여 로직의 변화 없이 새로운 기능을 제공해줄 수도 있습니다.

 보안과는 별개로 사용 방법에 따라 매우 편리한 기능이라 할 수 있습니다.

 

 리플렉션을 이용하면 클래스의 이름/제어자/정보/부모클래스/생성자/메소드/변수의 정보를 얻을 수 있으며

 스프링 프레임워크에서 자주 사용되어 친숙한 Annotation(주석의 일종, @RequestMapping 등) 객체도 얻을 수 있습니다.

 

 

 위에서 설명한 리플렉션의 기능을 원활하게 사용하기 위해서는 클래스의 정보를 얻을 수 있는 API가 필요합니다.

 이를 위해서 자바에서는 java.lang.Class라는 클래스를 제공해주고 있으며,

 일반적으로 아래 예제와 같은 형태로 객체의 정보를 얻어냅니다. (우리가 분석할 evilObject에서도 동일한 패턴이 사용됩니다)

 코드를 살펴보면, forName() 메소드에 String 클래스의 이름을 넣어주고 Class 객체를 얻었습니다.

 이후에 getDeclaredConstructors() 메소드를 이용하여 String 클래스에 정의된 생성자들을 Constructor<?> 객체 형태로 얻습니다.

 

 

 getDeclaredConstructors() 메소드는 배열 형태로 생성자(Constructor)들을 반환해주며,

 배열 인덱스([0], [1], [2]...)를 이용하면 원하는 생성자만 얻어낼 수 있습니다. (오버로딩 때문에 생성자가 여러개일 수 있으므로)

 이때 결과값은 다양한 생성자를 고려하여 모든 타입을 수용하는 <?> 제네릭(Generics) 형태입니다.

 어떤 생성자들이 반환됐는지 출력해보면 아래와 같습니다. 개인적으로 제가 원했던 new String("문자열")의 형태는 [12]에 있었네요.

 

 

 

 ② 본문코드 분석

 이제 evilObject에서 AnnotationInvocationHandler 객체를 리플렉션으로 생성하는 부분을 살펴보겠습니다.

 Class.forName() 메소드로 AnnotationInvocationHandler 객체를 얻고, getDeclaredConstructors()[0] 메소드로 첫번째 생성자를 얻었습니다.

 이후에 newInstance를 이용하여 생성자에 lazyMap을 인자로 전달하면서 취약점을 발생시키는 객체를 생성합니다.

 lazymap을 인자로 전달하는것으로 보아 최종적으로 RCE가 발생하는 부분은 secondInvocatinHandler에서 발생할 것으로 추정됩니다.

 첫번째로 생성됨에도 불구하고 변수명이 second로 시작되는 이유는 해당 핸들러가 다른 객체의 인자로 들어감으로써

 두번째로 생성되는 invoacationHandlerToSerialize 핸들러 보다 RCE 흐름상 뒷부분에 위치하기 때문입니다.

 

 

 lazymap을 인자로 받는 AnnotationInvocationHandler 클래스의 생성자를 살펴보면 Map<String, Object> 타입의

 memberValues 파라미터를 두번째 인자로 받고 있습니다. 이것은 ysoserial에서 굳이 AnnotationInvocationHandler를 타겟으로 하여

 도구를 제작한 이유와 무관하지 않아보입니다. 아마도 LazyMap과 ChainedTransformer의 조합으로 취약점이 발생하는것을

 발견하고 Map을 인자로 받는 클래스를 찾지 않았을까요.

 

 

 

 

 4. invocationHandlerToSerialize :: 두번째 Handler

 

 ① Proxy를 이용한 객체 생성

 두번째로 생성되는 invocationHandlerToSerialize 객체 생성 코드를 분석하려면 프록시(Proxy) 디자인 패턴에 대한 이해가 필요합니다.

 일단 프록시 디자인 패턴에 대한 원리는 쉽게 설명해 놓은 글이 있어서 아래의 블로그 주소를 소개합니다.

 프록시 패턴 :: http://devbox.tistory.com/entry/DesignPattern-%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%8C%A8%ED%84%B4

 

 내용을 요약하자면, 가벼운일은 대리인 PrinterProxy에서 처리하고, 무거운일(heavyJob)은 Printer를 호출해서 처리하는 로직입니다.

 이런 디자인패턴을 구현하게 되면 핵심기능의 앞 뒤로 원하는 추가 기능을 넣을 수 있을 뿐만 아니라,

 A/B/C 3개의 클래스가 있다고 가정했을 때, 프록시에서 1번만 구현하면 되기 때문에 코드 관리가 편리해집니다.

 

 하지만 우리가 분석중인 evilObject에서는 정적 프록시 패턴이 아닌, 리플렉션의 다이나믹 프록시(Dynamic Proxy)를 사용합니다.

 사실 기존의 정적 프록시는 A/B/C 클래스가 단일 프록시로 구현되어 있을 때 새로운 D/E/F 클래스가 추가되야 한다고 가정하면 

 매번 추가되는 신규 클래스의 인터페이스를 프록시에 구현해야 하는 번거로움이 있습니다.

 또한 부가기능/접근제어와 같은 코드는 자주 활용되는 것들이 많기 때문에 유사한 중복 코드가 빈번하게 발생하기도 합니다.

 

 다이나믹 프록시는 이러한 정적 프록시의 단점을 극복하고자 리플렉션의 특징을 도입하여 설계되었습니다.

 이때 사용하는 리플렉션의 특징은 바로 위에서 학습했던 InvocationHandler의 invoke() 메소드입니다.

 프록시 객체 생성시 인자로 전달되는 InvocationHandler에 부가기능을 한번만 구현하여 중복코드 문제를 해결합니다.

 또한 A/B/C 클래스의 앞뒤로 원하는 출력이나 기능을 덧붙이는 기능도 그대로 유지할 수 있습니다.

 프록시는 클라이언트에게 요청받은 내용을 핸들러에게 넘기고, 핸들러에서는 invoke()에서 처리한 후 결과값을 반환해줍니다.

 아래에서 디버깅을 진행할 때에도 프록시를 이용한 호출이 있다면, 프록시가 가지고 있는 핸들러의 invoke() 함수를 눈여겨 봐야합니다.

 아래 그림은 InvocationHandler를 이용하여 다이나믹 프록시를 설명하는 다이어그램입니다.

 

 그림출처 : https://jsdom.wordpress.com/2011/09/05/java-reflection-in-action-study-note-5/

 

 

 예제로 우리가 분석중인 다이나믹 프록시에 인자값으로 전달되는 AnnotationInvocationHandler 클래스를 살펴봅시다.

 클래스의 기본 설명은 Annotation의 Dynamic Proxy 구현을 위한 InvocationHandler 라고 되어 있습니다.

 프록시 패턴에 대해 잘 아시는 분이라면 다이나믹 프록시에 등록하여 사용하는 핸들러임을 바로 파악하지 않았을까 싶네요.

 InvocationHandler, Serializable 인터페이스를 구현했기 때문에 invoke/readObject 메소드가 있습니다.

 

 

 프록시를 통해 toString을 실행할 경우 toStringImp() 메소드를 이용하여 annotation의 형태의 문자열을 출력합니다.

 이런식으로 invoke()를 통해 처리하지 않고, 인터페이스를 구현한 메소드는 가벼운일에 속한다고 봐야할것 같습니다.

 ex) @("Original toString()")

 

 

 사실 이부분까지 알필요는 없지만, 취약점의 핵심이 되는 AnnotationInvocationHandler 클래스는 도대체 어디에 쓰이는걸까요.

 검색을 해봐도 별다른 결과가 나오지 않아서 주로 프레임워크에서 사용되지 않을까 싶어 JDK 라이브러리에 검색을 했습니다.

 여러 결과가 나왔으나, AnnotationParser 클래스가 눈에 띕니다.

 

 

 실제로 코드를 살펴보면 annotationForMap을 호출하면서 타입과 Map을 넘겨주면 AnnotationInvocatioanHandler를 리턴해줍니다.

 이렇게 사용하면 사용자는 annotationForMap을 사용하면서 자동으로 프록시의 기능을 이용하게 될것으로 보입니다.

 

 

 실제로 활용 용도나 인자값을 고려하지 않고 실행되는것에 초점을 맞춘 예제를 작성하여 돌려보면 

 마치 프록시처럼 문자열 "@" "(" 등을 덧붙이는 부가기능이나 타입을 반환하는 메소드는 annotationForMap 본인이 처리하고 있습니다.

 

 

 

 그외의 무거운일(HeavyJob)인 equals 메소드는 invoke()를 통해 해결합니다.

 

 

 

 ② 본문코드 분석

 본론으로 돌아와서, Proxy를 어떻게 사용하고 있는지 살펴봅니다.

 newProxyInstance 메소드로 아래의 인자를 넣어주면서 다이나믹 프록시의 객체(evilProxy)를 생성하고 있습니다.

 인자는 프록시 클래스를 정의하기 위한 클래스로더, 클래스가 구현해야할 인터페이스, 실제 작업을 처리하는 핸들러입니다.

 또한 이렇게 얻은 객체(evilProxy)를 InvocationHandler 객체를 생성하면서 인자로 넣어주는것을 볼 수 있습니다.

 

 ① 첫번째 인자 :: 클래스로더(ClassLoader)

 동적으로 클래스의 정보(Class.forName), 생성자(getConstructor)를 얻거나 정의(defineClass)하는데 사용

 ② 두번째 인자 :: 프록시가 구현해야할 인터페이스 리스트

 해당 리스트를 이용하여 generateProxyClass 메소드에서 인터페이스를 구현한 .class 파일을 FOS스트림으로 생성

  

 ③ 세번째 인자 :: 프록시가 호출하는 Handler

 바로 위에서 살펴본 AnnotationInvocationHandler이므로, 어떤 기능을 수행하는지는 생략

 

 프록시의 객체(evilProxy)를 생성하면서 재밌는 점은 구현해야할 인터페이스인 두번째 인자 Map.class 입니다.

 왜하필 Map.class를 상속받았는지 의문점이 있었으나, 바로 아래에 두번째 Invocationhandler 객체를 생성할때 의문이 풀리게됩니다.

 AnnotationInvocationHandler 클래스의 생성자는 Map<String, Object> 형태의 두번째 인자를 받습니다.

 따라서 Map.class를 구현한 evilProxy 객체는 이 타이밍에서 Map.class로 업캐스팅되겠죠.

 Map 인터페이스를 구현하지 않으면 RCE를 발생시키지 못하니 구현한것인지,

 혹은 원래 이렇게 사용하는 핸들러인지는 잘 모르겠으나, 어쨋든 Map 인터페이스를 구현한 것이 이유가 있는것은 확실합니다.

 

 

 핸들러의 invoke 함수 내부를 살펴보면 RCE가 발생하는 주요 포인트인 memberValues.get(member) 코드가 존재합니다.

 여기서 memberVlues는 방금 바로 위에서 본 proxy 객체입니다. 따라서 proxy.get(member) 라고 바꿔도 무방합니다.

 또한 proxy는 Map 인터페이스를 구현하면서 Map.class의 get() 메소드가 이미 존재합니다.

 이후는 우리가 Payload only Execute에서 살펴본대로 가젯 체인이 연쇄 호출되며 사용자 명령어(calc 등..)가 실행될것입니다.

 

 

 

 5. invocationToSerialize의 구조

 디버깅을 하면서 가장 헷갈렸던 부분은 핸들러 안에 핸들러가 들어가고, 중간에 프록시까지 들어가는 복잡한 구조입니다.

 정리하지 않고 무작정 디버깅으로 넘어가면 머릿속에서 핸들러의 미로(?)에 빠질수도 있습니다.

 일단 객체 생성코드 부분을 다시 체크하고 구조를 살펴보겠습니다.

 ① secondInvocationHandler는 Lazymap을 인자로 넣어주면서 생성↓↓

  InvocationHandler secondInvocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);

 ② invocationHandlerToSerialize는 Map 인터페이스를 구현한 Proxy를 인자로 넣어주면서 생성 ↓↓

  InvocationHandler invocationHandlerToSerialize = (InvocationHandler) constructor.newInstance(Override.class, evilProxy);

 

 이렇게 생성된 invocationToSerialize 객체의 구조를 이클립스로 살펴보면 아래와 같습니다.

 invocationHandlerToSerialize 인스턴스를 생성하면서 인자로 넣어준 ProxymemberValues로 선언되어 있고,

 Proxy 인스턴스를 생성하면서 넣어준 두번째 AIHandlerh(handler)에 선언되어 자리를 잡았습니다.

 두번째 AnnotationInvocationHandler는 인스턴스를 생성시 LazyMap을 인자로 전달했기 때문에 핸들러 변수(h)에 있습니다.

 

 

 이렇게 이해하기 어려운 구조로 객체를 생성해야 하는 이유는 사용자 명령을 서버단에서 실행하기 위해서입니다.

 간단하게 아래처럼 Serializable 인터페이스를 구현한 객체를 만들고 readObject() 내부에 사용자 명령어를 넣어주고 전달하면 끝아닌가?

 라는 생각을 할수도 있지만, 이러한 로직이 성립하려면 서버(or 응용프로그램)에서 동일한 MyObject 객체를 가지고 있어야합니다.

 (실제로 테스트를 해보면 MyObject를 찾을 수 없다면서 java.lang.ClassNotFoundException 오류가 발생할겁니다.)

 객체를 공유할 수 있는 기능이 제공되지 않는 이상 발생하기 희박한 상황이겠지요.

 

 

 하지만 ysoserial 페이로드는 Java Class Path Commons Collection 라이브러리가 포함되어 있고, 역직렬화를 수행하면

 같은 객체를 공유하지 않더라도 서버에서 실행될 수 있습니다.

 

 

 

 

  0x03  Gadget Chain Debugging

 

 이제 마지막으로 가젯체인에 있는 순서대로 RCE가 발생하는 포인트까지 디버깅을 진행하겠습니다.

 

 1. Gadget Chain :: ObjectInputStream.readObject()

 ObjectInputStream의 readObject 메소드가 호출되면 직렬화된 데이터를 읽어들이기 위해 역직렬화 과정을 거치게 됩니다.

 역직렬화가 진행되면 ObjectInputStream이 읽어들인 직렬화된 클래스 내부의 readObject를 호출하게 됩니다.

 이때 readObject 메소드 호출 역시 Reflection 기능을 이용하여 readObject를 invoke 메소드로 호출하는 로직입니다.

 만약 Serializable 인터페이스를 구현한 객체가 readObject 메소드를 가지고 있다면 해당 readObject가 호출됩니다.

 AnnotationInvocationHandler의 경우 Serialize와 Map 인터페이스를 구현했기 떄문에 readObject()를 가지고 있습니다.

 

 

 

 2. Gadget Chain :: readObject() + Map.entrySet()

 참고로 Deserialize가 진행되면서 readObject() 메소드는 second 핸들러 → toSerialize 핸들러 순으로 총 2번 호출됩니다.

 (정확한 이유는 모르겠으나 serialize 인터페이스를 구현한 클래스가 중첩되었을 경우 각각 직렬화/역직렬화 과정을 진행하나봅니다)

 second 핸들러에서는 RCE가 발생하지 않으며, 두번째로 호출되는 toSerialize 핸들러의 AnnotationInvocationHandler에서 RCE가 발생합니다.

 

 proxy를 가지고 있는 toSerialize의 역직렬화를 살펴보겠습니다. 

 AnnotationInvocationHandler는 readObject() 메소드를 실행할때 생성자를 통해 받은 Map의 entrySet() 메소드를 호출합니다.

 그리고 entrySet() 메소드를 실행하는 순간 evilProxy가 가지고 있는 InvocationHandlerinvoke() 메소드로 실행흐름이 넘어갑니다.

 쌩뚱맞게 갑자기 invoke() 메소드로 흐름이 넘어가는 부분에 대해서 나름대로 분석을 해보면..

 ① memberValues = Proxy이므로, 실행되는 메소드는 evilProxy.entrySet으로 볼 수 있음

 ② 요청(entrySet)은 프록시가 가진 핸들러(AnnotationInvocationHandler)가 처리 (프록시 다지안 패턴)

 ③ 이때 Proxy는 Map 인터페이스를 구현(Imp)하였으므로, entrySet 메소드를 호출하여도 에러가 발생하지 않음

 ④ InvocationHandler는 통칭 무거운일(Heavy Job)을 invoke() 메소드로 처리하므로 invoke() 메소드로 흐름이 넘어감

 

 

 

 3. Gadget Chain :: invoke() → Payload only Execute

 proxy를 생성할때 넣어준 AnnotationInvocationHandler의 invoke() 함수로 진입했습니다.

 디버깅을 진행하다보면 중간 부분에 memberValues.get(member) 메소드를 호출하는 부분이 나옵니다.

 

 

 이때 memberValues는 main 에서 첫번째 AnnotationInvocationHandler 객체를 생성할때 넣어준 LazyMap입니다.

 생성할때와 동일하게 factory에 ChinedTransformer를 가지고 있고, map은 HashMap입니다.

 해당 코드를 실행하게 되면 Payload only execute 챕터에서 보았던 RCE가 정상적으로 실행됩니다.

 

 

 단순히 툴을 사용하면 하나의 직렬화된 객체가 짠 하고 나오지만, 그 중간과정은 매우 복잡하네요.

 디버깅을 끝으로 Ysoserial - CommonsCollection1 Exploit 페이로드 분석을 마칩니다.

 

 

 

 

  0x04  Debugging/Error Tip

 

 1. UnsuppoertedOperationException : Serialization support for org.apache.commons..

 InvokerTransformer의 보안 때문에 오류가 난다면 Common-Collections 3.2.1 Ver 이하로 라이브러리로 바꿔주어야 합니다.

 (저의 경우에는 3.2.1 Ver을 설치했다고 착각했지만 확인해보니 3.2.2 Ver을 사용하여 오류 발생....)

 

 

 

 2. IncompleteAnnotationException : java.lang.Override msiing element entrySet

 프로젝트 설정에서 JDK 1.8 → JDK 1.7 Ver 으로 변경하여 실행 (여전히 오류는 발생하지만 RCE는 발생함)

 (미리 1.7 버전을 다운로드 받아서 이클립스 build path에 별도로 추가해주어야 탭으로 선택할 수 있습니다)

 

 

 

 

 

 

  0x05  마치며..

 

 포스팅을 진행하면서 디버깅 할일이 많아지다 보니, 마치 개발(?)을 하는듯한 느낌이 들더군요.

 물론 개념만 알면 그냥 넘어갈 수 있는 부분도 많았고, 응용이 필요하진 않았지만

 프레임워크에서 사용하는 각종 디자인 패턴과 자바의 개념들이 머리속에서 뒤엉키는 경우가 많았습니다.

 내용이 길어지고 내용들이 얽히고 섥히다 보니 오타나 흐름이 이상한 부분이 있을 수 있습니다.

 틀린 내용이나 바로 잡아야할 내용이 있을 시 댓글 남겨주시면 감사하겠습니다.

 긴 글 읽어주셔서 감사합니다.

 

 

posted By Message

Commit your way to the LORD, trust in him and he will do this. [PSALms 37:5]

redScreen.tistory.com Blog :(

 

 

':: 취약점' 카테고리의 다른 글

[CVE-2014-6332] OLE 자동화 배열 취약점 분석  (1) 2016.07.24
FCKeditor 취약점 + 간단실습  (0) 2016.04.20
posted by Red_Message
2016. 7. 24. 03:09 :: 취약점

안녕하세요 Message입니다.

오늘은 OLE 자동화 배열 취약점 분석을 주제로 포스팅하려 합니다.

초보자의 시각으로 OLE 취약점이 존재하는 웹페이지를 분석하는 글이므로,

삽질 또는 잘못된 내용이 있더라도 너그럽게 봐주시기 바랍니다.

 

 

실습환경은 Windows7 / IE / VMware 환경 입니다.

 

---------------------------------------------------------

1. OLE 자동화 배열 취약점이란?

---------------------------------------------------------

해당 취약점(CVE-2014-6332)은  IE 환경에서 발생하는 취약점 으로서,

갓모드에 진입하여 악성코드를 유포할 수 있는 취약점입니다.

 

Tip. 갓모드(GodMode)

OLE 취약점에서 갓모드는 무엇이든 할 수 있는 상태를 말합니다.

예를 들면, SafeMode에 의해 차단되는 아래와 같은 Shell Code가 동작할 수 있게됩니다.

  ex) set sh=createobject("Shell.Application")

SafeMode 뿐만 아니라 ASLR, DEP, EMET, DFI 등의 보호기술들이 다 소용없게 됩니다.

우리가 보편적으로 알고 있는 갓모드는 윈도우 OS(7, 8.1, 10)에서

아래 그림처럼 제어판의 확장인데, 혼동하면 안되겠습니다.

 

 

---------------------------------------------------------

2. 취약점 발생 원인

---------------------------------------------------------

취약점의 발생 원인은 IE에서 OLE 객체를 처리하는 과정에서 발생합니다.

OLEAUT32.dll 내부의 SafeArrayRedim() API에서 VBScript 배열의 크기를 변경하는 과정에서

충분한 에러처리를 수행하지 않아 버퍼오버플로우가 발생하게 됩니다.

OLE 취약점 발생 원인이나 동작 원리는 보안업체 블로그 등에서 상세히 확인할 수 있으며,

여기서는 실제 OLE 취약점이 존재하는 웹페이지를 살펴봅니다.

취약점에 대한 개념을 먼저 익히시고 보시면 이해하시는데 편하실겁니다.

 안랩 보안이슈 : 갓모드(GodMode) 공격의 시작, 제로데이 익스플로잇

 http://www.ahnlab.com/kr/site/securityinfo/secunews/secuNewsView.do?curPage=1&menu_dist=2&seq=23225

 

 

---------------------------------------------------------

3. 스크립트 살펴보기

---------------------------------------------------------

 

1) 전반부 : 역순으로 나열된 스크립트

OLE 취약점이 존재하는 웹페이지의 특징은 역순으로 배열되어 있는 스크립트 입니다.

이러한 특징은 분석가가 한눈에 알아보기 힘들게 만들기도 하지만

흔하지 않은 특징이라면, 보안솔루션의 탐지 시그니처가 될 수 있습니다.

아래의 그림을 참고하시면, 8~9라인에서부터 역순으로 배열된 스크립트를 볼 수 있습니다.

 

2) 중반부 : 난독화된 스크립트

엄청난 길이의 난독화 되어 있는 스크립트가 나열되어 있습니다.

이걸 복호화 하려면 제실력으로는 힘들것 같습니다.

초보인 저는 다른 방법을 강구해 보아야 겠죠?

 

3) 후반부 : VBScript

난독화된 스크립트를 지나서, 후반부는 VBScript로 구성되어 있습니다.

대부분은 함수의 선언으로 이루어져 있으며

전반부의 스크립트를 실행시키는 코드가 좀더 아랫부분에 존재합니다. (현재 그림에는 안보임)

 

 

---------------------------------------------------------

4. VBScript 스크립트 분석

---------------------------------------------------------

 

1) 역순으로 나열된 스크립트 재배열

이렇게 역순으로 나열된 스크립트를 분명 어디선가 다시 정상적으로 재배열 후에

스크립트를 적용시켜야 정상적으로 동작할겁니다.

그렇다면 textarea 태그에 지정되어 있는 ID값 lshdic200Xpage을 어디선가 사용할겁니다.

분석 초기부터 해당 ID값을 이용하여 추적했다면 좀더 빠른 분석이 가능했겠지만

저는 처음엔 이상한 문자열이네.. 생각하고 그냥 넘어갔더랬습니다.. (삽질++)

 

id값으로 스크립트의 문자열을 검색하면 StrReverse() 함수를 이용하여 텍스트를 역순으로 되돌리고

document.write() 함수를 이용하여 스크립트를 실행시키는 부분을 찾을 수 있습니다.

본래 화면에 문자열을 보이게 만드는 함수지만 스크립트 실행 용도로 사용 가능합니다.

 

정상으로 돌리는 부분을 찾아냈으니 역순으로 배열된 코드를 볼 방법은 여러가지가 생겼습니다.

단순히 document 함수를 alert()로 띄어보는 방법이 있고,

파이썬의 reversed() 함수 또는 for문을 이용해도 간단히 정상 코드를 볼 수 있습니다.

정상코드로 복원하여 alert() 함수로 띄운 내용은 아래와 같습니다.

 

 

 

2) SafeMode 해제, 갓모드로 진입

팝업창의 내용을 살펴보면 Begin() 이라는 함수의 내용입니다.

아래 그림처럼, VBScript가 있었던 후반부에서 Begin() 함수를 실행시킵니다.

사실, 후반부는 함수의 선언이 대부분이고, 한줄 적혀있는 Begin() 실행이 다입니다.

우리가 팝업으로 띄운 스크립트가 사실상 메인의 역할을 하고 있다고 보면 됩니다.

 Tip. 앞으로 등장할 VBScript 문법

 1. <script> </script> : VBscript는 단일 웹페이지에서 여러 개의 <script> </script> 문단을 가질 수 있습니다.

 2. dim arr(num) : 배열선언 / 주의점 : arr(10)으로 선언했을 경우 11개의 배열요소를 가짐(0부터 10까지)

 3. redim Preserve arr(5) : 배열 크기 변경, Preserve 명령 사용 시 이전 배열값은 유지

 4. Sub 서브루틴이름(매개변수) ... End Sub : 반환값 없는 함수

 5. Function 함수이름(매개변수) ... End Function : 반환값 있는 함수 / 함수이름 = 반환값

 6. For start=시작값 to 종료값 step 증감 : 반복횟수를 알고있는 경우 반복문 / 빠져나오려면 exit for 사용

 7. inStr() : 주어진 문자열에서 특정 문자열을 찾아서 그 위치를 반환 / 찾지 못하면 0 값을 반환

 8. IsObject() : 유효한 ActiveX 또는 OLE 자동화 객체를 참조하는지 여부 반환 / Trure or False

 9. cInt() : 인자로 전달된 값을 integer형으로 반환

 10. varType(변수) : 변수의 타입을 상수로 반환 / 부동소수점의 경우 반환값은 4

 11. randomize(number) : number를 사용하여 Rnd 난수 발생기를 초기화하고 새 시드값을 제공

 12. rnd(number) : 0보다 크고너 같고 1보다 작은 난수 값 반환, number의 값은 난수 발생 방법 결정

                                - num < 0 : number를 시드로 하여 매번 같은 수

                                - num > 0 : 순차적으로 생성되는 그 다음의 난수

                                - num = 0 : 가장 최근에 생성한 난수

                                - num = x : 순차적으로 생성되는 그 다음의 난수

 

 

3) Begin 파헤치기(1)

Begin() 함수의 내용을 본격적으로 살펴봅니다. 

먼저 위쪽의 내용을 살펴보면 Navigetor.UserAgent로 얻어낸 값을 instr() 함수를 이용하여

사용자 PC의 WIn64 여부와 IE환경이 맞는지 체크하고 있습니다.

info에 어떤 값이 담기는지 궁금해서 출력해보았습니다.

 

 

이후 BeginInit()과 Create() 함수를 연달아 호출합니다.

각 함수의 역할은 아래와 같습니다.

--------------------------------------------------------------------------------------------------------

1. BeginInit()

- 버퍼오버플로우를 일으키기 위한 배열 2개와 변수 초기화

- aa(5) : OLE취약점 발생시키기 위한 배열1

- ab(5) : OLE취약점 발생시키기 위한 배열2

- a0 : 랜덤초기화 후에, 숫자를 계속 높이며 취약점 발생 조건을 찾는데 사용

- a3 : 랜덤초기화 후에, a0를 계속 증가시키는 증감변수로 사용

 

2. Create()

- 내부의 Over() 함수를 이용하여 취약점 조건 만듬

- 배열 aa와 ab가 8바이트 간격으로 동적 할당 되어 취약점이 발생할 때까지 반복

--------------------------------------------------------------------------------------------------------

 

 

 

Create() 함수 내부에는 Over() 함수가 존재합니다.

해당 함수의 내용은 아래와 같습니다.

 Over()

 - Begininit()에서 초기화한 배열 2개(aa, ab)를 이용하여 취약점을 발생시킴

 - aa배열과 ab배열이 8바이트 차이를 두고 동적 할당 될 경우 취약점 발생 조건이 만족됨

 - redim을 통한 aa의 배열 재할당은 실패하지만, VBScript의 배열 정보가 담긴 SAFEARRAY구조체에서는 확장된 크기값이 저장됨 

 - intVersion 값이 대부분 높으므로 아랫부분의 else 이후에 초점을 맞춰서 해석 진행

 - aa(a1)의 VarType을 체크할 경우, ab(0)에 할당한 부동소수점(1.123456...) 값이 결과값으로 반환됨 => 취약점 발생 성공

 

 

취약점이 발생하려면 aa와 ab가  8바이트를 두고 동적 할당이 되어야 합니다. 

그런데 조건 상태가 복잡하여 표로 나타내 보았습니다.

 a0 = 1 + a3(=2) = 3  / a0는 BeginInit()에서 13+@로 할당됨

 a1 = 3 + 2 = 5

 a2 = a0(=1) + h8000000

 a3 = 2(증감연산자)  / a3는 BeginInit()에서 7+@로 할당됨

위 조건대로 진행될 경우 아래와 같이 type1=h2f66이 성립되어 Over=True가 됩니다.

 

 

4) Begin 파헤치기(2)

Over() 함수를 통하여 취약점이 발생되는 조건을 달성한 뒤에는

VBScript에서 파일생성과 cmd 명령어 실행 등을 자유롭게 수행할 수 있도록

IE의 보호모드(SafeMode)를 해제하는 setnotsafemode() 함수를 실행시킵니다.

해당 함수를 실행시키기 이전에 정상적인 값에 덮어 씌울 myarray 값을 초기화 합니다.

myarray값은 SAFEARRAY구조체의 값에 SafeMode 플래그인 0x1E를 0으로 바꾸어 놓는 값입니다.

-----------------------------------------------------------------

-> 01 00 80 08 01 00 00 00 00 00 00 00 00 00 00 00

-> 00 00 FF 7F 00 00 00

-----------------------------------------------------------------

 

 

setnotsafemode() 함수의 처음에 mydata() 함수를 실행시키게 되는데

아래와 같이 배열을 할당하고, 아무 기능을 수행하지 않는

더미 함수(=testaa)의 주소값을 리턴합니다.

 

여기서 i=teataa를 할당하고 바로 아래에서 null을 할당한 것을 보고 멘붕이 왔습니다.

검색해보니 VBScript에서 Function Object의 주소를 얻는게 불가능하다고 하더군요.

그래서 나온 꼼수가 바로 이것입니다.

i=testaa 구문을 만나면 testaa 값이 스택에 쌓입니다.

이후에 i에 할당하려 하지만 오류가 발생합니다.

그리고 "On Error Resume Next" 구문에 의하여 다음라인의 i=null을 만나게 됩니다.

하지만 i의 값이 null로 대체 되는것이 아니라 스택에 쌓인 인자값,

즉 testaa의 주소값에 vt 플래그만 0x01(null)로 대체 된다고 합니다.

결국은 i에 Function Object Address가 할당됩니다 :)

 

아래는 해외 사이트에서 이것을 테스트한 것을 일부 가져왔습니다.

null을 할당하자 vt 플래그만 대체되어 04350001이 되고, 결국 testaa의 주소값을 나타내게 됩니다.

URL : http://www.secniu.com/how-to-use-vbscript-to-turn-on-the-god-mode/

 

mydata()에서 설정하는 값의 배치가 어려워서 표로 나타내면 아래와 같습니다.

aa(a1), aa(a1+2)의 값과 타입에 값을 입력한 것을 알 수 있습니다.

해당 설정들은 이후에 실행할 readmemo()와 setnotsafemode()의 나머지 부분에서 사용됩니다.

 

 

4) Begin 파헤치기(3)

이제 safemode를 해제할 일만 남았습니다. 하지만 여기서 분석이 안되는 부분이 있었습니다.

 

- 첫번째 의문점입니다.

안랩 보안이슈에서 공개된 내용을 보게 되면

--------------------------------------------------------------------------------------------------------

"기본적으로 IE는 특정한 조건 하에서만 VBScript가 실행되도록 제한하며,

VBScript를 실행하기 전에 OleScript 객체 안에 있는 세이프모드를 확인"

--------------------------------------------------------------------------------------------------------

그렇다면 세이프모드를 해제하기 전까지 실행되고 있는 VBScript는 왜 실행되고 있는것인가?

이 부분에대해서 위의 URL의 초반부에 이러한 내용과 함께 예제가 있더군요.

 

한마디로 Shell.Application과 같은 Create Object 함수의 경우 SafeMode에 의해서 차단된다는...

그래서 아래와 같이 간단히 테스트해본 결과 MsgBoX는 띄워진 반면,

계산기 프로그램은 실행되지 않았습니다.

즉, SafeMode를 해제하는 평범한(?) 스크립트는 Script Engine이 SafeMode를 체크하지 않는다는 결론입니다.

 

 

- 두번째 의문점

CScriptEntryPoint 주소가 왜 더미 Sub에서 8바이트 떨어진 곳에 위치하냐는 점입니다.

아직 왜 그러한지 알아내지 못했습니다. 조금 더 연구가 필요할 것 같습니다.

원래 그런것이니 그렇다고 하면 초보 분석가인 저는 그저 상처받을뿐

이부분에 아시는분은 댓글을 남겨주시면 감사하겠습니다.

 

 

5) 갓모드 진입 후 어떤 행위가..?

갓모드로 진입한 이후에는 runmumaa() 함수를 통하여 원하는 소스코드를 실행시킵니다.

실행흐름을 따라가 보면 아래 그림처럼

runmumaa()는 runurl()을 호출하고

runurl()에서 흐름이 끊기는것처럼 보입니다.

 

하지만 alert() 함수로 for문이 끝난 후에 runurl을 출력해보면

아래와 같이 rechange(t) 함수를 document.write()함수를 통해 실행시키는 것을 볼 수 있습니다. 

 

rechange() 함수에 넘어가는 인자값 t와 난독화가 해제된 문자열을 출력해보면

중반부에 나오는 난독화된 문자열이 드디어 모습을 드러냅니다.

 

 

위의 스크립트의 내용을 정리하면 아래와 같습니다.

---------------------------------------------------------------------------------------------------------------------

1. TEMP 경로에 uninst.gif.vbs 파일 생성 및 echo 기능으로 내용 작성

2. cmd 콘솔에서 vbs 파일을 실행시킬 수 있는 cscript.exe 기능을 이용하여 작성한 vbs 실행

3. 파일명과 인자값 2개(URL, 파일경로) 넘김

4. Get명령어를 이용하여 URL에 접근 후 악성코드 다운로드

5. 악성코드 실행

---------------------------------------------------------------------------------------------------------------------

일단 OLE취약점이 발생하게 되어 SafeMode가 해제되게 되면

이후의 행동은 위에서 분석한 shell 실행 말고도 다양한 행동이 가능할 겁니다.

하지만 이러한 케이스를 많이 접할수록 악성행위들의 패턴에 대해 인지하는데 도움이 많이 될 것 같습니다.

 

 

---------------------------------------------------------

5. 마치며

---------------------------------------------------------

생각보다 포스팅이 길어졌습니다.

분석이라는 것이 개발분야를 알아야 좀더 수월하다는걸 다시 알게됩니다.

중간중간 제가 봐도 이해안되는 문맥의 단절과 설명되지 않는 부분이 많은 것 같습니다...

그럼에도 긴글 읽어주셔서 감사합니다.

틀린점이나 부족한 점은 언제든지 댓글 달아주세요. 감사합니다.

 

posted By Message.

Commit your way to the LORD, trust in him and he will do this. [PSALms 37:5]

 

posted by Red_Message
2016. 4. 20. 19:19 :: 취약점

안녕하세요 Message입니다.

실습환경은 Windows7 / APM / Chrome 입니다.

 

------------------------------------------------------------------------------------------------------------------------------------------

1. FCKeditor란?

------------------------------------------------------------------------------------------------------------------------------------------

FCKeditor는 게시판 에디터입니다.

거의 모든 브라우저에서 사용 가능한 에디터로서, 글작성 / html편집 /표삽입 등이 가능합니다.

현재 FCKeditor는 3.x 대로 업그레이드 되면서 CKeditor로 변경되었으나,

아직 많은 곳에서 FCKeditor를 사용하고 있습니다.

공식사이트 : http://ckeditor.com

FCkeditor 2.6.11 Ver : http://sourceforge.net/projects/fckeditor/files/

 

 

 

------------------------------------------------------------------------------------------------------------------------------------------

2. 취약점 발생 원인

------------------------------------------------------------------------------------------------------------------------------------------

취약점이 발생하는 이유는 FCKeditor에서 제공되는 페이지관리 또는 파일업로드 기능때문입니다.

해당 기능을 사용할 수 있는 페이지에 적절한 보안조치가 이루어지지 않으면

웹쉘이 업로드되거나 홈페이지의 많은 정보가 노출될 수 있습니다.

 

아래 그림은 FCKeditor 2.6.11 버전의 기본 경로입니다.

아래처럼 connectors 폴더의 test.html 파일이나, browser\default폴더의 browser.html 파일등이 

관리되지 않고 그대로 유지되거나, 또는 파일명이 변경되지 않고 웹서버에 올라갈 경우

웹쉘을 업로드하고, 웹서버를 탐색하는 용도로 사용될 수 있습니다.

 

 

------------------------------------------------------------------------------------------------------------------------------------------

3. 간단실습

------------------------------------------------------------------------------------------------------------------------------------------

1) APMSETUP or 웹서버

APMSETUP을 이용하여 실습환경을 구성했습니다. (설치방법은 매우 간단하니 생략)

Visual Studio 등으로 웹어플리케이션 프로젝트 생성도 무방할 것 같으나,

제 PC에 이미 APM이 설치되어 있어서 빠른 결정....^^;

설치후 정상동작을 확인해줍니다.

 

2) 웹서버에 FCKeditor 설치

상단의 FCKeditor 2.6.11을 다운로드 후

C:\APM_Setup\htdocs\fckeditor 경로에 압축을 풀어줍니다.

압축해제 후 C:\APM_Setup\htdocs\fckeditor\_samples\default.html 페이지에 접속하여 아래와 같은 창이 뜨면 성공입니다.

 

3) 업로드 페이지 접속

fckeditor/editor/filemanager/connectors/test.html에 접속하면

파일을 업로드할 수 있는 아래와 같은 페이지가 뜨게됩니다.

Connector를 PHP로 변경해주고, 파일선택 후에 업로드를 시도해봅니다.

보안설정 때문에 아마 정상동작하지 않을겁니다.

 

4) 설정변경

파일업로드를 시도하면 아래와 같은 보안설정 오류가 발생합니다.

실습을 위해서 fckeditor\editor\filemanager\connectors\php\config.php 파일의 설정을 변경해야 합니다. (+삽질지수 증가)

 

아래와 같이 설정을 Config['Enabled'] 설정을 false에서 true로 바꿔줍니다.

 

하지만 이후에도, 아래와 같은 오류창이 발생합니다.

파일확장자에 대한 제한이 걸려 있어서 발생하는 오류입니다.

 

파일확장자를 jpg 등으로 우회해서 올리거나

아래와 같이 HTML확장자 제한과 관련된 Config['HtmlExtensions'] 옵션과

파일확장자 제한과 관련된 Config['AllowedExtensions']['File'] 옵션에 html 확장자를 추가해줍니다.

 

 이후에는 아래와 같이 정상적으로 파일이 업로드 되는 것을 확인할 수 있습니다.

 

파일이 업로드 되는 경로는 Config['UserFilesPath'] 옵션에 적혀 있는 경로입니다.

따로 변경하지 않을 경우 아래와 같이 htdocs/userfile/ 경로에 파일이나 폴더가 생성됩니다.

 

5) 웹쉘업로드

파일업로드가 정상적으로 된다면, 아래와같이 웹쉘이 업로드되어 홈페이지의 정보가 유출될 수 있습니다.

웹쉘은 구글링을 하시면 쉽게 얻으실 수 있습니다.

 

6) 기타 취약한 경로들

버전이나 홈페이지마다 다를 수 있지만, 취약한 기본적인 경로는 아래와 같습니다.

- /FCKeditor/editor/filemanager/browser/default/connectors/test.html

- /FCKeditor/editor/filemanager/browser/default/browser.html

- /FCKeditor/editor/filemanager/upload/test.html

 

------------------------------------------------------------------------------------------------------------------------------------------

4. 대응방안

------------------------------------------------------------------------------------------------------------------------------------------

1) 샘플파일 또는 테스트파일을 삭제하거나 관리자 외 접근할 수 없도록 접근제어 방안을 마련합니다.

2) 서버에서 파일의 실행권한을 제거합니다.

3) IPS와 같은 보안솔루션을 이용하여 파일업로드 또는 웹쉘 업로드를 차단합니다.

 

 

마무리

FCK취약점으로 실제 어떻게 파일이 업로드 되는지 실습해보고자

환경을 구축하고 웹쉘을 업로드 해보았습니다.

읽어주셔서 감사합니다.

 

posted By Message.

Commit your way to the LORD, trust in him and he will do this. [PSALms 37:5]

posted by Red_Message