ניתוח JavaScript סטטי¶
מבוא¶
ניתוח JavaScript סטטי הוא אחת הטכניקות החשובות ביותר בחקירה מתקדמת. אפליקציות ווב מודרניות מבוססות על frameworks כמו React, Angular, ו-Vue, שאורזות כמות גדולה של לוגיקה בצד הלקוח. בתוך קבצי JavaScript אלו מסתתרים נתיבי API נסתרים, מפתחות, טוקנים, ונקודות קצה שלא מתועדות - מידע שיכול להוביל לגילוי חולשות קריטיות.
בשיעור זה נלמד לאתר קבצי JavaScript, לנתח אותם, לחלץ מידע רגיש, ולהשתמש בכלים אוטומטיים ובטכניקות ידניות.
למה ניתוח JavaScript חשוב¶
מה מסתתר בקוד JavaScript¶
מפתחים לעיתים קרובות משאירים מידע רגיש בקוד הלקוח:
- נתיבי API פנימיים - נקודות קצה שלא מופיעות בתיעוד
- מפתחות API - מפתחות לשירותים חיצוניים (AWS, Google Maps, Firebase)
- כתובות פנימיות - שרתי staging, development, ו-admin
- לוגיקת הרשאות - בדיקות הרשאה שנעשות בצד הלקוח בלבד
- תצורת ניתוב - כל הנתיבים של האפליקציה, כולל נתיבי admin
- הערות מפתחים - TODO, FIXME, HACK שחושפים בעיות ידועות
- קוד debug - נקודות debug שנשארו בפרודקשן
איתור קבצי JavaScript¶
שיטה ידנית¶
# בדפדפן:
# 1. פתחו את כלי הפיתוח (F12)
# 2. עברו לטאב Sources
# 3. חפשו קבצי .js תחת הדומיין
# 4. בטאב Network, סננו לפי JS
# ב-Burp Suite:
# 1. Target > Site Map
# 2. לחצו ימני על הדומיין > Engagement tools > Find scripts
# 3. או סננו את ה-Site Map לפי MIME type: script
איתור אוטומטי עם כלים¶
# gau - קבלת כל ה-URLs ההיסטוריים של דומיין
# מתוך Wayback Machine, Common Crawl, ועוד
echo "target.com" | gau | grep "\.js$" | sort -u > js_files.txt
# waybackurls - דומה ל-gau, מתמקד ב-Wayback Machine
echo "target.com" | waybackurls | grep "\.js$" | sort -u >> js_files.txt
# hakrawler - סורק אתרים ומוצא קישורים לקבצי JS
echo "https://target.com" | hakrawler -d 3 | grep "\.js$"
# getJS - כלי ייעודי לחילוץ קבצי JS מאתר
getJS --url https://target.com --complete
# דוגמה - חיפוש קבצי JS גם בסאבדומיינים
cat subdomains.txt | httpx -silent | getJS --complete | sort -u > all_js.txt
הורדת קבצי JavaScript¶
# הורדת כל קבצי ה-JS שמצאנו
while read url; do
filename=$(echo "$url" | sed 's/[^a-zA-Z0-9]/_/g')
curl -s "$url" -o "js_files/${filename}.js"
done < js_files.txt
# או בשורה אחת עם wget
wget -i js_files.txt -P js_downloads/ --no-check-certificate
מפות מקור - Source Maps¶
מה הם Source Maps¶
כאשר קוד JavaScript עובר תהליכי בנייה (bundling, minification, transpilation), נוצר קובץ Source Map שמקשר בין הקוד המעובד לקוד המקורי. קובץ זה מאפשר למפתחים לדבג את הקוד המקורי בדפדפן.
אם קובץ Source Map נגיש בפרודקשן, אנחנו יכולים לשחזר את כל קוד המקור המקורי - כולל הערות, שמות משתנים מקוריים, ומבנה הפרויקט המלא.
זיהוי Source Maps¶
# חיפוש הפניה ל-source map בתוך קובץ JS
# בסוף קובץ JS מינימלי, מופיעה לרוב השורה:
# //# sourceMappingURL=app.js.map
# חיפוש בקבצים שהורדנו
grep -r "sourceMappingURL" js_downloads/
# ניסיון גישה ישירה לנתיבים נפוצים
curl -s "https://target.com/static/js/main.chunk.js.map" -o main.map
curl -s "https://target.com/assets/app.js.map" -o app.map
curl -s "https://target.com/dist/bundle.js.map" -o bundle.map
# בדיקת כותרת HTTP - לפעמים ה-source map מוגדר בכותרת
curl -sI "https://target.com/app.js" | grep -i "SourceMap"
# SourceMap: /app.js.map
שחזור קוד מקור מ-Source Map¶
# התקנת unwebpack-sourcemap
npm install -g unwebpack-sourcemap
# שחזור קוד מקור מקובץ map
unwebpack-sourcemap app.js.map -o recovered_source/
# או עם הכלי sourcemapper
go install github.com/nicholasgasior/sourcemapper@latest
sourcemapper -url "https://target.com/assets/main.js.map" -output recovered/
# הכלי smap - חלופה פשוטה
pip install smap
smap app.js.map -o output/
# מבנה התיקיות ששוחזר:
recovered_source/
src/
components/
AdminPanel.jsx
UserProfile.jsx
PaymentForm.jsx
services/
api.js # <-- נתיבי API מלאים
auth.js # <-- לוגיקת אותנטיקציה
config.js # <-- הגדרות ומפתחות
utils/
helpers.js
App.jsx
routes.js # <-- כל נתיבי האפליקציה
ניתוח Webpack ו-Vite Bundles¶
זיהוי מבנה Bundle¶
// קוד webpack טיפוסי - הקוד מחולק ל-modules עם מזהים מספריים
(self.webpackChunkapp = self.webpackChunkapp || []).push([
[792],
{
1234: function(module, exports, __webpack_require__) {
// Module code here
},
5678: function(module, exports, __webpack_require__) {
// Another module
}
}
]);
// חיפוש נתיבי API בתוך ה-bundle:
// חפשו את המילים: /api/, endpoint, baseURL, fetch(, axios
חילוץ נתיבים מ-Bundle¶
# extract_routes.py - חילוץ נתיבי API מקובץ JS
import re
import sys
def extract_api_routes(js_content):
patterns = [
# נתיבי API ישירים
r'["\'](/api/[a-zA-Z0-9/_\-{}:.]+)["\']',
# נתיבי fetch
r'fetch\(["\']([^"\']+)["\']',
# נתיבי axios
r'axios\.\w+\(["\']([^"\']+)["\']',
# הגדרות baseURL
r'baseURL:\s*["\']([^"\']+)["\']',
# נתיבי XMLHttpRequest
r'\.open\(["\'](?:GET|POST|PUT|DELETE|PATCH)["\'],\s*["\']([^"\']+)["\']',
# React Router / Vue Router paths
r'path:\s*["\']([^"\']+)["\']',
]
routes = set()
for pattern in patterns:
matches = re.findall(pattern, js_content)
routes.update(matches)
return sorted(routes)
if __name__ == "__main__":
filename = sys.argv[1]
with open(filename, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
routes = extract_api_routes(content)
print(f"[+] Found {len(routes)} routes:")
for route in routes:
print(f" {route}")
כלים אוטומטיים¶
LinkFinder¶
LinkFinder מחפש נקודות קצה ופרמטרים בתוך קבצי JavaScript.
# התקנה
git clone https://github.com/GerbenJav);ado/LinkFinder.git
cd LinkFinder
pip3 install -r requirements.txt
# שימוש על קובץ בודד
python3 linkfinder.py -i https://target.com/static/js/app.js -o cli
# שימוש על דומיין שלם
python3 linkfinder.py -i https://target.com -d -o results.html
# שימוש על קובץ מקומי
python3 linkfinder.py -i /path/to/downloaded.js -o cli
# שימוש על מספר קבצים
cat js_files.txt | while read url; do
python3 linkfinder.py -i "$url" -o cli
done | sort -u > all_endpoints.txt
SecretFinder¶
SecretFinder מחפש מפתחות, טוקנים, וסודות בקבצי JavaScript.
# התקנה
git clone https://github.com/m4ll0k/SecretFinder.git
cd SecretFinder
pip3 install -r requirements.txt
# שימוש
python3 SecretFinder.py -i https://target.com/app.js -o cli
# חיפוש בכל קבצי ה-JS
cat js_files.txt | while read url; do
echo "=== $url ==="
python3 SecretFinder.py -i "$url" -o cli
done
JSParser¶
# התקנה
git clone https://github.com/nichdiekman/JSParser.git
cd JSParser
pip3 install -r requirements.txt
# שימוש
python3 handler.py -u https://target.com/static/js/main.js
Nuclei עם תבניות JS¶
# סריקת חשיפת מידע בקבצי JS עם nuclei
nuclei -u https://target.com -t exposures/ -tags js
# סריקת source maps חשופים
nuclei -u https://target.com -t http/exposures/configs/
ניתוח ידני - דפוסים לחיפוש¶
חיפוש מפתחות API וטוקנים¶
// דפוסים נפוצים לחיפוש בקבצי JS:
// מפתחות AWS
// AKIA[0-9A-Z]{16}
const AWS_KEY = "AKIAIOSFODNN7EXAMPLE";
// מפתח Google API
// AIza[0-9A-Za-z\-_]{35}
const GOOGLE_API_KEY = "AIzaSyA1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q";
// טוקן Firebase
const firebaseConfig = {
apiKey: "AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
authDomain: "project-name.firebaseapp.com",
projectId: "project-name",
storageBucket: "project-name.appspot.com",
};
// טוקן Stripe
// sk_live_[0-9a-zA-Z]{24}
const stripe = Stripe("sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXX");
// טוקן GitHub
// ghp_[0-9a-zA-Z]{36}
const GITHUB_TOKEN = "ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
חיפוש נתיבים פנימיים¶
// הגדרות ניתוב טיפוסיות ב-React:
// React Router
const routes = [
{ path: "/", component: Home },
{ path: "/dashboard", component: Dashboard },
{ path: "/admin", component: AdminPanel }, // נתיב admin
{ path: "/admin/users", component: UserManagement }, // ניהול משתמשים
{ path: "/api/debug", component: DebugPanel }, // פאנל debug
{ path: "/internal/metrics", component: Metrics }, // מטריקות פנימיות
{ path: "/dev/test", component: TestPage }, // דף בדיקה
];
// Vue Router
const router = new VueRouter({
routes: [
{ path: "/settings/advanced", component: AdvancedSettings },
{ path: "/super-admin", component: SuperAdmin, meta: { requiresAdmin: true } },
]
});
// Angular Routes
const appRoutes: Routes = [
{ path: "admin/config", component: ConfigComponent, canActivate: [AdminGuard] },
{ path: "debug/logs", component: LogViewerComponent },
];
חיפוש נקודות קצה API¶
// הגדרת שירות API טיפוסי:
const API_BASE = "https://api.target.com/v2";
class ApiService {
getUsers() { return fetch(`${API_BASE}/users`); }
getUser(id) { return fetch(`${API_BASE}/users/${id}`); }
deleteUser(id) { return fetch(`${API_BASE}/admin/users/${id}`, { method: "DELETE" }); }
getConfig() { return fetch(`${API_BASE}/internal/config`); }
getLogs() { return fetch(`${API_BASE}/debug/logs`); }
exportData() { return fetch(`${API_BASE}/admin/export`); }
getMetrics() { return fetch(`${API_BASE}/metrics/dashboard`); }
}
// הגדרת axios:
const api = axios.create({
baseURL: "https://internal-api.target.com",
headers: {
"X-API-Key": "internal-key-12345",
"Authorization": "Bearer hardcoded-token"
}
});
חיפוש הערות מפתחים¶
// דפוסים לחיפוש בהערות:
// TODO: fix authentication bypass on /api/admin
// FIXME: SQL injection in search parameter
// HACK: temporary workaround for auth
// XXX: this is insecure but works for now
// NOTE: admin password is admin123 for testing
// DEBUG: remove this before production
// TEMP: hardcoded credentials for staging
# חיפוש הערות בקבצי JS שהורדנו
grep -rn "TODO\|FIXME\|HACK\|XXX\|DEBUG\|TEMP\|password\|secret\|key\|token" js_downloads/
טכניקות דה-אובפוסקציה - Deobfuscation¶
זיהוי סוגי אובפוסקציה¶
// קוד מינימלי (minified) - רק הסרת רווחים ושמות קצרים
var a=document.getElementById("b");a.addEventListener("click",function(){var c=new XMLHttpRequest;c.open("GET","/api/data");c.send()});
// קוד אובפוסקטד - שינוי מבנה מכוון
var _0x1a2b=["getElementById","addEventListener","click","open","GET","/api/data","send"];
var _0x3c4d=document[_0x1a2b[0]]("b");
_0x3c4d[_0x1a2b[1]](_0x1a2b[2],function(){
var _0x5e6f=new XMLHttpRequest;
_0x5e6f[_0x1a2b[3]](_0x1a2b[4],_0x1a2b[5]);
_0x5e6f[_0x1a2b[6]]();
});
// קידוד JSFuck - שימוש רק ב-6 תווים: []()!+
// קידוד Eval-based - הכל עטוף ב-eval()
eval(String.fromCharCode(97,108,101,114,116,40,49,41));
כלי דה-אובפוסקציה¶
# js-beautify - פירמוט קוד מינימלי
pip install jsbeautifier
js-beautify -o formatted.js minified.js
# de4js - כלי מקוון לדה-אובפוסקציה
# https://lelinhtinh.github.io/de4js/
# שימוש ב-Node.js לפענוח:
node -e "
const code = require('fs').readFileSync('obfuscated.js', 'utf8');
// החלפת eval ב-console.log לחשיפת הקוד
const decoded = code.replace(/eval\(/g, 'console.log(');
require('fs').writeFileSync('decoded.js', decoded);
"
דה-אובפוסקציה ידנית¶
// שלב 1 - פירמוט הקוד (prettier / js-beautify)
// שלב 2 - זיהוי מערך מחרוזות
var _0x1a2b = ["getElementById", "addEventListener", "click"];
// שלב 3 - החלפת הפניות למחרוזות הקריאות
// _0x1a2b[0] -> "getElementById"
// _0x1a2b[1] -> "addEventListener"
// שלב 4 - שינוי שמות משתנים למשמעותיים
// _0x3c4d -> element
// _0x5e6f -> xhr
// שלב 5 - הסרת קוד מת (dead code)
// חלקים שלעולם לא ירוצו (if(false){...})
// תוצאה:
var element = document.getElementById("b");
element.addEventListener("click", function() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/api/data");
xhr.send();
});
דוגמה מעשית - ניתוח אפליקציית React SPA¶
שלב 1 - איתור קבצי JavaScript¶
# סריקת האתר וחיפוש קבצי JS
curl -s https://target.com | grep -oP 'src="[^"]*\.js[^"]*"' | sed 's/src="//;s/"//'
# תוצאה טיפוסית:
# /static/js/runtime-main.abc123.js
# /static/js/2.def456.chunk.js
# /static/js/main.ghi789.chunk.js
שלב 2 - בדיקת Source Maps¶
# בדיקה האם קיימים source maps
curl -sI "https://target.com/static/js/main.ghi789.chunk.js" | grep -i sourcemap
curl -s "https://target.com/static/js/main.ghi789.chunk.js.map" -o main.map
# אם הקובץ קיים, שחזור:
unwebpack-sourcemap main.map -o recovered/
שלב 3 - ניתוח הקוד¶
# חיפוש נתיבי API
grep -rn "api\|fetch\|axios\|endpoint" recovered/src/
# חיפוש מפתחות
grep -rn "API_KEY\|SECRET\|TOKEN\|password\|apiKey" recovered/src/
# חיפוש נתיבי ניתוב
grep -rn "path:\|Route\|navigate\|redirect" recovered/src/
# חיפוש הערות
grep -rn "TODO\|FIXME\|HACK\|admin\|debug" recovered/src/
שלב 4 - שימוש בממצאים¶
# נתיבי API שנמצאו - בדיקת גישה
curl -s "https://target.com/api/v1/admin/users" -H "Authorization: Bearer test"
curl -s "https://target.com/api/internal/config"
curl -s "https://target.com/api/debug/logs"
# מפתח API שנמצא - בדיקה האם עדיין פעיל
curl -s "https://maps.googleapis.com/maps/api/geocode/json?key=FOUND_KEY&address=test"
השוואת גרסאות JavaScript¶
למה להשוות גרסאות¶
כאשר אפליקציה מתעדכנת, קבצי JavaScript משתנים. השוואת הגרסה הישנה לחדשה יכולה לחשוף:
- נקודות קצה API חדשות
- תכונות שטרם הושקו
- תיקוני באגים שחושפים את החולשה המקורית
# שלב 1 - הורדת גרסה ישנה מ-Wayback Machine
curl -s "https://web.archive.org/web/2024/https://target.com/static/js/main.js" > old_main.js
# שלב 2 - הורדת גרסה נוכחית
curl -s "https://target.com/static/js/main.js" > new_main.js
# שלב 3 - פירמוט שני הקבצים
js-beautify old_main.js > old_formatted.js
js-beautify new_main.js > new_formatted.js
# שלב 4 - השוואה
diff old_formatted.js new_formatted.js > changes.diff
# או שימוש בכלי ויזואלי
vimdiff old_formatted.js new_formatted.js
סיכום¶
ניתוח JavaScript סטטי הוא שלב קריטי בחקירה מתקדמת. הנקודות המרכזיות:
- איתור - שימוש ב-gau, waybackurls, וכלי סריקה למציאת כל קבצי ה-JS
- Source Maps - בדיקה האם קבצי .map חשופים ושחזור קוד מקור
- ניתוח אוטומטי - שימוש ב-LinkFinder, SecretFinder לחילוץ מהיר
- ניתוח ידני - חיפוש דפוסים של מפתחות, נתיבים, הערות, והגדרות
- דה-אובפוסקציה - פענוח קוד מוסתר באמצעות כלים וטכניקות ידניות
- השוואת גרסאות - גילוי שינויים בין גרסאות לחשיפת תכונות חדשות
כל נתיב API, מפתח, או כתובת פנימית שמצאנו הופכים למטרות לבדיקה בשלבים הבאים.