DEV Community

Daniel
Daniel

Posted on

CVE-2026-40021: How Invalid XML Properties Could Silently Drop Log Events in Apache Log4net

While researching the Apache Log4j Bug Bounty Program on YesWeHack, I found a flaw in Apache Log4net that could cause individual logging events to disappear during XML serialization.

The affected path was XmlLayoutSchemaLog4J.

The problem was not ordinary XML escaping. Property names and rendered property values were written directly into XML attributes without first masking characters that are forbidden by XML 1.0.

When one of those characters reached the layout, the XML writer could throw. The appender pipeline caught the exception, but the event itself was never written.

That turned a formatting failure into a security relevant logging problem: attacker influenced data could suppress the log entry that was supposed to record the activity.

The issue was assigned CVE-2026-40021 and remained Medium through triage. At the time of writing, the YesWeHack report has not received a bounty payment.

The vulnerable serialization path

The relevant code was inside XmlLayoutSchemaLog4J.FormatXml().

When the layout serialized event properties, it wrote each property key and rendered value directly into XML attributes:

PropertiesDictionary properties = loggingEvent.GetProperties();

if (properties.Count > 0)
{
  writer.WriteStartElement("log4j:properties", "log4j", "properties", "log4net");

  foreach (KeyValuePair<string, object?> entry in properties)
  {
    writer.WriteStartElement("log4j:data", "log4j", "data", "log4net");
    writer.WriteAttributeString("name", entry.Key);

    string? valueStr =
      loggingEvent.Repository?.RendererMap.FindAndRender(entry.Value);

    if (!string.IsNullOrEmpty(valueStr))
    {
      writer.WriteAttributeString("value", valueStr);
    }

    writer.WriteEndElement();
  }

  writer.WriteEndElement();
}
Enter fullscreen mode Exit fullscreen mode

The critical operations were:

writer.WriteAttributeString("name", entry.Key);
writer.WriteAttributeString("value", valueStr);
Enter fullscreen mode Exit fullscreen mode

Neither value passed through invalid character masking before reaching the XML writer.

That matters because escaping and validity are different problems.

Characters such as <, > and & can be represented safely through XML escaping. Some control characters, including U+0001, are not valid XML 1.0 characters at all.

With normal character checking enabled, attempting to write one of those characters can cause serialization to fail.

Log4net already had the expected protection

The behavior stood out because the layout hierarchy already exposed a setting intended to handle invalid XML characters:

public string InvalidCharReplacement { get; set; } = "?";
Enter fullscreen mode Exit fullscreen mode

The corresponding documentation states that character replacement applies to log messages, property names and property values.

Other fields in the XML layout already used transformation helpers that account for invalid characters.

For example:

Transform.WriteEscapedXmlString(
  writer,
  loggingEvent.RenderedMessage,
  InvalidCharReplacement
);
Enter fullscreen mode Exit fullscreen mode

Property serialization did not receive equivalent treatment.

The issue was therefore not an absence of XML safety logic across Log4net. It was an inconsistent path where properties bypassed the protection expected by the layout.

Why the event disappeared

The next question was what happened after serialization failed.

Appender execution catches nonfatal exceptions around the call that writes the logging event:

try
{
  _recursiveGuard = true;

  if (FilterEvent(loggingEvent) && PreAppendCheck())
  {
    Append(loggingEvent);
  }
}
catch (Exception ex) when (!ex.IsFatal())
{
  ErrorHandler.Error("Failed in DoAppend", ex);
}
finally
{
  _recursiveGuard = false;
}
Enter fullscreen mode Exit fullscreen mode

If XmlLayoutSchemaLog4J throws while formatting the event, the exception is handled internally.

The application can continue running.

But the affected event is not written.

That behavior is what makes the bug security relevant. The failure occurs inside the logging pipeline, and the evidence represented by that individual event can be lost.

Reproducing the issue

I reproduced the problem with a minimal Log4net configuration using a FileAppender and XmlLayoutSchemaLog4J.

The controlled property value was:

data.Properties["user"] = "A\u0001B";
Enter fullscreen mode Exit fullscreen mode

The inserted U+0001 character is forbidden by XML 1.0.

The event then went through the normal logging path:

var evt = new LoggingEvent(null, repo, data);
log.Logger.Log(evt);
Enter fullscreen mode Exit fullscreen mode

The expected behavior was for the invalid character to be replaced using InvalidCharReplacement, whose default value is ?.

The resulting property value should therefore remain serializable as something equivalent to:

A?B
Enter fullscreen mode Exit fullscreen mode

Instead, the raw value reached the XML attribute writer.

Serialization failed, the appender handled the exception, and the original event was absent from the expected output.

The proof did not require modifying an existing log file or exploiting a downstream XML parser. The event was lost before successful log emission.

Security impact

Applications often enrich logging events with contextual properties such as request metadata, user identifiers, authentication context, correlation values and other data that may originate from remote input.

If attacker controlled data is copied into one of those properties, a forbidden XML character can cause the corresponding event to fail serialization when XmlLayoutSchemaLog4J is used.

The practical consequence is reduced audit trail integrity.

An attacker does not gain the ability to rewrite previous records or modify arbitrary application data. The demonstrated impact is narrower and more precise: individual events containing the triggering data can be suppressed from the XML log output.

That can weaken investigation and detection when the lost event is the one documenting the attacker controlled request.

Severity and CVE assignment

The report was submitted as Medium with CVSS 3.1 score 5.3:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
Enter fullscreen mode Exit fullscreen mode

During triage, the impact model was revised.

The scope changed because the effect was considered to apply to the log collection system rather than the originating system. Integrity impact was set to Low and availability impact was removed.

The resulting YesWeHack vector was:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N
Enter fullscreen mode Exit fullscreen mode

with a final score of 5.8 Medium.

The report was later assigned:

CVE-2026-40021
Enter fullscreen mode Exit fullscreen mode

Apache later published the final advisory with a broader affected surface covering both XmlLayout and XmlLayoutSchemaLog4J, including MDC property keys, property values, and the identity field. Versions before 3.3.0 are affected, and 3.3.0 contains the fix. The Apache advisory credits the discovery to f00dat.

Remediation

The property serialization path should apply invalid character masking before writing both property names and rendered values into XML attributes.

Conceptually, the vulnerable code:

writer.WriteAttributeString("name", entry.Key);
Enter fullscreen mode Exit fullscreen mode

should instead sanitize the property name:

writer.WriteAttributeString(
  "name",
  Transform.MaskXmlInvalidCharacters(
    entry.Key,
    InvalidCharReplacement
  )
);
Enter fullscreen mode Exit fullscreen mode

The same protection should be applied to rendered values:

writer.WriteAttributeString(
  "value",
  Transform.MaskXmlInvalidCharacters(
    valueStr,
    InvalidCharReplacement
  )
);
Enter fullscreen mode Exit fullscreen mode

This keeps the event serializable while preserving the configured replacement behavior.

A regression test should create a logging event containing a forbidden XML character in a property, format it with XmlLayoutSchemaLog4J, and verify that the event is emitted successfully with the invalid character replaced.

The broader lesson

Logging code is part of the security boundary when applications depend on it for auditability and detection.

A formatter failure may look minor if the only question is whether the output remains valid XML.

The more important question is what happens to the event when formatting fails.

In this case, the exception was absorbed by the appender pipeline while the original event disappeared.

That changed the impact from malformed output to loss of security relevant evidence.

The bug came from a small inconsistency in how XML properties were serialized, but its effect reached the reliability of the audit trail itself.

That is what ultimately made the issue worthy of CVE-2026-40021.

Top comments (0)