วันศุกร์ที่ 13 สิงหาคม พ.ศ. 2553

Crack password Oracle database ตั้งแต่ 8i ถึง 11g

อันนี้ยกเครดิตให้ Blog Oracle and Java นะครับเห็นว่าน่าสนใจ


คงเป็นที่ทราบกันดีว่า password ที่เราเก็บไว้ในฐานข้อมูลเพื่อไว้ใช้ในการทำ authentication (ระบุตัวตน) นั้นในปกติการเก็บ password นั้นเราจะไม่เก็บกันตรง ๆ เช่นเราตั้ง password ว่า siam เวลาเก็บลงใน database จริงแล้วจะไม่เก็บคำว่า siam โดยคำว่า siam นั้นจะถูกแปลงเป็นคำอื่น ๆ โดยจะไม่เหลือเค้าโครงเดิมเลย โดยการแปลงนี้ก็มี algorithm (วิธีการ) ต่าง ๆ มากมายกันไปซึ่งเราจะเรียกวิธีการนี้ว่า การ hash password แต่ว่ามันอาจจะยังไม่เพียงพอ ทำให้บางคนนั้นก็ได้เพิ่ม keyword เข้าไปผสมลงใน password ก่อนจะผ่านกรรมวิธีการ hash password เพื่อให้แก้ไขได้ยากยิ่งขึ้นซึ่งเราเรียก keyword ที่เราไปผสม password นี้ว่า salt (ซึ่งเป็นตัวเดียวกันกับคำว่า เกลือ ครับ ก็เหมือนเวลาที่เรากินอาหารแล้วอยากให้อาหารอร่อยขึ้นเราก็เหยาะเกลือลงไป แต่ในที่นี้เพื่อที่จะทำให้การถอด password ยากขึ้น)

ซึ่งวิธีการนี้อาจะนำไปใช้ร่วมกับข้อมูลที่ป็นความลับ เพื่อทำการเข้ารหัส เพื่อไม่ให้คนอื่นที่สามารถทะลุทะลวงล้วงเข้ามาในฐานข้อมูลของเรา นั้นอ่านข้อมูลนี้ได้ ถึงได้ข้อมูลมาอาจจะต้องมานั่งเสียเวลาในการแก้ algorithm ตัวนี้มากขึ้น แต่ไม่น่าเชื่อว่าเจ้าแห่ง database อย่าง Oracle นั้น password ที่ถูกทำการ hash ไว้แล้วนั้นจะถูกแก้ออกมาได้อย่างง่ายดายและที่สำคัญไม่ใช่มีแค่ tool เดียวเท่านั้นที่สามารถแก้ได้ซึ่งแสดงออกมาดังตารางด้านล่างดังนี้

จะเห็นได้ว่าโปรแกรมที่ชื่อ worrauthbf version 0.2 นั้นใช้เวลาแค่ 3s วินาทีเท่านั้นเอง แต่ว่าที่เร็วกว่านั้นก็เพราะใช้ CPU ประมวลผลแบบ Dualcore แต่ถ้าเป็นแบบที่ไม่ใช่ CPU ที่เป็นแบบ Dualcore หละก็ orabf 0.7.6 จะเร็วกว่าครับ แต่ว่า password ที่ crack ได้เร็วนั้นขึ้นอยู่กับว่าจำนวน password นั้นยาวหรือสั้นมากแค่ไหนด้วยครับ ซึ่งถ้าใครอยากลองว่าจะ crack ได้จริงหรือเปล่าลองไปโหลดโปรแกรมที่เว็บนี้เลยครับ

ซึ่งเจ้าตัวนั้นไม่ห่วง source code ด้วยครับสามารถนำไปศึกษาได้ด้วยครับแต่เสียอย่างเดียวเป็นภาษา C ไม่ใช่ภาษา Java

