渗透经验分享之SQL注入思路拓展

## 写在文前

从实习到现在，因为从事打点工作的原因，实战经验积累了很多，就想写一些自己在实战中碰到的问题，以及自己的解决方法，因为保密的原因大多数不提供复现环境和截图，权当对渗透思路的一个拓展吧。可能会多写几篇，也可能因为实战经验不足就此荒废这个系列。总之看心情和时间安排吧。

## 注入的几个问题
SQL注入所涉及到的东西无非就以下几方面

1. 哪里会经常出现注入
2. bypass waf
3. 如何快速定位重要数据表
4. 大数据表托数据
5. 注入读写文件
6. 执行命令

## 哪里经常出现注入
要想利用注入，首先你得挖到一个注入点，在大多数的业务场景中，搜索框是一个容易出现注入的地方，他们的SQL语句形如

```sql
SELECT p.*, pac.all_cities FROM {p}_page AS p left join {p}_page_all_cities pac  on p.page_id=pac.page_id and p.lang=pac.lang left join {p}_page_all_provinces pap  on p.page_id=pap.page_id and p.lang=pap.lang WHERE p.[lang] = N'2' AND p.[hidden] = N'0' AND p.[parent_id] =14  And ( p.[title] like N'%sql%' )  And (p.[pri4]=N'1' Or p.[pri5]=N'1' Or p.[pri6]=N'1' Or p.[sec1]=N'1' Or p.[sec2]=N'1' Or p.[sec3]=N'1' Or p.[sec4]=N'1' Or p.[sec5]=N'1' Or p.[sec6]=N'1')  And ( wholeyear=N'1'  Or year1m9=N'1' Or  wholeyear=N'1'  Or year2m1=N'1')  ORDER BY  wholeyear
```
可能你觉得这里用预编译处理的话岂不是简简单单防止sql注入？但是有些开发没有安全意识，他嫌弃这个sql语句又臭又长，硬要进行拼接sql语句，造成注入。

还有就是order by的注入点，SQL预编译会解决sql注入问题，但是有些地方是不能参数化的。比如order by后就不能参数化，挖注入的时候看准orderby、sort参数，一挖一个准。

