
اعثر على البيانات الحساسة المتسربة من مثيلات ServiceNow.
snowpick هو ماسح تعرضات ServiceNow يكتشف أخطاء الإعداد في قوائم التحكم في الوصول (ACLs) الخاصة بالويدجات، وعناصر التحكم في الوصول لواجهة REST API، والتعرض غير المصرح به للبيانات.
يعرض ServiceNow البيانات عبر سطحين عامين شائعين: ليدجات Service Portal وواجهة Table REST API. عندما تكون عناصر التحكم في الوصول على أي من السطحين مهيأة بشكل غير صحيح، قد يتمكن المستخدمون غير المصادق عليهم من الاستعلام عن سجلات داخلية مثل تذاكر الحوادث، وأدلة المستخدمين، ومقالات قاعدة المعرفة، وعناصر كتالوج الخدمة. يختبر snowpick كلا السطحين، ويفصل بين كشف الصفوف والأدلة القائمة على العد فقط، وينتج تفاصيل إعادة الإنتاج للتحقق المصرح به.
يحصل snowpick على رمز جلسة من صفحة تسجيل الدخول في ServiceNow دون أي بيانات اعتماد، ثم يستخدمه للاستعلام عن واجهات برمجة التطبيقات الداخلية للمنصة. يفحص سطحَي هجوم:
الويدجات هي تطبيقات صغيرة تشغّل Service Portal في ServiceNow. لكل منها إعدادات التحكم في الوصول الخاصة بها. عندما تكون هذه الإعدادات متساهلة للغاية، قد تتمكن جلسة غير مصادق عليها من استرجاع البيانات من الويدجت. يطلب snowpick أولاً من المثيل قائمة بالويدجات المثبتة حيثما أمكن، ثم يفحص كل واحدة ويتحقق مما إذا كانت تُرجع سجلات أو أدلة قائمة على العد فقط.
Table API هي واجهة REST على /api/now/table/ للاستعلام عن جداول ServiceNow مباشرة. لها عناصر تحكم في الوصول منفصلة عن الويدجات، لذا يمكن أن يكون الهدف مغلقًا على أحد السطحين ومفتوحًا على الآخر. يفحص snowpick الجداول عالية القيمة مثل sys_user وincident وoauth_entity.
أوراكل العد (count oracle). أحيانًا يؤكد الويدجت أو الجدول وجود سجلات مطابقة دون إرجاع الصفوف. يصنّف snowpick هذا كـcount_oracle — ليس كشفًا كاملًا، ولكنه ليس نظيفًا أيضًا، إذ يمكن استقصاء العدادات باستخدام الفلاتر لاستنتاج البيانات بشكل أعمى.
❯ go install github.com/BishopFox/snowpick@latest
أو البناء من المصدر:
❯ git clone https://github.com/BishopFox/snowpick.git
❯ cd snowpick
❯ go build -o snowpick .
يتطلب Go 1.21 أو أحدث.
وجّه snowpick إلى مثيل ServiceNow. لا حاجة لبيانات اعتماد.
❯ snowpick -target prod.service-now.com
snowpick v1.0.0 | 1 targets | 6 widgets | proxy direct
[!] prod.service-now.com
| ticket-attachments 272 records [internal_operations:inferred]
[*] 1/1 targets complete, 1 with findings
RESULTS
1 targets scanned in 8.2s | 1 reachable | 1 with findings
EXPOSED prod.service-now.com 272 records [internal_operations:inferred]
| ticket-attachments 272 records [internal_operations:inferred]
| ServiceNow Overview and Open House 'How To.... Videos'
| How to - JIRA and Confluence Access Process
| Updating Your Personal Time Zone in ServiceNow
ATTACK SURFACE
ticket-attachments #################### 272 across 1 target
DATA CLASSIFICATION
OPERATIONS 1 finding across 1 target
يكتشف snowpick تلقائيًا الويدجات المثبتة على المثيل ويفحص كل واحدة منها إلى جانب القيم الافتراضية المدمجة. تُرتَّب النتائج حسب درجة الخطورة.
Progress markers (left of each host):
[!] findings [+] clean [-] unreachable or no session [*] progress
Summary buckets:
EXPOSED data or a confirmed count came back CLEAN nothing reachable
SKIP no public session (e.g. SSO-only login) DOWN host unreachable
Counts:
272 records total the instance reports as matching
272 records, 10 sampled total, plus the rows snowpick pulled as evidence
Tags:
[pii:observed] a hint, written as category:basis
basis observed = value seen in returned data
inferred = from a table/field/widget name
category pii, credentials, infrastructure,
internal_operations, review_required
[blind oracle] count confirmed but no rows returned (a count_oracle)
[API] finding came from the Table REST API, not a widget
[stats.do] the /stats.do diagnostic endpoint is publicly reachable
مرّر ملفًا يحتوي على اسم مضيف واحد في كل سطر. يفحصها snowpick بالتوازي.
❯ snowpick -targets hosts.txt -concurrency 5
snowpick v1.0.0 | 6 targets | 6 widgets | proxy direct
[!] prod.service-now.com
| ticket-attachments 272 records [internal_operations:inferred]
[*] 1/6 targets complete, 1 with findings
[+] staging.service-now.com clean
[*] 2/6 targets complete, 1 with findings
[!] dev.service-now.com [stats.do]
| ticket-attachments 61 records [internal_operations:inferred]
[*] 3/6 targets complete, 2 with findings
[+] sandbox.service-now.com clean
[*] 4/6 targets complete, 2 with findings
[-] internal.service-now.com bootstrap failed: /login.do returned 302
[+] test.service-now.com clean
[*] 6/6 targets complete, 2 with findings
يكشف ServiceNow أيضًا عن واجهة Table REST API على /api/now/table/ للاستعلام المباشر عن الجداول الخلفية. هذا سطح تحكم في الوصول منفصل عن الويدجات. استخدم -table-api لفحص كليهما.
❯ snowpick -target prod.service-now.com -table-api
snowpick v1.0.0 | 1 targets | 6 widgets | proxy direct
[!] prod.service-now.com
| [API] cmn_department 1,234 records, 10 sampled [infrastructure:inferred]
[*] 1/1 targets complete, 1 with findings
بالنسبة لنتائج Table API، يمثل record_count إجمالي السجلات المكشوفة عندما يُرجع ServiceNow عددًا إجماليًا. ويعرض sample_count عدد السجلات التي جمعها snowpick كأدلة محدودة النطاق.
افتراضيًا، يفحص snowpick مجموعة منتقاة من الجداول عالية القيمة (تذاكر الحوادث، سجلات المستخدمين، المرفقات، إلخ). مع -discover، يحاول تعداد أسماء الجداول من المثيل ويفحصها أيضًا.
❯ snowpick -target prod.service-now.com -discover -discover-limit 200
استخدم -fingerprint لتحديد إصدار ServiceNow الذي يعمل عليه الهدف. يساعد ذلك في تحديد التصحيحات الأمنية المطبّقة.
❯ snowpick -target prod.service-now.com -fingerprint
snowpick v1.0.0 | 1 targets | 6 widgets | proxy direct
[!] prod.service-now.com [Zurich (glide-zurich-07-01-2025__patch7-02-19-2026)] [stats.do]
| ticket-attachments 61 records [internal_operations:inferred]
[*] 1/1 targets complete, 1 with findings
مرّر حركة المرور عبر Burp أو ZAP أو وكيل SOCKS. استخدم -rate لتقليل معدل الطلبات على المثيلات الإنتاجية.
❯ snowpick -target prod.service-now.com -proxy socks5://127.0.0.1:1080 -insecure -rate 200ms
اكتم إخراج التقدم باستخدام -quiet. يذهب JSON إلى stdout افتراضيًا، أو إلى ملف مع -output. رمز الخروج 2 يعني وجود نتائج، و1 يعني تعذّر اختبار هدف واحد أو أكثر، و0 يعني اكتمال الفحص دون نتائج.
❯ snowpick -target prod.service-now.com -quiet -output results.json
❯ echo $?
2
يحتوي مخرج JSON على تفاصيل النتائج، بما في ذلك تلميحات التأثير، وتفاصيل الطلبات، وسجلات العينات المحدودة عند إرجاع الصفوف، ومواد إعادة الإنتاج حيثما ينطبق ذلك.
{
"target": "acme.service-now.com",
"reachable": true,
"token_obtained": true,
"stats_do": false,
"widgets": [
{
"id": "ff26f2b1db12220099f93691f0b8f5ce",
"name": "ticket-attachments",
"status": "exposed",
"record_count": 847,
"sample_count": 10,
"hints": [
{
"category": "pii",
"basis": "observed",
"reason": "email pattern in sample content",
"signals": ["content:[email protected]"]
},
{
"category": "internal_operations",
"basis": "inferred",
"reason": "widget name associated with internal operations",
"signals": ["widget:ticket-attachments"]
}
],
"reproduction": {
"method": "POST",
"url": "https://acme.service-now.com/api/now/sp/widget/ff26f2b1db12220099f93691f0b8f5ce",
"headers": {"X-UserToken": "...", "Cookie": "JSESSIONID=..."},
"body": "{\"payload\":{\"start\":0,\"end\":1}}",
"status_code": 200,
"curl": "curl -i -sS -X 'POST' ..."
}
}
]
}
تظهر نتائج تعداد الجداول تحت table_findings، وتظهر نتائج Table REST API تحت table_api_findings. تستخدم كلاهما بنية hints نفسها. تتضمن نتائج Table API أيضًا كلاً من record_count وsample_count عندما يتوفر عدد إجمالي.
اعرض جميع الأهداف التي لديها نتائج:
❯ jq -r '.findings[] | select(any(.widgets[]?; .status == "exposed" or .status == "count_oracle") or ((.table_findings // []) | length > 0) or ((.table_api_findings // []) | length > 0)) | .target' results.json
prod.service-now.com
dev.service-now.com
احصل على أوامر curl لإعادة إنتاج النتائج المكشوفة (انسخ والصق لإعادة الإنتاج):
❯ jq -r '.findings[] | (.widgets[]?, .table_findings[]?, .table_api_findings[]?) | select(.status == "exposed") | .reproduction.curl' results.json
curl -i -sS -X 'POST' 'https://acme.service-now.com/api/now/sp/widget/ff26f...' -H 'X-UserToken: MOCK_TOKEN' ...
اعرض نتائج PII مع الهدف، وناقل الهجوم، والحالة، والعدد، والعينات، وأساس التلميح:
❯ jq -r '.findings[] as $f | ($f.widgets[]?, $f.table_findings[]?, $f.table_api_findings[]?) | select(any(.hints[]?; .category=="pii")) | [$f.target, (.name // (.table + (if .field then "." + .field else "" end))), .status, ((.record_count // .count // 0)|tostring), ((.sample_count // 0)|tostring), ([.hints[]? | select(.category=="pii") | .basis] | unique | join(","))] | @tsv' results.json
acme.service-now.com ticket-attachments exposed 847 10 observed
acme.service-now.com sys_user exposed 250 10 inferred
احصل على عناوين العينات لنتيجة معينة:
❯ jq -r '.findings[] | select(.target == "acme.service-now.com") | .widgets[] | select(.name == "ticket-attachments") | .samples[]?.title' results.json
Synthetic ticket attachment title
Example onboarding guide
احسب إجمالي السجلات المكشوفة:
❯ jq '[.findings[] | (.widgets[]? | select(.status == "exposed") | .record_count), (.table_findings[]? | select(.status == "exposed") | .count), (.table_api_findings[]? | select(.status == "exposed") | .record_count)] | add // 0' results.json
12896
TARGETS
-target string Single ServiceNow hostname
-targets string File with one hostname per line
OUTPUT
-output string JSON output file (default: stdout)
-quiet Suppress informational output
-version Print version and exit
CONNECTION
-proxy string Proxy URL (http, https, socks5, socks5h)
-timeout duration HTTP timeout per request (default: 15s)
-insecure Skip TLS certificate verification
-rate duration Minimum interval between requests (default: unlimited)
-vendor string X-Vendor header for engagement tracking
SCANNING
-concurrency int Max parallel targets (default: 3)
-table-concurrency int Max parallel table probes per target (default: 5)
-samples int Sample records per exposed finding (default: 10)
WIDGET PROBING
-widgets string Comma-separated widget IDs (overrides defaults)
-widgets-file string JSON file with additional widget candidates
TABLE ENUMERATION
-tables Enumerate via widget-simple-list (default: true)
-tables-file string JSON file with additional table/field candidates
-table-api Probe /api/now/table/ REST API
DISCOVERY
-discover Discover tables dynamically from the instance
-discover-limit int Max tables from discovery (default: 100)
-discover-widgets-limit int
Max widgets from discovery (default: 50)
-fingerprint Detect ServiceNow version and release
يأتي snowpick افتراضيًا مع 6 ويدجات و26 زوجًا من الجداول/الحقول. يمكنك إضافة المزيد دون تعديل الكود باستخدام ملفات JSON:
ويدجات مرشحة (-widgets-file):
[{"id": "WIDGET_ID", "name": "custom-widget"}]
جداول مرشحة (-tables-file):
[{"table": "TABLE_NAME", "field": "FIELD_NAME"}]
تُدمج هذه مع القيم الافتراضية المدمجة. وتُزال التكرارات تلقائيًا.
تُوفَّر هذه الأداة لأغراض الاختبارات الأمنية المصرح بها والبحث فقط. أنت وحدك المسؤول عن ضمان حصولك على إذن صريح لاختبار أي مثيل ServiceNow تستهدفه. الوصول غير المصرح به إلى أنظمة الكمبيوتر غير قانوني. لا يتحمل المؤلفون مسؤولية إساءة الاستخدام.