เอาหละโม้มามากแล้วลงมือทำกันจริงๆ กันดีกว่าเดี๋ยวจะหาว่าผมมาโม้อีกโดน schema ที่ผมจะเอามาลองนั้นคือ schema HR โดยผมจะเปลี่ยน password เป็น “HRACLE” ตัวใหญ่หมดครับและ database ที่ลองคือ Oracle 11g R1 ครับโดยตอนแรกนั้นให้เราไปติดตั้งโปรแกรม worrauthbf version 0.2 ลงในเครื่องก่อนครับหลังจากนั้นก็ไปทำตามขึ้นตอนนี้ครับ
  • เตรียมข้อมูลเพื่อที่จะทำการถอด password ที่ทำการ hash แล้วให้กลับมาเป็น password ปกติโดยให้เรา login เข้าไปใน database ก่อนโดยเราต้องมีสิทธิ์เป็น admin ด้วยนะครับไม่งั้นดึงข้อมูลไม่ได้ โดยเราต้องใช้ข้อมูลตามรูปแบบดังนี้ครับ

    username:password hash:11g password hash:sid:server:

    username :: นั้นเป็นชื่อของ username ที่เราต้องการจะแก้ hash password ครับ
    password hash :: อันนี้คือ password ที่ถูกทำการ hash แล้วครับ
    11g password hash :: ที่นี้ก็คือ salt ที่ผมบอกนั้นเองครับโดย salt ตัวนี้จะไม่ซ้ำกันตามแต่ที่ระบบจะสร้างขึ้นมา
    sid :: คือชื่อของ Oracle database instance เช่น orcl
    server :: นั้นเป็นชื่อของ server ที่ database นั้นอยู่ครับ
    หมายเหตุ ในแต่ละ version ของ database นั้นอาจจะต้องการ parameter ที่แตกต่างกันเพื่อที่จะใช้เป็นข้อมูลที่จะทำการแก้ hash password ครับโดย salt นี้ Oracle เพิ่งจะมาเริ่มมีใน Oracle database 11g ครับ

    แต่ ว่าโชคดีครับที่เราไม่ต้องไปหา parameter เหล่านี้เองให้เราใช้คำสั่ง sql นี้ครับแต่ต้องอย่าลืมว่าต้อง login เป็น username ที่มีสิทธิ์เป็น admin ครับ ดังนี้

    select u.name||':'||u.password||':'||substr(u.spare4,3,63)||':'||d.name||':'||
    sys_context('USERENV','SERVER_HOST')||':'
    from sys.user$ u, sys.V_$DATABASE d
    where u.type#=1;

    ซึ่งเมื่อ run ใน database ที่ผมทดสอบผลที่ได้คือ

    HR:E313D0596944C41D:6E7036A1EB587DCCE6D84DFF73E6F80207BB8F980B5643D01AE23F747FAC:ORCL:vista:

  • หลังจากนั้นก็จะใช้ command prompt run โปรแกรมเพื่อที่จะทำการ crack hash password ตามขั้นตอนนี้ครับ

      เข้าไปใน home directory ที่เราติดตั้ง worrauthbf version 0.2 ไว้
      run คำสั่ง woraauthbf แล้วตามด้วย parameter ต่างๆ ดังนี้
        -p :: คือชื่อ file ที่เก็บข้อมูลที่เราเตรียมไว้ข้างต้นซึ่งในที่นี้คือ 11gHash.txt
        -t :: ในที่นี้คือเป็นการระบุว่า hash password ที่เราต้องต้องการแก้นั้นเป็น version อะไรในที่นี้ให้ระบุว่า 11g10g
        -c :: เป็นการระบุว่า password ที่เราจะแก้ hash password นั้นมี character ชนิดใดบ้างซึ่งแบ่งได้เป็น สามชนิดคือ
          "alpha" คือ [A-Z]
          "alphanum" คือ [A-Z0-9]
          "all" คือ [A-Z0-9!@#$%^&*()-_+=~`[]{}|\:;"'<>,.?/]

          ซึ่งให้เราเลือกว่าทดลองตามความเหมาะสมครับถ้าสมมติเรารู้ว่า password เรามีแค่ตัวอักษรและตัวอักษรปนกันก็ให้เลือก alphanum ส่วนในที่นี้เรารู้อยู่แล้วว่า password ของเรามีตัวอักษรอย่างเดียวก็เลือก alpha ครับ

        -m :: เป็นการระบุว่า password ของเรานั้นยาวเท่าไรครับ
        -d :: นั้นเป็นการระบุ dictionary file ครับซึ่งจะระบุหรือไม่ระบุก็ได้แต่ถ้าเราระบุมาก็จะทำให้แก้ hash password ได้เร็วขึ้นมากแต่ในที่นี้ผมไม่ใส่นะครับ

        ที่จริง parameter นั้นมากกว่านี้อีกซึ่งถ้าใครสนใจก็ลองข้าไปดูที่เว็บนี้ครับ

        http://soonerorlater.hu/index.khtml?article_id=513

เอาหละที่นี้เราลองมาดูผลลัพธ์กันเลย


หมายเหตุ คือผมลองแก้ hash password หลายตัวพร้อมกันครับผลที่ได้กว่าจะแก้ได้แต่ละตัวช้ามากครับ

สรุป

ถ้าไม่มี dictionary file ช้ามากถึงมากที่สุด

ใช้ กับ password ที่เป็นตัวใหญ่ได้อย่างเดียวนั้นก็คือถ้าเป็น 11g แล้วตั้ง password case sensitive หละก็จะแก้ไม่ได้ดังนั้นถึงจะบอกว่ารองรับ 11g แต่ก็ไม่เต็มร้อยครับต้องรอ version ใหม่ดีกว่า

ความยาวของ password รองรับแค่ 10 ตัวอักษรเท่านั้นถือว่าน้อยมากซึ่งตอนนี้มีตัว crack hash password ของ database Oracle ออก version ใหม่มาคือ checkpwd ซึ่งผมก็ยังไม่ได้ลองครับลองไปเล่นดูตามนี้เลยครับ http://www.red-database-security.com/software/checkpwd200a12.zip

อ้างอิง
http://www.petefinnigan.com/weblog/archives/00001103.htm
http://soonerorlater.hu/index.khtml?article_id=513

ปัญหาของการ Query Data กับ Index data type ที่เป็นรูปแบบวันที่

ในการทดสอบกับ Oracle ทั้งรุ่นเก่าและรุ่นใหม่ 11G การค้นหาข้อมูลกับข้อมูลขนาดใหญ่ในรูปแบบของวันที่ ในบ้างครั้ง การทำ Index นั้นไม่ได้ถูกนำมาใช้งาน

CONNECT BY คำสังขั้นเทพที่ไปเจอมา น่าจะมีประโยชน์กับการทำงานอยู่บ้าง

START WITH and CONNECT BY in Oracle SQL



select ... start with initial-condition connect by nocycle recurse-condition

select ... connect by recurse-condition

select ... start with initial-condition connect by nocycle recurse-condition

select ... connect by recurse-condition
The start with .. connect by clause can be used to select data that has a hierarchical relationship (usually some sort of parent->child (boss->employee or thing->parts).
It is also being used when an sql execution plan is explained.
recurse-condition can make use of the keyword prior:
connect by
prior foo = bar
This construct establishes the recursion. All records that are part of the next lower hierarchical level are found by having bar = foo. foo is a value found in the current hierarchical level.

A simple example

In the following example, the table from which that data is selected consists of just these attributes: parent and child. We make sure (by means of a unique constraint) that the child is uniqe within the table. This is just like in the real life where (as of yet) a child cannot have two different mothers.
The data filled into the table is such that a the sum over the children with the same parent is the value of the parent:
set feedback off

create table test_connect_by (
parent number,
child number,
constraint uq_tcb unique (child)
);
5 = 2+3
insert into test_connect_by values ( 5, 2);
insert into test_connect_by values ( 5, 3);
18 = 11+7
insert into test_connect_by values (18,11);
insert into test_connect_by values (18, 7);
17 = 9+8
insert into test_connect_by values (17, 9);
insert into test_connect_by values (17, 8);
26 = 13+1+12
insert into test_connect_by values (26,13);
insert into test_connect_by values (26, 1);
insert into test_connect_by values (26,12);
15=10+5
insert into test_connect_by values (15,10);
insert into test_connect_by values (15, 5);
38=15+17+6
insert into test_connect_by values (38,15);
insert into test_connect_by values (38,17);
insert into test_connect_by values (38, 6);
38, 26 and 18 have no parents (the parent is null)
insert into test_connect_by values (null, 38);
insert into test_connect_by values (null, 26);
insert into test_connect_by values (null, 18);
Now, let's select the data hierarchically:
select lpad(' ',2*(level-1)) || to_char(child) s
from test_connect_by
start with parent is null
connect by prior child = parent;
This select statement results in:
38
15
10
5
2
3
17
9
8
6
26
13
1
12
18
11
7

Interpreting connect by statements

How must a start with ... connect by select statement be read and interpreted? If Oracle encounters such an SQL statement, it proceeds as described in the following pseude code.
for rec in (select * from some_table) loop
if FULLFILLS_START_WITH_CONDITION(rec) then
RECURSE(rec, rec.child);
end if;
end loop;

procedure RECURSE (rec in MATCHES_SELECT_STMT, parent_id IN field_type) is
begin
APPEND_RESULT_LIST(rec);
for rec_recurse in (select * from some_table) loop
if FULLFILLS_CONNECT_BY_CONDITION(rec_recurse.id, parent_id) then
RECURSE(rec_recurse,rec_recurse.id);
end if;
end loop;
end procedure RECURSE;
Thanks to Frank Trenkamp who spotted an error in the logic in the above pseudo code and corrected it.
Thanks also to Abhishek Ghose who made me think about a better way to describe the logic.

Pruning branches

Sometimes, it might be a requirement to only partially retrieve a hierarchical tree and to prune branches. Here, a tree is filled. Each child is the number of its parent plus a new digit on the right side.
create table prune_test (
parent number,
child number
);

insert into prune_test values (null, 1);
insert into prune_test values (null, 6);
insert into prune_test values (null, 7);

insert into prune_test values ( 1, 12);
insert into prune_test values ( 1, 14);
insert into prune_test values ( 1, 15);

insert into prune_test values ( 6, 61);
insert into prune_test values ( 6, 63);
insert into prune_test values ( 6, 65);
insert into prune_test values ( 6, 69);

insert into prune_test values ( 7, 71);
insert into prune_test values ( 7, 74);

insert into prune_test values ( 12, 120);
insert into prune_test values ( 12, 124);
insert into prune_test values ( 12, 127);

insert into prune_test values ( 65, 653);

insert into prune_test values ( 71, 712);
insert into prune_test values ( 71, 713);
insert into prune_test values ( 71, 715);

insert into prune_test values ( 74, 744);
insert into prune_test values ( 74, 746);
insert into prune_test values ( 74, 748);

insert into prune_test values ( 712,7122);
insert into prune_test values ( 712,7125);
insert into prune_test values ( 712,7127);

insert into prune_test values ( 748,7481);
insert into prune_test values ( 748,7483);
insert into prune_test values ( 748,7487);
Now, we want to retrieve the tree, but prune everything below the branch 1 and 71. It would be false to put these into a where clause of the sql statement, rather, it belongs to the connect by clause:
select
lpad(' ', 2*level) || child
from
prune_test
start with
parent is null
connect by
prior child=parent
and parent not in (1, 71);

This returns:
  1
6
61
63
65
653
69
7
71
74
744
746
748
7481
7483
7487
See also another example for pruning.

Do two items stand in a ancestor descendant relationship

Sometimes, one want's to know if two items are in an ancestor descendant relationship, that is if XYZ as grandfather, or grand-grandfather, or ... of ABC. The following template of a query can be used to determine that.
set feedback off

drop table parent_child;

create table parent_child(parent_ varchar2(20), child_ varchar2(20));

insert into parent_child values (null, 'a')

insert into parent_child values ( 'a', 'af');
insert into parent_child values ( 'a', 'ab');
insert into parent_child values ( 'a', 'ax');

insert into parent_child values ( 'ab', 'abc');
insert into parent_child values ( 'ab', 'abd');
insert into parent_child values ( 'ab', 'abe');

insert into parent_child values ('abe','abes');
insert into parent_child values ('abe','abet');

insert into parent_child values ( null, 'b');

insert into parent_child values ( 'b', 'bg');
insert into parent_child values ( 'b', 'bh');
insert into parent_child values ( 'b', 'bi');

insert into parent_child values ( 'bi', 'biq');
insert into parent_child values ( 'bi', 'biv');
insert into parent_child values ( 'bi', 'biw');
The following query 'asks' for a parent and a supposed child (grand child, grand grand child) and answers the question if the are indeed in an ancester successor relationship.
set verify off

select
case when count(*) > 0 then
'&&parent is an ancestor of &&child' else
'&&parent is no ancestor of &&child' end
"And here's the answer"
from
parent_child
where
child_ = '&&child'
start with
parent_ = '&&parent'
connect by
prior child_ = parent_;

undefine child
undefine parent

Features of 9i

sys_connect_by_path

With sys_connect_by_path it is possible to show the entire path from the top level down to the 'actual' child:

Using hierarchical result sets

With this technique, it is possible to show all kind of hierarchical data relations. Here is an example that lists privileges, roles and users in their hierarchical relation.
See also flat hiearchy.

connect_by_root

connect_by_root is a new operator that comes with Oracle 10g and enhances the ability to perform hierarchical queries.
I have yet to dig into this subject and will write about it when things become clearer.

connect_by_is_leaf

connect_by_isleaf is a new operator that comes with Oracle 10g and enhances the ability to perform hierarchical queries.
I have yet to dig into this subject and will write about it when things become clearer.

connect_by_iscycle

connect_by_is_cycle is a new operator that comes with Oracle 10g and enhances the ability to perform hierarchical queries.
I have yet to dig into this subject and will write about it when things become clearer.

Thanks

Thanks to Peter Bruhn, Jonathan Schmalze, Jeff Jones, Keith Britch and Fabian Iturralde who each pointed out an error, misstake or typo on this page.

Further links

บันทึกประสพการณ์ที่เกี่ยวกับ Oracle ในการทำงานจริง

ตอนแรกก็คิดว่าจะเก็บไว้ใช้ในการทบทวนความจำ เวลานำกลับมาใช้ใหม่แต่คิดอีกที ให้คนอื่นได้ศึกษาด้วยจะดีกว่า