为什么orderby不能参数化查询？[移步这里](https://www.cnblogs.com/lsdb/p/12084038.html)

再重点关注下日期类型的参数，形如

```sql
SELECT * FROM `wp_posts` where post_content like '%%' and post_date BETWEEN '2020/01/01' and '2020/09/02'
```
日期类型如果在接受参数的时候没有进行强制类型转换很容易出现注入问题，也是直接拼接的原因。

1. 搜索
2. orderby、limit
3. 日期类型

其实这些大家都知道，测的时候细心一些就可以了。我习惯每个参数都测一测，`lang=cn`参数有注入的情况也不是没有。

## bypass waf
这点没什么好说的，404师傅的 [MYSQL_SQL_BYPASS_WIKI](https://github.com/aleenzz/MYSQL_SQL_BYPASS_WIKI) 讲的姿势很多了。其实绕waf最重要的是你需要清楚他拦截的是你payload的哪一部分。我的思路是一段一段测，比如：

```sql
select user() -- 拦截
```
拦了select还是拦了user()？还是拦了`/select.*?user/`这个正则？

改成`selact`试试

```sql
selact user() -- 不拦截
```

改成select usar()试试

```sql
select usar() -- 不拦截
```
那就说明他拦截的是`/select.*?user/`，打乱正则就行了。

还有就是内联注释并不是万能的，更多的时候需要配合数据库的特性以及中间件的解析特性来绕过。[一个很牛逼的例子就是这篇文章](https://xz.aliyun.com/t/7767)

## 如何快速定位重要数据表
渗透中总是有一些大型的数据库，一个数据库中有几百个表，一个一个看脑壳疼。这个时候sqlmap有一个参数叫做`--search`，可以用来搜索列、表或数据库名称。

1. --search  -D：搜索某个数据库
2. --search  -T：搜索某个表名
3. --search  -C：搜索某个字段名

通过搜索形如username、password的字段即可快速定位重要数据表。

## 大数据表托数据
一般上是直接托文件，比如mysql可以用自带的mysqldump，如果是站库分离可以自己传一个mysqldump上去指定`-h`参数就行了。mysqldump是没有依赖的，单exe就能运行，直接拖sql文件比一点一点拖快得多。

mssql的话直接拖mdf，或者osql命令。

## 注入读写文件
以下全部基于你有路径的前提下。

当你通过注入没有下一步思路的时候，就要考虑读写文件了。读写文件很多情况下都需要用到堆叠语句，MySQL中如果用的是mysqli pdo处理的话，有的可以堆叠，mssql+aspx是原生堆叠，Oracle要看代码层面是怎么写的，具体根据实际判断吧，环境不一样问题不一样。

MySQL读文件值得一提的就是`load data infile`和`load data local infile`，不受`secure-file-priv`的限制，但是需要堆叠，或者你也可以找找phpmyadmin，phpmyadmin的话还受到`open_basedir`限制。通过注入拓展任意文件读取，也算一种思路吧。如果你是mysql低权限账户可以试着读一下`user.MYD`，万一读到root密码呢？

至于`load data local infile`的权限问题一直是一个谜，我理解的他的权限应该是和MySQL的权限一样的，因为碰到了一次读不到apache用户www目录下的源码，MySQL用户和apache权限不一样。但是其实我自己都不是很确定，希望有师傅看到了指点一下。

MySQL写文件的话`into outfile`、`into dumpfile`还有就是日志写文件`general log`，绝对路径写shell，插件写udf，写mof。

mssql的话读写文件的操作更多样化一些。

列目录`xp_dirtree`、`xp_subdirs`
写文件`xp_cmdshell 'echo 1 > c:/1.txt'`、`sp_oacreate`、`sp_oamethod`配合写shell

```sql
declare @sp_passwordxieo int, @f int, @t int, @ret int;
exec sp_oacreate 'scripting.filesystemobject', @sp_passwordxieo out;
exec sp_oamethod @sp_passwordxieo, 'createtextfile', @f out, 'c:/www/1.aspx', 1;
exec @ret = sp_oamethod @f, 'writeline', NULL,'this is shell';
```
或者出网的话直接写一个vbs下载器，随意发挥。

读文件的话

```sql
USE test;
DROP TABLE cmd;
CREATE TABLE cmd ( a text );
BULK INSERT cmd FROM 'd:/config.aspx' WITH (FIELDTERMINATOR = 'n',ROWTERMINATOR = 'nn')
SELECT	* FROM	cmd
```

站库分离的话看下数据库服务器有没有web服务，如果直接访问IP是iis默认页面可以直接往iis的默认目录写aspx。没有web服务的话可以写一个dns的马进去，xp_cmdshell执行，或者调用wscript.shell执行。

postgresql的`copy from`，`copy to`读写文件，要是有别人的马直接读文件岂不是美滋滋。

## 执行命令
MySQL udf mof不说了
mssql xp_cmdshell、自己创建clr、调用wscript.shell、调用Shell.Application、写启动项、写dll劫持。

之前碰到过一个站库分离，有xp_cmdshell，但是只出dns的。通过certutil转exe为base64，通过echo写入文件，调用目标的certutil转回exe执行上线。

## 其他利用场景
任意文件上传，没有路径，找找注入在数据库中肯定存储了文件的路径，配合sqlmap的--sql-shell和--search参数就能找到shell地址。

## 总结
通过注入可以拓展很多思路，因为数据库基本上都实现了读写文件的操作，执行命令也可以通过拓展或者系统自带的存储过程来实现。限制我们的更多的是权限的问题，一个绝对意义的低权限注入点神仙也没办法。思路给到，具体情况具体分析。欢迎师傅们分享更多由注入拓展的姿势。