Skip to content

Commit

Permalink
Use class transformer
Browse files Browse the repository at this point in the history
  • Loading branch information
makindotcc committed Dec 11, 2021
1 parent f8849fa commit ae1e6ea
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 24 deletions.
16 changes: 15 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation

plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '7.0.0'
}

compileJava {
Expand All @@ -16,14 +19,25 @@ repositories {
mavenCentral()
}

jar {
dependencies {
implementation group: 'org.ow2.asm', name: 'asm', version: '9.2'
}

shadowJar {
archiveFileName = 'Log4jCveFix.jar'
manifest {
attributes "Premain-Class": "cc.makin.log4jfix.Agent"
attributes "Can-Retransform-Classes": true
attributes "Can-Redefine-Classes": true
}
}

task relocateShadowJar(type: ConfigureShadowRelocation) {
target = tasks.shadowJar
prefix = "cc.makin"
}
tasks.shadowJar.dependsOn tasks.relocateShadowJar

test {
useJUnitPlatform()
}
73 changes: 50 additions & 23 deletions src/main/java/cc/makin/log4jfix/Agent.java
Original file line number Diff line number Diff line change
@@ -1,38 +1,65 @@
package cc.makin.log4jfix;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class Agent {
public static void premain(String args, Instrumentation instrumentation) {
// unexploitable (11.12.2021) logger
System.out.println("[INFO] Initializing log4j anti bomb.");
setNoneMatcherAsDefault();
System.out.println("[LOG4j FIX] Initializing log4j anti bomb.");
instrumentation.addTransformer(new StrSubstitutorTransformer(), true);
}

private static void setNoneMatcherAsDefault() {
try {
Object noneMatcher = getNoneMatcher();

Class<?> strSubstitutor = Class.forName("org.apache.logging.log4j.core.lookup.StrSubstitutor");
Field defaultPrefixField = strSubstitutor.getField("DEFAULT_PREFIX");
defaultPrefixField.setAccessible(true);
static class StrSubstitutorTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) {
// endsWith - tolerate remapped packages
if (className.endsWith("org/apache/logging/log4j/core/lookup/StrSubstitutor")) {
ClassReader classReader = new ClassReader(classfileBuffer);
ClassWriter classWriter = new ClassWriter(classReader, 0);
classReader.accept(new StrSubstitutorOdwiedzacz(classWriter), 0);
return classWriter.toByteArray();
} else {
return classfileBuffer;
}
}

Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(defaultPrefixField, defaultPrefixField.getModifiers() & ~Modifier.FINAL);
private static class StrSubstitutorOdwiedzacz extends ClassVisitor {
public StrSubstitutorOdwiedzacz(ClassVisitor classVisitor) {
super(Opcodes.ASM8, classVisitor);
}

defaultPrefixField.set(null, noneMatcher);
} catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException e) {
throw new RuntimeException("Unsupported log4j version.", e);
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if ("<clinit>".equals(name)) {
return new NoneMatcherPrefixVisitor(super.visitMethod(access, name, descriptor, signature, exceptions));
} else {
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
}
}
}

private static Object getNoneMatcher() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Class<?> strMatcherClazz = Class.forName("org.apache.logging.log4j.core.lookup.StrMatcher");
Field noneMatcherField = strMatcherClazz.getDeclaredField("NONE_MATCHER");
noneMatcherField.setAccessible(true);
return noneMatcherField.get(null);
private static class NoneMatcherPrefixVisitor extends MethodVisitor {
public NoneMatcherPrefixVisitor(MethodVisitor child) {
super(Opcodes.ASM8, child);
}

@Override
public void visitLdcInsn(Object value) {
if ("${".equals(value)) {
System.out.println("[LOG4J FIX] Replacing log4j substitutor prefix matcher to none.");
super.visitLdcInsn("");
} else {
super.visitLdcInsn(value);
}
}
}
}
}

0 comments on commit ae1e6ea

Please sign in to comment.