forked from klsecservices/rpivot
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathclient.py
290 lines (240 loc) · 11.4 KB
/
client.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
#!/usr/bin/env python
import socket
import re
import sys
import time
from struct import unpack
import select
import optparse
import errno
import relay
import threading
from ntlm import NtlmProxyContext
from common import create_logger, ls, Relay, RelayMainError
def key_by_value(my_dict, value):
for k, v in my_dict.items():
if v == value:
return k
return None
class SocksRelay(Relay):
def __init__(self, command_socket):
super(SocksRelay, self).__init__(command_socket)
self.establishing_dict = {}
self.forward_socket = None
self.data = None
self.ping_thread = threading.Thread(target=self.ping_worker, name='Ping')
self.ping_thread.start()
#
# Common methods
#
def ping_worker(self):
while True:
time.sleep(self.ping_delay)
current_time = time.time()
if self.remote_side_is_down:
log.debug('Remote side down. Exiting ping worker')
return
if current_time - self.last_ping > self.relay_timeout:
log.error('No response from remote side for {0} seconds. '
'Restarting relay...'.format(relay.relay_timeout))
self.command_socket.close()
return
def close_connection_with_server(self):
self.command_socket.close()
self.input_connections.remove(self.command_socket)
#
# Handle commands
#
def close_channel_hdl(self, channel_id):
establishing_sock = key_by_value(self.establishing_dict, channel_id)
if establishing_sock is not None:
log.debug('[{0}] Closing establishing channel...'.format(channel_id))
del self.establishing_dict[establishing_sock]
return
elif channel_id not in self.channels:
log.debug('Channel {0} non existent'.format(channel_id))
return
sock_to_close = self.channels[channel_id]
self.unset_channel(channel_id)
log.debug('[{}] Closing channel...'.format(channel_id))
sock_to_close.close()
self.input_connections.remove(sock_to_close)
def open_channel_hdl(self, data):
channel_id, packed_ip, port = unpack('<HIH', data[1:9])
ip = socket.inet_ntoa(data[3:7])
log.debug('Got new channel request with id {0}. '
'Opening new forward connection to host {1} port {2}'.format(channel_id, ip, port))
self.establish_forward_socket(channel_id, ip, port)
def ping_command_hdl(self):
self.last_ping = time.time()
self.send_proxy_cmd(relay.PING_CMD)
#
# SOCKS client's methods
#
def establish_forward_socket(self, channel_id, host, port):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(0)
log.debug("[{}] Opening {}:{}".format(channel_id, host, port))
sock.connect_ex((host, port))
except socket.error as err:
(code, msg) = err.args
log.error("[{}] Caught exception socket.error: {}: {}".format(channel_id, code, msg))
self.send_proxy_cmd(relay.FORWARD_CONNECTION_FAILURE, channel_id)
return
log.debug('[{}] New pending forward connection: {}'.format(channel_id, sock))
self.establishing_dict[sock] = channel_id
#
# ...
#
def run(self):
ready_to_read = None
ready_to_write = None
while True:
try:
time.sleep(relay.delay)
log.debug('Active channels: {0}. Pending Channels {1}'.format(
ls(self.channels.keys()), ls(self.establishing_dict.values())))
ready_to_read, ready_to_write, _ = \
select.select(self.input_connections, self.establishing_dict.keys(), [], 15)
except KeyboardInterrupt:
log.info('SIGINT received. Closing relay and exiting')
self.send_proxy_cmd(relay.CLOSE_RELAY)
self.shutdown()
except (select.error, socket.error) as err:
(code, msg) = err.args
log.error('Select error on select. Errno: {0} Msg: {1}'.format(errno.errorcode[code], msg))
self.shutdown()
for sock in ready_to_write:
channel_id = self.establishing_dict[sock]
log.debug('[{0}] Establishing connection with channel id {0}'.format(channel_id))
try:
sock.recv(0)
except socket.error as err:
(code, err_msg) = err.args
if code == errno.ECONNREFUSED or code == errno.ETIMEDOUT:
if sock in ready_to_read:
ready_to_read.remove(sock)
del self.establishing_dict[sock]
self.send_proxy_cmd(relay.FORWARD_CONNECTION_FAILURE, channel_id)
sock.close()
continue
elif code == errno.EAGAIN:
log.debug('Recv(0) return errno.EAGAIN for socket {0} on channel {1}. '
'Connection established.'.format(sock, channel_id))
elif code == 10035:
log.debug('Recv(0) raised windows-specific exception 10035. Connection established.')
else:
raise
log.info('Connection established on channel {0}'.format(channel_id))
sock.setblocking(1)
self.send_proxy_cmd(relay.FORWARD_CONNECTION_SUCCESS, self.establishing_dict[sock])
del self.establishing_dict[sock]
self.input_connections.append(sock)
self._set_channel(sock, channel_id)
for selected_input_socket in ready_to_read:
if selected_input_socket == self.command_socket:
try:
self.manage_proxy_socket()
except RelayMainError:
log.debug('Remote side closed socket')
self.close_sockets(self.input_connections)
return
else:
try:
self.manage_socks_client_socket(selected_input_socket)
except RelayMainError as err:
log.debug(err)
def main():
global log
parser = optparse.OptionParser(description='Reverse socks client')
parser.add_option('--server-ip', action="store", dest='server_ip')
parser.add_option('--server-port', action="store", dest='server_port', default='9999')
parser.add_option('--verbose', action="store_true", dest="verbose", default=False)
parser.add_option('--logfile', action="store", dest="logfile", default=None)
proxy_group = optparse.OptionGroup(parser, 'Ntlm Proxy authentication')
proxy_group.add_option('--ntlm-proxy-ip', dest='ntlm_proxy_ip', default=None, action='store',
help='IP address of NTLM proxy')
proxy_group.add_option('--ntlm-proxy-port', dest='ntlm_proxy_port', default=None, action='store',
help='Port of NTLM proxy')
proxy_group.add_option('--username', dest='username', default='', action='store',
help='Username to authenticate with NTLM proxy')
proxy_group.add_option('--domain', dest='domain', default='', action='store',
help='Domain to authenticate with NTLM proxy')
proxy_group.add_option('--password', dest='password', default='', action='store',
help='Password to authenticate with NTLM proxy')
proxy_group.add_option('--hashes', dest='hashes', default=None, action='store',
help='Hashes to authenticate with instead of password. Format - LMHASH:NTHASH')
parser.add_option_group(proxy_group)
cmd_options = parser.parse_args()[0]
if cmd_options.server_ip is None:
print('Server IP required')
sys.exit()
log = create_logger(__name__, True, cmd_options.verbose, cmd_options.logfile)
log.info('============ Start proxy client ============')
while True:
log.info('Backconnecting to server {0} port {1}'.format(cmd_options.server_ip, cmd_options.server_port))
backconnect_host = cmd_options.server_ip
backconnect_port = int(cmd_options.server_port)
bc_sock = None
while True:
try:
bc_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if cmd_options.ntlm_proxy_ip is not None:
if cmd_options.ntlm_proxy_port is None:
log.error('Error. Must specify ntlm proxy port')
sys.exit(1)
if cmd_options.hashes is not None:
if re.match('[a-zA-Z0-9]{32}:[a-zA-Z0-9]{32}', cmd_options.hashes) is None:
log.error('Hash format error. Valid hash format - LMHASH:NTHASH')
sys.exit(1)
log.info('Connecting via NTLM proxy at {0}:{1}'.format(
cmd_options.ntlm_proxy_ip, cmd_options.ntlm_proxy_port))
ntlm_con = NtlmProxyContext(
bc_sock,
proxy_ip=cmd_options.ntlm_proxy_ip,
proxy_port=int(cmd_options.ntlm_proxy_port),
username=cmd_options.username,
domain=cmd_options.domain,
password=cmd_options.password,
nthash=None if cmd_options.hashes is None else cmd_options.hashes.split(':')[1],
lmhash=None if cmd_options.hashes is None else cmd_options.hashes.split(':')[0])
bc_sock = ntlm_con
bc_sock.connect((backconnect_host, backconnect_port))
break
except socket.error as err:
(code, msg) = err.args
log.error('Unable to connect to {0}:{1}. Caught socket error trying to establish '
'connection with RPIVOT server. Code {2}. Msg {3}. '
'Retrying...'.format(cmd_options.server_ip, cmd_options.server_port, code, msg))
time.sleep(5)
try:
bc_sock.send(relay.banner)
banner_reponse_rcv = bc_sock.recv(4096)
if banner_reponse_rcv != relay.banner_response:
log.error("Wrong banner response {0} from server. Retrying".format(repr(banner_reponse_rcv)))
bc_sock.close()
time.sleep(5)
continue
except socket.error as err:
(code, msg) = err.args
log.error('Caught socket error trying to establish connection with RPIVOT server. '
'Code {0}. Msg {1}'.format(code, msg))
bc_sock.close()
time.sleep(5)
continue
socks_relayer = SocksRelay(bc_sock)
try:
socks_relayer.run()
except socket.error as err:
(code, msg) = err.args
log.error('Exception in socks_relayer.run(). '
'Errno: {0} Msg: {1}. Restarting relay...'.format(errno.errorcode[code], msg))
bc_sock.close()
continue
except KeyboardInterrupt:
log.error("Ctrl C - Stopping server...")
sys.exit(1)
time.sleep(10)
if __name__ == '__main__':
main()