对不起,我骗了你们

对不起,我用《对不起,我骗了你们》骗了你们🙃,诶诶诶,别着急离开,我是真心来认错的,的确有挺长时间没有更新了,不是我咕了,而是我一直在准备素材,毕竟这个技术文,单单是码字都要码好几个小时,更别提你要去把你想要讲的每一个技术点吃透所需要的时间了。

其实,除了准备素材之外,我最近还在忙一件事,在这件事情上,我的确是骗了大家

也不算是骗,只是之前没有告诉大家而已

你们可能知道我是一个18岁的安全研究员以及puff newbie大赛优胜选手

但是你们不知道的是,在安全圈划水只是我的兴趣爱好

我的主业其实是做平面模特!!!

而我最近在忙的这件事其实就是一场平面模特大赛——AXZS

你们可能不太了解这个比赛,可以去搜一下,这个比赛在我们模特界很有分量,每三年才举办一次

为了这个比赛我已经悄悄准备了三年,无论是从锻炼还是护肤上,我都下足了功夫

每天早晨5点,在大多数人还在呼呼大睡的时候,我就已经开始锻炼了,基本上就是慢跑十公里

下午6点左右,在大家网上冲浪、刷剧的时候,我会去健身房做无氧增肌

晚上10点,在大家准备洗洗睡了的时候,我会拿着镜子练习表情管理,再配合直播等方式练习在镜头前的表现力

是不是大有唐伯虎诗中“名不显时心不朽,再挑灯火看文章”那味儿了?

为了这次比赛,我准备了三年,我必须拿到名次!

就在这周,比赛正式开始了

为期两天,比赛结束时,每位参赛人员需要提供五组照片,然后评委会从这些照片中进行评选,选出前五名,而这前五名则会被载入AXZS的荣誉榜

在模特界,说这五个人名垂青史也不为过!

这个时候有些小伙伴可能就要急了:”阿信呐,说了这么多,你到底拿没拿到名次呀“

自信一点,把【没拿】去掉,隔壁【坤坤那么努力】都能以C位出道,我这么努力怎么也得拿个

第三名吧!

对,你没有看错,你关注的这个你本以为是个猥琐的技术宅的男人,居然拿到了全国最知名的平面模特大赛的季军

不得不说这是一个魔幻的世界

虽然没能如愿登顶AXZS,但是能够拿到这个名次也算是差强人意了吧

可能也有人比较好奇前五名都有谁

这个嘛…

其实我也记不太清楚他们的名字了,只有一个大概的印象,第一名好像叫彭什么晏,然后第二名好像叫什么彦祖,第三名就是我,第四名好像是个摄影爱好者,但是不知道为什么大家都让他以后不要拍照了,大家都叫他冠希哥,第五名我已经完全想不起名字了…

我知道,你们可能不太相信我说的话,所以,我带了我参赛时的照片过来,喏~

怎么样,我没有骗你吧~

其实,关于这次比赛我还想写很多,但是我转念一想,我还得给你们更文章呀,所以…

这次的文章还是关于某个CMS的审计,其中所涉及到的漏洞有sql注入,越权,逻辑漏洞,RCE,XSS,CSRf等,反正这个cms到处都是漏洞…

我审计的这个CMS不是最新版本,可能其中的某些漏洞在最新版中已经被修复了,当然还有一些是没有被修复的(我用新版测试过),所以不能公布该CMS的名称,但是如果哪位师傅想用来练练手,可以加我的微信tntaxin,手把手答疑😅,除此之外,这个CMS最近被很多人审计过了,我看到过很多关于它的审计文章,但是这些文章中所公布的漏洞也是不全面的,可能是因为这个CMS漏洞实在是太多了,作者不想写8

ok,和往常一样,我的文章会更偏向我挖掘的一个思路或者流程,希望帮到大家

准备工作

在正式开始审计之前,我们需要大体扫描一下该程序的目录结构,

├── 404.html
├── A(admin后台的一些文件)
├── Conf(一些网站的配置文件,公共函数)
├── FrPHP(框架核心)
├── Home(普通用户相关功能代码)
├── Public(上传文件保存的地方)
├── README.md
├── admin.php(后台入口)
├── backup(数据库备份文件)
├── cache(网站缓存)
├── favicon.ico
├── index.php(前台入口)
├── install(安装目录)
├── readme.txt
├── sitemap.xml
├── static(一些静态文件)
└── web.config

通过目录以及其中的一些关键文件,可以看出来这是一个MVC架构的程序,对于MVC架构的程序,我习惯性的去看一下它的路由规则,如果路由设计的不好也会产生RCE的问题,除了看路由规则,我们还需要确定程序是否支持自定义路由以及自定义路由是通过什么方式实现的。

