לדלג לתוכן

ניתוח 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

שיטה ידנית

# In the browser:
# 1. Open dev tools (F12)
# 2. Go to the Sources tab
# 3. Search for .js files under the domain
# 4. In the Network tab, filter by JS

# In Burp Suite:
# 1. Target > Site Map
# 2. Right-click the domain > Engagement tools > Find scripts
# 3. Or filter the Site Map by MIME type: script

איתור אוטומטי עם כלים

# gau - get all historical URLs for a domain
# from Wayback Machine, Common Crawl, and more
echo "target.com" | gau | grep "\.js$" | sort -u > js_files.txt

# waybackurls - similar to gau, focuses on Wayback Machine
echo "target.com" | waybackurls | grep "\.js$" | sort -u >> js_files.txt

# hakrawler - crawls sites and finds links to JS files
echo "https://target.com" | hakrawler -d 3 | grep "\.js$"

# getJS - dedicated tool for extracting JS files from a site
getJS --url https://target.com --complete

# example - searching for JS files across subdomains too
cat subdomains.txt | httpx -silent | getJS --complete | sort -u > all_js.txt

הורדת קבצי JavaScript

# Download all the JS files we found
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

# Or in one line with 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

# Search for a reference to the source map inside the JS file
# At the end of a minified JS file, this line usually appears:
# //# sourceMappingURL=app.js.map

# Search in the downloaded files
grep -r "sourceMappingURL" js_downloads/

# Try direct access to common paths
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

# Check the HTTP header - sometimes the source map is defined in the header
curl -sI "https://target.com/app.js" | grep -i "SourceMap"
# SourceMap: /app.js.map

שחזור קוד מקור מ-Source Map

# Install unwebpack-sourcemap
npm install -g unwebpack-sourcemap

# Recover source code from a map file
unwebpack-sourcemap app.js.map -o recovered_source/

# Or with the sourcemapper tool
go install github.com/nicholasgasior/sourcemapper@latest
sourcemapper -url "https://target.com/assets/main.js.map" -output recovered/

# smap tool - a simple alternative
pip install smap
smap app.js.map -o output/
# The recovered directory structure:
recovered_source/
    src/
        components/
            AdminPanel.jsx
            UserProfile.jsx
            PaymentForm.jsx
        services/
            api.js          # <-- full API paths
            auth.js         # <-- authentication logic
            config.js       # <-- settings and keys
        utils/
            helpers.js
        App.jsx
        routes.js           # <-- all app routes

ניתוח Webpack ו-Vite Bundles

זיהוי מבנה Bundle

// Typical webpack code - the code is split into modules with numeric IDs
(self.webpackChunkapp = self.webpackChunkapp || []).push([
  [792],
  {
    1234: function(module, exports, __webpack_require__) {
      // Module code here
    },
    5678: function(module, exports, __webpack_require__) {
      // Another module
    }
  }
]);

// Search for API paths inside the bundle:
// Look for the words: /api/, endpoint, baseURL, fetch(, axios

הextraction נתיבים מ-Bundle

# extract_routes.py - extract API paths from a JS file
import re
import sys

def extract_api_routes(js_content):
    patterns = [
        # Direct API paths
        r'["\'](/api/[a-zA-Z0-9/_\-{}:.]+)["\']',
        # fetch paths
        r'fetch\(["\']([^"\']+)["\']',
        # axios paths
        r'axios\.\w+\(["\']([^"\']+)["\']',
        # baseURL settings
        r'baseURL:\s*["\']([^"\']+)["\']',
        # XMLHttpRequest paths
        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}")
# Run
python3 extract_routes.py main.chunk.js

כלים אוטומטיים

LinkFinder

LinkFinder מחפש נקודות קצה ופרמטרים בתוך קבצי JavaScript.

# Installation
git clone https://github.com/GerbenJav);ado/LinkFinder.git
cd LinkFinder
pip3 install -r requirements.txt

# Usage on a single file
python3 linkfinder.py -i https://target.com/static/js/app.js -o cli

