Jun 18, 2026·6 min read·27 visits
A local symlink-following vulnerability in BBOT's github_workflows module allows an attacker sharing the scan directory to overwrite arbitrary local files when a victim scans a targeted repository.
The github_workflows module in BBOT (Black Lantern Security OSINT framework) versions 2.0.0 through 2.8.4 constructs local directory paths from user-controlled repository and owner names without validating for symbolic links. A local attacker sharing the scan directory can pre-plant a symlink at the predictable output path, forcing BBOT to write downloaded workflow artifacts or run logs to an arbitrary location on the filesystem.
BBOT is an active OSINT and reconnaissance framework used to gather threat intelligence and map external attack surfaces. The github_workflows module within BBOT retrieves workflow run logs and artifacts from a target GitHub repository. This functionality is exposed to the local filesystem through automatic directory creation and file storage procedures.
To store these gathered resources locally, the module organizes downloads based on the target repository owner and repository name. Because these names are determined dynamically during a scan, they serve as user-controlled variables. When configuring directories to save these files, the framework relies on path construction techniques that do not verify the underlying file types.
This lack of verification introduces an attack surface classified as CWE-59: Improper Link Resolution Before File Access ('Link Following'). A local adversary who has write access to the shared scan directory or a predictable public path (e.g., /tmp) can create symbolic links targeting sensitive system resources. When the victim initiates a scan, the framework resolves these links blindly, allowing arbitrary file writes to occur.
The root cause of CVE-2026-12567 lies in the omission of path-sanitization checks prior to invoking directory creation and file-write calls. Specifically, the framework constructs target paths by joining a trusted base directory with variables retrieved from GitHub API responses or user configurations.
The vulnerable execution flow follows this sequence:
folder = self.output_dir / owner / repo
self.helpers.mkdir(folder)
file_destination = folder / filenameThe self.helpers.mkdir utility creates the requested folder structure recursively. However, it fails to verify whether any intermediate directory component (such as owner or repo) is an existing symbolic link pointing outside the designated output_dir boundary.
By leveraging this behavioral gap, a local attacker can exploit predictable paths. Because the framework organizes output directories using the format {output_dir}/{owner}/{repo}, the exact layout of the target directory is known beforehand. This allows the pre-creation of symlinks that route the eventual file creation payloads toward sensitive locations, bypassing normal filesystem organizational limits.
In BBOT versions prior to 2.8.5, paths were created directly using the pathlib division operator without path-element inspection. The fix introduced in commit 16d9c42b6c591c07ee94d260cb0588e72d4eae2b mitigates this weakness by recursively inspecting each subdirectory component starting from the safe base path.
Below is the comparison between the vulnerable and patched file operations:
# Vulnerable Path construction (bbot/modules/github_workflows.py)
async def download_run_logs(self, owner, repo, run_id):
folder = self.output_dir / owner / repo
self.helpers.mkdir(folder) # Vulnerable: Creates path recursively without link checks
filename = f"run_{run_id}.zip"
file_destination = folder / filenameThe patch introduces the _check_output_path helper function to validate path components individually:
# Patched Implementation (bbot/modules/github_workflows.py)
def _check_output_path(self, folder):
try:
# Ensure the target folder is a child of the output_dir
rel = folder.relative_to(self.output_dir)
except ValueError:
return False
current = self.output_dir
# Walk down the relative components and verify none are symlinks
for part in rel.parts:
current = current / part
if current.is_symlink():
self.warning(f"Refusing to write through symlink: {current}")
return False
return TrueWhile this fix successfully blocks basic symlink redirection, a critical analysis reveals two limitations:
Time-of-Check to Time-of-Use (TOCTOU): The framework performs the validation in Python before invoking the mkdir and file-write commands. In an active, multi-user shared filesystem, an attacker could monitor directory modifications. Using tools like inotify, the attacker can swap the verified directory with a symlink immediately after _check_output_path returns True but before the write operation finishes.
Unchecked Base Directory: The validation assumes self.output_dir itself is secure and not a symlink. If the parent or base scan output directory is compromised, the logic can be subverted.
Hardlink Attacks: The check depends on is_symlink(), which ignores hardlinks. If hardlink protection (fs.protected_hardlinks) is not enabled at the operating system level, an attacker might still attempt link attacks on files sharing the same physical partition.
To successfully execute a symlink attack against a BBOT instance using the github_workflows module, the following conditions must be met:
/tmp or a shared group folder.The attack flow is visualized below:
An attacker begins by pre-creating the target directory structure. For example, if the victim is expected to scan a repository named testrepo owned by testowner, the attacker constructs the path inside the target scan folder:
mkdir -p /tmp/bbot_output/testowner
ln -s /home/victim/.ssh/authorized_keys /tmp/bbot_output/testowner/testrepoWhen the victim runs BBOT, the module queries the GitHub API, retrieves run logs, and writes them to the target destination. Because the symlink points to /home/victim/.ssh/authorized_keys, the written data (in this case, compressed zip logs or text log formats) is directed into the SSH configuration file, corrupting it or appending uncontrolled text.
The impact of CVE-2026-12567 is limited compared to remote code execution bugs, resulting in a CVSS v3.1 score of 2.2 (Low). The vector breakdown is CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:N/I:L/A:N.
The attack vector is restricted to local system access (AV:L), and requires high attack complexity (AC:H) due to the timing and naming coordination required. The attacker must anticipate the exact GitHub repository and owner configuration targeted by the victim.
While the integrity impact is rated Low (I:L), successful exploitation results in writing user-controlled GitHub artifacts and logs into administrative or target files. An attacker could overwrite configuration files, system scripts, or security keys. This could potentially disrupt local services or allow the attacker to manipulate environmental variables or access credentials in subsequent phases of an intrusion.
CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:N/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
BBOT Black Lantern Security | >= 2.0.0, <= 2.8.4 | 2.8.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-59 |
| Attack Vector | Local |
| CVSS v3.1 Score | 2.2 |
| EPSS Score | 0.0009 (Percentile: 0.60%) |
| Impact | Low Integrity Impact (Unsanitized local file write) |
| Exploit Status | Proof of Concept (PoC) available in official test suite |
| KEV Status | Not listed in CISA KEV |
The software attempts to access a file based on a filename, but it does not properly associate the name with the intended file entity, which can allow an attacker to substitute a different file (e.g. via symbolic links).
CVE-2026-58263 is a high-severity Mutation Cross-Site Scripting (mXSS) vulnerability affecting Jodit Editor prior to version 4.12.28. The flaw exists in Jodit's built-in clean-html sanitizer plugin, which fails to securely parse and sanitize nested elements containing foreign namespaces like MathML and SVG. Attackers can bypass sanitization by smuggling malicious payload elements inside rawtext container tags like style inside a MathML node, leading to DOM mutation and unauthenticated arbitrary script execution in the context of the user's browser session.
Jodit Editor versions prior to 4.13.6 are vulnerable to client-side Cross-Site Scripting (XSS). The clean-html plugin's sanitization routine performs case-sensitive lookups against uppercase-only element blacklists. When processing XML-based foreign namespaces such as SVG or MathML, DOM engines preserve the lowercase format of tags. Because Jodit's denyTags check fails to normalize tag casing, malicious script blocks nested inside foreign namespace elements completely bypass validation and serialize directly into the editor output.
A critical code injection vulnerability exists in Savon, a widely used SOAP client library for Ruby, prior to version 2.17.2. The vulnerability resides within the Savon::Model.all_operations module, where operation names fetched from a target Web Services Description Language (WSDL) document are dynamically evaluated via module_eval without sanitization. An attacker capable of manipulating the target WSDL document (e.g., through Man-in-the-Middle attacks, DNS hijacking, or Server-Side Request Forgery) can execute arbitrary Ruby code in the context of the parent application process.
An integer conversion overflow vulnerability exists in the XCF decoder of ImageMagick before version 6.9.13-51 and 7.1.2-26. The issue arises from mixed-type arithmetic that promotes calculation results to floating-point representations, causing an undefined cast back to integer. Under optimizing compilers, this undefined behavior results in bounds checks being bypassed, allowing out-of-bounds heap reads.
An authenticated file upload validation bypass vulnerability exists in the REDAXO CMS Mediapool addon in versions 5.18.2 through 5.21.0. Under permissive web server configurations, this allows authenticated users with media upload privileges to achieve remote code execution via multi-segment extension file uploads.
A critical SQL injection vulnerability exists in the @nocobase/plugin-notification-in-app-message plugin of NocoBase prior to version 2.0.61. The flaw is caused by direct string interpolation of user-controlled input into a Sequelize.literal() query, allowing authenticated users to execute stacked PostgreSQL queries and achieve remote code execution on the underlying database server.