在这个程序中,路由规则很简单,有经验的师傅甚至都不用看具体的路由代码实现,单单是看目录以及不同请求的url就可以知悉它的路由规则。

sql注入

在大致了解了该程序的运作方式过后,就正式开始审计了,我主要是根据功能点来进行审计,这样效率可能更高一点。所以,接下来我们就开始浏览这个网站吧,首先是首页,随便翻了一翻,发现两处功能点,如下图所示

看到这两个功能,心中立马浮现出几个问题:

  • 分页功能处有sql注入吗?
  • 搜索功能处有sql注入或者xss吗?

经验证,分页功能处没有问题,那么就来到了搜索功能处,输入123,然后发现跳转到了http://localhost/search?molds=article&word=123,然后我们到代码中找到相应的处理逻辑,根据路由规则定位到Home/c/HomeController.php下的search函数,精简后的代码如下:

function search(){
    $tables = explode('|',$this->webconf['search_table']);
    $molds = $this->frparam('molds',1);//搜索的模块
    $tid = $this->frparam('tid',1);
    if(in_array($molds,$tables) && $molds!=''){
        $word = $this->frparam('word',1);
        if($word==''){
            if($this->frparam('ajax')){
                JsonReturn(['code'=>1,'data'=>'','msg'=>'请输入关键词搜索!']);
            }
            Error('请输入关键词搜索!');
            
        }
        $this->word = $word;
        $sql =  " isshow=1 and title like '%".$word."%'  ";
        if($tid){
            $sql.=' and tid in('.$tid.') ';
        }
        
        $page = new Page($molds);
        $page->typeurl = 'search';
        $data = $page->where($sql)->orderby('id desc')->limit($this->frparam('limit',0,15))->page($this->frparam('page',0,1))->go();
        $pages = $page->pageList(3,'&page=');
        ...

逻辑很简单,就是从客户端拿到参数,然后拼接sql语句,查数据库,然后返回数据,比较有意思的是该程序通过frparam()函数对客户端传过来的一些参数进行了处理,这个函数几乎用在了所有对参数的处理上,是一个全局的过滤函数,看来程序作者还是有安全意识的,我们看一下frparam的实现,发现其对所有的参数都调用了format_param(),继续跟进该函数

function format_param($value=null,$int=0){
	if($value==null){ return '';}
	switch ($int){
		case 0://整数
			return (int)$value;
		case 1://字符串
			$value = SafeFilter($value);
			$value=htmlspecialchars(trim($value), ENT_QUOTES);
			if(version_compare(PHP_VERSION,'7.4','>=')){
				$value = addslashes($value);
			}else{
				if(!get_magic_quotes_gpc())$value = addslashes($value);
			}
			
			return $value;
		case 2://数组
			if($value=='')return '';
			array_walk_recursive($value, "array_format");
			return $value;
		case 3://浮点
			return (float)$value;
		case 4:
			if(version_compare(PHP_VERSION,'7.4','>=')){
				$value = addslashes($value);
			}else{
				if(!get_magic_quotes_gpc())$value = addslashes($value);
			}
			return trim($value);
	}
}

其中有一个函数值得注意,那就是SafeFilter,代码如下:

//过滤XSS攻击
function SafeFilter($arr) 
{
   $ra=Array('/([\x00-\x08])/','/([\x0b-\x0c])/','/([\x0e-\x19])/','/script/','/javascript/');
   $arr = preg_replace($ra,'',$arr);   
   return $arr;
}

可看出来,这是一个xss过滤器,但是emmm,这个xss过滤器虽然不能说是写的天衣无缝,但也算是毫无用处吧🤔

虽然这个xss过滤形同虚设,但是结合着上一个format_param看,所有的参数都经过了html编码,这基本上是xss的克星,而且也能预防一部分的sql注入,好几个点都是因为这该死的htmlspecialchars()函数无法利用,format_param()函数最后还对单引号进行了一波转义

好家伙,这一套过滤走下来,着实比较难受

如果作者在每一处有输入的地方都正确的使用了这些过滤,基本上是可以杜绝所有的sql注入,xss虽然不能够完全杜绝,但是也能够防下大部分了,只是一些特殊环境下的输出,仍然可能存在xss,例如url环境中

因为这个过滤函数的存在,我们的挖掘思路也就可以明确下来了,针对sql注入,我们就只能找一些没有使用frparam(),或者错误使用frparam的地方,针对xss,我们就只有找一些特殊的输出环境。

上面是看完frparam我得到的一些信息,我们继续回到搜索功能处,可以看到代码中有一个参数tid,这个参数也是经过frparam过滤的,但是当我看到它的使用场景,我直接乐开了花,这不说啥来啥嘛,tid是这样拼接到sql语句中的

$sql.=' and tid in('.$tid.') '

前面的frparam函数能够过滤单引号、双引号等特殊符号,但是这一处注入不需要这些符号,只需要一个小小的)以及#就可以完成注入,这就是一处错误的使用frparam()函数的例子,不到十分钟,一个漏洞到手,这一处注入也是其他人没有注意到的点,所以我放在了前面(明明很明显~)

然后,我们继续看其他功能~

且慢,在我写这篇文章的时候,突然想起来,既然作者有这么写sql语句的习惯,那么其他地方还有没有同样的写法呢?于是我全局搜索了一下in('

你猜猜这些地方有没有注入?

我没有每个都去求证,但是图中搜索结果的第一个就是一处注入,这个是我在后面挖到的,而不是通过这种全局搜索方式挖出来的,大家可以去看一看其他点

首页功能看完了,我们接着往后走,【商品】与【新闻】页面没啥功能,就只有一些数据库的查询操作,大体看了下都没有注入,然后【联系】页面就是一个文本展示页,没有交互,接下来来到【留言】页面,留言功能是大家都不想放过的,一不小心就又是一个sql注入或者存储型xss,所以,我们着重看一下吧,随便留个言,抓包,请求如下:

定位到对应的controller,然后发现所有的参数都经过了frparam过滤,那还玩个p

正打算我准备走人的时候,发现留言功能处把用户的ip也入库了,获取ip的函数是GetIP(),这把应该有戏了吧,但是当我点进去,发现…

function GetIP(){ 
  static $ip = '';
  $ip = $_SERVER['REMOTE_ADDR'];
  if(isset($_SERVER['HTTP_CDN_SRC_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CDN_SRC_IP'])) {
    $ip = $_SERVER['HTTP_CDN_SRC_IP'];
  } elseif (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {
    $ip = $_SERVER['HTTP_CLIENT_IP'];
  } elseif(isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
    foreach ($matches[0] AS $xip) {
      if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) {
        $ip = $xip;
        break;
      }
    }
  }
  return $ip;
}

这作者简直把我拿捏的死死的,我还玩个🐍🐍

至此,该程序无需登录的功能点基本上都测试完了,接下来,我们就需要注册一个账号,然后解锁新的功能点了

用户中心界面长这样

可以看到,功能还是挺多的,经过一番挖掘,没有发现能够getshell的点,都是一些业务级别的漏洞,类似越权修改别人文章,越权删除别人文章,csrf等等,就不展开了,都是老一套,当然还有逻辑漏洞,在邀请用户注册那里

后台getshell

本程序在文件上传处的处理没有大问题,所有前台也就没有找到getshell的点,所以只有到后台来看一看了,后台功能很丰富,经过一番挖掘,也是挖到了我前面提到的那种类型的xss,但是后台xss没啥意义,除此之外也还有很多sql注入,我发现的就有两处

/admin.php/Classtype/change_status.html 参数 id存在sqli , id=0 or sleep(1)#

admin - deleteall() 只要是 id in (可控) 处都是存在sqli的,直接全局搜索 findall(看参数有惊喜

但是,我们要的是什么?

是爱情!

额,不对,是shell!

我把目标放在了getshell以及ssrf这种漏洞上面,好巧不巧还真发现了一处,就在下载插件的地方

如果你的各个目录处于正常的可写入状态,点击下载按钮,会发送四个请求,分别是

start_download:下载download_url指定的压缩包到缓存目录,这个压缩包地址可控

get-file-size: 获取压缩包文件大小

file-upzip: 把压缩包解压到A/exts/目录

action_do: 执行插件的安装,实际上就是进行了一些文件包含操作

其中就包含了压缩包解压后的文件夹中的PluginsController.php文件,所以,看完上述流程你知道怎么操作了吧,其实上面的截图就是我的一个复现过程,不过还差一步,我这里总结一下

  • 创建一个恶意文件PluginsController.php,然后打包成shell.zip
  • 将该shell部署到web服务器上
  • 依次发送上述截图中的三个请求,完成文件下载、解压工作,其中,filepath参数是你的压缩包在目标服务器上解压后的名字,例如,你的压缩包是shell.zip,解压后的文件夹名字就位shell,不要填错了
  • 然后执行执行以下请求,其中,path是解压后的目录名

可以看到,成功执行了我的恶意文件,返回了phpinfo信息。

其他

呼,终于写完了,写作状态不是很好,审计这个cms的时间跨度是一周,断断续续的,本想写一下整个挖掘的流程,但是由于时间跨度太长,不能清晰的复现当时的思路以及想法,所以,有点散乱。同样的,这个程序漏洞很多,很适合练手,后台的功能我都没怎么看,相信后台还有很多问题,特别是充值那一块,应该还有比较有意思的问题🤔,但是我实在顶不住了,看不下去了

那么,下一篇文章写啥呢?That is a question!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值