说到病毒,80后的我们立马就会想到“新型冠状病毒”,会想到“武汉加油,中国加油”。作为一名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加密):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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数据文件即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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工具来抽取。
恢复过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
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字典信息进行抽取,极端情况下需要提供表结构来手工构造(所以有一套和生产库表结构一模一样的开发库或者测试库还是有好处的),过程也非常麻烦,但恢复思路还是不变的。