כלי אבטחה
ה-toolkit ל-bug bounty ו-NotHoly CLI
toolkit קטן וישר ל-bug bounty למתחילים, בתוספת NotHoly CLI בטרמינל עם scope רשום כך שהפקודות נשארות בתוך התוכנית.
להריץ את סריקת ה-baseline הפסיבית של ZAP מול Juice Shop על המחשב שלך, לקרוא את הדוח ולבדוק התראה אחת ידנית לפני שסומכים עליה.
Zed Attack Proxy, שנקרא בדרך כלל ZAP, הוא כלי חינמי למציאת בעיות באפליקציות web. הוא עושה שני דברים שרלוונטיים למדריך הזה. הוא עובד כפרוקסי מיירט, כך שאפשר לקרוא כל בקשה שהדפדפן שולח וכל תשובה שהוא מקבל. והוא מריץ סורקים שבודקים את התעבורה הזו ומחפשים בה דפוסים חלשים.
פרוקסי הוא מתווך. בדרך כלל הדפדפן שולח בקשה ישירות אל 127.0.0.1:3000. כשהיא עוברת דרך ZAP, הדפדפן שולח אותה אל ZAP. הכלי מעביר אותה לאפליקציה, מקבל את התשובה, שומר את הזוג בהיסטוריה שלו ומחזיר את התשובה לדפדפן. האפליקציה לא יודעת ששום דבר השתנה. פשוט מתקבל אצלך עותק של השיחה.
המיקום הזה הוא הסיבה שהכלי עוזר. סורק רואה מה השרת מחזיר. פרוקסי מראה מה הדפדפן עשה בפועל, כולל כותרות, עוגיות, הפניות וה-JavaScript שהדף טען. הרבה בעיות web מתגלות רק כשמשווים בין שתי התצוגות האלה.
ל-ZAP יש שני סוגי סריקה, וההבדל קובע מה מותר להריץ ואיפה.
סריקה פסיבית קוראת תעבורה שכבר עברה דרך ZAP. כל תשובה נבדקת לדברים כמו כותרת Content-Security-Policy חסרה, עוגיות בלי הדגל HttpOnly או stack traces בדפי שגיאה. החוקים הפסיביים לא שולחים מחרוזות תקיפה. הם רק מסתכלים על מה שחזר.
סריקה אקטיבית שולחת בקשות חדשות שנבנו כדי לעורר תגובה. היא משנה פרמטרים, שולחת קלט מוזר ובודקת איך האפליקציה מגיבה. רוב הממצאים הרציניים מגיעים מהסוג הזה של בדיקה, וזה גם הסוג שמתנהג כמו תקיפה. להריץ אותה רק מול מעבדה שבבעלותך, או מול מערכת שיש לך עבורה הרשאה בכתב וסקופ מוגדר.
סריקת ה-baseline במדריך הזה פסיבית. התיעוד של ZAP מתאר אותה כסקריפט שמריץ את ה-spider מול היעד לדקה אחת כברירת מחדל, מחכה שהחוקים הפסיביים יסיימו ואז כותב את הדוח. היא לא מריצה payloads של תקיפה, ולכן היא צעד ראשון סביר במעבדה שלך.
כל התראה של ZAP היא חוק שהתאים. חוק יכול להתאים לכותרת חסרה, לדפוס בגוף התשובה או לשילוב של שניהם. הוא לא יכול לדעת למה האפליקציה שלך נועדה, על אילו נתונים היא מגנה או אם תוקף יכול להגיע לנתיב החשוב. התראה אומרת איפה לחפש. היא לא מאשרת שאפשר לנצל חולשה.
שני סוגי טעויות מופיעים כל הזמן. חלק מההתראות הן false positives: החוק התאים למשהו לא מזיק. בעיות אחרות מתפספסות לגמרי, כי הסורק לא הגיע לדף או לא שלח את הקלט שמפעיל את הפגם. מה שהופך רמז לממצא הוא צעד שאפשר לחזור עליו בעצמך: בקשה ששלחת, התשובה שראית והסבר פשוט למה זה משנה. עד שיש את שלושתם, ההתראה נשארת ברשימה מסומנת "לאימות".
כדאי לזכור את הכלל הזה לאורך המדריך. הסורק נותן נקודת התחלה, ועבודת האימות היא שלך.
צריך ארבעה דברים:
docker version.המעבדה נשארת על המחשב שלך. יש לפרסם את הפורט שלה רק על 127.0.0.1. פורט שמתפרסם בלי הקידומת הזו יכול להאזין על כל ממשק רשת, ו-Juice Shop בנוי כדי להיפרץ.
מפעילים את Juice Shop ברקע עם שם שאפשר להשתמש בו אחר כך:
docker run -d --name juice-local -p 127.0.0.1:3000:3000 bkimminich/juice-shopהדגל -d מריץ את הקונטיינר במצב detached. השמטתי את --rm בכוונה, כדי שהקונטיינר יישאר עד שעוצרים ומסירים אותו לפי השם. הקידומת 127.0.0.1: מרחיקה את הפורט מהרשת שלך.
בודקים שהוא עונה:
curl -i http://127.0.0.1:3000/אצלי השורות הראשונות של התשובה נראו כך. הסרתי את מד ההתקדמות שהכלי curl מדפיס כשהוא לא רץ עם -s:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Feature-Policy: payment 'self'
X-Recruiting: /#/jobs
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sun, 08 Nov 2026 09:06:18 GMT
ETag: W/"2700-1a1ba6896a5"
Content-Type: text/html; charset=UTF-8
Content-Length: 9984הכותרות והתאריכים שלך יהיו שונים. כדאי לרשום גם את גרסת Juice Shop, כי האפליקציה משתנה בין גרסאות. ה-endpoint הזה מחזיר אותה:
curl -s http://127.0.0.1:3000/rest/admin/application-versionאצלי הוא הדפיס {"version":"20.3.0"}. את המספר הזה רושמים בהערות ליד תוצאות הסריקה.
מריצים את הסורק מהתיקייה שבה הדוח אמור להופיע. זו הפקודה עבור Docker Desktop:
docker run --rm -t -v "$(pwd)":/zap/wrk ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.htmlלכל חלק יש תפקיד:
--rm מסיר את קונטיינר הסורק כשהסריקה מסתיימת.-v "$(pwd)":/zap/wrk מחבר את התיקייה הנוכחית שלך לתוך הקונטיינר, כך שהדוח נכתב לדיסק שלך.-t http://host.docker.internal:3000 הוא היעד. מתוך קונטיינר, host.docker.internal מצביע על המחשב שלך, שבו המעבדה מאזינה.-r zap-report.html כותב את דוח ה-HTML המלא לתיקייה המחוברת.ב-Linux, ה-host alias של Docker Desktop לא קיים כברירת מחדל. מוסיפים מיפוי host לאותה פקודה:
docker run --rm -t --add-host=host.docker.internal:host-gateway -v "$(pwd)":/zap/wrk ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.htmlבדקתי רק את הגרסה של Docker Desktop. ב-Linux, פורט שמתפרסם על 127.0.0.1 לא בהכרח יקבל חיבורים שמגיעים דרך כתובת ה-bridge של Docker. אם הסורק מדווח על שגיאות חיבור, צריך לקרוא את השורות הראשונות של הפלט שלו לפני שמשנים משהו אחר.
הניסיון הראשון נכשל בלי לסרוק כלום. בקונסולה הופיע A file based option has been specified but the directory '/zap/wrk' is not mounted, אחר כך טקסט ה-usage, והפקודה יצאה עם קוד 3. ה-mount לא עבד בסשן ה-Git Bash שלי, ולא גיליתי למה. הרצת הפקודה עם MSYS_NO_PATHCONV=1 ועם נתיב Windows מלא בדגל -v פתרה את זה:
MSYS_NO_PATHCONV=1 docker run --rm -t -v "D:/path/to/your/folder:/zap/wrk" ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.htmlצריך להחליף את הנתיב בתיקייה שלך. ההרצה הראשונה גם מורידה את האימג׳ של ZAP, ובחיבור שלי זה לקח כמעט שתים־עשרה דקות. הרצות מאוחרות יותר עולות תוך שניות.
הסורק מדפיס שורה אחת לכל חוק. זה החלק שחשוב מההרצה שלי, עם הרשימה הארוכה של החוקים שעברו מקוצרת:
Using the Automation Framework
Total of 116 URLs
PASS: Vulnerable JS Library (Powered by Retire.js) [10003]
PASS: Cookie No HttpOnly Flag [10010]
...
PASS: CSP [10055]
...
WARN-NEW: Content Security Policy (CSP) Header Not Set [10038] x 5
WARN-NEW: Cross-Domain Misconfiguration [10098] x 5
WARN-NEW: Modern Web Application [10109] x 5
FAIL-NEW: 0 FAIL-INPROG: 0 WARN-NEW: 3 WARN-INPROG: 0 INFO: 0 IGNORE: 0 PASS: 59WARN-NEW אומר שחוק סימן משהו שהסריקה לא ראתה קודם. PASS אומר שהחוק רץ ולא מצא כלום. המספר אחרי כל שם חוק הוא מזהה החוק של ZAP, ואפשר לחפש אותו בתיעוד של ZAP.
בהרצה שלי הפקודה יצאה עם קוד 2. התיעוד של baseline מפרט את קודי היציאה: 0 הוא הצלחה, 1 אומר לפחות FAIL אחד, 2 אומר לפחות WARN אחד ובלי FAIL, ו-3 הוא כל כישלון אחר. קוד 2 הוא נורמלי כאן. הוא רק אומר שהסורק מצא משהו לקרוא.
פותחים את zap-report.html בדפדפן. הסיכום בראש הדוח שלי הציג High 0, Medium 2, Low 0 ו-Informational 1. כל הרשימה הייתה שלוש התראות:
Content-Security-Policy, כותרת שמאפשרת לדפדפן להחליט אילו סקריפטים ו-frames לטעון.Access-Control-Allow-Origin: *, וזה מאפשר לכל אתר לקרוא את התשובה בדפדפן.לכל התראה יש אותם חלקים: תיאור, ה-URLs שבהם היא הופעלה, הראיה ש-ZAP ראה, כמה הערות נוספות והצעת תיקון. קודם קוראים את הראיה ואת ה-URL. התיאור מסביר מה החוק אומר באופן כללי. הראיה מראה מה קרה בתשובה הזו.
לא מדרגים את הדוח רק לפי תווית הסיכון. ZAP קובע רמת סיכון לפי החוק שלו, ולא לפי הנתונים של האפליקציה שלך. Medium על דף בלי נתונים רגישים עשוי להיות פחות חשוב מ-Low על endpoint שמטפל בחשבונות.
בוחרים בהתראת ה-CSP. הראיה שלה היא היעדרות, ולכן הבדיקה פשוטה: מבקשים את הדף, מחפשים את הכותרת ורושמים את התוצאה. הפקודה הזו מדפיסה את מספר השורות התואמות:
curl -s -i http://127.0.0.1:3000/ -o /dev/null -D - | grep -i -c '^content-security-policy'אצלי היא הדפיסה 0. גם ברשימת הכותרות המלאה שתפסתי קודם לא הייתה שורת Content-Security-Policy. ההתראה תואמת למה שהשרת שולח.
עכשיו ההתראה השנייה. הראיה היא השורה Access-Control-Allow-Origin: * שכבר הופיעה בפלט של curl -i. הבדיקה שלך צריכה לענות על שאלה אחרת: האם התשובה הזו מכילה משהו שאתר צד שלישי לא אמור לקרוא? דף השורש של המעבדה הוא HTML ציבורי, ולכן הכותרת עצמה היא לא הבעיה. הטקסט של ZAP אומר את אותו דבר. דפדפנים לא נותנים לדף של צד שלישי לקרוא בדרך הזו תשובות מאומתות. ההתראה נעשית רצינית רק כש-endpoint שמחזיר נתונים פרטיים שולח גם wildcard origin. אם נמצא endpoint כזה, בודקים אותו עם חשבון המעבדה שלך ורושמים מה הוא מחזיר.
בהתראה השלישית אין מה להוכיח. היא מתארת את סוג האפליקציה. משאירים אותה בדוח כהקשר וממשיכים.
כל תוצאה מאומתת נרשמת בטבלה או בקובץ טקסט עם ארבע עמודות: ההתראה, הבקשה ששלחת, מה שראית וההכרעה שלך. הכרעה יכולה להיות "אושר", "לא שוחזר" או "צריך עוד הקשר". שלוש התשובות האלה מכסות את רוב מה שעושים עם פלט של סורק.
השלב הזה מאפשר לראות בקשות אמיתיות, וזה החלק שהופך את הפרוקסי לשימושי מעבר לסריקה. אפשר לעשות את זה עם האפליקציה השולחנית או עם קונטיינר שמריץ ZAP במצב daemon. שתי הדרכים מתוארות כאן. הרצתי את גרסת ה-daemon. את ההדרכה השולחנית לא הרצתי, ולכן אין ממנה פלט בהמשך.
מתקינים את ZAP מהאתר הרשמי. דף ה-getting started מקשר למתקין הנוכחי. פותחים אותו ובוחרים Manual Explore, שמפעיל דפדפן ששולח את התעבורה שלו דרך ZAP. מזינים http://127.0.0.1:3000 ועוברים על דף הבית. אחר כך פותחים את הלשונית History. כל שורה היא זוג של בקשה ותשובה, עם ה-method, ה-URL וקוד הסטטוס. משווים את השורות האלה למה שרואים בכלי המפתחים של הדפדפן שלך. ההבדלים מראים מה הדפדפן שלח בלי שציפית לזה.
מפעילים את ZAP כפרוקסי רקע על פורט 8080:
docker run -d --name zap-proxy-lab -p 127.0.0.1:8080:8080 ghcr.io/zaproxy/zaproxy:stable zap.sh -daemon -host 0.0.0.0 -port 8080 -config api.disablekey=true -config api.addrs.addr.name=.* -config api.addrs.addr.regex=trueהאפשרות api.disablekey=true מסירה את בדיקת מפתח ה-API כדי שהפקודות בהמשך יעבדו בלי הגדרות נוספות. צריך להשאיר את הפורט מפורסם על 127.0.0.1 ולהסיר את הקונטיינר בסיום. כל מי שיכול להגיע ל-API יכול לשלוט ב-ZAP.
בודקים שה-API עונה:
curl -s http://127.0.0.1:8080/JSON/core/view/version/הפלט אצלי:
{"version":"2.17.0"}שולחים שתי בקשות דרך הפרוקסי. הדגל -x אומר לכלי curl להשתמש ב-ZAP. שם היעד הוא host.docker.internal כי ZAP רץ בתוך Docker, והשם שהוא משתמש בו עבור המחשב שלך הוא השם שהוא מפענח משם:
curl -s -x http://127.0.0.1:8080 -o /dev/null -w 'proxied root http=%{http_code}\n' http://host.docker.internal:3000/
curl -s -x http://127.0.0.1:8080 -o /dev/null -w 'proxied api http=%{http_code}\n' "http://host.docker.internal:3000/rest/products/search?q=apple"פלט:
proxied root http=200
proxied api http=200שואלים את ZAP כמה הודעות הוא רשם:
curl -s "http://127.0.0.1:8080/JSON/core/view/numberOfMessages/"אצלי הוא החזיר {"numberOfMessages":"5"}. אחר כך מציגים אותן עם one-liner קצר ב-Python שמדפיס את שורת הבקשה ואת שורת הסטטוס של התשובה לכל אחת:
curl -s "http://127.0.0.1:8080/JSON/core/view/messages/?start=1&count=5" | python -c "import json,sys; d=json.load(sys.stdin); [print(m['requestHeader'].splitlines()[0], '->', m['responseHeader'].splitlines()[0]) for m in d['messages']]"פלט:
GET http://host.docker.internal:3000/ HTTP/1.1 -> HTTP/1.1 200 OK
GET http://host.docker.internal:3000 HTTP/1.1 -> HTTP/1.0 0
GET http://host.docker.internal:3000/rest/products/search?q=apple HTTP/1.1 -> HTTP/1.1 200 OK
GET http://host.docker.internal:3000/rest HTTP/1.1 -> HTTP/1.0 0
GET http://host.docker.internal:3000/rest/products HTTP/1.1 -> HTTP/1.0 0שלחתי שתי בקשות דרך הפרוקסי, אבל בהיסטוריה יש חמש רשומות. לשלוש מהן אין תשובה, וזה מופיע כסטטוס 0. לא בדקתי מאיפה הן הגיעו. אין לקרוא אותן כראיה על Juice Shop. זו הסיבה לבדוק את ההיסטוריה בעצמך ולא לסמוך על ספירה. כשהרשימה שלך שונה, רושמים מה רואים ושואלים למה לפני שמסיקים מסקנה.
בסיום עוצרים ומסירים את הפרוקסי:
docker rm -f zap-proxy-labלכוון את הסורק אל יעד שלא בבעלותך. הדגל -t מקבל כל URL, והסורק ישלח אליו בקשות. היעד צריך להישאר ה-alias של המעבדה במחשב שלך.
לפרסם את המעבדה על כל הממשקים. -p 3000:3000 בלי כתובת מאזין על כל ממשק. תמיד לכתוב 127.0.0.1:3000:3000.
להתייחס לאזהרות כאל ציון. דוח עם שלוש אזהרות אומר ששלושה חוקים התאימו. צריך לאמת כל אחת לפני שסופרים אותה.
לדלג על הגרסה. Juice Shop משתנה בין גרסאות, וכך גם החוקים של ZAP. רושמים את שתי הגרסאות עם הדוח. הגרסאות שהשתמשתי בהן היו Juice Shop 20.3.0 ו-ZAP 2.17.0.
להשאיר את הפרוקסי רץ. מפתח ה-API של ה-daemon מושבת. מסירים את הקונטיינר אחרי כל סשן.
לקרוא את עמודת הסטטוס כהוכחה. שורה עם סטטוס 0 אומרת שלא נרשמה תשובה. זה לא ממצא.
לחבר את התיקייה הלא נכונה. אם הדוח חסר, בודקים קודם את ה-mount של -v. הסורק יכול לרוץ בהצלחה ועדיין לא לכתוב כלום במקום הצפוי.
כדאי להחזיק קובץ הערות אחד למעבדה. כל שורה מכילה התראה אחת, את הבקשה שבדקה אותה, מה שראית ואת ההכרעה שלך. אחרי כמה סריקות הקובץ נעשה תיעוד של מה הכלי מצא, מה אישרת ובמה הוא טעה.
אם דילגת על מדריך ההתקנה של Juice Shop, כדאי לקרוא אותו, כי המאמר הזה מניח את אותה מעבדה. המאמר הבא משתמש באותה מעבדה כדי להסביר SQL injection. שם קל יותר לראות למה התראה של סורק צריכה בדיקה ידנית: הפגם קשור לדרך שבה קלט מגיע לשאילתה, וכלי יכול רק לרמוז על זה.
התיעוד של ZAP מכסה contexts, אימות וסריקה אקטיבית. הנושאים האלה נעשים רלוונטיים ברגע שאפשר להבדיל בין בעיה מאומתת להתאמה של חוק. כדאי ללמוד אותם מול המעבדה, עם הרשאה, ולא להעביר אותם למערכות אחרות.
הפרויקט של Juice Shop מחזיק את רשימת האתגרים ואת הערות ההתקנה במאגר GitHub שלו. כדאי לבדוק בדף ה-releases שלו מהי הגרסה הנוכחית לפני שמושכים את האימג׳.
כלי אבטחה
toolkit קטן וישר ל-bug bounty למתחילים, בתוספת NotHoly CLI בטרמינל עם scope רשום כך שהפקודות נשארות בתוך התוכנית.
כלי אבטחה
הסוכן רץ על המכונה שלכם באישורכם, המודל מקבל את עבודת האבטחה, ו-engagement בודק כל פקודה מול ה-scope שלכם לפני שהיא רצה.
כלי אבטחה
סקרו את הקובץ או את ה-git diff בעורך שלכם, עם ההקשר שהקוד נותן. המודל מקבל את שאלת האבטחה והופך ממצא לבדיקה או לחוקה.