-
Notifications
You must be signed in to change notification settings - Fork 33
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
0 parents
commit 8e77d48
Showing
8 changed files
with
514 additions
and
0 deletions.
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 |
---|---|---|
@@ -0,0 +1 @@ | ||
/__pycache__ |
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,17 @@ | ||
# Burp2Malleable | ||
This is a quick python utility I wrote to turn HTTP requests from burp suite into Cobalt Strike Malleable C2 profiles. | ||
As of now, it currently does not perform custom mangling such as prepend, append and encodings, but rather generates the rough profile with a default encoding of mask+base64. You are expected to add your own prepends, appends and encoding if you want to make it more convincing. I am working on ways to automate this as well. | ||
|
||
## Installation | ||
``` | ||
pip install -r requirements.txt | ||
``` | ||
## Usage | ||
``` | ||
python burp2malleable.py request.txt response.txt | ||
``` | ||
|
||
Work in progress, will be updated if I think of ideas. Feel free to submit issues/PRs/suggestions. | ||
|
||
## TODO | ||
- Detect base64 strings in original request and response and automatically use those to store beacon data |
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,259 @@ | ||
import burpee | ||
import sys | ||
import os | ||
from malleablec2 import Profile | ||
from malleablec2.components import * | ||
from termcolor import colored | ||
|
||
toolbanner = """ | ||
█▄▄ █░█ █▀█ █▀█ ▀█ █▀▄▀█ ▄▀█ █░░ █░░ █▀▀ ▄▀█ █▄▄ █░░ █▀▀ | ||
█▄█ █▄█ █▀▄ █▀▀ █▄ █░▀░█ █▀█ █▄▄ █▄▄ ██▄ █▀█ █▄█ █▄▄ ██▄ | ||
https://github.com/CodeXTF2/Burp2Malleable | ||
""" | ||
print(colored(toolbanner,"cyan")) | ||
|
||
#print functions | ||
def printsuccess(msg): | ||
print(colored(colored("[+] ","green") + msg,attrs=["bold"])) | ||
|
||
def printfail(msg): | ||
global errors | ||
print(colored("[!] ","red",attrs=['bold']) + colored(msg,"yellow",attrs=['bold'])) | ||
|
||
def printbold(msg): | ||
print(colored(msg,attrs=['bold'])) | ||
|
||
def printmsg(msg): | ||
print(colored("[*] ","cyan",attrs=["bold"]) + msg) | ||
|
||
|
||
def storelocation(item): | ||
print(colored(toolbanner,"cyan")) | ||
location = input(f"Where do you want to store {item}?\n\t1. Header\n\t2. Body\n\t3. URI-Param\n>") | ||
if location == "1": | ||
headername = input("Header name: ") | ||
return ['header',headername] | ||
elif location == "3": | ||
paramname = input("Param name: ") | ||
return ['uriparam',paramname] | ||
else: | ||
return ['body',''] | ||
|
||
#is the req body used already | ||
|
||
|
||
# fix the files | ||
reqfilename = sys.argv[1] | ||
resfilename = sys.argv[2] | ||
|
||
reqfile = open(reqfilename,"r",errors='ignore').read() | ||
resfile = open(resfilename,"r",errors='ignore').read() | ||
|
||
with open("tempreq","w+",errors='ignore') as f: | ||
f.write(reqfile.replace("\"","\'").replace("\\","\\\\")) | ||
|
||
with open("tempres","w+",errors='ignore') as f: | ||
f.write(resfile.replace("\"","\'").replace("\\","\\\\")) | ||
|
||
reqfile = open("tempreq").read() | ||
resfile = open("tempres").read() | ||
requri = reqfile.split("\n")[0].split(" ")[1].split("?")[0] | ||
|
||
try: | ||
reqparams = reqfile.split("\n")[0].split(" ")[1].split("?")[1] | ||
except: | ||
reqparams= "" | ||
reqmethod = reqfile.split("\n")[0].split(" ")[0] | ||
|
||
reqheaders, reqdata = burpee.parse_request("tempreq") | ||
resheaders, resdata = burpee.parse_request("tempres") | ||
os.remove("tempreq") | ||
os.remove("tempres") | ||
reqfile_commented = "" | ||
for x in reqfile.split("\n"): | ||
reqfile_commented += "# " + x + "\n" | ||
|
||
resfile_commented = "" | ||
for x in resfile.split("\n"): | ||
resfile_commented += "# " + x + "\n" | ||
|
||
|
||
|
||
original = "# Original HTTP request\n#\n" + reqfile_commented + "\n#" | ||
original += "\n# Original HTTP response\n#\n" + resfile_commented + "\n#" | ||
original += "#\n#\n" | ||
|
||
|
||
|
||
|
||
|
||
uri_used = False | ||
body_used = False | ||
beaconmeta = storelocation("Beacon metadata") | ||
while beaconmeta [0] == "body" and reqmethod != "POST": | ||
printfail(f"Request body may only be used in POST requests. This is a {reqmethod} request.") | ||
beaconmeta = storelocation("Beacon metadata") | ||
|
||
|
||
beaconid = storelocation("Beacon ID") | ||
while beaconid[0] == "body" and reqmethod != "POST": | ||
printfail(f"Request body may only be used in POST requests. This is a {reqmethod} request.") | ||
beaconid = storelocation("Beacon ID") | ||
while beaconid[0] == "body" and body_used: | ||
printfail(f"Request body already in use") | ||
beaconid = storelocation("Beacon ID") | ||
if beaconid[0] == "body": | ||
body_used = True | ||
|
||
|
||
|
||
beaconresponse = storelocation("Beacon response") | ||
while beaconresponse[0] == "body" and reqmethod != "POST": | ||
printfail(f"Request body may only be used in POST requests. This is a {reqmethod} request.") | ||
beaconresponse = storelocation("Beacon response") | ||
while beaconresponse[0] == "body" and body_used: | ||
printfail(f"Request body already in use") | ||
beaconresponse = storelocation("Beacon ID") | ||
if beaconid[0] == "body": | ||
body_used = True | ||
|
||
|
||
profilebanner = """ | ||
############################################################################ | ||
# Generated by Burp2Malleable - https://github.com/CodeXTF2/Burp2Malleable # | ||
# By: CodeX # | ||
############################################################################ | ||
""" | ||
|
||
|
||
#create our profile | ||
profile = Profile.from_scratch() | ||
|
||
|
||
|
||
#http.get block | ||
http_get = HttpGetBlock() | ||
http_get.set_option("verb", reqmethod) | ||
http_get.set_option("uri", requri.lower()) | ||
|
||
client_get = ClientBlock() | ||
metadata = MetadataBlock() | ||
for x in reqheaders.items(): | ||
client_get.add_statement("header", x[0], x[1]) | ||
|
||
metadata.add_statement("mask") | ||
metadata.add_statement("base64url") | ||
|
||
#metadata | ||
if beaconmeta[0] == "body": | ||
metadata.add_statement("print") | ||
printmsg(f"Storing beacon metadata in request body") | ||
elif beaconmeta[0] == "uriparam": | ||
metadata.add_statement("parameter",beaconmeta[1]) | ||
printmsg(f"Storing beacon metadata in the URI parameter {beaconmeta[1]}") | ||
else: | ||
metadata.add_statement("header",beaconmeta[1]) | ||
printmsg(f"Storing beacon metadata in request header {beaconmeta[1]}") | ||
|
||
client_get.add_code_block(metadata) | ||
server_get = ServerBlock() | ||
output_get = OutputBlock() | ||
output_get.add_statement("mask") | ||
output_get.add_statement("base64url") | ||
#beacon tasking | ||
output_get.add_statement("print") | ||
|
||
|
||
|
||
server_get.add_code_block(output_get) | ||
for x in resheaders.items(): | ||
server_get.add_statement("header", x[0], x[1]) | ||
|
||
http_get.add_code_block(client_get) | ||
http_get.add_code_block(server_get) | ||
|
||
|
||
|
||
|
||
#http.post block | ||
http_post = HttpPostBlock() | ||
http_post.set_option("verb", reqmethod) | ||
if requri == "/": | ||
requri += "/" | ||
http_post.set_option("uri", requri.upper()) | ||
|
||
|
||
#http.post.client | ||
client_post = ClientBlock() | ||
|
||
#add the output_post block for the client | ||
output_post = OutputBlock() | ||
output_post.add_statement("mask") | ||
output_post.add_statement("base64url") | ||
|
||
#beaconupload | ||
if beaconresponse[0] == "body": | ||
output_post.add_statement("print") | ||
printmsg(f"Storing beacon response in request body") | ||
elif beaconresponse[0] == "uriparam": | ||
output_post.add_statement("parameter",beaconresponse[1]) | ||
printmsg(f"Storing beacon response in the URI parameter {beaconresponse[1]}") | ||
else: | ||
output_post.add_statement("header",beaconresponse[1]) | ||
printmsg(f"Storing beacon response in request header {beaconresponse[1]}") | ||
|
||
|
||
post_id = IdBlock() | ||
post_id.add_statement("mask") | ||
post_id.add_statement("base64url") | ||
#beaconid | ||
if beaconid[0] == "body": | ||
post_id.add_statement("print") | ||
printmsg(f"Storing beacon ID in request body") | ||
elif beaconid[0] == "uriparam": | ||
post_id.add_statement("parameter",beaconid[1]) | ||
printmsg(f"Storing beacon ID in the URI Parameter {beaconid[1]}") | ||
else: | ||
post_id.add_statement("header",beaconid[1]) | ||
printmsg(f"Storing beacon ID in request header {beaconid[1]}") | ||
|
||
|
||
for x in reqheaders.items(): | ||
client_post.add_statement("header", x[0], x[1]) | ||
|
||
client_post.add_code_block(post_id) | ||
client_post.add_code_block(output_post) | ||
|
||
|
||
|
||
#http.post.server | ||
server_post = ServerBlock() | ||
|
||
#add the output_post block for the server | ||
output_post = OutputBlock() | ||
output_post.add_statement("mask") | ||
output_post.add_statement("base64url") | ||
output_post.add_statement("print") | ||
server_post.add_code_block(output_post) | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
for x in resheaders.items(): | ||
server_post.add_statement("header", x[0], x[1]) | ||
|
||
http_post.add_code_block(client_post) | ||
http_post.add_code_block(server_post) | ||
|
||
|
||
#build the profile | ||
profile.add_code_block(http_get) | ||
profile.add_code_block(http_post) | ||
|
||
with open("generated.profile","w+") as f: | ||
f.write(profilebanner + str(profile)) | ||
printsuccess("Profile saved in generated.profile. Remember to run ./c2lint!") |
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,74 @@ | ||
# https://github.com/xscorp/Burpee | ||
import requests | ||
|
||
debug = False | ||
|
||
def print_debug(*msg): | ||
if debug == True: | ||
for info in msg: | ||
print(info , end = "") | ||
|
||
def parse_request(file_name): | ||
line = "" | ||
headers = {} | ||
post_data = "" | ||
header_collection_done = False | ||
file_object = open(file_name , "r") | ||
file_object.seek(0) | ||
file_object.readline() | ||
for line in file_object.readlines(): | ||
if header_collection_done is False: | ||
if line.startswith("\n"): | ||
header_collection_done = True | ||
else: | ||
headers.update({ | ||
line[0:line.find(":")].strip() : line[line.find(":")+1 :].strip() | ||
}) | ||
else: | ||
post_data = post_data + line | ||
file_object.close() | ||
return (headers , post_data) | ||
|
||
def dump_headers(file_name): | ||
headers , post_data = parse_request(file_name) | ||
for header , value in headers.items(): | ||
print(header , ": " , value , sep = "") | ||
|
||
def dump_data(file_name): | ||
headers , post_data = parse_request(file_name) | ||
print(post_data) | ||
|
||
def get_method_and_resource(file_name): | ||
file_object = open(file_name , "r") | ||
request_line = file_object.readline() | ||
file_object.close() | ||
request_line = request_line.split(" ") | ||
method_name = request_line[0] | ||
if request_line[1].startswith("/"): | ||
resource_name = request_line[1] | ||
else: | ||
resource_name = request_line[1][request_line[1].find("/"):] | ||
return method_name , resource_name | ||
|
||
def request(file_name , https = False , proxies = None): | ||
headers , post_data = parse_request(file_name) | ||
method_name , resource_name = get_method_and_resource(file_name) | ||
protocol = "https" if (https is True) else "http" | ||
url = protocol + "://" + headers["Host"] + resource_name | ||
|
||
#debug information | ||
print_debug("DEBUG INFORMATION") | ||
print_debug("\n==========================================") | ||
print_debug("\nProtocol : " , protocol) | ||
print_debug("\n\nMethod name : " , method_name) | ||
print_debug("\n\nResource requested : " , resource_name) | ||
print_debug("\n\nPost data : " , post_data) | ||
print_debug("\n\nHeaders : " , headers) | ||
print_debug("\n\nProxies : " , proxies) | ||
print_debug("\n==========================================\n\n") | ||
|
||
if method_name.lower() == "get": | ||
response = requests.get(url = url , headers = headers , proxies = proxies , verify = False) | ||
elif method_name.lower() == "post": | ||
response = requests.post(url = url , headers = headers , data = post_data , proxies = proxies , verify = False) | ||
return response |
Oops, something went wrong.