Skip to content

Commit

Permalink
Land #731, Fix Java Meterpreter Symlink Handling on Windows
Browse files Browse the repository at this point in the history
Land #731, Fix Java Meterpreter Symlink Handling on Windows
  • Loading branch information
dledda-r7 authored Dec 6, 2024
2 parents 1e4276b + af89b27 commit 7c3a26f
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ protected CommandManager() throws Exception {
Class.forName("java.util.ServiceLoader");
apiVersion = ExtensionLoader.V1_6;

Class.forName("java.util.Objects");
apiVersion = ExtensionLoader.V1_7;

Class.forName("java.util.Optional");
apiVersion = ExtensionLoader.V1_8;

Class.forName("java.util.Optional").getMethod("stream");
apiVersion = ExtensionLoader.V1_9;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public interface ExtensionLoader {
public static final int V1_4 = 14;
public static final int V1_5 = 15;
public static final int V1_6 = 16;
public static final int V1_7 = 17;
public static final int V1_8 = 18;
public static final int V1_9 = 19;
public static final int V1_15 = 25;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.metasploit.meterpreter.stdapi;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;

public class FsUtils {
public static boolean isSymlink(File file) throws IOException {
String osName = System.getProperty("os.name");
if (osName != null && osName.toLowerCase().contains("windows") && isWindowsSymlink(file)) {
return true;
}

File canon;
if (file.getParent() == null) {
canon = file;
} else {
File canonDir = file.getParentFile().getCanonicalFile();
canon = new File(canonDir, file.getName());
}

return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
}

private static boolean isWindowsSymlink(File file) {
// this uses reflection to access the java.nio.file classes necessary that are available on Java 7+
try {
// first check using isSymbolicLink
Class<?> filesClass = Class.forName("java.nio.file.Files");
Class<?> pathClass = Class.forName("java.nio.file.Path");

Method isSymbolicLinkMethod = filesClass.getMethod("isSymbolicLink", pathClass);
Method toPathMethod = File.class.getMethod("toPath");

Object path = toPathMethod.invoke(file);
if ((Boolean)isSymbolicLinkMethod.invoke(null, path)) {
return true;
}

// next check if the target is a junction because isSymbolicLink doesn't handle that
Class<?> linkOptionClass = Class.forName("java.nio.file.LinkOption");
Object linkOptionArray = java.lang.reflect.Array.newInstance(linkOptionClass, 0);
Method toRealPath = pathClass.getMethod("toRealPath", linkOptionArray.getClass());
Object realPath = toRealPath.invoke(path, linkOptionArray);

// toRealPath resolves junctions so the result will be different
Method equalsMethod = pathClass.getMethod("equals", Object.class);
if (!(Boolean)equalsMethod.invoke(path, realPath)) {
return true;
}
} catch (ReflectiveOperationException e) {
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static void setCWD(File newCWD) {
public void load(CommandManager mgr) throws Exception {
mgr.registerCommand(CommandId.CORE_CHANNEL_OPEN, stdapi_channel_open.class, V1_2, V1_15);
mgr.registerCommand(CommandId.STDAPI_FS_CHDIR, stdapi_fs_chdir.class);
mgr.registerCommand(CommandId.STDAPI_FS_DELETE_DIR, stdapi_fs_delete_dir.class);
mgr.registerCommand(CommandId.STDAPI_FS_DELETE_DIR, stdapi_fs_delete_dir.class, V1_2, V1_7);
mgr.registerCommand(CommandId.STDAPI_FS_DELETE_FILE, stdapi_fs_delete_file.class);
mgr.registerCommand(CommandId.STDAPI_FS_FILE_EXPAND_PATH, stdapi_fs_file_expand_path.class, V1_2, V1_5); // %COMSPEC% only
mgr.registerCommand(CommandId.STDAPI_FS_FILE_MOVE, stdapi_fs_file_move.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ public class stdapi_fs_delete_dir implements Command {
public int execute(Meterpreter meterpreter, TLVPacket request, TLVPacket response) throws Exception {
String path = request.getStringValue(TLVType.TLV_TYPE_DIRECTORY_PATH);
File file = Loader.expand(path);
if (isSymlink(file)) {
if (!file.delete()) {
if (FsUtils.isSymlink(file)) {
if (!deleteSymlink(file)) {
throw new IOException("Cannot delete symbolic link " + file.getCanonicalPath());
}
} else if (file.isDirectory()) {
Expand All @@ -26,22 +26,15 @@ public int execute(Meterpreter meterpreter, TLVPacket request, TLVPacket respons
return ERROR_SUCCESS;
}

private static boolean isSymlink(File file) throws IOException {
File canon;
if (file.getParent() == null) {
canon = file;
} else {
File canonDir = file.getParentFile().getCanonicalFile();
canon = new File(canonDir, file.getName());
}
return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
protected boolean deleteSymlink(File file) throws IOException {
return file.delete();
}

private boolean rmtree(File file) throws IOException {
boolean ret = true;
for (File subFile : file.listFiles()) {
if (isSymlink(subFile)) {
ret = ret && subFile.delete();
if (FsUtils.isSymlink(subFile)) {
ret = ret && deleteSymlink(subFile);
} else if (subFile.isDirectory()) {
ret = ret && rmtree(subFile);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.metasploit.meterpreter.stdapi;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

public class stdapi_fs_delete_dir_V1_7 extends stdapi_fs_delete_dir {
@Override
protected boolean deleteSymlink(File file) throws IOException {
String osName = System.getProperty("os.name");
if (osName != null && osName.toLowerCase().contains("windows")) {
Files.delete(file.toPath());
return true;
}
return file.delete();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ public int execute(Meterpreter meterpreter, TLVPacket request, TLVPacket respons
}
}
File file = new File(path);
if (!file.exists()) {
if (!exists(file)) {
file = Loader.expand(path);
}
if (!file.exists()) {
if (!exists(file)) {
throw new IOException("File/directory does not exist: " + path);
}
response.add(TLVType.TLV_TYPE_STAT_BUF, stat(file));
Expand Down Expand Up @@ -76,6 +76,10 @@ protected boolean canExecute(File file) {
return false;
}

private boolean exists(File file) throws IOException {
return file.exists() || FsUtils.isSymlink(file);
}

/**
* Convert an integer to little endian.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ protected Process execute(String cmd, ArrayList<String> args) throws IOException
}

protected Process execute(String cmdstr) throws IOException {
Process proc = Runtime.getRuntime().exec(cmdstr);
Process proc = Runtime.getRuntime().exec(cmdstr, null, Loader.getCWD());
return proc;
}
}
2 changes: 1 addition & 1 deletion java/version-compatibility-check/java14/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<target>
<mkdir dir="${project.basedir}/target/generated-sources/copy/" />
<copy todir="${project.basedir}/target/generated-sources/copy">
<fileset dir="${project.basedir}/../java15/target/generated-sources/copy" includes="**/*.java" excludes="**/*_V1_5.java"/>
<fileset dir="${project.basedir}/../java15/target/generated-sources/copy" includes="**/*.java" excludes="**/*_V1_5.java" />
</copy>
</target>
</configuration>
Expand Down
2 changes: 1 addition & 1 deletion java/version-compatibility-check/java15/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<target>
<mkdir dir="${project.basedir}/target/generated-sources/copy/" />
<copy todir="${project.basedir}/target/generated-sources/copy">
<fileset dir="${project.basedir}/../java16/target/generated-sources/copy" includes="**/*.java" excludes="**/*_V1_6.java"/>
<fileset dir="${project.basedir}/../java16/target/generated-sources/copy" includes="**/*.java" excludes="**/*_V1_6.java" />
</copy>
</target>
</configuration>
Expand Down
19 changes: 15 additions & 4 deletions java/version-compatibility-check/java16/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,21 @@
<mkdir dir="${project.basedir}/target/generated-sources/copy/" />
<copy todir="${project.basedir}/target/generated-sources/copy">
<fileset dir="${project.basedir}/../../javapayload/src/main/java" includes="**/*.java" excludes="rmi/**" />
<fileset dir="${project.basedir}/../../meterpreter/meterpreter/src/main/java" includes="**/*.java"/>
<fileset dir="${project.basedir}/../../meterpreter/shared/src/main/java" includes="**/*.java"/>
<!-- Webcam_audio_record_V1_4 depends on Sun proprietary API -->
<fileset dir="${project.basedir}/../../meterpreter/meterpreter/target/extension-src" includes="**/*.java" excludes="**/stdapi_webcam_audio_record_V1_4.java" />
<fileset dir="${project.basedir}/../../meterpreter/meterpreter/src/main/java" includes="**/*.java" />
<fileset dir="${project.basedir}/../../meterpreter/shared/src/main/java" includes="**/*.java" />
<fileset dir="${project.basedir}/../../meterpreter/meterpreter/target/extension-src" includes="**/*.java">
<!-- stdapi_webcam_audio_record_V1_4 depends on Sun proprietary API -->
<exclude name="**/stdapi_webcam_audio_record_V1_4.java" />
<scriptselector language="javascript">
if (!(new RegExp('_V[0-9]_[0-9]+\.java')).test(filename)) {
self.setSelected(true);
} else if ((new RegExp('_V1_[0-6]\.java')).test(filename)) {
self.setSelected(true);
} else {
self.setSelected(false);
}
</scriptselector>
</fileset>
</copy>
</target>
</configuration>
Expand Down

0 comments on commit 7c3a26f

Please sign in to comment.