# Usage on an entire domain
python3 linkfinder.py -i https://target.com -d -o results.html

# Usage on a local file
python3 linkfinder.py -i /path/to/downloaded.js -o cli

# Usage on multiple files
cat js_files.txt | while read url; do
    python3 linkfinder.py -i "$url" -o cli
done | sort -u > all_endpoints.txt

SecretFinder

SecretFinder מחפש מפתחות, טוקנים, וסודות בקבצי JavaScript.

# Installation
git clone https://github.com/m4ll0k/SecretFinder.git
cd SecretFinder
pip3 install -r requirements.txt

# Usage
python3 SecretFinder.py -i https://target.com/app.js -o cli

# Search across all JS files
cat js_files.txt | while read url; do
    echo "=== $url ==="
    python3 SecretFinder.py -i "$url" -o cli
done

JSParser

# Installation
git clone https://github.com/nichdiekman/JSParser.git
cd JSParser
pip3 install -r requirements.txt

# Usage
python3 handler.py -u https://target.com/static/js/main.js

Nuclei עם תבניות JS

# Scan for information disclosure in JS files with nuclei
nuclei -u https://target.com -t exposures/ -tags js

# Scan for exposed source maps
nuclei -u https://target.com -t http/exposures/configs/

ניתוח ידני - דפוסים לחיפוש

חיפוש מפתחות API וטוקנים

// Common patterns to search for in JS files:

// AWS keys
// AKIA[0-9A-Z]{16}
const AWS_KEY = "AKIAIOSFODNN7EXAMPLE";

// Google API key
// AIza[0-9A-Za-z\-_]{35}
const GOOGLE_API_KEY = "AIzaSyA1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q";

// Firebase token
const firebaseConfig = {
    apiKey: "AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    authDomain: "project-name.firebaseapp.com",
    projectId: "project-name",
    storageBucket: "project-name.appspot.com",
};

// Stripe token
// sk_live_[0-9a-zA-Z]{24}
const stripe = Stripe("sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXX");

// GitHub token
// ghp_[0-9a-zA-Z]{36}
const GITHUB_TOKEN = "ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

חיפוש נתיבים פנימיים

// Typical routing settings in React:

// React Router
const routes = [
    { path: "/", component: Home },
    { path: "/dashboard", component: Dashboard },
    { path: "/admin", component: AdminPanel },           // admin route
    { path: "/admin/users", component: UserManagement }, // user management
    { path: "/api/debug", component: DebugPanel },       // debug panel
    { path: "/internal/metrics", component: Metrics },   // internal metrics
    { path: "/dev/test", component: TestPage },          // test page
];

// 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

// Typical API service definition:
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 configuration:
const api = axios.create({
    baseURL: "https://internal-api.target.com",
    headers: {
        "X-API-Key": "internal-key-12345",
        "Authorization": "Bearer hardcoded-token"
    }
});

חיפוש הערות מפתחים

// Patterns to search for in comments:

// 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
# Search for comments in the downloaded JS files
grep -rn "TODO\|FIXME\|HACK\|XXX\|DEBUG\|TEMP\|password\|secret\|key\|token" js_downloads/

טכניקות דה-אובפוסקציה - Deobfuscation

זיהוי סוגי אובפוסקציה

// Minified code - just whitespace removal and short names
var a=document.getElementById("b");a.addEventListener("click",function(){var c=new XMLHttpRequest;c.open("GET","/api/data");c.send()});

// Obfuscated code - deliberately altered structure
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 encoding - uses only 6 characters: []()!+
// Eval-based encoding - everything wrapped in eval()
eval(String.fromCharCode(97,108,101,114,116,40,49,41));

כלי דה-אובפוסקציה

# js-beautify - format minified code
pip install jsbeautifier
js-beautify -o formatted.js minified.js

# de4js - online deobfuscation tool
# https://lelinhtinh.github.io/de4js/

