Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ RUST_BACKTRACE=full
#STACKDOG_PHPSHELL_MAX_FILE_BYTES=2097152
# Extra signatures, JSON: [{"id","description","pattern","weight":"strong|medium|weak"}]
#STACKDOG_PHPSHELL_SIGNATURES=/etc/stackdog/php-shell-signatures.json
# PHP2Shell (CVE-2026-109618, CVE-2026-109619): alert on PHP builds older than
# the fixed releases, on the host and in running containers. Alert only.
#STACKDOG_PHP_VERSION_CHECK_ENABLED=true

# ---------------------------------------------------------------------------
# Dashboard container (docker-compose.yml / docker-compose.app.yml)
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.2.8] - 2026-10-31

### Added

- PHP2Shell exposure check (CVE-2026-109618, CVE-2026-109619). Each sniff pass
reads the PHP version on the host and in running containers (package
database, and `PHP_VERSION` for official images) and alerts on builds older
than 8.2.35, 8.3.36, 8.4.27 or 8.5.12 (`vuln.php2shell`). Distribution
packages get a "check your vendor" alert instead, because backports keep the
old version number. `STACKDOG_PHP_VERSION_CHECK_ENABLED=false` turns it off.
- `php.worker-crash`: an alert when php-fpm or Apache workers keep dying on
memory faults, which is what attempts against PHP memory bugs leave behind.
Neither finding bans.

### Fixed

- Scan findings (file integrity, PHP shell files, package and posture checks)
shared one dedup key per detector, so when a pass found several distinct
things, only the first alerted and the rest were dropped. They now key on
what they found; log findings still key on the detector alone.

## [0.2.7] - 2026-10-31

### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "stackdog"
version = "0.2.7"
version = "0.2.8"
authors = ["Vasili Pascal <info@try.direct>"]
edition = "2021"
description = "Security platform for Docker containers and Linux servers"
Expand Down
2 changes: 1 addition & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Stackdog Security - Development Plan

**Last Updated:** 2026-04-07
**Current Version:** 0.2.7
**Current Version:** 0.2.8
**Status:** Phase 2 In Progress

## Project Vision
Expand Down
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Stackdog Security

