说到病毒,一般人第一时间就会想到“新型冠状病毒”。作为一名Oracle DBA,提到病毒,我们还会想起最近今年来,在Windows平台上大量爆发的“比特币勒索病毒”,它曾感染了某个国家的交通系统,导致交通系统瘫痪,也曾感染某省的国土资源厅系统,使不动产系统长达10天之久不能正常对外提供服务。
比特币勒索病毒它是什么呢?
它其实也就是一类软件,此类软件对指定文件按照它配置的规则进行加密。加密完成后,会在Windows桌面出现提示文件已经加密,如需解密,就转***比特币,所以我们称谓“比特币勒索病毒”。
它加密的软件一定需要转比特币才能解密吗?
答案:肯定不是的,否则就不会有今天的文章了。
比特币勒索病毒已经由原来刚出来的一款软件变化到今天的很多款软件,每款软件的加密规则都不一样,但是原理是类似的。对符合条件的文件,按照指定的格式加密,生成新的文件,新文件名为元文件后加上.xxxx(加密程序不同后缀也不同),如文件1.txt,经加密程序加密之后将变成1.txt.xxx,同时删除原来文件。
如果加密的文件是Oracle数据的数据文件,那么整个Oracle数据库就不能打开。
此时我们能向黑客低头吗?肯定不能。
如果你很不幸遇到这样的case,别慌张,可以找我们,让我们一起来帮你分析。
针对比特币勒索病毒加密Oracle数据库的不同的文件,对数据库的影响,我们做了如下的总结:
- 数据库软件被加密——无所谓,重新安装一个Oracle软件就好
- Oracle控制文件和redo被加密——此时控制文件和redo都没有那么重要了
- Oracle数据文件被加密——这是我们需要分析的重点,也是恢复的重点
常见的比特币勒索病毒有很多种,并且还在不断的新增加,加密规则也在不断的发生变化,我们对加密规则做如下的总结:
- 文件前1M加密(本文将模拟该情况)
- 文件前2M加密
- 8k间隔加密
- 文件首尾各加密1M
- 全加密等等
通过我们以前恢复的经验,加密程序通常并不会对全文件进行加密,我们完全可以通过工具或者一些特殊手段将大部分未加密的数据抽取出来,以实现最大程度的恢复。如果数据文件全加密,那么大部分也不会对bak备份文件或者dmp文件做全加密,我们仍然能从备份中尽可能的恢复出数据。
恢复基本思路:
确认文件加密范围,用于评估恢复比例和恢复难度。
使用工具来抽取,本文推荐使用odu,如果没有听说或者没有使用过的朋友可以访问www.oracleodu.com进行了解。
故障模拟(破坏文件前1M,模拟前1M加密):
代码语言:javascript复制SQL> select name from v$datafile;
NAME
--------------------------------------------------------------------------------
/u01/app/oracle/oradata/rescureora/datafile/o1_mf_system_gy6tfgvc_.dbf
/u01/app/oracle/oradata/rescureora/datafile/o1_mf_sysaux_gx4pp1yd_.dbf
/u01/app/oracle/oradata/rescureora/datafile/o1_mf_undotbs1_gzblym25_.dbf
/u01/app/oracle/oradata/rescureora/datafile/o1_mf_users_gx4pq47j_.dbf
/u01/app/oracle/oradata/rescureora/datafile/o1_mf_test_gx4wvds6_.dbf
dd if=log.bbd of=/u01/app/oracle/oradata/rescureora/datafile/o1_mf_system_gy6tfgvc_.dbf bs=1M count=1 conv=notrunc
dd if=log.bbd of=/u01/app/oracle/oradata/rescureora/datafile/o1_mf_sysaux_gx4pp1yd_.dbf bs=1M count=1 conv=notrunc
dd if=log.bbd of=/u01/app/oracle/oradata/rescureora/datafile/o1_mf_undotbs1_gzblym25_.dbf bs=1M count=1 conv=notrunc
dd if=log.bbd of=/u01/app/oracle/oradata/rescureora/datafile/o1_mf_users_gx4pq47j_.dbf bs=1M count=1 conv=notrunc
dd if=log.bbd of=/u01/app/oracle/oradata/rescureora/datafile/o1_mf_test_gx4wvds6_.dbf bs=1M count=1 conv=notrunc
1.确认文件加密范围,每一个数据文件加密的算法和范围都是一样的,所以只需要分析Oracle的System数据文件即可。
代码语言:javascript复制declare -i bksize
declare -i n
declare -i j
fname=$1
bksize=$2
n=`ls -l $fname|awk '{print $5}'`
j=$n/$bksize
for ((i=1; i<=$j; i ))
do
dd if=$fname bs=$bksize skip=$i count=1|od -x|head -1|awk '{print $2,$5$4,'$i'}'|awk $([[ $(awk --version) = GNU* ]] && echo --non-decimal-data) 'BEGIN {OFS = FS}
{$4 = sprintf("%d", "0x" $2)-4194304
$5 = sprintf("%d", "0x" $2)
print
}'|awk '{if ($4!=$3 && $5!=$3) print $3}'>> check.log
done
cat check.log|awk '{print $0,NR,$0-NR}'|awk '{max[$3]=max[$3]>$1?max[$3]:$1}END{for(i in max)print i,max[i]}' >a.txt
cat check.log|awk '{print $0,NR,$0-NR}'|awk '{if(!min[$3])min[$3]=20121231235959;min[$3]=min[$3]<$1?min[$3]:$1}END{for(i in min)print i,min[i]}' >b.txt
awk 'NR==FNR{a[$1]=$2;}NR!=FNR && a[$1] {print $0,a[$1]}' a.txt b.txt|sort -n -k1|awk '{if ($2==$3) print $2" block was was Encrypted!";else print $2"-"$3" block was Encrypted!"}' > error.log
rm -f check.log
rm -f a.txt
rm -f b.txt
[oracle@rescureora ~]$ nohup sh check.sh '/u01/app/oracle/oradata/rescureora/datafile/o1_mf_system_gy6tfgvc_.dbf' 8192 &
[1] 3578
[oracle@rescureora ~]$ nohup: ignoring input and appending output to `nohup.out'
[oracle@rescureora ~]$ cat error.log
1-127 block was Encrypted!
可以看出从文件头开始到第127个块全部被加密。
这里补充一个LMT的数据文件的基础常识:
文件开头存放的是数据文件头和文件位图块等元数据块,不同的blocksize文件位图块个数不同:
- 8k:前128个块是元数据块(其中2-127是文件位图块)
- 16k:前64个块是元数据块(其中2-63是文件位图块)
- 32k:前32个块是元数据块(其中2-31是文件位图块)
共同点就是不管blocksize为多少,数据文件的前1M都是数据文件的元数据块,包括OS块、数据文件头、文件位图块。
恢复比例和难度评估:
从脚本error.log输出结果来看,只是文件前1M被加密,所以并不会丢失任何数据。甚至可以手工构造文件头强制open数据库然后exp导出。本文演示使用odu工具来抽取。
恢复过程:
代码语言:javascript复制1.配置好odu control.txt之后,申请lisence。
vi control.txt:
0 1 1 /u01/app/oracle/oradata/rescureora/datafile/o1_mf_system_gx4pofo6_.dbf 8192 0 N 131072
1 2 2 /u01/app/oracle/oradata/rescureora/datafile/o1_mf_sysaux_gx4pp1yd_.dbf 8192 0 N 78080
4 4 4 /u01/app/oracle/oradata/rescureora/datafile/o1_mf_users_gx4pq47j_.dbf 8192 0 N 12800
5 5 5 /u01/app/oracle/oradata/rescureora/datafile/o1_mf_test_gx4wvds6_.dbf 8192 0 N 131072
ODU> save control
The file write completed.
2.unload dict
ODU> unload dict
file '/u01/app/oracle/oradata/rescureora/datafile/o1_mf_system_gx4pofo6_.dbf' has not valid file header or block format
can not get bootstrap$ address from SYSTEM tablespace
ODU> unload dict block 520
CLUSTER C_USER# file_no: 1 block_no: 208
TABLE OBJ$ obj_no: 18 file_no: 1 block_no: 240
CLUSTER C_OBJ# file_no: 1 block_no: 144
CLUSTER C_OBJ# file_no: 1 block_no: 144
found IND$'s obj# 19
found IND$'s dataobj#:2,ts#:0,file#:1,block#:144,tab#:3
found TABPART$'s obj# 591
found TABPART$'s dataobj#:591,ts#:0,file#:1,block#:4000,tab#:0
found INDPART$'s obj# 596
found INDPART$'s dataobj#:596,ts#:0,file#:1,block#:4040,tab#:0
found TABSUBPART$'s obj# 603
found TABSUBPART$'s dataobj#:603,ts#:0,file#:1,block#:4096,tab#:0
found INDSUBPART$'s obj# 608
found INDSUBPART$'s dataobj#:608,ts#:0,file#:1,block#:4136,tab#:0
found IND$'s obj# 19
found IND$'s dataobj#:2,ts#:0,file#:1,block#:144,tab#:3
found LOB$'s obj# 80
found LOB$'s dataobj#:2,ts#:0,file#:1,block#:144,tab#:6
found LOBFRAG$'s obj# 624
found LOBFRAG$'s dataobj#:624,ts#:0,file#:1,block#:4264,tab#:0
由于1号文件文件头损坏,unload dict不能找到rootdba,需要手工指定block。
3.unload user
ODU> unload user RESCUREORA
Unloading user RESCUREORA's tables.
Unloading table: BIN$mJ5c4aC3Bm3gU2U4qMDdLw==$0,object ID: 87892 at 2020-02-26 14:32:52
Unloading segment,storage(Obj#=87892 DataObj#=87893 TS#=4 File#=4 Block#=130 Cluster=0)
Table BIN$mJ5c4aC3Bm3gU2U4qMDdLw==$0 0 rows unloaded
At 2020-02-26 14:32:52
Unloading table: RESCUREORA_TABLE,object ID: 87903 at 2020-02-26 14:32:52
Unloading segment,storage(Obj#=87903 DataObj#=87904 TS#=4 File#=4 Block#=138 Cluster=0)
Table RESCUREORA_TABLE 0 rows unloaded
At 2020-02-26 14:32:52
Unloading table: TEST,object ID: 87968 at 2020-02-26 14:32:52
Unloading segment,storage(Obj#=87968 DataObj#=87972 TS#=4 File#=4 Block#=146 Cluster=0)
Table TEST 0 rows unloaded
At 2020-02-26 14:32:52
Unloading table: TEST1,object ID: 87976 at 2020-02-26 14:32:52
Unloading segment,storage(Obj#=87976 DataObj#=87976 TS#=0 File#=1 Block#=95120 Cluster=0)
Table TEST1 86882 rows unloaded
At 2020-02-26 14:32:54
unload user 'RESCUREORA' finished.
前1M加密的恢复非常简单,如果加密的更多,则需要通过构造odu字典信息进行抽取,极端情况下需要提供表结构来手工构造(所以有一套和生产库表结构一模一样的开发库或者测试库还是有好处的),过程也非常麻烦,但恢复思路还是不变的。