-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5da37e6
commit 92a066c
Showing
13 changed files
with
932 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
# MalPull | ||
A CLI interface to search for a MD-5/SHA-1/SHA-256 hash on multiple malware databases and download the sample from the first hit | ||
A CLI interface to search for a MD-5/SHA-1/SHA-256 hash on multiple malware databases and download the sample from the first hit. More information can be found <a href="https://maxkersten.nl/projects/malpull/">here</a>.If there are any questions, feature suggestions, or bug reports: please send me a message my Twitter (<a href="https://twitter.com/LibraAnalysis">@LibraAnalysis</a>). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<groupId>malwarepuller</groupId> | ||
<artifactId>MalPull</artifactId> | ||
<version>1.0-stable</version> | ||
<packaging>jar</packaging> | ||
<properties> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
<maven.compiler.source>1.8</maven.compiler.source> | ||
<maven.compiler.target>1.8</maven.compiler.target> | ||
</properties> | ||
<pluginRepositories> | ||
<pluginRepository> | ||
<id>central</id> | ||
<name>Central Repository</name> | ||
<url>https://repo.maven.apache.org/maven2</url> | ||
<layout>default</layout> | ||
<snapshots> | ||
<enabled>false</enabled> | ||
</snapshots> | ||
<releases> | ||
<updatePolicy>never</updatePolicy> | ||
</releases> | ||
</pluginRepository> | ||
</pluginRepositories> | ||
<repositories> | ||
<repository> | ||
<id>central</id> | ||
<name>Central Repository</name> | ||
<url>https://repo.maven.apache.org/maven2</url> | ||
<layout>default</layout> | ||
<snapshots> | ||
<enabled>false</enabled> | ||
</snapshots> | ||
</repository> | ||
</repositories> | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<artifactId>maven-assembly-plugin</artifactId> | ||
<configuration> | ||
<archive> | ||
<manifest> | ||
<mainClass>malpull.MalPull</mainClass> | ||
</manifest> | ||
</archive> | ||
<descriptorRefs> | ||
<descriptorRef>jar-with-dependencies</descriptorRef> | ||
</descriptorRefs> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
<dependencies> | ||
<dependency> | ||
<groupId>com.squareup.okhttp3</groupId> | ||
<artifactId>okhttp</artifactId> | ||
<version>4.2.2</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.json</groupId> | ||
<artifactId>json</artifactId> | ||
<version>20190722</version> | ||
</dependency> | ||
</dependencies> | ||
<name>MalPull</name> | ||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl] | ||
* | ||
* 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 3 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, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package endpoints; | ||
|
||
import malpull.Downloader; | ||
|
||
/** | ||
* This abstract class contains the shared code base for all endpoints, which is | ||
* done to re-use code. | ||
* | ||
* @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl] | ||
*/ | ||
public abstract class GenericEndpoint { | ||
|
||
/** | ||
* The downloader wrapper class that is used to make HTTP requests | ||
*/ | ||
protected Downloader downloader; | ||
|
||
/** | ||
* The base URL of the API, to which specific API actions can be appended | ||
*/ | ||
protected String apiBase; | ||
|
||
public GenericEndpoint(String apiBase) { | ||
//Sets the apiBase variable | ||
this.apiBase = apiBase; | ||
//Initialises the downloader class | ||
downloader = new Downloader(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
/* | ||
* Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl] | ||
* | ||
* 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 3 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, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package endpoints; | ||
|
||
import exceptions.Error404NotFoundException; | ||
import exceptions.Error429TooManyRequestsException; | ||
import exceptions.HttpConnectionFailed; | ||
import exceptions.SampleNotFoundException; | ||
import okhttp3.Request; | ||
import org.json.JSONArray; | ||
import org.json.JSONObject; | ||
|
||
/** | ||
* This class is used to get a sample from the Koodous database via its API. | ||
* | ||
* @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl] | ||
*/ | ||
public class Koodous extends GenericEndpoint { | ||
|
||
/** | ||
* The API key that is used to interact with the MalShare API | ||
*/ | ||
private String key; | ||
|
||
/** | ||
* Creates an object to interact with the Koodous API | ||
* | ||
* @param key the MalShare API key which is required to use the API | ||
*/ | ||
public Koodous(String key) { | ||
//Sets the apiBase variable in the abstract GenericEndpoint class | ||
super("https://koodous.com/api/apks"); | ||
//Sets the key variable | ||
this.key = key; | ||
} | ||
|
||
/** | ||
* Gets the sample from Koodous, if it is present. Throws an exception if it | ||
* is not present. | ||
* | ||
* @param hash the hash to look for | ||
* @return the sample as a byte array | ||
* @throws HttpConnectionFailed if no connection can be made from the | ||
* current machine, or to the given host | ||
* @throws SampleNotFoundException if the sample cannot be found | ||
* @throws Error404NotFoundException if the target returns a 404 status code | ||
* @throws Error429TooManyRequestsException if the target returns a 429 | ||
* status code | ||
*/ | ||
public byte[] getSample(String hash) throws HttpConnectionFailed, SampleNotFoundException, Error404NotFoundException, Error429TooManyRequestsException { | ||
//Get the SHA-256 hash via the search function of the API, as only SHA-256 hashes can be used when downloading a sample | ||
String sha256Hash = getSha256Hash(hash); | ||
//Return the API's response | ||
return download(sha256Hash); | ||
} | ||
|
||
/** | ||
* Get the SHA-256 hash based upon a given MD-5, SHA-1, or SHA-256 hash. The | ||
* download API of Koodous only accepts SHA-256 hashes when queried. | ||
* | ||
* @param hash the hash to query the back-end for, and obtain the SHA-256 | ||
* hash of the sample | ||
* @return the SHA-256 hash of the given sample | ||
* @throws HttpConnectionFailed if no connection can be made from the | ||
* current machine, or to the given host | ||
* @throws SampleNotFoundException if the sample cannot be found | ||
* @throws Error404NotFoundException if the target returns a 404 status code | ||
* @throws Error429TooManyRequestsException if the target returns a 429 | ||
*/ | ||
private String getSha256Hash(String hash) throws HttpConnectionFailed, SampleNotFoundException, Error404NotFoundException, Error429TooManyRequestsException { | ||
//Create the url | ||
String url = apiBase + "?search=" + hash + "&page=%1&page_size=%100"; | ||
//Create the requested, based on the given URL and with the required header token | ||
Request request = new Request.Builder() | ||
.url(url) | ||
.addHeader("Authorization", "Token " + key) | ||
.build(); | ||
//Get the result from the get request as a string | ||
String result = new String(downloader.get(request)); | ||
//Convert the string in a JSON object | ||
JSONObject jsonObject = new JSONObject(result); | ||
//Get the count based on the search result | ||
int count = jsonObject.optInt("count", 0); | ||
if (count == 0) { | ||
//If there are no hits, the sample is not present | ||
throw new SampleNotFoundException(); | ||
} | ||
//Get the results if there are any | ||
JSONArray results = jsonObject.getJSONArray("results"); | ||
//Return the SHA-256 hash of the first hit | ||
return results.getJSONObject(0).getString("sha256"); | ||
} | ||
|
||
/** | ||
* Download the sample from the API | ||
* | ||
* @param hash the SHA-256 hash of the sample to download | ||
* @return the API's response, which is the raw file | ||
* @throws HttpConnectionFailed if no connection can be made from the | ||
* current machine, or to the given host | ||
* @throws Error404NotFoundException if the target returns a 404 status code | ||
* @throws Error429TooManyRequestsException if the target returns a 429 | ||
*/ | ||
private byte[] download(String hash) throws HttpConnectionFailed, Error404NotFoundException, Error429TooManyRequestsException { | ||
//Create the URL | ||
String url = apiBase + "/" + hash + "/download"; | ||
//Prepare the request with teh API token | ||
Request request = new Request.Builder() | ||
.url(url) | ||
.addHeader("Authorization", "Token " + key) | ||
.build(); | ||
//Get the result from the API as a string | ||
String temp = new String(downloader.get(request)); | ||
//Convert the string into a JSON object, and get the direct download URL | ||
String directUrl = new JSONObject(temp).getString("download_url"); | ||
//Reset the request to the direct download URL | ||
request = new Request.Builder() | ||
.url(directUrl) | ||
.addHeader("Authorization", "Token " + key) | ||
.build(); | ||
//Return the value of the direct download link | ||
return downloader.get(request); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright (C) 2020 Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl] | ||
* | ||
* 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 3 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, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package endpoints; | ||
|
||
import exceptions.Error404NotFoundException; | ||
import exceptions.Error429TooManyRequestsException; | ||
import exceptions.SampleNotFoundException; | ||
import exceptions.HttpConnectionFailed; | ||
import okhttp3.Request; | ||
|
||
/** | ||
* This class is used to get a sample from the MalShare database via its API. | ||
* | ||
* @author Max 'Libra' Kersten [@LibraAnalysis, https://maxkersten.nl] | ||
*/ | ||
public class MalShare extends GenericEndpoint { | ||
|
||
/** | ||
* The API key that is used to interact with the MalShare API | ||
*/ | ||
private String key; | ||
|
||
/** | ||
* Creates an object to interact with the MalShare API | ||
* | ||
* @param key the MalShare API key which is required to use the API | ||
*/ | ||
public MalShare(String key) { | ||
//Sets the apiBase variable in the abstract GenericEndpoint class | ||
super("https://malshare.com/api.php?api_key=" + key + "&action="); | ||
//Sets the key variable | ||
this.key = key; | ||
} | ||
|
||
/** | ||
* This API call will return a byte array if the sample is found. If it is | ||
* not found, a plain text response that starts with "Sample not found by | ||
* hash" is given. | ||
* | ||
* @param hash the sample's hash | ||
* @return the URL to get the sample from | ||
*/ | ||
private String getDownloadUrl(String hash) { | ||
return apiBase + "getfile&hash=" + hash; | ||
} | ||
|
||
/** | ||
* Gets the sample from MalShare, if it is present. Throws an exception if | ||
* it is not present. | ||
* | ||
* @param hash the hash to look for | ||
* @return the sample as a byte array | ||
* @throws HttpConnectionFailed if no connection can be made from the | ||
* current machine, or to the given host | ||
* @throws SampleNotFoundException if the sample cannot be found | ||
* @throws Error404NotFoundException if the target returns a 404 status code | ||
* @throws Error429TooManyRequestsException if the target returns a 429 | ||
* status code | ||
*/ | ||
public byte[] getSample(String hash) throws HttpConnectionFailed, SampleNotFoundException, Error404NotFoundException, Error429TooManyRequestsException { | ||
//Gets the URL | ||
String url = getDownloadUrl(hash); | ||
//Create the request based on the URL | ||
Request request = new Request.Builder() | ||
.url(url) | ||
.build(); | ||
//Get the result from the API | ||
byte[] result = downloader.get(request); | ||
//Convert the result into a new string to check what the result is | ||
String temp = new String(result); | ||
/** | ||
* If the sample is not present in the MalShare API, a plain text string | ||
* is returned. This string contains the "Sample not found by hash" | ||
* text. If string contains this text, the sample cannot be found. | ||
* Otherwise, the returned value is the raw file | ||
*/ | ||
if (temp.contains("Sample not found by hash")) { | ||
//If the sample cannot be found, an exception is thrown | ||
throw new SampleNotFoundException(); | ||
} | ||
//Return the sample | ||
return result; | ||
} | ||
|
||
} |
Oops, something went wrong.