![Version](https://img.shields.io/badge/version-0.2.7-blue.svg)
![Version](https://img.shields.io/badge/version-0.2.8-blue.svg)
![License](https://img.shields.io/badge/license-MIT-green.svg)
![Rust](https://img.shields.io/badge/rust-1.75+-orange.svg)
![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey.svg)
Expand Down Expand Up @@ -179,7 +179,7 @@ curl -fsSL https://raw.githubusercontent.com/trydirect/stackdog/main/install.sh

Pin a specific version:
```bash
curl -fsSL https://raw.githubusercontent.com/trydirect/stackdog/main/install.sh | sudo bash -s -- --version v0.2.7
curl -fsSL https://raw.githubusercontent.com/trydirect/stackdog/main/install.sh | sudo bash -s -- --version v0.2.8
```

If your repository has no published stable release yet, use `--version` explicitly.
Expand Down Expand Up @@ -614,6 +614,28 @@ a JSON file:
"pattern": "acme_shell_v\\d", "weight": "strong"}]
```

#### PHP2Shell exposure (CVE-2026-109618, CVE-2026-109619)

PHP2Shell is unauthenticated remote code execution in the PHP interpreter. The
fix is upgrading PHP; Stackdog tells you where you still have to.

| Finding | When | Action |
|---|---|---|
| `vuln.php2shell` | a PHP build older than 8.2.35, 8.3.36, 8.4.27 or 8.5.12, or any 8.1 and older | Critical alert for upstream builds (official `php` images, read from `PHP_VERSION`); Medium "check your vendor" alert for distribution packages, which backport fixes without changing the version number |
| `php.worker-crash` | 3 or more php-fpm or Apache workers dying on SIGSEGV, SIGBUS, SIGABRT or SIGILL in one log source within one pass; 1–2 per pass never alert, so one flaky extension stays quiet | alert; repeated crashes are what attempts against PHP memory bugs leave behind |

The version check reads `php8.x-common` (Debian, Ubuntu) and `php8x-common` or `php8x` (Alpine)
from the package database on the host and inside each running container, and
`PHP_VERSION` from container environments. RPM-based hosts are not covered.
Neither finding names a client, so neither bans. Turn the version check off
with `STACKDOG_PHP_VERSION_CHECK_ENABLED=false`.

Crash lines come from php-fpm and Apache error logs. Container logs are read
on their own (official images send php-fpm errors to stderr); add host logs
such as `/var/log/php8.3-fpm.log` to `STACKDOG_LOG_SOURCES`.

Stackdog does not detect the exploit request itself and cannot patch PHP.

---

## 📦 Installation
Expand Down
2 changes: 1 addition & 1 deletion VERSION.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.7
0.2.8
2 changes: 1 addition & 1 deletion docs/INDEX.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Stackdog Security - Documentation Index

**Version:** 0.2.7
**Version:** 0.2.8
**Last Updated:** 2026-04-07

---
Expand Down
6 changes: 3 additions & 3 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# Usage:
# curl -fsSL https://raw.githubusercontent.com/trydirect/stackdog/main/install.sh | sudo bash
# curl -fsSL https://raw.githubusercontent.com/trydirect/stackdog/main/install.sh | sudo bash -s -- --version v0.2.7
# curl -fsSL https://raw.githubusercontent.com/trydirect/stackdog/main/install.sh | sudo bash -s -- --version v0.2.8
#
# Installs the stackdog binary to /usr/local/bin.
# Requires: curl, tar, sha256sum (or shasum), Linux x86_64 or aarch64.
Expand Down Expand Up @@ -73,7 +73,7 @@ resolve_version() {
fi

if [ -z "$TAG" ]; then
error "Could not determine latest release. Create a GitHub release, or specify one with --version (e.g. --version v0.2.7)."
error "Could not determine latest release. Create a GitHub release, or specify one with --version (e.g. --version v0.2.8)."
fi

VERSION="$(echo "$TAG" | sed 's/^v//')"
Expand Down Expand Up @@ -136,7 +136,7 @@ main() {
echo "Install stackdog binary to ${INSTALL_DIR}."
echo ""
echo "Options:"
echo " --version VERSION Install a specific version (e.g. v0.2.7)"
echo " --version VERSION Install a specific version (e.g. v0.2.8)"
echo " --help Show this help"
exit 0
;;
Expand Down
4 changes: 2 additions & 2 deletions src/detectors/audits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ fn uncommented_lines(content: &str) -> Vec<String> {
.collect()
}

fn parse_dpkg_status(content: &str) -> Vec<(String, String)> {
pub(crate) fn parse_dpkg_status(content: &str) -> Vec<(String, String)> {
let mut packages = Vec::new();

for stanza in content.split("\n\n") {
Expand All @@ -341,7 +341,7 @@ fn parse_dpkg_status(content: &str) -> Vec<(String, String)> {
packages
}

fn parse_apk_installed(content: &str) -> Vec<(String, String)> {
pub(crate) fn parse_apk_installed(content: &str) -> Vec<(String, String)> {
let mut packages = Vec::new();
let mut package = None;
let mut version = None;
Expand Down
21 changes: 20 additions & 1 deletion src/detectors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod audits;
pub(crate) mod http_request;
mod integrity;
mod ml;
pub mod php2shell;
pub mod php_shell;

use std::collections::HashSet;
Expand All @@ -21,7 +22,10 @@ use self::audits::{ConfigAssessmentMonitor, DockerPostureMonitor, PackageInvento
use self::http_request::{extract_request, HttpRequest};
use self::integrity::FileIntegrityMonitor;
use self::ml::MlBehavioralDetector;
use self::php_shell::{PhpShellAccessDetector, PhpShellOptions, PhpShellScanner, ScanRoot};
use self::php2shell::{PhpVersionMonitor, PhpWorkerCrashDetector};
use self::php_shell::{
ContainerFilesystem, PhpShellAccessDetector, PhpShellOptions, PhpShellScanner, ScanRoot,
};
use crate::database::connection::DbPool;
use crate::sniff::analyzer::{AnomalySeverity, LogAnomaly};
use crate::sniff::reader::LogEntry;
Expand Down Expand Up @@ -98,6 +102,7 @@ pub struct DetectorRegistry {
package_inventory_monitor: PackageInventoryMonitor,
docker_posture_monitor: DockerPostureMonitor,
php_shell_scanner: PhpShellScanner,
php_version_monitor: PhpVersionMonitor,
}

impl DetectorRegistry {
Expand All @@ -109,6 +114,7 @@ impl DetectorRegistry {
package_inventory_monitor: PackageInventoryMonitor,
docker_posture_monitor: DockerPostureMonitor,
php_shell_scanner: PhpShellScanner::new(),
php_version_monitor: PhpVersionMonitor::new(),
}
}

Expand Down Expand Up @@ -139,6 +145,7 @@ impl DetectorRegistry {
self.register(PhpShellAccessDetector::new(
self.php_shell_scanner.flagged_files(),
));
self.register(PhpWorkerCrashDetector);
}

pub fn detect_log_anomalies(&self, entries: &[LogEntry]) -> Vec<LogAnomaly> {
Expand Down Expand Up @@ -187,6 +194,18 @@ impl DetectorRegistry {
.collect())
}

pub fn detect_php_version_anomalies(
&self,
containers: &[ContainerFilesystem],
host_root: Option<&str>,
) -> Vec<LogAnomaly> {
self.php_version_monitor
.detect(containers, host_root)
.into_iter()
.map(|finding| finding.to_log_anomaly())
.collect()
}

pub fn detect_config_assessment_anomalies(&self, paths: &[String]) -> Result<Vec<LogAnomaly>> {
Ok(self
.config_assessment_monitor
Expand Down
Loading
Loading