GitHub Security Advisory: GHSA-whv5-4q2f-q68g
Package: devcode-it/openstamanager (COMPOSER)
Severity: HIGH (CVSS 7.2)
CWE: CWE-502: Deserialization of Untrusted Data
Vulnerable versions: <= 2.10.1
Patched in: 2.10.2

Summary: OpenSTAManager Affected by Remote Code Execution via Insecure Deserialization in OAuth2

Details:
## Description

The `oauth2.php` file in OpenSTAManager is an **unauthenticated** endpoint (`$skip_permissions = true`). It loads a record from the `zz_oauth2` table using the attacker-controlled GET parameter `state`, and during the OAuth2 configuration flow calls `unserialize()` on the `access_token` field **without any class restriction**.

An attacker who can write to the `zz_oauth2` table (e.g., via the arbitrary SQL injection in the Aggiornamenti module reported in [GHSA-2fr7-cc4f-wh98](https://github.com/devcode-it/openstamanager/security/advisories/GHSA-2fr7-cc4f-wh98)) can insert a malicious serialized PHP object (gadget chain) that upon deserialization executes arbitrary commands on the server as the `www-data` user.

## Affected code

### Entry point — `oauth2.php`

```php
$skip_permissions = true;                              // Line 23: NO AUTHENTICATION
include_once __DIR__.'/core.php';

$state = $_GET['state'];                               // Line 28: attacker-controlled
$code = $_GET['code'];

$account = OAuth2::where('state', '=', $state)->first(); // Line 33: fetches injected record
$response = $account->configure($code, $state);          // Line 51: triggers the chain
```

### Deserialization — `src/Models/OAuth2.php`

```php
// Line 193 (checkTokens):
$access_token = $this->access_token ? unserialize($this->access_token) : null;

// Line 151 (getAccessToken):
return $this->attributes['access_token'] ? unserialize($this->attributes['access_token']) : null;
```

`unserialize()` is called without the `allowed_classes` parameter, allowing instantiation of any class loaded by the Composer autoloader.

## Execution flow

```
oauth2.php (no auth)
  → configure()
    → needsConfiguration()
      → getAccessToken()
        → checkTokens()
          → unserialize($this->access_token)   ← attacker payload
            → Creates PendingBroadcast object (Laravel/RCE22 gadget chain)
          → $access_token->hasExpired()         ← PendingBroadcast lacks this method → PHP Error
        → During error cleanup:
          → PendingBroadcast.__destruct()       ← fires during shutdown
            → system($command)                  ← RCE
```

The HTTP response is 500 (due to the `hasExpired()` error), but the command has already executed via `__destruct()` during error cleanup.

## Full attack chain

This vulnerability is combined with the arbitrary SQL injection in the Aggiornamenti module ([GHSA-2fr7-cc4f-wh98](https://github.com/devcode-it/openstamanager/security/advisories/GHSA-2fr7-cc4f-wh98)) to achieve unauthenticated RCE:

1. **Payload injection** (requires admin account): Via `op=risolvi-conflitti-database`, arbitrary SQL is executed to insert a malicious serialized object into `zz_oauth2.access_token`
2. **RCE trigger** (unauthenticated): A GET request to `oauth2.php?state=<known_value>&code=x` triggers the deserialization and executes the command

**Persistence note**: The `risolvi-conflitti-database` handler ends with `exit;` (line 1