shared-hosting-recon — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited shared-hosting-recon (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You're about to do engineering work on a production system hosted on shared hosting (cPanel, Plesk, DirectAdmin, etc.) where you don't have SSH/shell access. Before you write a deploy script, before you choose an architecture, before you trust anything: you do reconnaissance.
This skill is a checklist. Walk through it once, completely, at the start. The findings will inform every architectural decision you make for the next 8 hours.
Specifically use this when:
Do NOT use this for:
Walk through every section. Don't skip any. The ones you skip are the ones that will surprise you later.
Upload a small _recon.php to the document root with this content. Run it once. Note every output. Delete it afterwards.
<?php
header('Content-Type: text/plain');
echo "=== PHP ===\n";
echo "version: " . PHP_VERSION . "\n";
echo "sapi: " . php_sapi_name() . "\n";
echo "uname: " . php_uname() . "\n";
echo "os: " . PHP_OS . "\n";
echo "\n=== INI ===\n";
$inis = ['memory_limit', 'max_execution_time', 'allow_url_fopen', 'allow_url_include',
'open_basedir', 'safe_mode', 'disable_functions', 'disable_classes',
'upload_max_filesize', 'post_max_size', 'max_input_time', 'session.save_path',
'date.timezone', 'short_open_tag', 'display_errors', 'log_errors',
'error_log'];
foreach ($inis as $k) echo "$k: " . ini_get($k) . "\n";
echo "\n=== Functions (commonly disabled on shared hosting) ===\n";
$fns = ['exec','passthru','shell_exec','system','popen','proc_open','proc_close',
'curl_exec','curl_multi_exec','escapeshellarg','escapeshellcmd',
'file_get_contents','fopen','fsockopen','pfsockopen','stream_socket_client',
'mail','dl','phpinfo','getmypid','posix_getpwuid','posix_kill',
'symlink','link','readlink','chmod','chown','chgrp'];
foreach ($fns as $f) {
echo str_pad("$f:", 32) . (function_exists($f) ? 'AVAILABLE' : 'DISABLED') . "\n";
}
echo "\n=== Classes (commonly relied on for deploy/zip work) ===\n";
$classes = ['ZipArchive','PharData','Phar','SplFileObject','RecursiveDirectoryIterator',
'RecursiveIteratorIterator','PDO','mysqli','SoapClient','DOMDocument'];
foreach ($classes as $c) {
echo str_pad("$c:", 32) . (class_exists($c) ? 'AVAILABLE' : 'MISSING') . "\n";
}
echo "\n=== Extensions ===\n";
echo implode(', ', get_loaded_extensions()) . "\n";
echo "\n=== Filesystem ===\n";
$paths = ['/tmp', sys_get_temp_dir(), getcwd(), dirname(__FILE__),
$_SERVER['DOCUMENT_ROOT'] ?? '?',
dirname($_SERVER['DOCUMENT_ROOT'] ?? '/')];
foreach (array_unique(array_filter($paths)) as $p) {
$perms = file_exists($p) ? sprintf('%04o', fileperms($p) & 0777) : 'MISSING';
$w = is_writable($p) ? 'W' : '-';
$r = is_readable($p) ? 'R' : '-';
$link = is_link($p) ? ' -> ' . @readlink($p) : '';
echo str_pad($p, 50) . " mode=$perms $r$w$link\n";
}
echo "\n=== Server ===\n";
foreach (['SERVER_SOFTWARE','GATEWAY_INTERFACE','DOCUMENT_ROOT','SERVER_ADDR',
'HTTP_HOST','HTTPS'] as $k) {
echo str_pad("$k:", 24) . ($_SERVER[$k] ?? '(unset)') . "\n";
}Critical things to look for:
| Output | What it means | What to do |
|---|---|---|
passthru: DISABLED | No shell-out from PHP | Cannot use tar, rsync, git from PHP. Build deploy in pure PHP using ZipArchive or PharData. |
curl_exec: DISABLED | cURL functions disabled | Use file_get_contents() with stream_context_create() for HTTP requests. Confirm allow_url_fopen=1. |
allow_url_fopen: 0 | Cannot fetch URLs from PHP | Major problem for any deploy that pulls from GitHub. Check if fsockopen works as fallback. |
ZipArchive: AVAILABLE | Zip extraction works | Good — can use GitHub zipball API for deploys. |
ZipArchive: MISSING | No zip lib | Try PharData instead (uses tar.gz). |
open_basedir: <something> | Can only access listed paths | Note the constraint. Cannot read/write outside it. |
/tmp mode=0755 (instead of 1777) | Cannot write temp files as user | Critical: breaks cPanel's own deploy task runner. Build your deploy outside of any tooling that needs /tmp. |
/home/<user>/tmp -> /tmp | Symlink to broken /tmp | Same as above. |
disable_functions: includes escapeshellarg | Even arg-escaping disabled | Strong signal hosting locked down hard. Plan for pure-PHP everything. |
If on cPanel, test the UAPI (User API). Hit each in the browser while logged into cPanel:
/cpsess<session>/execute/Fileman/list_files?dir=/home/<user>
/cpsess<session>/execute/Fileman/get_file_content?dir=/home/<user>&file=test.txt
/cpsess<session>/execute/Fileman/save_file_content (POST)
/cpsess<session>/execute/VersionControl/list_repositoriesCheck which functions return {"status":1} and which return {"errors":["The system could not find the function..."]}. Different cPanel versions expose different UAPI surfaces.
Functions commonly missing on older cPanel:
Fileman/compressFileman/chmodFileman/remove_filesFileman/rename_fileFileman/move_files_to_trashFileman/empty_trashIf most of these are missing, you're stuck with: list, read, write. You'll need to do anything else (delete, chmod, rename) via the File Manager UI by clicking, OR by uploading PHP that does it.
cPanel URLs have a cpsess<digits> segment. This is per-session. If your session is invalidated:
Fileman API returns 401 with "Invalid Security Token."cpsess value, replay the request.If you're scripting via a browser tool, always extract the cpsess fresh from the current URL rather than caching it. We learned this the hard way.
In cPanel → Git Version Control:
https://user:[email protected]/...). cPanel rejects this with "The clone URL cannot include a password." Workaround: write ~/.git-credentials and ~/.gitconfig first via the Fileman API, then use plain HTTPS clone URL.~/.cpanel/logs/user_task_runner.log. The cPanel UI sometimes reports "complete" when the underlying task failed. The log file is the source of truth.Permission denied creating /home/<user>/tmp/...tmp.... This means /tmp is mode 0755 instead of 1777 (server-wide misconfig). cPanel's deploy is broken on this host. You need a custom PHP-based deploy.cPanel → Disk Usage (or directly: /cpsess<>/frontend/paper_lantern/diskusage/)Note:
zikqEi0T, ziCYIb3s, etc.) — old account-transfer artifacts, probably, but they were publicly downloadable.Walk the document root looking for these — they are surprisingly common on legacy hosting:
| File / pattern | Risk | Action |
|---|---|---|
phpinfo.php, info.php, pinfo.php | Information disclosure (PHP version, env, paths) | Delete immediately. |
mysqldumper.php, adminer.php, phpmyadmin.php (outside phpmyadmin/) | Public DB admin tool | Move outside web root or delete. |
*.sql, dump.sql, db.sql.gz at web root | DB contents downloadable | Move out of web root. |
*.zip, backup.tar.gz at web root | Backup downloadable | Move out of web root. |
_old.php, *.bak, *.orig | Old code accessible | Audit; delete or rename. |
.git, .svn directories at web root | Source code leak | Block via .htaccess or remove. |
| Random binary files with no extension and no obvious purpose | Unknown — investigate | Run file (if shell) or check magic bytes via Fileman/get_file_content. |
WordPress wp-config.php~, wp-config.php.bak | Credentials leak | Delete. |
echo ini_get('error_log');Note the path. Tail it to see what's actually happening on your prod. You will be amazed at how many silent errors are happening that nobody knows about.
If error_log is empty (i.e., logs go to Apache's), check ~/access-logs/ or ~/logs/ or the cPanel Errors page.
cPanel → Cron JobsList everything currently scheduled. You inherit these. Some may be:
Read each cron command. If it's incomprehensible, find the script and read it. Don't disable any cron until you know what it does.
cPanel → phpMyAdmin → Export → SQL → SaveTake a one-time DB dump as part of recon. Even if you do nothing else with it, you have a recovery point that's not the host's responsibility.
cPanel → Zone Editor (or DNS Zone Editor)
cPanel → SSL/TLS StatusConfirm:
If cPanel → Git Version Control shows existing repositories, list them with their paths. Don't delete them — they may be legitimate or may be the previous developer's experiments. Just note they exist.
Produce a structured Markdown document with these sections:
# Shared-hosting reconnaissance: <hostname>
Date: <YYYY-MM-DD>
## PHP capabilities
- Version: ...
- Disabled functions: ... (highlight: passthru, exec, etc.)
- Available classes: ZipArchive, PharData, ...
- Critical INI settings: ...
- Filesystem: /tmp mode=..., ...
## cPanel API surface
- Working: list_files, save_file_content, get_file_content
- Missing: compress, chmod, ...
## /tmp situation
- /home/<user>/tmp -> /tmp (mode 0755) ⚠️ BROKEN
- Implication: cPanel deploys will fail silently. Custom PHP deploy required.
## Disk usage breakdown
- Total: ... GB
- public_html: ... GB (which is ... % code, ... % media)
- Mystery files at web root: ...
## Security findings
- ⚠️ admin/phpinfo.php (info disclosure) — DELETE
- ⚠️ test/admin/mysqldumper.php (public DB tool) — MOVE OUT OF WEB ROOT
- ⚠️ Database dump downloadable at /db.sql — MOVE
- 4 unidentified binary files totalling 37 GB at web root — INVESTIGATE
## Pre-existing automation
- 3 cron jobs found: <list with descriptions>
- 1 git repository in ~/repositories/<name>: <last update, purpose>
## Architectural implications
- Cannot use cPanel's native deploy (broken /tmp).
- Cannot shell out from PHP (disabled functions).
- Have ZipArchive + PharData + allow_url_fopen → can fetch + extract zips.
- Recommended deploy mechanism: <X>.
## What to fix immediately (before any other work)
1. Delete <list of security holes>
2. Take a DB backup
3. Document existing cron jobs
## What to defer
- <list of nice-to-haves>This document becomes your reference for the rest of the engagement. Every architectural decision should cite a specific line from it.
/tmp may be wrong. /home/<user>/tmp may be a symlink. /usr/bin/tar may not be reachable from PHP. Test, don't assume.In one engagement, the order of discovery was:
~/.cpanel/logs/user_task_runner.log. See Permission denied: /home/reg/tmp/..../home/reg/tmp. Discover it's a symlink to /tmp./tmp is mode 0755 instead of 1777.passthru('tar ...'). Returns empty.system. Same.disable_functions. See: passthru, exec, shell_exec, system, popen, proc_open, curl_exec, escapeshellarg, ....ZipArchive is available. It is. Build around that.Each of those discoveries took 5–30 minutes in the moment, with bewildered "why is this happening" energy. This skill compresses the entire chain into one upfront pass: 30 minutes of recon and you know everything you needed to know.
The skill is short. The pain it prevents is not.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.