上一回我们拿单引号炸出了数据库的第章报错信息,像撬开了一道门缝。注入这回不玩报错了,第章直接上联合查询——UNION SELECT。注入这玩意儿才是第章真正搬开砖头往里瞅的招。
联合查询有个死规矩:前后两个SELECT语句的注入列数必须一模一样。列数不对,第章数据库直接甩脸子给你看。注入所以第一步永远是第章猜列数。怎么猜?注入拿ORDER BY试。你写`ORDER BY 3`,第章它不报错,注入说明表至少有3列;写`ORDER BY 4`报错了,第章好,注入列数就是第章3。这法子笨,但稳。我见过有人拿脚本从1跑到50,跑完还得意,其实没必要,二分法几轮就定位了。
确定列数后,你得找哪个字段能回显到页面上。不是所有列都显示给你看的,有的藏在后台逻辑里。经典操作是`UNION SELECT 1,2,3`,然后看页面上哪个数字冒出来了。冒出来的那个位置,就是你注入点。要是啥都没显示,别急,用`AND 1=2 UNION SELECT 1,2,3`把前面正常查询的结果顶掉——这叫“空查”,专门逼数据库只输出你构造的内容。
有一次我测一个站点,回显位置在第二列。那就好办了,直接`UNION SELECT 1,database(),3`,数据库名当场就蹦出来。那是个政府站,库名叫`gov_news`,土得掉渣,但权限给得挺大方。接着`UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema=database()`——这一步是掏家底。information_schema是MySQL的元数据库,所有表结构都记在里面。group_concat能把多行结果拼成一行,省得你一条条翻。
很多人卡在拿不到数据这一步,其实不是技术问题,是思路问题。你光盯着回显位置看,忘了数据库还有报错这条道。报错注入在联合查询没回显时特别好使——比如`AND extractvalue(1,concat(0x7e,(SELECT user()),0x7e))`。0x7e是波浪号~,用来把报错信息包起来,方便你截取。extractvalue本来是用来解析XML的,你给它传个畸形路径,它就把路径内容原样吐在报错里。这招在MySQL 5.1到5.6之间几乎通杀。
再说个坑。有些站点的参数是数字型的,你直接拼`id=1 UNION SELECT...`可能被WAF拦。WAF这东西,说穿了就是个正则匹配器。它拦`union`、拦`select`,但拦不住`UNION ALL SELECT`加注释符`/*!50000*/`。MySQL有个特性,版本号在注释里会执行,所以`/*!50000UNION*/`能绕过不少弱智规则。当然,这属于对抗范畴了,入门阶段你只要知道有这回事就行。
最后提一嘴手工和工具的区别。SQLMap跑得快,但跑不出你对手工注入的理解。我见过太多人拿SQLMap一把梭,报错信息都看不懂,更别提自己拼语句了。手工注入就像学骑自行车——摔几次,你才知道平衡感在哪。SQLMap是电动车,省力但永远练不出车感。等你能闭着眼写出`UNION SELECT`的完整流程,再上工具也不迟。
这条道走到黑,后面还有布尔盲注、时间盲注、堆叠注入等着你。但今天先把联合查询玩熟,把information_schema当自家后院逛。数据库这东西,你摸透了它的脾气,它就任你摆布。摸不透,它就拿报错信息扇你耳光。挨打多了,自然就学会了。