# Using Node.js to decode:
node -e "
const code = require('fs').readFileSync('obfuscated.js', 'utf8');
// Replace eval with console.log to reveal the code
const decoded = code.replace(/eval\(/g, 'console.log(');
require('fs').writeFileSync('decoded.js', decoded);
"

דה-אובפוסקציה ידנית

// Step 1 - format the code (prettier / js-beautify)
// Step 2 - identify the string array
var _0x1a2b = ["getElementById", "addEventListener", "click"];

// Step 3 - replace references with readable strings
// _0x1a2b[0] -> "getElementById"
// _0x1a2b[1] -> "addEventListener"

// Step 4 - rename variables to meaningful names
// _0x3c4d -> element
// _0x5e6f -> xhr

// Step 5 - remove dead code
// parts that will never run (if(false){...})

// Result:
var element = document.getElementById("b");
element.addEventListener("click", function() {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "/api/data");
    xhr.send();
});

דוגמה מעשית - ניתוח אפליקציית React SPA

שלב 1 - איתור קבצי JavaScript

# Scan the site and search for JS files
curl -s https://target.com | grep -oP 'src="[^"]*\.js[^"]*"' | sed 's/src="//;s/"//'

# Typical result:
# /static/js/runtime-main.abc123.js
# /static/js/2.def456.chunk.js
# /static/js/main.ghi789.chunk.js

שלב 2 - בדיקת Source Maps

# Check whether source maps exist
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

# If the file exists, recover it:
unwebpack-sourcemap main.map -o recovered/

שלב 3 - ניתוח הקוד

# Search for API paths
grep -rn "api\|fetch\|axios\|endpoint" recovered/src/

# Search for keys
grep -rn "API_KEY\|SECRET\|TOKEN\|password\|apiKey" recovered/src/

# Search for routing paths
grep -rn "path:\|Route\|navigate\|redirect" recovered/src/

# Search for comments
grep -rn "TODO\|FIXME\|HACK\|admin\|debug" recovered/src/

שלב 4 - שימוש בממצאים

# API paths found - checking access
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 key found - check whether it's still active
curl -s "https://maps.googleapis.com/maps/api/geocode/json?key=FOUND_KEY&address=test"

השוואת גרסאות JavaScript

למה להשוות גרסאות

כאשר אפליקציה מתעדכנת, קבצי JavaScript משתנים. השוואת הגרסה הישנה לחדשה יכולה לחשוף:
- נקודות קצה API חדשות
- תכונות שטרם הושקו
- תיקוני באגים שחושפים את החולשה המקורית

# Step 1 - download the old version from Wayback Machine
curl -s "https://web.archive.org/web/2024/https://target.com/static/js/main.js" > old_main.js

# Step 2 - download the current version
curl -s "https://target.com/static/js/main.js" > new_main.js

# Step 3 - format both files
js-beautify old_main.js > old_formatted.js
js-beautify new_main.js > new_formatted.js

# Step 4 - compare
diff old_formatted.js new_formatted.js > changes.diff

# Or use a visual tool
vimdiff old_formatted.js new_formatted.js

סיכום

ניתוח JavaScript סטטי הוא שלב קריטי בחקירה מתקדמת. הנקודות המרכזיות:

  1. איתור - שימוש ב-gau, waybackurls, וכלי סריקה למציאת כל קבצי ה-JS
  2. Source Maps - בדיקה האם קבצי .map חשופים ושחזור קוד מקור
  3. ניתוח אוטומטי - שימוש ב-LinkFinder, SecretFinder לextraction מהיר
  4. ניתוח ידני - חיפוש דפוסים של מפתחות, נתיבים, הערות, והגדרות
  5. דה-אובפוסקציה - decoding קוד מוסתר באמצעות כלים וטכניקות ידניות
  6. השוואת גרסאות - גילוי שינויים בין גרסאות לחשיפת תכונות חדשות

כל נתיב API, מפתח, או כתובת פנימית שמצאנו הופכים למטרות לבדיקה בשלבים הבאים.