Skip to content

Commit

Permalink
Added flags, file input, and accepts UNC paths
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgrandy committed Nov 10, 2020
1 parent de54740 commit 62792a9
Show file tree
Hide file tree
Showing 60 changed files with 13,294 additions and 108 deletions.
7 changes: 5 additions & 2 deletions MiddleOut/MiddleOut.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
<StartupObject>MiddleOut.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="CommandLine, Version=2.8.0.0, Culture=neutral, PublicKeyToken=5a870481e358d379, processorArchitecture=MSIL">
<HintPath>..\packages\CommandLineParser.2.8.0\lib\net45\CommandLine.dll</HintPath>
</Reference>
<Reference Include="Costura, Version=4.1.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
<HintPath>..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll</HintPath>
</Reference>
Expand Down Expand Up @@ -77,12 +80,12 @@
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Fody.6.1.1\build\Fody.targets" Condition="Exists('..\packages\Fody.6.1.1\build\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Fody.6.1.1\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.1.1\build\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props'))" />
<Error Condition="!Exists('..\packages\Fody.6.3.0\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.3.0\build\Fody.targets'))" />
</Target>
<Import Project="..\packages\Fody.6.3.0\build\Fody.targets" Condition="Exists('..\packages\Fody.6.3.0\build\Fody.targets')" />
</Project>
222 changes: 127 additions & 95 deletions MiddleOut/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
using Ionic.Zip;
using System;
using System.Collections.Generic;
using System.Security;
using CommandLine;
using CommandLine.Text;
using System.Threading;
using System.IO;
using System.Text;
using System.Linq;

namespace MiddleOut
Expand All @@ -11,101 +16,62 @@ namespace MiddleOut
class Program
{
public static string ZipFileName;
public static string Pass;
public static List<string> ArgList = new List<string>();

public enum PathType { NonExisting = 0, File = 1, Directory = 2 };
public static PathType GetPathType(string path)


public class Options
{
if (File.Exists(path))
return PathType.File;
if (Directory.Exists(path))
return PathType.Directory;
return PathType.NonExisting;
public static Options Instance { get; set; }

// Command line options
[Option('v', "verbose", Required = false, HelpText = "Set output to verbose")]
public bool Verbose { get; set; }

[Option('f', "file", Group = "Input", Required = true, HelpText = "Specify a text file containing files to compress (one per line)")]
public string File { get; set; }

[Option('i', "input", Group = "Input", Required = true, HelpText = "Specify multiple files to compress (separated by commas)")]
public string Input { get; set; }

[Option('o', "output", Required = false, HelpText = "Specify a zip file to output to", Default = null)]
public string Output { get; set; }

[Option('p', "password", Required = false, HelpText = "Specify a password to encrypt the zip file", Default = null)]
public string Password { get; set; }
}

static void ParseArgs(string[] arguments)
static void DisplayHelp<T>(ParserResult<T> result, IEnumerable<Error> errs)
{
// Takes in x params with the second to last arg being the zip file and the last being
// an optional password

try
var helpText = HelpText.AutoBuild(result, h =>
{
Pass = arguments[arguments.Length - 1];
h.AdditionalNewLineAfterOption = false;
h.Heading = "MiddleOut v 1.1"; //change header
h.Copyright = ""; //change copyright text
h.AutoVersion = false;
return HelpText.DefaultParsingErrorsHandler(result, h);
}, e => e);
Console.WriteLine(helpText);
System.Environment.Exit(1);
}

if (GetPathType(Pass) == PathType.NonExisting)
{
ZipFileName = arguments[arguments.Length - 2];
if (ZipFileName.EndsWith("\\") || ZipFileName.EndsWith("\\*"))
{
string sanitizedPath = ZipFileName.TrimEnd(ZipFileName[ZipFileName.Length - 1]);
ZipFileName = sanitizedPath;
}
if (GetPathType(ZipFileName) == PathType.NonExisting)
{
if (ZipFileName.EndsWith(".zip"))
{
//do nothing, it's a good filename
}
else
{
ZipFileName = ZipFileName + ".zip";
}
}
//else if (GetPathType(ZipFileName) == PathType.File)
//{
// Console.WriteLine("\n[-] ERROR: Zip file already exists!\n[-] ERROR: Exiting...");
// System.Environment.Exit(1);
//}
else
{
ZipFileName = arguments[arguments.Length - 1];
Pass = null;
if (ZipFileName.EndsWith(".zip"))
{
//do nothing, it's a good filename
}
else
{
ZipFileName = ZipFileName + ".zip";
}
}
}

if ((GetPathType(Pass)) == PathType.File || (GetPathType(Pass)) == PathType.Directory)
{
Console.WriteLine("\n[-] ERROR: Zip file already exists or you didn't specify a zip file!\n[-] ERROR: Exiting...");
System.Environment.Exit(1);
}
else
{
//System.Environment.Exit(1);
}

List<string> list = new List<string>(arguments);
if (Pass != null)
{
Console.WriteLine("\n[+] Continuing with password\n");
list.RemoveAt(list.Count - 1);
list.RemoveAt(list.Count - 1);
}
else
{
Console.WriteLine("\n[+] Continuing without password\n");
list.RemoveAt(list.Count - 1);
}
ArgList = list;
}

catch (Exception e)
{
Console.WriteLine("\n[-] Something bad happened with the arguments passed");
Console.WriteLine("Please see the exception below: \n\n");
Console.WriteLine(e);
return;
}
public static PathType GetPathType(string path)
{
if (File.Exists(path))
return PathType.File;
if (Directory.Exists(path))
return PathType.Directory;
return PathType.NonExisting;
}






