Skip to content

Commit 7c35c68

Browse files
authored
Merge branch 'main' into main
2 parents 2b73dd3 + e5a1e7e commit 7c35c68

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
SafeLog4j can identify and resolve the log4j2 [CVE-2021-45046](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-45046). It can work with custom and third party applications that run on Java and does not require source code.
44

55
The patch works by connecting to a Java process, looking for affected versions of Log4j2, and neutralizing the vulnerability. Other application functionality is unaffected and applications proceed as normal with a reduced risk profile.
6+
7+
This tool tests and verifies the vulnerability using a functioning yet safe payload. This ensures that the security tool takes action if and only if it is necessary without false positives.
8+
69
- If the application was previously vulnerable, this will patch it.
710
- If the application was not previously vulnerable, this will simply do nothing and can remain in place.
811

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
<transformer
5555
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
5656
<manifestEntries>
57+
<Main-Class>com.contrastsecurity.App</Main-Class>
5758
<Premain-Class>com.contrastsecurity.SafeLog4J</Premain-Class>
5859
<Agent-Class>com.contrastsecurity.SafeLog4J</Agent-Class>
5960
<Can-Redefine-Classes>true</Can-Redefine-Classes>
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package com.contrastsecurity;
2+
3+
import java.io.IOException;
4+
import java.net.URISyntaxException;
5+
import java.util.List;
6+
7+
import com.sun.tools.attach.AgentInitializationException;
8+
import com.sun.tools.attach.AgentLoadException;
9+
import com.sun.tools.attach.AttachNotSupportedException;
10+
import com.sun.tools.attach.VirtualMachine;
11+
import com.sun.tools.attach.VirtualMachineDescriptor;
12+
13+
public class App {
14+
15+
private static final String SELF;
16+
17+
private static boolean CAN_ATTACH;
18+
19+
static{
20+
String self;
21+
try {
22+
self = App.class.getProtectionDomain()
23+
.getCodeSource()
24+
.getLocation()
25+
.toURI()
26+
.getPath();
27+
} catch (URISyntaxException e) {
28+
self=null;
29+
}
30+
SELF=self;
31+
//Need the agent's location or Attach won't work.
32+
try {
33+
Class.forName("com.sun.tools.attach.VirtualMachine");
34+
CAN_ATTACH=SELF!=null;
35+
} catch (ClassNotFoundException e) {
36+
CAN_ATTACH=false;
37+
}
38+
39+
}
40+
41+
public static void main(String[] args){
42+
System.out.println("SafeLog4j by Contrast Security");
43+
44+
if(args.length>0 && CAN_ATTACH){
45+
String options = args.length>=2 ? args[1] : null;
46+
patch(args[0].trim(), options);
47+
}else{
48+
showHelp();
49+
}
50+
}
51+
52+
public static void listProcesses(){
53+
List<VirtualMachineDescriptor> vms = VirtualMachine.list();
54+
vms.stream()
55+
.filter(vm -> !vm.displayName().contains("safelog")) //No need to patch ourselves
56+
.forEach(vm -> {
57+
System.out.println(vm.id() + " \t" + vm.displayName());
58+
});
59+
}
60+
61+
private static void showHelp(){
62+
System.out.println("This tool can be used in two modes for custom and third party applications:");
63+
System.out.println("1. At application startup, through the -javaagent flag.");
64+
System.out.println("2. Without application restart, if supported.");
65+
66+
if(!CAN_ATTACH){
67+
System.out.println(" -- Option 2 is not supported on this system.");
68+
}else{
69+
System.out.println();
70+
System.out.println("SafeLog4j can connect to and patch the following Java processes:");
71+
try{
72+
listProcesses();
73+
74+
System.out.println();
75+
System.out.println("To patch a process, add an argument of the ID or use the word all.");
76+
}catch(NoClassDefFoundError err){
77+
System.err.println("Please use jcmd to list Java processes.");
78+
}
79+
}
80+
}
81+
82+
private static void patch(String process, String options){
83+
try{
84+
int pid = Integer.parseInt(process);
85+
patch(pid, options);
86+
}catch(NumberFormatException ex){
87+
if("all".equalsIgnoreCase(process)){
88+
System.out.println("Patching all...");
89+
List<VirtualMachineDescriptor> vms = VirtualMachine.list();
90+
vms.stream()
91+
.filter(vm -> !vm.displayName().contains("safelog")) //No need to patch ourselves
92+
.map(vm -> vm.id())
93+
.forEach(id -> patch(Integer.parseInt(id), options));
94+
}else{
95+
System.err.println("Uknown process: " + process);
96+
System.err.println(" Use a number or all.");
97+
}
98+
}
99+
}
100+
101+
private static void patch(int process, String options){
102+
try {
103+
VirtualMachine vm = VirtualMachine.attach(String.valueOf(process));
104+
vm.loadAgent(SELF, options);
105+
System.out.println("Successfully patched process "
106+
+ process
107+
+ ". You should see safelog4j messages in its log.");
108+
} catch (AttachNotSupportedException | IOException e) {
109+
if("Non-numeric value found - int expected".equals(e.getMessage())){
110+
//Odd interplay between majors. https://bugs.eclipse.org/bugs/show_bug.cgi?id=534489
111+
String javaHome = System.getProperty("java.home");
112+
System.err.println("Unable to bridge Java version to process "
113+
+ process
114+
+ ". Please re-run this tool from its Java installation instead of " + javaHome);
115+
}else{
116+
System.err.println("Unable to connect to " + process + ". " + e.getMessage());
117+
}
118+
} catch (AgentLoadException | AgentInitializationException e) {
119+
System.err.println("Attempt to patch was unsuccessful. Process "
120+
+ process
121+
+ " must be restarted to be patched. "
122+
+ e.getMessage());
123+
}
124+
}
125+
}

src/main/java/com/contrastsecurity/SafeLog4J.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,22 @@ public static void transform(String args, Instrumentation inst) {
8181
}
8282

8383
builder.installOn(inst);
84+
85+
Loggers.log("Looking for previously loaded Log4J2.");
86+
Class[] classes = inst.getAllLoadedClasses();
87+
List<Class> reTransform = Arrays.stream(classes).filter(clazz -> clazz.getName().endsWith("suffix"))
88+
.collect(Collectors.toList());
89+
if(reTransform.isEmpty()){
90+
Loggers.log("No previously loaded Log4J2 detected.");
91+
}else{
92+
try {
93+
inst.retransformClasses(reTransform.toArray(new Class[reTransform.size()]));
94+
Loggers.log("Engaged " + reTransform.size() + " Log4J classes.");
95+
} catch (UnmodifiableClassException e) {
96+
Loggers.log("Unable to retransorm and defend previously loaded loggers: " + e.getMessage());
97+
Loggers.log(reTransform.stream().map(clazz -> clazz.getName()).collect(Collectors.joining(", ")));
98+
}
99+
}
84100
}
85101

86102
}

0 commit comments

Comments
 (0)