Skip to content

mayflower/morcilla

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Morcilla — Crash Oracle Extension for PHP

Morcilla is a PHP extension that acts as a crash oracle for grey-box security scanning. It intercepts specified function calls during PHP execution and returns a JSON log of all observed calls with their arguments via a response header — without modifying the target application.

How It Works

  1. A security scanner sends an HTTP request to the target PHP application with two headers:
    • Morcilla-Key: A secret API token for authentication
    • Morcilla-Intercept: A comma-separated list of functions/methods to monitor
  2. Morcilla validates the key using constant-time comparison
  3. During request execution, it observes calls to the specified functions using the Zend Observer API
  4. After execution, it returns the results as a base64-encoded JSON payload in the X-Morcilla-Result response header

When no morcilla headers are present (normal traffic), the extension adds negligible overhead — a single boolean check per function call.

Requirements

  • PHP 8.2 or later (uses the Zend Observer API)
  • A C compiler (gcc or clang)
  • PHP development headers (php-dev / php-devel package, or a PHP source tree)

Installation

Option A: Standalone Build with phpize (recommended)

Copy the ext/morcilla directory to your own project, then:

cd morcilla/
phpize
./configure --enable-morcilla
make
sudo make install

Add to your PHP configuration:

extension=morcilla.so
morcilla.key=your-secret-api-key-here

Option B: Build with PHP Source Tree

cd php-src/
./buildconf --force
./configure --enable-morcilla [other options...]
make -j$(nproc)

Option C: As a Shared Extension from PHP Source Tree

cd php-src/
./buildconf --force
./configure --enable-morcilla=shared [other options...]
make -j$(nproc)

Then add extension=morcilla.so to your php.ini.

Verify Installation

php -d morcilla.key=test -m | grep morcilla
# Output: morcilla

php -d morcilla.key=test -i | grep morcilla
# Shows morcilla configuration section

Configuration

Directive Scope Default Description
morcilla.key PHP_INI_SYSTEM "" Shared secret for authenticating scanner requests. Must be set for the extension to activate. Can only be set in php.ini or via -d, not at runtime.

Set it in php.ini:

morcilla.key = "a-long-random-secret-token"

Or pass it on the command line (useful for testing):

php -d morcilla.key=mysecret -S localhost:8080

Important: If morcilla.key is empty or unset, the extension is completely inert — no observer handlers are installed and there is zero performance impact.

Usage

Request Headers

Header Required Description
Morcilla-Key Yes Must match the configured morcilla.key value exactly
Morcilla-Intercept Yes Comma-separated list of function/method names to intercept

Intercept Target Format

  • Plain functions: file_put_contents, exec, system, mail
  • Class methods: PDO::query, mysqli::prepare, SplFileObject::fwrite
  • Matching is case-insensitive
  • Whitespace around commas is trimmed

Response Header

Results are returned in the X-Morcilla-Result response header as a base64-encoded JSON array.

Example

Start a test application:

php -d morcilla.key=secret123 -S localhost:8080 -t /var/www/app

Send a scanning request:

curl -s -D- \
  -H "Morcilla-Key: secret123" \
  -H "Morcilla-Intercept: PDO::query, file_put_contents, exec, system" \
  http://localhost:8080/index.php?id=1

Extract and decode the result:

curl -s -D- \
  -H "Morcilla-Key: secret123" \
  -H "Morcilla-Intercept: PDO::query, file_put_contents, exec" \
  http://localhost:8080/index.php?id=1 \
  2>&1 | grep X-Morcilla-Result | cut -d' ' -f2 | base64 -d | jq .

Output Format

The decoded JSON is an array of call records:

[
  {
    "function": "PDO::query",
    "file": "/var/www/app/db.php",
    "line": 42,
    "args": [
      "\"SELECT * FROM users WHERE id='1'\""
    ]
  },
  {
    "function": "file_put_contents",
    "file": "/var/www/app/upload.php",
    "line": 17,
    "args": [
      "\"/tmp/upload_a1b2c3\"",
      "\"<?php echo 'hello'; ?>\"..."
    ]
  }
]

