已关闭
fix(cve): 修复 CVE-2026-104721 在 logback 中的漏洞 #51
infra_team创建于 27 天前关闭于 17 天前
fix(cve): 修复 CVE-2026-104721 在 logback 中的漏洞 #51
已关闭
共 2 个文件变更+169-1
| @@ -0,0 +1,164 @@ | |||
| 1 | +From 5c10a5691f25841931d714a21c3baa828950ad9b Mon Sep 17 00:00:00 2001 | ||
| 2 | +From: infra_team <zhaiwenjie1@huawei.com> | ||
| 3 | +Date: Fri, 2 Oct 2026 16:32:41 +0000 | ||
| 4 | +Subject: [PATCH] fix: CVE-2026-104721 - reject mischievous MDC values in | ||
| 5 | + MDCBasedDiscriminator | ||
| 6 | + | ||
| 7 | +Backport of the logback 1.6.5 fix for CVE-2026-104721 (incomplete | ||
| 8 | +protection against mischievous MDC values in MDCBasedDiscriminator, | ||
| 9 | +CWE-22). getDiscriminatingValue() previously returned the raw MDC value, | ||
| 10 | +which flows into the nested FileAppender's file name via SiftingAppender, | ||
| 11 | +allowing path traversal through MDC values. | ||
| 12 | + | ||
| 13 | +The value is now rejected (falling back to DefaultValue) when it is | ||
| 14 | +empty, longer than 64 characters, contains any of the characters | ||
| 15 | +/\${}[]()|?*+%@, or the sequence "..". Rejected-value warnings are rate | ||
| 16 | +limited to one per 10 minutes via an inline timestamp guard, standing in | ||
| 17 | +for the upstream BatchedFixedIntervalInvocationGate(4, | ||
| 18 | +Duration.buildByMinutes(10)) which does not exist in 1.2.8. | ||
| 19 | + | ||
| 20 | +Upstream-commit: https://github.com/qos-ch/logback/commit/1c6f0bd528882cfb75e760116f3c5f5f0e277ae3 | ||
| 21 | +Signed-off-by: infra_team <zhaiwenjie1@huawei.com> | ||
| 22 | +--- | ||
| 23 | + .../classic/sift/MDCBasedDiscriminator.java | 105 +++++++++++++++++- | ||
| 24 | + 1 file changed, 104 insertions(+), 1 deletion(-) | ||
| 25 | + | ||
| 26 | +diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java b/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java | ||
| 27 | +index d65f1b1..c1dada0 100644 | ||
| 28 | +--- a/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java | ||
| 29 | ++++ b/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java | ||
| 30 | +@@ -24,11 +24,41 @@ import java.util.Map; | ||
| 31 | + * the said value is null, then a default value is returned. | ||
| 32 | + * <p/> | ||
| 33 | + * <p>Both Key and the DefaultValue are user specified properties. | ||
| 34 | ++ * </p> | ||
| 35 | ++ * <p> | ||
| 36 | ++ * MDC values containing any of the characters {@code /}, {@code \}, {@code $}, | ||
| 37 | ++ * <code>{</code>, <code>}</code>, <code>[</code>, <code>]</code>, <code>(</code>, | ||
| 38 | ++ * <code>)</code> , <code>|</code>, <code>?</code>, <code>*</code>, <code>+</code>, | ||
| 39 | ++ * <code>%</code>, <code>,</code>, <code>@</code> or the sequence {@code ..} are rejected, | ||
| 40 | ++ * in which case the default value is returned. This prevents path separators, relative path components, | ||
| 41 | ++ * variable substitutions such as <code>${file.separator}</code>, regex | ||
| 42 | ++ * special characters as well as the anchor character '%' or the ',' and '@' characters from | ||
| 43 | ++ * leaking into file names or email addresses. | ||
| 44 | ++ * </p> | ||
| 45 | ++ * | ||
| 46 | ++ * <p>Moreover, the maximum length of an MDC value is limited to 64 characters.</p> | ||
| 47 | + * | ||
| 48 | + * @author Ceki Gülcü | ||
| 49 | + */ | ||
| 50 | + public class MDCBasedDiscriminator extends AbstractDiscriminator<ILoggingEvent> { | ||
| 51 | + | ||
| 52 | ++ static final String FORBIDDEN_CHARACTERS = "/\\${}[]()|?*+%@,"; | ||
| 53 | ++ private static final int MAX_MDC_VALUE = 64; | ||
| 54 | ++ | ||
| 55 | ++ private static final char DOT = '.'; | ||
| 56 | ++ | ||
| 57 | ++ static final String EMPTY_VALUE_WARNING = "MDC value [%s] is empty, using default value [%s] instead"; | ||
| 58 | ++ static final String REJECTED_VALUE_WARNING = "MDC value [%s] contains forbidden characters, using default value [%s] instead"; | ||
| 59 | ++ static final String REJECTED_VALUE_LENGTH_WARNING = "MDC value of length %s is longer than the maximum allowed length of " + MAX_MDC_VALUE + " characters, using default value [%s] instead"; | ||
| 60 | ++ | ||
| 61 | ++ /** | ||
| 62 | ++ * Limits how often rejected-value warnings are emitted on the hot path. | ||
| 63 | ++ * Inline stand-in for the upstream BatchedFixedIntervalInvocationGate(4, | ||
| 64 | ++ * Duration.buildByMinutes(10)) which does not exist in this version. | ||
| 65 | ++ */ | ||
| 66 | ++ private static final long WARNING_INTERVAL_MILLIS = 10L * 60L * 1000L; | ||
| 67 | ++ private long lastWarningTimestamp = 0; | ||
| 68 | ++ | ||
| 69 | + private String key; | ||
| 70 | + private String defaultValue; | ||
| 71 | + | ||
| 72 | +@@ -36,6 +66,15 @@ public class MDCBasedDiscriminator extends AbstractDiscriminator<ILoggingEvent> | ||
| 73 | + * Return the value associated with an MDC entry designated by the Key | ||
| 74 | + * property. If that value is null, then return the value assigned to the | ||
| 75 | + * DefaultValue property. | ||
| 76 | ++ * <p> | ||
| 77 | ++ * If the MDC value is empty, is longer than {@link #MAX_MDC_VALUE} | ||
| 78 | ++ * characters, or contains any of the characters {@code /}, {@code \}, | ||
| 79 | ++ * {@code $}, <code>{</code>, <code>}</code>, <code>[</code>, | ||
| 80 | ++ * <code>]</code>, <code>(</code>, <code>)</code>, <code>|</code>, | ||
| 81 | ++ * <code>?</code>, <code>*</code>, <code>+</code>, <code>%</code>, | ||
| 82 | ++ * <code>,</code>, <code>@</code> or the sequence {@code ..}, then the value | ||
| 83 | ++ * assigned to the DefaultValue property is returned. | ||
| 84 | ++ * </p> | ||
| 85 | + */ | ||
| 86 | + public String getDiscriminatingValue(ILoggingEvent event) { | ||
| 87 | + // http://jira.qos.ch/browse/LBCLASSIC-213 | ||
| 88 | +@@ -47,8 +86,72 @@ public class MDCBasedDiscriminator extends AbstractDiscriminator<ILoggingEvent> | ||
| 89 | + if (mdcValue == null) { | ||
| 90 | + return defaultValue; | ||
| 91 | + } else { | ||
| 92 | +- return mdcValue; | ||
| 93 | ++ String sanitized = sanitizePathCharacters(mdcValue, event.getTimeStamp()); | ||
| 94 | ++ return sanitized == null ? defaultValue : sanitized; | ||
| 95 | ++ } | ||
| 96 | ++ } | ||
| 97 | ++ | ||
| 98 | ++ /** | ||
| 99 | ++ * Returns the value unchanged if it is safe as a file-name segment, and | ||
| 100 | ++ * {@code null} otherwise. | ||
| 101 | ++ * <p> | ||
| 102 | ++ * A value is rejected if it is empty, if it is longer than | ||
| 103 | ++ * {@link #MAX_MDC_VALUE} characters, if it contains any of the characters | ||
| 104 | ++ * {@code /}, {@code \}, {@code $}, <code>{</code> or <code>}</code>, or the | ||
| 105 | ++ * sequence {@code ..}. A (rate limited) warning is emitted for each | ||
| 106 | ++ * rejected value. | ||
| 107 | ++ * </p> | ||
| 108 | ++ * <p> | ||
| 109 | ++ * Performs a single scan and no allocation. | ||
| 110 | ++ * </p> | ||
| 111 | ++ */ | ||
| 112 | ++ String sanitizePathCharacters(String value, long timestamp) { | ||
| 113 | ++ if (value == null) { | ||
| 114 | ++ return null; | ||
| 115 | ++ } | ||
| 116 | ++ final int len = value.length(); | ||
| 117 | ++ if (len == 0) { | ||
| 118 | ++ if (!isTooSoon(timestamp)) { | ||
| 119 | ++ addWarn(String.format(EMPTY_VALUE_WARNING, value, defaultValue)); | ||
| 120 | ++ } | ||
| 121 | ++ return null; | ||
| 122 | ++ } | ||
| 123 | ++ | ||
| 124 | ++ if (len > MAX_MDC_VALUE) { | ||
| 125 | ++ if (!isTooSoon(timestamp)) { | ||
| 126 | ++ addWarn(String.format(REJECTED_VALUE_LENGTH_WARNING, len, defaultValue)); | ||
| 127 | ++ } | ||
| 128 | ++ return null; | ||
| 129 | ++ } | ||
| 130 | ++ | ||
| 131 | ++ char previous = 0; | ||
| 132 | ++ for (int i = 0; i < len; i++) { | ||
| 133 | ++ char c = value.charAt(i); | ||
| 134 | ++ if (isForbiddenCharacter(c) || (c == DOT && previous == DOT)) { | ||
| 135 | ++ if (!isTooSoon(timestamp)) { | ||
| 136 | ++ addWarn(String.format(REJECTED_VALUE_WARNING, value, defaultValue)); | ||
| 137 | ++ } | ||
| 138 | ++ return null; | ||
| 139 | ++ } | ||
| 140 | ++ previous = c; | ||
| 141 | ++ } | ||
| 142 | ++ return value; | ||
| 143 | ++ } | ||
| 144 | ++ | ||
| 145 | ++ private static boolean isForbiddenCharacter(char c) { | ||
| 146 | ++ return FORBIDDEN_CHARACTERS.indexOf(c) != -1; | ||
| 147 | ++ } | ||
| 148 | ++ | ||
| 149 | ++ /** | ||
| 150 | ++ * Inline rate limiter standing in for the upstream invocation gate: allows | ||
| 151 | ++ * one warning per {@link #WARNING_INTERVAL_MILLIS}. | ||
| 152 | ++ */ | ||
| 153 | ++ private boolean isTooSoon(long timestamp) { | ||
| 154 | ++ if (timestamp - lastWarningTimestamp < WARNING_INTERVAL_MILLIS) { | ||
| 155 | ++ return true; | ||
| 156 | + } | ||
| 157 | ++ lastWarningTimestamp = timestamp; | ||
| 158 | ++ return false; | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + @Override | ||
| 162 | +-- | ||
| 163 | +2.39.5 | ||
| 164 | + | ||
| @@ -1,6 +1,6 @@ | |||
| 1 | Name: logback | 1 | Name: logback |
| 2 | Version: 1.2.8 | 2 | Version: 1.2.8 |
| 3 | -Release: 4 | 3 | +Release: 5 |
| 4 | Summary: A Java logging library | 4 | Summary: A Java logging library |
| 5 | License: LGPLv2 or EPL-1.0 | 5 | License: LGPLv2 or EPL-1.0 |
| 6 | URL: http://logback.qos.ch/ | 6 | URL: http://logback.qos.ch/ |
| @@ -9,6 +9,7 @@ Source0: https://github.com/qos-ch/logback/archive/v_%{version}.tar.gz | |||
| 9 | Patch0001: logback-1.2.8-jetty.patch | 9 | Patch0001: logback-1.2.8-jetty.patch |
| 10 | Patch0002: CVE-2023-6378-and-CVE-2023-6481.patch | 10 | Patch0002: CVE-2023-6378-and-CVE-2023-6481.patch |
| 11 | Patch0003: CVE-2024-12798-and-CVE-2024-12801.patch | 11 | Patch0003: CVE-2024-12798-and-CVE-2024-12801.patch |
| 12 | +Patch0004: CVE-2026-104721.patch | ||
| 12 | 13 | ||
| 13 | BuildRequires: java-devel >= 1:1.6.0 maven-local mvn(javax.mail:mail) | 14 | BuildRequires: java-devel >= 1:1.6.0 maven-local mvn(javax.mail:mail) |
| 14 | BuildRequires: mvn(javax.servlet:javax.servlet-api) mvn(junit:junit) mvn(log4j:log4j:1.2.17) | 15 | BuildRequires: mvn(javax.servlet:javax.servlet-api) mvn(junit:junit) mvn(log4j:log4j:1.2.17) |
| @@ -124,6 +125,9 @@ cp -r %{name}-examples/pom.xml %{name}-examples/src %{buildroot}%{_datadir}/%{na | |||
| 124 | %files help -f .mfiles-javadoc | 125 | %files help -f .mfiles-javadoc |
| 125 | 126 | ||
| 126 | %changelog | 127 | %changelog |
| 128 | +* Sat Oct 03 2026 infra_team <zhaiwenjie1@huawei.com> - 1.2.8-5 | ||
| 129 | +- Fix CVE-2026-104721 | ||
| 130 | + | ||
| 127 | * Mon Jan 20 2025 yaoxin <1024769339@qq.com> - 1.2.8-4 | 131 | * Mon Jan 20 2025 yaoxin <1024769339@qq.com> - 1.2.8-4 |
| 128 | - Fix CVE-2024-12798 and CVE-2024-12801 | 132 | - Fix CVE-2024-12798 and CVE-2024-12801 |
| 129 | 133 | ||