public static void Zipper(string[] zipList, string password, string location)
{
using (ZipFile zip = new ZipFile())
Expand Down Expand Up @@ -138,20 +104,72 @@ public static void Main(string[] args)
List<string> wildcardList = new List<string>();
string currentDirectory = Directory.GetCurrentDirectory();

if (args.Length < 2)
// Parse arguments passed
var parser = new Parser(with =>
{
with.CaseInsensitiveEnumValues = true;
with.CaseSensitive = false;
with.HelpWriter = null;
});

var parserResult = parser.ParseArguments<Options>(args);
parserResult.WithParsed<Options>(o =>
{
Console.WriteLine("\n[-] You didn't provide enough arguments");
Console.WriteLine("\nTo use, add in the files to compress (any amount), the second to last flag is the zip file name and the final flag is the password (optional). For some examples:\n");
Console.WriteLine(" > MiddleOut.exe test.txt this.txt file.exe zipArchive.zip");
Console.WriteLine(" > MiddleOut.exe * zipArchive.zip");
Console.WriteLine(" > MiddleOut.exe someDirectory\\test.txt someOtherDirectory\\* zipArchive.zip");
return;
Options.Instance = o;
})
.WithNotParsed(errs => DisplayHelp(parserResult, errs));
var options = Options.Instance;
Console.WriteLine();

ZipFileName = options.Output;

//If file was passed, add values to compresslist
if (!String.IsNullOrEmpty(options.File) && File.Exists(options.File))
{
try
{
using (StreamReader rdr = new StreamReader(options.File))
{
string line;
while ((line = rdr.ReadLine()) != null)
{
CompressList.Add(line);
}
}
}
catch (Exception e)
{
Console.WriteLine("Unable to parse file, try only having one file per line\nFull error:");
Console.WriteLine(e);
}
}

// Parse the arguments and go from there
ParseArgs(args);
//If a list of URLs was passed
else if (!String.IsNullOrEmpty(options.Input))
{
if (options.Input.Contains(','))
{
try
{
CompressList = options.Input.Split(',').ToList();
}
catch (Exception e)
{
Console.WriteLine("Unable to parse list of files, try removing whitespace before/after path\nFull error:");
Console.WriteLine(e);
}
}
else
{
CompressList.Add(options.Input);
}
}

List<string> CompressListTemp = CompressList.ToList();


foreach (string item in ArgList)
//Go through each item passed
foreach (string item in CompressListTemp)
{
if (item.EndsWith("\\") || item.EndsWith("\\*"))
{
Expand All @@ -162,7 +180,7 @@ public static void Main(string[] args)
{
try
{
wildcardList.Add(a);
wildcardList.Add(Path.GetFullPath(a));
}
catch (System.UnauthorizedAccessException)
{
Expand Down Expand Up @@ -265,15 +283,29 @@ public static void Main(string[] args)
}
}

if (String.IsNullOrEmpty(ZipFileName))
{
ZipFileName = "MiddleOut__" + DateTime.Now.ToString("M-dd-yyyy_HH-mm-ss") + ".zip";
}

//Do one final check (supports: MiddleOut.exe testfile test test) being executed twice in a row
if (GetPathType(ZipFileName) == PathType.File)
if (File.Exists(ZipFileName))
{
Console.WriteLine("\n[-] ERROR: Zip file already exists!\n[-] ERROR: Exiting...");
System.Environment.Exit(1);
}
string[] compressArray;
if (wildcardList.Count() > 0)
{
foreach (string item in wildcardList)
CompressList.Add(item);
}
compressArray = CompressList.Distinct().ToArray();
Zipper(compressArray, Pass, Path.GetFullPath(ZipFileName));

//foreach(string item in compressArray)
//Console.WriteLine(item);

Zipper(compressArray, options.Password, Path.GetFullPath(ZipFileName));

Console.WriteLine("[+] Finished compression, save location: \n" + Path.GetFullPath(ZipFileName));
}
Expand Down
3 changes: 2 additions & 1 deletion MiddleOut/packages.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommandLineParser" version="2.8.0" targetFramework="net45" />
<package id="Costura.Fody" version="4.1.0" targetFramework="net45" />
<package id="DotNetZip" version="1.13.7" targetFramework="net46" />
<package id="Fody" version="6.1.1" targetFramework="net45" developmentDependency="true" />
<package id="Fody" version="6.3.0" targetFramework="net45" developmentDependency="true" />
<package id="SharpZipLib" version="1.2.0" targetFramework="net45" />
</packages>
20 changes: 10 additions & 10 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# MiddleOut

## This tool was created to compress files through the command line and will work with Cobalt Strike's execute-assembly.

To use, add in the files to compress (any amount), the second to last flag is the zip file name, and the final flag is an optional password.

For some examples:
To use, pass in the files to compress (any amount) or pass it a text file of files, and supply an optional password. MiddleOut also accepts UNC paths as well.

`MiddleOut.exe test.txt this.txt file.exe zipArchive.zip`

`MiddleOut.exe test.txt this.txt file.exe zipArchive.zip SecurePassword123`
## This tool was created to compress files through the command line and will work with Cobalt Strike's execute-assembly.

`MiddleOut.exe * zipArchive.zip`
## Usage

`MiddleOut.exe someDirectory\test.txt someOtherDirectory\* zipArchive.zip`
```
MiddleOut.exe
MiddleOut.exe -i test.txt
MiddleOut.exe -i test.txt,another.txt -o output.zip
MiddleOut.exe -i ..\..\SomeFolder\* -o outputfile.zip
MiddleOut.exe -f filesToCompress.txt -o output.zip -p password
```
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
21 changes: 21 additions & 0 deletions packages/CommandLineParser.2.8.0/License.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2005 - 2015 Giacomo Stelluti Scala & Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Loading

0 comments on commit 62792a9

Please sign in to comment.