Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable user-authentication and external table query file #7

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,25 @@ Perform a fast check that only scans for the table `kb_knowledge` using the `--f
python3 servicescan.py --url https://redacted.service-now.com --fast-check
```

### Extended tests
To perform more tests than what is provided by default, you can specify a `--table-file TABLE_FILE` argument to query particular tables a fields. A sample file is provided in `sample_table_query.txt`:
```bash
python3 servicescan.py --url https://redacted.service-now.com --table-file sample_table_query.txt
```

### Using a Proxy
To use a proxy server, use the `--proxy` option:
```bash
python3 servicescan.py --url https://redacted.service-now.com --proxy http://host:port
```

### Credentials
By default, the script will perform a scan without logging-in. If interested in performing a scan using a particular user
```bash
python3 servicescan.py --url https://redacted.service-now.com --user test.snc.internal
```
You will be prompted to specify a password if not provided via the `--password` argument.

### Example Output
If the target instance is found to be vulnerable, you'll receive an output similar to the following:
```bash
Expand Down
26 changes: 26 additions & 0 deletions sample_table_query.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
t=cmdb_model&f=name
t=cmn_department&f=app_name
t=kb_knowledge&f=text
t=licensable_app&f=app_name
t=alm_asset&f=display_name
t=sys_attachment&f=file_name
t=oauth_entity&f=name
t=cmn_cost_center&f=name
t=cmdb_model&f=name
t=sc_cat_item&f=name
t=sn_admin_center_application&f-name
t=cmn_company&f=name
t=customer_account&f=name
t=sys_email_attachment&f=email
t=sys_email_attachment&f=attachment
t=cmn_notif_device&f=email_address
t=sys_portal_age&f=display_name
t=incident&f=short_description
t=work_order&f=number
t=incident&f=number
t=sn_customerservice_case&f=number
t=task&f=number
t=customer_project&f=number
t=customer_project_task&f=number
t=sys_user&f=name
t=customer_contact&f=name
56 changes: 48 additions & 8 deletions servicescan.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import requests
import json
import re
import getpass

requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)


def check_vulnerability(url, g_ck_value, cookies, s, proxies, fast_check, display):
def check_vulnerability(url, g_ck_value, cookies, s, proxies, fast_check, display, tables):
table_list = [
"t=cmdb_model&f=name",
"t=cmn_department&f=app_name",
Expand Down Expand Up @@ -39,6 +40,8 @@ def check_vulnerability(url, g_ck_value, cookies, s, proxies, fast_check, displa
"t=sys_user&f=name",
"t=customer_contact&f=name"
]
if tables is not None:
table_list = tables

if fast_check:
table_list = ["t=kb_knowledge"]
Expand Down Expand Up @@ -67,7 +70,10 @@ def check_vulnerability(url, g_ck_value, cookies, s, proxies, fast_check, displa
if 'data' in response_json['result']:
if 'count' in response_json['result']['data'] and response_json['result']['data']['count'] > 0:
if response_json['result']['data']['list'] and len(response_json['result']['data']['list']) > 0:
print(f"{post_url} is EXPOSED, and LEAKING data. Check ACLs ASAP.")
if any(d['display_field']['display_value'] is not None for d in response_json['result']['data']['list']):
print(f"{post_url} is EXPOSED, and REALLY LEAKING data. Check ACLs ASAP.")
else:
print(f"{post_url} is EXPOSED, and POTENTIALLY LEAKING data. Check ACLs ASAP.")
if display:
try:
items = response_json['result']['data']['list']
Expand All @@ -88,9 +94,20 @@ def check_vulnerability(url, g_ck_value, cookies, s, proxies, fast_check, displa
return vulnerable_urls


def check_url_get_headers(url, proxies):
def check_url_get_headers(url, proxies, username, password):
# get the session
url = url.strip()
url = url.rstrip('/')

s = requests.Session()
if username is not None:
login_url = f"{url}/login.do"
payload = {
'user_name': username,
'user_password': password,
'sys_action': 'sysverb_login'
}
s.post(login_url, payload, proxies=proxies)
response = s.get(url, verify=False, proxies=proxies)
cookies = s.cookies.get_dict()

Expand All @@ -105,19 +122,19 @@ def check_url_get_headers(url, proxies):
return None, cookies, s


def main(url, fast_check, proxy, display, headers):
def main(url, fast_check, proxy, display, headers, tables, username, password):
if proxy:
proxies = {'http': proxy, 'https': proxy}
else:
proxies = None

url = url.strip()
url = url.rstrip('/')
g_ck_value, cookies, s = check_url_get_headers(url, proxies)
g_ck_value, cookies, s = check_url_get_headers(url, proxies, username, password)
if g_ck_value is None:
print(f"{url} has no g_ck. Continuing test without X-UserToken header")

vulnerable_url = check_vulnerability(url, g_ck_value, cookies, s, proxies, fast_check, display)
vulnerable_url = check_vulnerability(url, g_ck_value, cookies, s, proxies, fast_check, display, tables)
if vulnerable_url and headers :
print("Headers to forge requests:")
if g_ck_value is not None:
Expand All @@ -138,20 +155,43 @@ def main(url, fast_check, proxy, display, headers):
parser.add_argument('--proxy', help='Proxy server in the format http://host:port', default=None)
parser.add_argument('--display', action='store_true', help='output display name for quick visual')
parser.add_argument('--headers', action='store_true', help='print headers to forge request with any vulnerable systems')
parser.add_argument('--table-file', help='The specific tables to test. Sample sample_table_query.txt')
parser.add_argument('--username', help='The username to login with')
parser.add_argument('--password', help='The password to login with')
args = parser.parse_args()
fast_check = args.fast_check
display = args.display
proxy = args.proxy
headers = args.headers
table_file = args.table_file
username = args.username
password = args.password

# Prompt for a password if required
if username is not None and password is None:
password = getpass.getpass("Enter password: ")

# Collect tables to test from file if required
tables = None
if table_file is not None:
try:
table_file = args.table_file
with open(table_file, 'r') as file:
tables = file.read().splitlines()
except FileNotFoundError:
print(f"Could not find {url_file}")
except Exception as e:
print(f"Error occurred: {e}")

if args.url:
any_vulnerable = main(args.url, fast_check, proxy, display, headers)
any_vulnerable = main(args.url, fast_check, proxy, display, headers, tables, username, password)
else:
try:
url_file = args.file
with open(url_file, 'r') as file:
url_list = file.readlines()
for url in url_list:
if main(url, fast_check, proxy, display, headers):
if main(url, fast_check, proxy, display, headers, tables, username, password):
any_vulnerable = True # At least one URL was vulnerable
except FileNotFoundError:
print(f"Could not find {url_file}")
Expand Down