From 6e83295658e47920e1e003dc8ed966f4aa7a7654 Mon Sep 17 00:00:00 2001 From: tux_mind Date: Fri, 2 Oct 2015 16:41:36 +0200 Subject: [PATCH 1/2] first msf library draft --- msf/.gitignore | 1 + msf/build.gradle | 8 + msf/src/main/java/org/csploit/msf/Author.java | 37 ++++ .../main/java/org/csploit/msf/DataHolder.java | 8 + .../main/java/org/csploit/msf/DataStore.java | 113 ++++++++++ .../main/java/org/csploit/msf/Exploit.java | 67 ++++++ .../main/java/org/csploit/msf/Framework.java | 24 +++ .../main/java/org/csploit/msf/License.java | 32 +++ msf/src/main/java/org/csploit/msf/Module.java | 98 +++++++++ .../java/org/csploit/msf/ModuleManager.java | 101 +++++++++ .../main/java/org/csploit/msf/ModuleSet.java | 68 ++++++ .../main/java/org/csploit/msf/Offspring.java | 10 + msf/src/main/java/org/csploit/msf/Option.java | 88 ++++++++ .../java/org/csploit/msf/OptionContainer.java | 50 +++++ .../main/java/org/csploit/msf/arch/Arch.java | 32 +++ .../java/org/csploit/msf/module/ArchSet.java | 57 +++++ .../csploit/msf/module/ModuleDataStore.java | 37 ++++ .../java/org/csploit/msf/module/Platform.java | 118 +++++++++++ .../org/csploit/msf/module/PlatformList.java | 43 ++++ .../java/org/csploit/msf/module/Rank.java | 43 ++++ .../org/csploit/msf/module/Reference.java | 37 ++++ .../org/csploit/msf/module/SiteReference.java | 51 +++++ .../java/org/csploit/msf/module/Target.java | 32 +++ .../csploit/msf/options/AddressOption.java | 37 ++++ .../msf/options/AddressRangeOption.java | 81 ++++++++ .../csploit/msf/options/BooleanOption.java | 26 +++ .../org/csploit/msf/options/EnumOption.java | 36 ++++ .../csploit/msf/options/IntegerOption.java | 29 +++ .../org/csploit/msf/options/PathOption.java | 35 ++++ .../org/csploit/msf/options/PortOption.java | 33 +++ .../org/csploit/msf/options/RawOption.java | 42 ++++ .../org/csploit/msf/options/RegexpOption.java | 29 +++ .../org/csploit/msf/options/StringOption.java | 38 ++++ .../java/org/csploit/msf/DataStoreTest.java | 59 ++++++ .../java/org/csploit/msf/RpcSampleTest.java | 196 ++++++++++++++++++ .../csploit/msf/module/PlatformListTest.java | 24 +++ .../org/csploit/msf/module/PlatformTest.java | 33 +++ settings.gradle | 2 +- 38 files changed, 1854 insertions(+), 1 deletion(-) create mode 100644 msf/.gitignore create mode 100644 msf/build.gradle create mode 100644 msf/src/main/java/org/csploit/msf/Author.java create mode 100644 msf/src/main/java/org/csploit/msf/DataHolder.java create mode 100644 msf/src/main/java/org/csploit/msf/DataStore.java create mode 100644 msf/src/main/java/org/csploit/msf/Exploit.java create mode 100644 msf/src/main/java/org/csploit/msf/Framework.java create mode 100644 msf/src/main/java/org/csploit/msf/License.java create mode 100644 msf/src/main/java/org/csploit/msf/Module.java create mode 100644 msf/src/main/java/org/csploit/msf/ModuleManager.java create mode 100644 msf/src/main/java/org/csploit/msf/ModuleSet.java create mode 100644 msf/src/main/java/org/csploit/msf/Offspring.java create mode 100644 msf/src/main/java/org/csploit/msf/Option.java create mode 100644 msf/src/main/java/org/csploit/msf/OptionContainer.java create mode 100644 msf/src/main/java/org/csploit/msf/arch/Arch.java create mode 100644 msf/src/main/java/org/csploit/msf/module/ArchSet.java create mode 100644 msf/src/main/java/org/csploit/msf/module/ModuleDataStore.java create mode 100644 msf/src/main/java/org/csploit/msf/module/Platform.java create mode 100644 msf/src/main/java/org/csploit/msf/module/PlatformList.java create mode 100644 msf/src/main/java/org/csploit/msf/module/Rank.java create mode 100644 msf/src/main/java/org/csploit/msf/module/Reference.java create mode 100644 msf/src/main/java/org/csploit/msf/module/SiteReference.java create mode 100644 msf/src/main/java/org/csploit/msf/module/Target.java create mode 100644 msf/src/main/java/org/csploit/msf/options/AddressOption.java create mode 100644 msf/src/main/java/org/csploit/msf/options/AddressRangeOption.java create mode 100644 msf/src/main/java/org/csploit/msf/options/BooleanOption.java create mode 100644 msf/src/main/java/org/csploit/msf/options/EnumOption.java create mode 100644 msf/src/main/java/org/csploit/msf/options/IntegerOption.java create mode 100644 msf/src/main/java/org/csploit/msf/options/PathOption.java create mode 100644 msf/src/main/java/org/csploit/msf/options/PortOption.java create mode 100644 msf/src/main/java/org/csploit/msf/options/RawOption.java create mode 100644 msf/src/main/java/org/csploit/msf/options/RegexpOption.java create mode 100644 msf/src/main/java/org/csploit/msf/options/StringOption.java create mode 100644 msf/src/test/java/org/csploit/msf/DataStoreTest.java create mode 100644 msf/src/test/java/org/csploit/msf/RpcSampleTest.java create mode 100644 msf/src/test/java/org/csploit/msf/module/PlatformListTest.java create mode 100644 msf/src/test/java/org/csploit/msf/module/PlatformTest.java diff --git a/msf/.gitignore b/msf/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/msf/.gitignore @@ -0,0 +1 @@ +/build diff --git a/msf/build.gradle b/msf/build.gradle new file mode 100644 index 0000000000..5b04692e32 --- /dev/null +++ b/msf/build.gradle @@ -0,0 +1,8 @@ +apply plugin: 'java' + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'junit:junit:4.12' + compile 'commons-io:commons-io:2.4' + compile 'org.apache.commons:commons-lang3:3.4' +} \ No newline at end of file diff --git a/msf/src/main/java/org/csploit/msf/Author.java b/msf/src/main/java/org/csploit/msf/Author.java new file mode 100644 index 0000000000..7e3b3bd062 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/Author.java @@ -0,0 +1,37 @@ +package org.csploit.msf; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Represent an author + */ +public class Author { + private static Pattern MAIL = Pattern.compile("<(.+)>"); + + private String name; + private String email; + + public Author(String name, String email) { + this.name = name; + this.email = email; + } + + @Override + public String toString() { + if(email != null) { + return name + " <" + email + ">"; + } else { + return name; + } + } + + public static Author fromString(String s) { + Matcher matcher = MAIL.matcher(s); + if(matcher.matches()) { + return new Author(s.substring(0, matcher.start()), matcher.group(1)); + } else { + return new Author(s, null); + } + } +} diff --git a/msf/src/main/java/org/csploit/msf/DataHolder.java b/msf/src/main/java/org/csploit/msf/DataHolder.java new file mode 100644 index 0000000000..e6acb5b292 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/DataHolder.java @@ -0,0 +1,8 @@ +package org.csploit.msf; + +/** + * A class that hold a DataStore instance + */ +public interface DataHolder { + DataStore getDataStore(); +} diff --git a/msf/src/main/java/org/csploit/msf/DataStore.java b/msf/src/main/java/org/csploit/msf/DataStore.java new file mode 100644 index 0000000000..7709c71692 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/DataStore.java @@ -0,0 +1,113 @@ +package org.csploit.msf; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * The data store is just a bitbucket that holds keyed values. It is used + * by various classes to hold option values and other state information. + */ +public class DataStore extends HashMap { + protected Set imported = new HashSet<>(); + protected Set defaults = new HashSet<>(); + + @Override + public String put(String key, String value) { + key = findKeyCase(key); + + defaults.remove(key); + imported.remove(key); + + return super.put(key, value); + } + + @Override + public String get(Object key) { + String k = (key instanceof String) ? (String) key : key.toString(); + return super.get(findKeyCase(k)); + } + + public void updateValue(String key, String value) { + super.put(key, value); + } + + /** + * This method is a helper method that imports the default value for + * all of the supplied options + */ + public void importOptions(OptionContainer options, boolean isDefault, boolean overwrite) { + for(Option opt : options.values()) { + String name = opt.getName(); + if(containsKey(name) && !overwrite) { + continue; + } + if(opt.haveDefaultValue() && (overwrite || get(name) == null)) { + String val = opt.display(opt.defaultValue); + importOption(name, val, true, isDefault); + } + } + } + + public void importOptions(OptionContainer options, boolean isDefault) { + importOptions(options, isDefault, false); + } + + public void importOptions(OptionContainer options) { + importOptions(options, false, false); + } + + public void importOption(String name, String val, boolean imported, boolean isDefault) { + if(imported) { + this.imported.add(name); + } + + if(isDefault) { + defaults.add(name); + } + + super.put(name, val); + } + + public void importOption(String name, String val, boolean imported) { + importOption(name, val, imported, false); + } + + public void importOption(String name, String val) { + importOption(name, val, true, false); + } + + public synchronized Map userDefined() { + Map res = new HashMap<>(); + + for (String key : keySet()) { + if (!imported.contains(key)) { + res.put(key, super.get(key)); + } + } + + return res; + } + + public void clearNotUserDefined() { + for(String key : imported) { + if(defaults.remove(key)) { + remove(key); + } + } + } + + protected String findKeyCase(String k) { + + if(k == null) + return null; + + for(String key : keySet()) { + if(key.toLowerCase().equals(k.toLowerCase())) + return key; + } + + return k; + } +} diff --git a/msf/src/main/java/org/csploit/msf/Exploit.java b/msf/src/main/java/org/csploit/msf/Exploit.java new file mode 100644 index 0000000000..151793a01f --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/Exploit.java @@ -0,0 +1,67 @@ +package org.csploit.msf; + +import org.csploit.msf.module.ArchSet; +import org.csploit.msf.module.PlatformList; +import org.csploit.msf.module.Reference; +import org.csploit.msf.module.Target; + +/** + * Represent an exploit module + */ +public class Exploit extends Module { + private Target[] targets; + private int defaultTarget = -1; + + public Exploit() { + super(); + } + + // we may should use a Builder + public Exploit(String name, + String description, + String version, + Author[] authors, + ArchSet arch, + PlatformList platform, + Reference[] references, + boolean privileged, + License license, + Target[] targets, + int defaultTarget + ) { + super(name, description, version, authors, arch, platform, references, privileged, license); + this.targets = targets; + this.defaultTarget = defaultTarget; + } + + @Override + public String getType() { + return "exploit"; + } + + protected void setTargets(Target[] targets) { + this.targets = targets; + } + + public Target[] getTargets() { + return targets; + } + + public Target getTarget() { + String val = datastore.get("TARGET"); + + if(val != null) { + try { + return targets[Integer.parseInt(val)]; + } catch (NumberFormatException | IndexOutOfBoundsException e) { + // ignore + } + } + + if(defaultTarget >= 0 && defaultTarget < targets.length) { + return targets[defaultTarget]; + } + + return null; + } +} diff --git a/msf/src/main/java/org/csploit/msf/Framework.java b/msf/src/main/java/org/csploit/msf/Framework.java new file mode 100644 index 0000000000..10ed7e57b1 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/Framework.java @@ -0,0 +1,24 @@ +package org.csploit.msf; + +/** + * Represent a MetaSploit Framework instance + */ +public class Framework implements DataHolder { + + private DataStore dataStore; + private ModuleManager moduleManager; + + public Framework() { + dataStore = new DataStore(); + moduleManager = new ModuleManager(this, "all"); + } + + @Override + public DataStore getDataStore() { + return dataStore; + } + + public ModuleManager getModuleManager() { + return moduleManager; + } +} diff --git a/msf/src/main/java/org/csploit/msf/License.java b/msf/src/main/java/org/csploit/msf/License.java new file mode 100644 index 0000000000..4672e34dd3 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/License.java @@ -0,0 +1,32 @@ +package org.csploit.msf; + +/** + * A license + */ +public enum License { + MSF("Metasploit Framework License (BSD)"), + GPL("GNU Public License v2.0"), + BSD("BSD License"), + ARTISTIC("Perl Artistic License"), + UNKNOWN("Unknown License"); + + String name; + + License(String name) { + this.name = name; + } + + public String toString() { + return name; + } + + public static License fromString(String value) { + if(value != null) { + for (License l : values()) { + if (l.name.toLowerCase().startsWith(value.toLowerCase())) + return l; + } + } + return UNKNOWN; + } +} diff --git a/msf/src/main/java/org/csploit/msf/Module.java b/msf/src/main/java/org/csploit/msf/Module.java new file mode 100644 index 0000000000..5b41db0404 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/Module.java @@ -0,0 +1,98 @@ +package org.csploit.msf; + +import org.csploit.msf.module.ArchSet; +import org.csploit.msf.module.ModuleDataStore; +import org.csploit.msf.module.PlatformList; +import org.csploit.msf.module.Reference; + +import java.util.Collection; + +/** + * Represent a module of the MSF + */ +public abstract class Module implements Offspring, DataHolder { + protected Author[] authors; + protected ArchSet arch; + protected PlatformList platform; + protected Reference[] references; + protected OptionContainer options; + protected Framework framework; + protected ModuleDataStore datastore; + protected boolean privileged; + protected License license; + + protected String name; + protected String refname; + protected String description; + protected String version; + + public Module(String name, String description, String version, Author[] authors, ArchSet arch, PlatformList platform, Reference[] references, boolean privileged, License license ) { + this.name = name; + this.description = description; + this.version = version; + this.authors = authors; + this.arch = arch; + this.platform = platform; + this.references = references; + this.privileged = privileged; + this.license = license; + this.options = new OptionContainer(); + this.datastore = new ModuleDataStore(this); + } + + public Module() { + this("No module name", "No module description", "0", null, new ArchSet(), new PlatformList(), null, false, License.MSF); + } + + @Override + public boolean haveFramework() { + return framework != null; + } + + @Override + public Framework getFramework() { + return framework; + } + + @Override + public void setFramework(Framework framework) { + this.framework = framework; + } + + @Override + public DataStore getDataStore() { + return datastore; + } + + public String getRefname() { + return refname; + } + + public void setRefname(String refname) { + this.refname = refname; + } + + public abstract String getType(); + + public String getFullName() { + return getType() + "/" + getRefname(); + } + + public String getShortName() { + String[] parts = getRefname().split("/"); + return parts[parts.length - 1]; + } + + public void registerOption(String name, Option option, boolean advanced, boolean evasion) { + options.addOption(name, option, advanced, evasion); + } + + public Collection getinvalidOptions() { + return options.getInvalidOptions(getDataStore()); + } + + @Override + public int hashCode() { + return refname != null ? refname.hashCode() : 0; + } +} diff --git a/msf/src/main/java/org/csploit/msf/ModuleManager.java b/msf/src/main/java/org/csploit/msf/ModuleManager.java new file mode 100644 index 0000000000..a87a532e93 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/ModuleManager.java @@ -0,0 +1,101 @@ +package org.csploit.msf; + +import java.util.HashMap; +import java.util.Map; + +/** + * Manage all framework modules + */ +public class ModuleManager implements Offspring { + + private final static String[] validModuleTypes = { + "encoder", + "exploit", + "nop", + "auxiliary", + "payload", + "post" + }; + + private Map sets = new HashMap<>(); + private Framework framework; + + public ModuleManager(Framework framework, String types) { + this(framework, types.equals("all") ? validModuleTypes : types.split(",")); + } + + public ModuleManager(Framework framework, String[] types) { + setFramework(framework); + for(String type : types) { + type = type.trim(); + if(isValidType(type)) + initSet(type); + } + } + + private void initSet(String type) { + if(sets.containsKey(type)) + return; + + ModuleSet set = ModuleSet.forType(type); + set.setFramework(getFramework()); + sets.put(type, set); + } + + private static boolean isValidType(String type) { + for(String t : validModuleTypes) { + if(t.equals(type)) + return true; + } + return false; + } + + public Module get(String name) { + String[] parts = name.split("/"); + String refname; + + if(!sets.containsKey(parts[0])) { + if(!isValidType(parts[0])) { + return worldSearch(name); + } else { + return null; + } + } + + refname = name.substring(parts[0].length() + 1); + + return sets.get(parts[0]).get(refname); + } + + private Module worldSearch(String name) { + for(ModuleSet set : sets.values()) { + Module m = set.get(name); + if(m != null) + return m; + } + return null; + } + + public void put(Module module) { + String type = module.getType(); + if(!sets.containsKey(type)) { + return; + } + sets.get(type).add(module); + } + + @Override + public boolean haveFramework() { + return framework != null; + } + + @Override + public Framework getFramework() { + return framework; + } + + @Override + public void setFramework(Framework framework) { + this.framework = framework; + } +} diff --git a/msf/src/main/java/org/csploit/msf/ModuleSet.java b/msf/src/main/java/org/csploit/msf/ModuleSet.java new file mode 100644 index 0000000000..d4d1a5f49c --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/ModuleSet.java @@ -0,0 +1,68 @@ +package org.csploit.msf; + +import org.csploit.msf.module.ArchSet; +import org.csploit.msf.module.PlatformList; + +import java.util.HashMap; + +/** + * A module set contains zero or more named module classes of an arbitrary + * type. + */ +public class ModuleSet extends HashMap implements Offspring { + protected String type; + private Framework framework; + + public ModuleSet(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + @Override + public boolean haveFramework() { + return framework != null; + } + + @Override + public Framework getFramework() { + return framework; + } + + @Override + public void setFramework(Framework framework) { + this.framework = framework; + } + + public void add(Module module) { + module.setFramework(framework); + put(module.getRefname(), module); + } + + public ModuleSet filter(ArchSet architectures, PlatformList platforms) { + ModuleSet res = new ModuleSet(getType()); + res.setFramework(framework); + for(Module m : values()) { + + if(architectures != null && m.arch.intersect(architectures).isEmpty()) { + continue; + } + + if(platforms != null && m.platform.intersect(platforms).isEmpty()) { + continue; + } + + //TODO: custom filter + + res.add(m); + } + return res; + } + + public static ModuleSet forType(String type) { + //TODO: PayloadSet + return new ModuleSet(type); + } +} diff --git a/msf/src/main/java/org/csploit/msf/Offspring.java b/msf/src/main/java/org/csploit/msf/Offspring.java new file mode 100644 index 0000000000..fb2015cf85 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/Offspring.java @@ -0,0 +1,10 @@ +package org.csploit.msf; + +/** + * CLasses that ave a reference to the framework + */ +public interface Offspring { + boolean haveFramework(); + Framework getFramework(); + void setFramework(Framework framework); +} diff --git a/msf/src/main/java/org/csploit/msf/Option.java b/msf/src/main/java/org/csploit/msf/Option.java new file mode 100644 index 0000000000..c246ce3159 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/Option.java @@ -0,0 +1,88 @@ +package org.csploit.msf; + +import org.csploit.msf.options.StringOption; + +/** + * Represent an option + */ +public abstract class Option { + protected boolean advanced; + protected T defaultValue; + protected String description; + protected boolean evasion; + protected String name; + protected Module owner; + protected boolean required; + + public Option(String name, boolean required, String description, T defaultValue) { + this.name = name; + this.required = required; + this.description = description; + this.defaultValue = defaultValue; + } + + public Option(String name, String description, T defaultValue) { + this(name, false, description, defaultValue); + } + + public boolean isAdvanced() { + return advanced; + } + + public void setAdvanced(boolean advanced) { + this.advanced = advanced; + } + + public T getDefaultValue() { + return defaultValue; + } + + public boolean haveDefaultValue() { + return defaultValue != null; + } + + public String getDescription() { + return description; + } + + public boolean isEvasion() { + return evasion; + } + + public void setEvasion(boolean evasion) { + this.evasion = evasion; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Module getOwner() { + return owner; + } + + public void setOwner(Module owner) { + this.owner = owner; + } + + public boolean isRequired() { + return required; + } + + protected abstract T normalize(String input); + + protected String display(T value) { + return value.toString(); + } + + protected boolean isValid(T input) { + if(isRequired() && (input == null || input.toString().isEmpty())) { + return false; + } + return true; + } +} diff --git a/msf/src/main/java/org/csploit/msf/OptionContainer.java b/msf/src/main/java/org/csploit/msf/OptionContainer.java new file mode 100644 index 0000000000..68de7115fa --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/OptionContainer.java @@ -0,0 +1,50 @@ +package org.csploit.msf; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * A container of options + */ +class OptionContainer extends HashMap { + + public void addAdvancedOption(String name, Option option) { + addOption(name, option, true, false); + } + + public void addEvasionOption(String name, Option option) { + addOption(name, option, false, true); + } + + public void addOption(String name, Option option, boolean advanced, boolean evasion) { + option.setEvasion(evasion); + option.setAdvanced(advanced); + put(name, option); + } + + /** + * this should be the validate method in the MSF. + * I decided to change it because updating dataStore + * within modules will write global values into it. + */ + @SuppressWarnings("unchecked") + public Collection getInvalidOptions(DataStore dataStore) { + ArrayList errors = new ArrayList<>(); + + for(Map.Entry entry : entrySet()) { + String name = entry.getKey(); + Option option = entry.getValue(); + + String val = dataStore.get(name); + Object normalized = option.normalize(val); + + if(!option.isValid(normalized)) { + errors.add(name); + } + } + + return errors; + } +} diff --git a/msf/src/main/java/org/csploit/msf/arch/Arch.java b/msf/src/main/java/org/csploit/msf/arch/Arch.java new file mode 100644 index 0000000000..54e5137ad8 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/arch/Arch.java @@ -0,0 +1,32 @@ +package org.csploit.msf.arch; + +/** + * Machines architectures + * + * source: lib/rex/constants.rb + */ +public enum Arch { + ANY, + X86, + X86_64, + X64, + MIPS, + MIPSLE, + MIPSBE, + PPC, + PPC64, + CBEA, + CBEA64, + SPARC, + CMD, + PHP, + TTY, + ARMLE, + ARMBE, + JAVA, + RUBY, + DALVIK, + PYTHON, + NODEJS, + FIREFOX; +} \ No newline at end of file diff --git a/msf/src/main/java/org/csploit/msf/module/ArchSet.java b/msf/src/main/java/org/csploit/msf/module/ArchSet.java new file mode 100644 index 0000000000..36c407f490 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/module/ArchSet.java @@ -0,0 +1,57 @@ +package org.csploit.msf.module; + +import java.util.Collection; +import java.util.HashSet; +import org.csploit.msf.arch.Arch; + +/** + * A container for architectures supported by a module + */ +public class ArchSet extends HashSet { + + public ArchSet() { + super(); + } + + public ArchSet(String s) { + this(s.split(",")); + } + + public ArchSet(String[] a) { + super(a.length); + + for(String arch : a) { + add(Arch.valueOf(arch.trim().toUpperCase())); + } + } + + private ArchSet(Collection other) { + super(other); + } + + public boolean isSupported(Arch arch) { + return arch == Arch.ANY || contains(arch); + } + + public ArchSet intersect(ArchSet other) { + ArchSet res = new ArchSet(this); + res.retainAll(other); + return res; + } + + @Override + public String toString() { + if(isEmpty()) + return ""; + StringBuilder sb = new StringBuilder(); + + for (Arch a : this) { + if(sb.length() > 0) { + sb.append(", "); + } + sb.append(a.toString()); + } + + return sb.toString(); + } +} diff --git a/msf/src/main/java/org/csploit/msf/module/ModuleDataStore.java b/msf/src/main/java/org/csploit/msf/module/ModuleDataStore.java new file mode 100644 index 0000000000..931a4750de --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/module/ModuleDataStore.java @@ -0,0 +1,37 @@ +package org.csploit.msf.module; + +import org.csploit.msf.Module; + +/** + * DataStore wrapper for modules that will attempt to back values against the + * framework's datastore if they aren't found in the module's datastore. This + * is done to simulate global data store values. + */ +public class ModuleDataStore extends org.csploit.msf.DataStore { + private Module module; + + public ModuleDataStore(Module module) { + this.module = module; + } + + public boolean isDefault(String key) { + return defaults.contains(key); + } + + @Override + public String get(Object key) { + String k, res; + + k = key != null ? key.toString() : null; + k = findKeyCase(k); + res = null; + if(!isDefault(k)) + res = super.get(key); + + if(res == null && module != null && module.haveFramework()) { + res = module.getFramework().getDataStore().get(k); + } + + return res != null ? res : super.get(k); + } +} diff --git a/msf/src/main/java/org/csploit/msf/module/Platform.java b/msf/src/main/java/org/csploit/msf/module/Platform.java new file mode 100644 index 0000000000..083a0893cf --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/module/Platform.java @@ -0,0 +1,118 @@ +package org.csploit.msf.module; + +/** + * A platform + */ +public enum Platform { + WINDOWS("Windows", 1, 0, 0), + WINDOWS_95("Windows 95", 1, 1, 0), + WINDOWS_98("Windows 98", 1, 2, 0), + WINDOWS_98_FE("Windows 98 FE", 1, 2, 1), + WINDOWS_98_SE("Windows 98 SE", 1, 2, 2), + WINDOWS_ME("Windows ME", 1, 3, 0), + WINDOWS_NT("Windows NT", 1, 4, 0), + WINDOWS_NT_SP0("Windows NT SP0", 1, 4, 1), + WINDOWS_NT_SP1("Windows NT SP1", 1, 4, 2), + WINDOWS_NT_SP2("Windows NT SP2", 1, 4, 3), + WINDOWS_NT_SP3("Windows NT SP3", 1, 4, 4), + WINDOWS_NT_SP4("Windows NT SP4", 1, 4, 5), + WINDOWS_NT_SP5("Windows NT SP5", 1, 4, 6), + WINDOWS_NT_SP6("Windows NT SP6", 1, 4, 7), + WINDOWS_NT_SP6A("Windows NT SP6a", 1, 4, 8), + WINDOWS_2000("Windows 2000", 1, 5, 0), + WINDOWS_2000_SP0("Windows 2000 SP0", 1, 5, 1), + WINDOWS_2000_SP1("Windows 2000 SP1", 1, 5, 2), + WINDOWS_2000_SP2("Windows 2000 SP2", 1, 5, 3), + WINDOWS_2000_SP3("Windows 2000 SP3", 1, 5, 4), + WINDOWS_2000_SP4("Windows 2000 SP4", 1, 5, 5), + WINDOWS_XP("Windows XP", 1, 6, 0), + WINDOWS_XP_SP0("Windows XP SP0", 1, 6, 1), + WINDOWS_XP_SP1("Windows XP SP1", 1, 6, 2), + WINDOWS_XP_SP2("Windows XP SP2", 1, 6, 3), + WINDOWS_XP_SP3("Windows XP SP3", 1, 6, 4), + WINDOWS_2003("Windows 2003", 1, 7, 0), + WINDOWS_2003_SP0("Windows 2003 SP0", 1, 7, 1), + WINDOWS_2003_SP1("Windows 2003 SP1", 1, 7, 2), + WINDOWS_VISTA("Windows Vista", 1, 8, 0), + WINDOWS_VISTA_SP0("Windows Vista SP0", 1, 8, 1), + WINDOWS_VISTA_SP1("Windows Vista SP1", 1, 8, 2), + WINDOWS_7("Windows 7", 1, 9, 0), + WINDOWS_8("Windows 8", 1, 10, 0), + NETWARE("Netware", 2, 0, 0), + ANDROID("Android", 3, 0, 0), + JAVA("Java", 4, 0, 0), + RUBY("Ruby", 5, 0, 0), + LINUX("Linux", 6, 0, 0), + CISCO("Cisco", 7, 0, 0), + SOLARIS("Solaris", 8, 0, 0), + SOLARIS_V4("Solaris V4", 8, 1, 0), + SOLARIS_V5("Solaris V5", 8, 2, 0), + SOLARIS_V6("Solaris V6", 8, 3, 0), + SOLARIS_V7("Solaris V7", 8, 4, 0), + SOLARIS_V8("Solaris V8", 8, 5, 0), + SOLARIS_V9("Solaris V9", 8, 6, 0), + SOLARIS_V10("Solaris V10", 8, 7, 0), + OSX("OSX", 9, 0, 0), + BSD("BSD", 10, 0, 0), + OPENBSD("OpenBSD", 11, 0, 0), + BSDI("BSDi", 12, 0, 0), + NETBSD("NetBSD", 13, 0, 0), + FREEBSD("FreeBSD", 14, 0, 0), + AIX("AIX", 15, 0, 0), + HPUX("HPUX", 16, 0, 0), + IRIX("Irix", 17, 0, 0), + UNIX("Unix", 18, 0, 0), + PHP("PHP", 19, 0, 0), + JAVASCRIPT("JavaScript", 20, 0, 0), + PYTHON("Python", 21, 0, 0), + NODEJS("NodeJS", 22, 0, 0), + FIREFOX("Firefox", 23, 0, 0), + MAINFRAME("Mainframe", 24, 0, 0); + + protected String realName; + protected int major; + protected int minor; + protected int patch; + + Platform(String realName, int major, int minor, int patch) { + this.realName = realName; + this.major = major; + this.minor = minor; + this.patch = patch; + } + + public String getRealName() { + return realName; + } + + /** + * is a platform a subset of another ? + * @param other possible ancestor to test + * @return true if {@code other} is a platform that contains {@code this} + */ + public boolean subsetOf(Platform other) { + if(other.major != major) + return false; + if(other.minor == 0) + return true; + if(other.minor != minor) + return false; + if(other.patch == 0) + return true; + if(other.patch != patch) + return false; + return true; + } + + public static Platform fromString(String s) { + if(s != null) { + s = s.trim(); + for (Platform pl : values()) { + if (pl.getRealName().equals(s)) + return pl; + } + } + return null; + } + +} diff --git a/msf/src/main/java/org/csploit/msf/module/PlatformList.java b/msf/src/main/java/org/csploit/msf/module/PlatformList.java new file mode 100644 index 0000000000..a4f5cdc464 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/module/PlatformList.java @@ -0,0 +1,43 @@ +package org.csploit.msf.module; + +import java.util.HashSet; + +/** + * A list of platforms + */ +public class PlatformList extends HashSet { + + public PlatformList() { + super(); + } + + public PlatformList(String s) { + this(s.split(",")); + } + + public PlatformList(String[] plats) { + super(plats.length); + for(String s : plats) { + Platform platform = Platform.fromString(s); + if(platform != null) { + add(platform); + } + } + } + + public PlatformList intersect(PlatformList other) { + PlatformList res = new PlatformList(); + + for(Platform pl1 : this) { + for(Platform pl2 : other) { + if(pl1.subsetOf(pl2)) { + res.add(pl1); + } else if(pl2.subsetOf(pl1)) { + res.add(pl2); + } + } + } + + return res; + } +} diff --git a/msf/src/main/java/org/csploit/msf/module/Rank.java b/msf/src/main/java/org/csploit/msf/module/Rank.java new file mode 100644 index 0000000000..b74854255b --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/module/Rank.java @@ -0,0 +1,43 @@ +package org.csploit.msf.module; + +/** + * A module rank + */ +public enum Rank { + MANUAL(0, "manual"), + LOW(100, "low"), + AVERAGE(200, "average"), + NORMAL(300, "normal"), + GOOD(400, "good"), + GREAT(500, "great"), + EXCELLENT(600, "excellent") + ; + + private String name; + private int value; + + Rank(int value, String name) { + this.value = value; + this.name = name; + } + + public static Rank fromName(String name) { + name = name.trim(); + name = name.toLowerCase(); + for (Rank r : values()) { + if (r.name.toLowerCase().equals(name)) { + return r; + } + } + return null; + } + + public static Rank fromValue(int value) { + for (Rank r : values()) { + if (r.value <= value) { + return r; + } + } + return null; + } +} diff --git a/msf/src/main/java/org/csploit/msf/module/Reference.java b/msf/src/main/java/org/csploit/msf/module/Reference.java new file mode 100644 index 0000000000..8ad1c18a23 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/module/Reference.java @@ -0,0 +1,37 @@ +package org.csploit.msf.module; + +/** + * A reference to some sort of information. This is typically a URL, but could + * be any type of referential value that people could use to research a topic. + */ +public class Reference { + protected String str; + + public Reference(String str) { + this.str = "" + str; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Reference reference = (Reference) o; + + return str.equals(reference.str); + } + + @Override + public int hashCode() { + return str.hashCode(); + } + + @Override + public String toString() { + return str; + } + + public static Reference fromString(String s) { + return new Reference(s); + } +} diff --git a/msf/src/main/java/org/csploit/msf/module/SiteReference.java b/msf/src/main/java/org/csploit/msf/module/SiteReference.java new file mode 100644 index 0000000000..8b012171a1 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/module/SiteReference.java @@ -0,0 +1,51 @@ +package org.csploit.msf.module; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * A reference to a website. + */ +public class SiteReference extends Reference { + protected String ctx_id; + protected String ctx_val; + + private final static Pattern SIMPLE_URL_PATTERN = Pattern.compile("(http://|https://|ftp://)"); + + private final static Map formats = new HashMap() {{ + put("OSVDB", "http://www.osvdb.org/%s"); + put("CVE", "http://cvedetails.com/cve/%s/"); + put("CWE", "https://cwe.mitre.org/data/definitions/%s.html"); + put("BID", "http://www.securityfocus.com/bid/%s"); + put("MSB", "http://technet.microsoft.com/en-us/security/bulletin/%s"); + put("EDB", "https://www.exploit-db.com/exploits/%s"); + put("US-CERT-VU", "http://www.kb.cert.org/vuls/id/%s"); + put("ZDI", "http://www.zerodayinitiative.com/advisories/ZDI-%s"); + put("WPVDB", "https://wpvulndb.com/vulnerabilities/%s"); + put("PACKETSTORM", "https://packetstormsecurity.com/files/%s"); + put("URL", "%s"); + }}; + + public SiteReference(String ctx_id, String ctx_val) { + super(""); + this.ctx_id = ctx_id; + this.ctx_val = ctx_val; + + if(formats.containsKey(ctx_id)) { + str = String.format(formats.get(ctx_id), ctx_val); + } else { + str = ctx_id; + if(ctx_val != null) { + str += " (" + ctx_val + ")"; + } + } + } + + public static SiteReference fromString(String str) { + if(!SIMPLE_URL_PATTERN.matcher(str).matches()) { + return null; + } + return new SiteReference("URL", str); + } +} diff --git a/msf/src/main/java/org/csploit/msf/module/Target.java b/msf/src/main/java/org/csploit/msf/module/Target.java new file mode 100644 index 0000000000..9262f7d25f --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/module/Target.java @@ -0,0 +1,32 @@ +package org.csploit.msf.module; + +/** + * Represent a target + */ +public class Target { + private String name; + private PlatformList platform; + private ArchSet arch; + + public Target(String name, PlatformList platform, ArchSet arch) { + this.name = name; + this.platform = platform; + this.arch = arch; + } + + public Target(String name) { + this(name, new PlatformList(), new ArchSet()); + } + + public String getName() { + return name; + } + + public PlatformList getPlatform() { + return platform; + } + + public ArchSet getArch() { + return arch; + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/AddressOption.java b/msf/src/main/java/org/csploit/msf/options/AddressOption.java new file mode 100644 index 0000000000..2c0d502217 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/AddressOption.java @@ -0,0 +1,37 @@ +package org.csploit.msf.options; + +import org.csploit.msf.Option; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Network address option. + */ +public class AddressOption extends Option { + + public AddressOption(String name, boolean required, String description, InetAddress defaultValue) { + super(name, required, description, defaultValue); + } + + public AddressOption(String name, boolean required, String description, String defaultValue) { + super(name, required, description, sNormalize(defaultValue)); + } + + public AddressOption(String name, String description, InetAddress defaultValue) { + super(name, description, defaultValue); + } + + @Override + protected InetAddress normalize(String input) { + return sNormalize(input); + } + + private static InetAddress sNormalize(String input) { + try { + return input != null ? InetAddress.getByName(input) : null; + } catch (UnknownHostException e) { + return null; + } + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/AddressRangeOption.java b/msf/src/main/java/org/csploit/msf/options/AddressRangeOption.java new file mode 100644 index 0000000000..422830b518 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/AddressRangeOption.java @@ -0,0 +1,81 @@ +package org.csploit.msf.options; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.csploit.msf.Option; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +/** + * Network address range option. + */ +public class AddressRangeOption extends Option { + + public AddressRangeOption(String name, boolean required, String description, InetAddress[] defaultValue) { + super(name, required, description, defaultValue); + } + + public AddressRangeOption(String name, String description, InetAddress[] defaultValue) { + super(name, description, defaultValue); + } + + @Override + protected InetAddress[] normalize(String input) { + if(input == null) + return null; + + if(input.startsWith("file:")) { + InputStream is = null; + try { + input = input.substring(5).trim(); + is = new FileInputStream(new File(input)); + List lines = IOUtils.readLines(is); + ArrayList result = new ArrayList<>(); + + for(String line : lines) { + try { + result.add(InetAddress.getByName(line.trim())); + } catch (UnknownHostException e) { + // ignore + } + } + + return result.toArray(new InetAddress[result.size()]); + } catch (IOException e) { + // ignore + } finally { + IOUtils.closeQuietly(is); + } + } else if ( input.startsWith("rand:")) { + throw new UnsupportedOperationException("random address ranges not supported"); + } else { + String[] parts = input.split(" "); + ArrayList result = new ArrayList<>(); + + for(String part : parts) { + try { + result.add(InetAddress.getByName(part)); + } catch (UnknownHostException e) { + // ignore + } + } + + return result.toArray(new InetAddress[result.size()]); + } + return null; + } + + @Override + protected String display(InetAddress[] value) { + return StringUtils.join(value, " "); + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/BooleanOption.java b/msf/src/main/java/org/csploit/msf/options/BooleanOption.java new file mode 100644 index 0000000000..768553640f --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/BooleanOption.java @@ -0,0 +1,26 @@ +package org.csploit.msf.options; + +import org.csploit.msf.Option; + +import java.util.regex.Pattern; + +/** + * Boolean option. + */ +public class BooleanOption extends Option { + private static final Pattern TRUE = Pattern.compile("^(y|yes|t|1|true)$", Pattern.CASE_INSENSITIVE); + private static final Pattern BOOLEAN = Pattern.compile("^(y|yes|n|no|t|f|0|1|true|false)$", Pattern.CASE_INSENSITIVE); + + public BooleanOption(String name, boolean required, String description, Boolean defaultValue) { + super(name, required, description, defaultValue); + } + + public BooleanOption(String name, String description, Boolean defaultValue) { + super(name, description, defaultValue); + } + + @Override + protected Boolean normalize(String input) { + return input != null && TRUE.matcher(input).matches(); + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/EnumOption.java b/msf/src/main/java/org/csploit/msf/options/EnumOption.java new file mode 100644 index 0000000000..d7a276710b --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/EnumOption.java @@ -0,0 +1,36 @@ +package org.csploit.msf.options; + +import org.csploit.msf.Option; + +/** + * Enum option. + */ +public class EnumOption extends Option { + + private String[] enums; + + public EnumOption(String name, boolean required, String description, String defaultValue, String[] enums) { + super(name, required, description, defaultValue); + this.enums = enums; + } + + public EnumOption(String name, String description, String defaultValue, String[] enums) { + super(name, description, defaultValue); + this.enums = enums; + } + + @Override + protected String normalize(String input) { + for(String s : enums) { + if(s.equals(input)) { + return s; + } + } + return null; + } + + @Override + protected boolean isValid(String input) { + return super.isValid(input) && (input == null || normalize(input) != null); + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/IntegerOption.java b/msf/src/main/java/org/csploit/msf/options/IntegerOption.java new file mode 100644 index 0000000000..72bec85aa3 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/IntegerOption.java @@ -0,0 +1,29 @@ +package org.csploit.msf.options; + +import org.csploit.msf.Option; + +/** + * Integer option. + */ +public class IntegerOption extends Option { + public IntegerOption(String name, boolean required, String description, Integer defaultValue) { + super(name, required, description, defaultValue); + } + + public IntegerOption(String name, String description, Integer defaultValue) { + super(name, description, defaultValue); + } + + @Override + protected Integer normalize(String input) { + try { + if (input.startsWith("0x")) { + return Integer.parseInt(input.substring(2), 16); + } else { + return Integer.parseInt(input); + } + } catch (NumberFormatException e) { + return null; + } + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/PathOption.java b/msf/src/main/java/org/csploit/msf/options/PathOption.java new file mode 100644 index 0000000000..1443fc09f7 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/PathOption.java @@ -0,0 +1,35 @@ +package org.csploit.msf.options; + +import org.csploit.msf.Option; + +import java.io.File; + +/** + * File system path option. + */ +public class PathOption extends Option { + + public PathOption(String name, boolean required, String description, File defaultValue) { + super(name, required, description, defaultValue); + } + + public PathOption(String name, String description, File defaultValue) { + super(name, description, defaultValue); + } + + @Override + protected File normalize(String input) { + File res = new File(input); + return res.exists() ? res : null; + } + + @Override + protected boolean isValid(File input) { + return super.isValid(input) && (input == null || input.exists()); + } + + @Override + protected String display(File value) { + return value.getAbsolutePath(); + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/PortOption.java b/msf/src/main/java/org/csploit/msf/options/PortOption.java new file mode 100644 index 0000000000..8094bb719f --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/PortOption.java @@ -0,0 +1,33 @@ +package org.csploit.msf.options; + +import org.csploit.msf.Option; + +/** + * Network port option. + */ +public class PortOption extends Option { + + public PortOption(String name, boolean required, String description, Integer defaultValue) { + super(name, required, description, defaultValue); + } + + public PortOption(String name, String description, Integer defaultValue) { + super(name, description, defaultValue); + } + + @Override + protected Integer normalize(String input) { + try { + int val = Integer.parseInt(input); + return isValid(val) ? val : null; + } catch (NumberFormatException e) { + // ignore + } + return null; + } + + @Override + protected boolean isValid(Integer input) { + return super.isValid(input) && (input == null || input >= 0 && input <= 65535); + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/RawOption.java b/msf/src/main/java/org/csploit/msf/options/RawOption.java new file mode 100644 index 0000000000..d6868ba70a --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/RawOption.java @@ -0,0 +1,42 @@ +package org.csploit.msf.options; + +import org.apache.commons.io.IOUtils; +import org.csploit.msf.Option; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; + +/** + * Raw, arbitrary data option. + */ +public class RawOption extends Option { + + public RawOption(String name, boolean required, String description, byte[] defaultValue) { + super(name, required, description, defaultValue); + } + + public RawOption(String name, String description, byte[] defaultValue) { + super(name, description, defaultValue); + } + + @Override + protected byte[] normalize(String input) { + if(input.startsWith("file:")) { + try { + IOUtils.toByteArray(new URI(input)); + } catch (IOException | URISyntaxException e) { + //ignore + } + return null; + } + return input.getBytes(); + } + + @Override + protected String display(byte[] value) { + return Arrays.toString(value); + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/RegexpOption.java b/msf/src/main/java/org/csploit/msf/options/RegexpOption.java new file mode 100644 index 0000000000..74d57cde35 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/RegexpOption.java @@ -0,0 +1,29 @@ +package org.csploit.msf.options; + +import org.csploit.msf.Option; + +import java.util.regex.Pattern; + +/** + * A regex option + */ +public class RegexpOption extends Option { + + public RegexpOption(String name, boolean required, String description, Pattern defaultValue) { + super(name, required, description, defaultValue); + } + + public RegexpOption(String name, String description, Pattern defaultValue) { + super(name, description, defaultValue); + } + + @Override + protected Pattern normalize(String input) { + return Pattern.compile(input); + } + + @Override + protected String display(Pattern value) { + return value.pattern(); + } +} diff --git a/msf/src/main/java/org/csploit/msf/options/StringOption.java b/msf/src/main/java/org/csploit/msf/options/StringOption.java new file mode 100644 index 0000000000..449f99da13 --- /dev/null +++ b/msf/src/main/java/org/csploit/msf/options/StringOption.java @@ -0,0 +1,38 @@ +package org.csploit.msf.options; + +import org.apache.commons.io.IOUtils; +import org.csploit.msf.Option; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * Option string + */ +public class StringOption extends Option { + + public StringOption(String name, boolean required, String description, String defaultValue) { + super(name, required, description, defaultValue); + } + + public StringOption(String name, String description, String defaultValue) { + super(name, description, defaultValue); + } + + @Override + protected String normalize(String input) { + if(input.startsWith("file:")) { + try { + return IOUtils.toString(new URI(input)); + } catch (IOException | URISyntaxException e) { + //ignore + } + return null; + } + return input; + } +} diff --git a/msf/src/test/java/org/csploit/msf/DataStoreTest.java b/msf/src/test/java/org/csploit/msf/DataStoreTest.java new file mode 100644 index 0000000000..737ccfe511 --- /dev/null +++ b/msf/src/test/java/org/csploit/msf/DataStoreTest.java @@ -0,0 +1,59 @@ +package org.csploit.msf; + +import org.csploit.msf.options.StringOption; +import org.junit.Test; + +import java.util.Set; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +/** + * Test the DataStore class + */ +public class DataStoreTest { + + private DataStore buildDataStoreWithDefaultOption() { + DataStore store = new DataStore(); + OptionContainer options = new OptionContainer(); + + options.addOption("A", + new StringOption("A", "description", "default value"), false, false); + + store.importOptions(options, true); + + return store; + } + + @Test + public void testImportOptions() throws Exception { + DataStore store = buildDataStoreWithDefaultOption(); + + assertThat(store.get("A"), is("default value")); + } + + @Test + public void testUserDefined() throws Exception { + DataStore store = buildDataStoreWithDefaultOption(); + + assertThat(store.userDefined().isEmpty(), is(true)); + + store.put("A", "user-defined value"); + + assertThat(store.userDefined().isEmpty(), is(false)); + } + + @Test + public void testClearNotUserDefined() throws Exception { + DataStore store = buildDataStoreWithDefaultOption(); + + store.put("B", "user-defined value"); + + store.clearNotUserDefined(); + + Set keys = store.keySet(); + + assertThat(keys.contains("B"), is(true)); + assertThat(keys.contains("A"), is(false)); + } +} \ No newline at end of file diff --git a/msf/src/test/java/org/csploit/msf/RpcSampleTest.java b/msf/src/test/java/org/csploit/msf/RpcSampleTest.java new file mode 100644 index 0000000000..3c0c414b1c --- /dev/null +++ b/msf/src/test/java/org/csploit/msf/RpcSampleTest.java @@ -0,0 +1,196 @@ +package org.csploit.msf; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +import org.csploit.msf.module.ArchSet; +import org.csploit.msf.module.PlatformList; +import org.csploit.msf.module.Reference; +import org.csploit.msf.module.SiteReference; +import org.csploit.msf.module.Target; +import org.csploit.msf.options.AddressOption; +import org.csploit.msf.options.BooleanOption; +import org.csploit.msf.options.EnumOption; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * Test msf library usage for rpc usage + */ +public class RpcSampleTest { + + private class ModuleInfoBase { + public String name,description,license,filepath; + public int rank; + public String[][] references; + public String[] authors; + } + + private class ExploitInfo extends ModuleInfoBase { + public String[] targets; + public int defaultTarget; + } + + private class AuxInfo extends ModuleInfoBase { + public String[] actions; + public int defaultAction; + } + + private class ModuleOptionInfo { + public String type, description; + public boolean required, advanced, evasion; + public T defaultValue; + public String[] enums; + + public ModuleOptionInfo(String type, String description, boolean required, boolean advanced, boolean evasion, T defaultValue, String[] enums) { + this.type = type; + this.description = description; + this.required = required; + this.advanced = advanced; + this.evasion = evasion; + this.defaultValue = defaultValue; + this.enums = enums; + } + + public ModuleOptionInfo(String type, String description, boolean required, boolean advanced, boolean evasion, T defaultValue) { + this(type, description, required, advanced, evasion, defaultValue, null); + } + + public ModuleOptionInfo(String type, String description, boolean required, boolean advanced, boolean evasion) { + this(type, description, required, advanced, evasion, null, null); + } + } + + @Test + public void putExploitTest() { + + ExploitInfo info = new ExploitInfo(); + + info.name = "A funny exploit"; + info.description = "a very long description"; + info.license = License.GPL.toString(); + info.filepath = "whocares"; + info.rank = 300; + info.references = new String[][] { + {"URL", "http://nowhere.com/index.html"}, + {"OSVDB", "12345"} + }; + info.authors = new String[] { + "Someone " + }; + info.targets = new String[] { + "IE / RealOne Player 2", + "IE / RealOne Player 3" + }; + info.defaultTarget = 0; + + + Framework framework = new Framework(); + + ModuleManager mgr = framework.getModuleManager(); + + // deserialize rpc stuff + + Target[] targets = new Target[info.targets.length]; + Author[] authors = new Author[info.authors.length]; + Reference[] references = new Reference[info.references.length]; + License license = License.fromString(info.license); + + for(int i = 0; i < info.targets.length; i++) { + targets[i] = new Target(info.targets[i]); + } + + for(int i = 0; i < info.authors.length; i++) { + authors[i] = Author.fromString(info.authors[i]); + } + + for(int i = 0; i < info.references.length; i++) { + String[] pair = info.references[i]; + String id = pair[0]; + String val = pair[1]; + references[i] = new SiteReference(id, val); + } + + Exploit exploit = new Exploit( + info.name, info.description, "0", authors, + new ArchSet(), new PlatformList(), references, + false, license, targets, 0); + + mgr.put(exploit); + + assertThat(exploit.getFramework(), is(framework)); + assertThat(exploit.license, is(License.GPL)); + assertThat(exploit.getTarget(), is(exploit.getTargets()[0])); + + // receive exploit options + Map options = new HashMap<>(); + + options.put("NTLM::SendNTLM", + new ModuleOptionInfo<>( + "bool", "A", true, true, false, true + )); + options.put("HTTP::uri_encode_mode", + new ModuleOptionInfo<>( + "enum", "B", false, false, true, "hex-normal", new String[] { + "none", "hex-normal", "hex-noslashes", "hex-random", + "hex-all", "u-normal", "u-all", "u-random" + } + )); + options.put("RHOST", + new ModuleOptionInfo<>( + "address", "C", true, false, false + )); + + // put them into the exploit + + for (Map.Entry entry : options.entrySet()) { + ModuleOptionInfo info1 = entry.getValue(); + String name = entry.getKey(); + + exploit.registerOption(name, getOptionForInfo(name, info1), info1.advanced, info1.evasion); + } + + // let's now try to edit an option + + // first in the local datastore + exploit.getDataStore().put("TARGET", "1"); + assertThat(exploit.getTarget(), is(exploit.getTargets()[1])); + exploit.getDataStore().remove("TARGET"); + assertThat(exploit.getTarget(), is(exploit.getTargets()[0])); + + // from global datastore + framework.getDataStore().put("TARGET", "1"); + assertThat(exploit.getTarget(), is(exploit.getTargets()[1])); + framework.getDataStore().remove("TARGET"); + assertThat(exploit.getTarget(), is(exploit.getTargets()[0])); + + // exploit settings should not be valid without the required RHOST options + + assertThat(exploit.getinvalidOptions().isEmpty(), is(false)); + + framework.getDataStore().put("RHOST", "invalid address"); + + assertThat(exploit.getinvalidOptions().isEmpty(), is(false)); + + framework.getDataStore().put("RHOST", "127.0.0.1"); + + assertThat(exploit.getinvalidOptions().isEmpty(), is(true)); + } + + private static Option getOptionForInfo(String name, ModuleOptionInfo info) { + switch (info.type) { + case "bool": + return new BooleanOption(name, info.required, info.description, + ((ModuleOptionInfo)info).defaultValue); + case "enum": + return new EnumOption(name, info.required, info.description, + ((ModuleOptionInfo)info).defaultValue, info.enums); + case "address": + return new AddressOption(name, info.required, info.description, + ((ModuleOptionInfo)info).defaultValue); + } + return null; + } +} diff --git a/msf/src/test/java/org/csploit/msf/module/PlatformListTest.java b/msf/src/test/java/org/csploit/msf/module/PlatformListTest.java new file mode 100644 index 0000000000..b9bbec1d2d --- /dev/null +++ b/msf/src/test/java/org/csploit/msf/module/PlatformListTest.java @@ -0,0 +1,24 @@ +package org.csploit.msf.module; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +/** + * Test unit for PlatformList class + */ +public class PlatformListTest { + + @Test + public void testIntersect() throws Exception { + PlatformList l1, l2, ex; + + l1 = new PlatformList("Windows 98, Solaris V4"); + l2 = new PlatformList("Windows 98 FE, Solaris"); + ex = new PlatformList("Windows 98 FE, Solaris V4"); + + assertThat(l1.intersect(l2), is(ex)); + assertThat(l2.intersect(l1), is(ex)); + } +} \ No newline at end of file diff --git a/msf/src/test/java/org/csploit/msf/module/PlatformTest.java b/msf/src/test/java/org/csploit/msf/module/PlatformTest.java new file mode 100644 index 0000000000..0735e860e4 --- /dev/null +++ b/msf/src/test/java/org/csploit/msf/module/PlatformTest.java @@ -0,0 +1,33 @@ +package org.csploit.msf.module; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.Test; + +/** + * Test unit for Platform enum + */ +public class PlatformTest { + + @Test + public void testFromString() throws Exception { + assertThat(Platform.fromString("Windows"), is(Platform.WINDOWS)); + assertThat(Platform.fromString("Windows NT SP3"), is(Platform.WINDOWS_NT_SP3)); + assertThat(Platform.fromString(null), nullValue()); + assertThat(Platform.fromString("Unknown"), nullValue()); + assertThat(Platform.fromString("Unix"), is(Platform.UNIX)); + } + + @Test + public void testSubsetOf() throws Exception { + assertThat(Platform.WINDOWS_2000_SP0.subsetOf(Platform.WINDOWS), is(true)); + assertThat(Platform.WINDOWS.subsetOf(Platform.WINDOWS_2000), is(false)); + assertThat(Platform.WINDOWS_VISTA_SP0.subsetOf(Platform.WINDOWS_NT_SP0), is(false)); + assertThat(Platform.UNIX.subsetOf(Platform.AIX), is(false)); + assertThat(Platform.SOLARIS_V10.subsetOf(Platform.SOLARIS), is(true)); + + for(Platform platform : Platform.values()) { + assertThat(platform.subsetOf(platform), is(true)); + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 47bb48eb2b..d07762bec5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':cSploit' +include ':cSploit', ':msf' From ce2e9c85c80b761eaaeb41a49f1a191456cc2b88 Mon Sep 17 00:00:00 2001 From: fattire Date: Sat, 3 Oct 2015 01:13:13 -0700 Subject: [PATCH 2/2] Terrible first pass at showing port on main scanner screen * this is just a POC. * It works until you start scrolling, then it turns into a shit show. * Need to figure some logic for when to do a portscan-- right now it's done when the item rolls into view in the listview. This is smart in that you're not scanning things until they show up on screen. This is dumb in that if you scroll, a ton of portscans are launched. * It's also dumb in that as new items pop into the listview, the order is no longer preserved and the wrong item gets changed (Reciever needs a better way of remembering which item it was scanning than just the position, because that changes as new items are listed. Using the view doesn't work either as thyey're recycled on scrolling. I'm sure I just need to think a second and the answer will be obvious. * No Preferences support.. it always scrolls. * It probably also crashes easily. I didn't test much. --- cSploit/res/layout/target_list_item.xml | 25 +++++-- .../src/org/csploit/android/MainActivity.java | 67 +++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/cSploit/res/layout/target_list_item.xml b/cSploit/res/layout/target_list_item.xml index 6c28880202..f5ff9f8e78 100644 --- a/cSploit/res/layout/target_list_item.xml +++ b/cSploit/res/layout/target_list_item.xml @@ -23,16 +23,33 @@ android:layout_toRightOf="@id/itemIcon" android:textColor="@color/app_color" android:fontFamily="sans-serif-light" - android:textSize="16sp" /> + android:textSize="16sp" + android:layout_toLeftOf="@+id/portCount" + android:layout_toStartOf="@+id/portCount" /> + android:textSize="14sp" + android:layout_toRightOf="@+id/itemIcon" + android:layout_toLeftOf="@+id/portCount" + android:layout_toStartOf="@+id/portCount" + android:layout_below="@+id/itemTitle" /> + \ No newline at end of file diff --git a/cSploit/src/org/csploit/android/MainActivity.java b/cSploit/src/org/csploit/android/MainActivity.java index 52ad38b668..7e38ff9911 100644 --- a/cSploit/src/org/csploit/android/MainActivity.java +++ b/cSploit/src/org/csploit/android/MainActivity.java @@ -27,6 +27,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.view.ActionMode; import android.text.Html; @@ -48,6 +49,7 @@ import android.widget.Toast; import org.csploit.android.core.Child; +import org.csploit.android.core.ChildManager; import org.csploit.android.core.Client; import org.csploit.android.core.CrashReporter; import org.csploit.android.core.Logger; @@ -83,6 +85,7 @@ import org.csploit.android.services.UpdateService; import org.csploit.android.services.receivers.MsfRpcdServiceReceiver; import org.csploit.android.services.receivers.NetworkRadarReceiver; +import org.csploit.android.tools.NMap; import org.csploit.android.update.CoreUpdate; import org.csploit.android.update.MsfUpdate; import org.csploit.android.update.RubyUpdate; @@ -132,6 +135,7 @@ private void createUpdateStatusText() { layout.addView(mUpdateStatus); } + private void createUpdateLayout() { lv.setVisibility(View.GONE); @@ -841,13 +845,14 @@ public View getView(int position, View convertView, ViewGroup parent) { .findViewById(R.id.itemTitle) : null); holder.itemDescription = (TextView) (row != null ? row .findViewById(R.id.itemDescription) : null); - + holder.portCount = (TextView) (row != null ? row + .findViewById(R.id.portCount) : null); if (row != null) row.setTag(holder); } else holder = (TargetHolder) row.getTag(); - Target target = System.getTarget(position); + final Target target = System.getTarget(position); if (target.hasAlias()) holder.itemTitle.setText(Html.fromHtml("" @@ -856,8 +861,7 @@ public View getView(int position, View convertView, ViewGroup parent) { else holder.itemTitle.setText(target.toString()); - - holder.itemTitle.setTextColor(getResources().getColor((target.isConnected() ? R.color.app_color : R.color.gray_text))); + holder.itemTitle.setTextColor(ContextCompat.getColor(getContext(), (target.isConnected() ? R.color.app_color : R.color.gray_text))); if (row != null && (getSharedPreferences("THEME", 0).getBoolean("isDark", false))) row.setBackgroundResource(R.drawable.card_background_dark); @@ -865,6 +869,23 @@ public View getView(int position, View convertView, ViewGroup parent) { holder.itemImage.setImageResource(target.getDrawableResourceId()); holder.itemDescription.setText(target.getDescription()); + // only do a portscan if/when target rolls into view and ports haven't been found... + if (target.getType() != Target.Type.NETWORK && target.getOpenPorts().size() == 0) { + final Receiver r = new Receiver(position, convertView); + Thread thread = new Thread() { + @Override + public void run() { + try { + System.getTools().nmap + .synScan(target, r, null); + } catch (ChildManager.ChildNotStartedException e) { + e.printStackTrace(); + } + } + }; + thread.run(); + } + return row; } @@ -918,6 +939,7 @@ class TargetHolder { ImageView itemImage; TextView itemTitle; TextView itemDescription; + TextView portCount; } } @@ -1093,4 +1115,41 @@ public void onReceive(Context context, Intent intent) { } } } + + private class Receiver extends NMap.SynScanReceiver { + int position; + final View cv; + + public Receiver(int position, View cv) { + this.position = position; + this.cv = cv; + } + + @Override + public void onEnd(int exitCode) { + MainActivity.this.runOnUiThread( + new Runnable() { + @Override + public void run() { + System.setCurrentTarget(position); + // new items may have snuck in so the order can change. Double check this + if (cv != null) { + TextView tv = (TextView) cv.findViewById(R.id.portCount); + if (tv != null) { + tv.setText(String.format("%d", System.getCurrentTarget().getOpenPorts().size())); + tv.setVisibility(System.getCurrentTarget().getOpenPorts().size() < 1 ? View.GONE : View.VISIBLE); + } + } + } + } + ); + super.onEnd(exitCode); + } + + @Override + public void onPortFound(int port, String protocol) { + System.setCurrentTarget(position); + System.addOpenPort(port, null); + } + } } \ No newline at end of file