Each record contains:

Field Type Description
function string Fully qualified function name (Class::method or function)
file string Source file where the call was made (caller location)
line integer Line number in the source file
args string[] Argument summaries (see below)

Argument Summaries

Arguments are serialized as human-readable strings with type information:

PHP Type Summary Format Example
null null null
bool true / false true
int decimal value 42
float decimal value 3.14
string "value" (max 256 chars) "SELECT * FROM users"
array array(N) array(3)
object object(ClassName) object(PDOStatement)
resource resource resource

Strings longer than 256 characters are truncated with "..." appended.

SAPI Compatibility

SAPI Support Mechanism
PHP-FPM Full sapi_getenv() in RINIT (fast path)
CGI / FastCGI Full sapi_getenv() in RINIT (fast path)
Apache mod_php Full sapi_getenv() in RINIT (fast path)
LiteSpeed Full sapi_getenv() in RINIT (fast path)
CLI dev server Full $_SERVER fallback (lazy activation)
CLI N/A No HTTP headers — extension stays inert
Embed N/A No HTTP headers — extension stays inert

For production SAPIs (FPM, CGI, Apache, LiteSpeed), headers are checked immediately in RINIT. If no morcilla headers are present, the per-call cost is a single if (!MG(active)) return; check in the begin handler.

For the CLI development server, headers are read lazily from $_SERVER on the first function call.

Limits

Limit Value Description
Max recorded calls 4096 Additional calls beyond this limit are dropped
Max argument string 256 String arguments are truncated at 256 chars
Max function name 512 Class::method names are truncated at 512 chars

These limits are defined in php_morcilla.h as MORCILLA_MAX_CALLS and MORCILLA_MAX_ARG_LEN.

Security Considerations

  • API key is system-level only (PHP_INI_SYSTEM): It cannot be changed by PHP scripts at runtime via ini_set(). It can only be set in php.ini, in a pool config, or via the -d CLI flag.
  • Constant-time key comparison: The key is validated using php_safe_bcmp() to prevent timing side-channel attacks.
  • No output modification: The extension only adds a response header. It does not alter the response body.
  • Choose a strong key: Use a long, random string (e.g., 32+ characters). The key is the only authentication mechanism.
  • Do not enable in production without a key: If morcilla.key is empty, the extension is completely inert.
  • Restrict access at the network level: Consider firewall rules or reverse proxy configuration to limit which clients can send morcilla headers.

Compile-Time Evaluated Functions

Some PHP built-in functions (e.g., strlen, strtolower) have the ZEND_ACC_COMPILE_TIME_EVAL flag. When called with constant arguments, PHP evaluates them at compile time and they do not trigger the observer API. This is expected behavior — these calls are optimized away before execution.

To intercept such functions, ensure they are called with dynamic arguments (e.g., from user input).

Troubleshooting

No X-Morcilla-Result header in response:

  1. Check that morcilla.key is configured: php -d morcilla.key=test -i | grep morcilla
  2. Verify the Morcilla-Key header value matches exactly (case-sensitive)
  3. Verify Morcilla-Intercept is not empty
  4. Check that the target functions are actually called during the request
  5. Check for compile-time evaluation (see above)

Empty JSON array []:

  • The intercepted functions were not called during the request, or the function names in Morcilla-Intercept don't match (check spelling, use Class::method format for methods)

Extension not loading:

  • Verify with php -m | grep morcilla
  • Check php --ini to confirm the right php.ini is being loaded
  • Check for errors: php -d morcilla.key=test -r "phpinfo();" 2>&1 | head

License

This extension is part of the PHP source tree and is subject to the PHP license. See the LICENSE file in the PHP source root for details.

About

Crash oracle PHP extension for grey-box security scanning

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors