Red Team Gamification of MITRE ATT&CK for purple teams
After falling in love with the C&C framework “sliver” , I decided to write something for the first time with Golang, as I was curious to play with his data-structures and start learning the lingo.
As a big fan of gamification, I put togheter a super-shitty, spaghetti code that basically simulates a random APT.
The tool, called ali0ssi in honour on a game of the ancitent Rome, just download the latest version of MITRE framework database and randomize some steps. It can be used to perform war games with the blue team, or just get some random idea when stuck during an assesment.
usage
Usage it’s pretty simple, just run:
./ali0ssi
and follow the instruction on screen.
code
note - TBD: soon on github
package main
import (
// "errors"
"fmt"
"math/rand"
"encoding/json"
"log"
"strings"
//"strconv"
"net/http"
"io/ioutil"
"github.com/manifoldco/promptui"
"github.com/fatih/color"
"os"
"strconv"
"reflect"
)
const baseurl = "https://raw.githubusercontent.com/mitre-attack/attack-stix-data/master/index.json"
const banner = `
_______ _ _________ _______ _______ _______ _________
( ___ )( \ \__ __/( __ )( ____ \( ____ \\__ __/
| ( ) || ( ) ( | ( ) || ( \/| ( \/ ) (
| (___) || | | | | | / || (_____ | (_____ | |
| ___ || | | | | (/ /) |(_____ )(_____ ) | |
| ( ) || | | | | / | | ) | ) | | |
| ) ( || (____/\___) (___| (__) |/\____) |/\____) |___) (___
|/ \|(_______/\_______/(_______)\_______)\_______)\_______/
"A purple team gamification (shitty) script"
v.0.1
`
func fetchAttackCollections() map[string]string {
entries := make(map[string]string)
var data map[string]interface{}
resp, err := http.Get(baseurl)
if err != nil {
fmt.Println("No response from MITRE's github")
os.Exit(1)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
//var result AttackCollections
if err := json.Unmarshal(body, &data); err != nil {
fmt.Println("Cannot unmarshal JSON from MITRE's github!")
os.Exit(1)
}
parsed := data["collections"].([]interface{})
for _, value := range parsed {
data := value.(map[string]interface{})
latestVersion := data["versions"].([]interface{})[0].(map[string]interface{})["url"]
entries[data["name"].(string)] = latestVersion.(string)
}
return entries
}
func fetchAttackPatterns(url string) map[string][]string {
entries := make(map[string][]string)
var data map[string]interface{}
resp, err := http.Get(url)
if err != nil {
fmt.Println("No response from MITRE's github")
os.Exit(1)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
//var result AttackCollections
if err := json.Unmarshal(body, &data); err != nil {
fmt.Println("Cannot unmarshal JSON from MITRE's github!")
os.Exit(1)
}
mitre_url := ""
objects := data["objects"].([]interface{})
fmt.Println(reflect.TypeOf(objects))
for _, value := range objects {
data := value.(map[string]interface{})
if data["type"] == "attack-pattern" {
reference, ok := data["external_references"].([]interface{})
if !ok {
continue
}
for _, v := range reference {
d := v.(map[string]interface{})
url, ok := d["url"].(string)
if (ok) {
if strings.Contains(url, "attack.mitre.org") {
mitre_url = d["url"].(string)
}
}
}
phase, ok := data["kill_chain_phases"].([]interface{})
if !ok {
continue
}
for _, v := range phase {
d := v.(map[string]interface{})
entryName := d["phase_name"].(string)
entry, ok := entries[entryName]
name := data["name"].(string)
if !ok {
slice := []string{}
slice = append(slice, name + " - " + mitre_url)
entries[entryName] = slice
} else {
slice := entry
slice = append(slice, name + " - " + mitre_url)
entries[entryName] = slice
}
}
//name := data["name")
}
}
return entries
}
func RemoveIndex(s []string, index int) []string {
return append(s[:index], s[index+1:]...)
}
func getRandomsAttacks(value int, dataset map[string][]string) map[string][]string {
data := make(map[string][]string)
for key := range dataset {
data[key] = []string{}
for i := 0; i < value; i++ {
randomIndex := rand.Intn(len(dataset[key]))
//get element
data[key] = append(data[key], dataset[key][randomIndex])
dataset[key] = RemoveIndex(dataset[key], randomIndex)
}
}
return data
}
func main() {
//spaghetti code
color.Magenta(banner)
log.Printf("Feching fresh datasets from MITRE github..")
collections := fetchAttackCollections()
log.Printf("Done, have fun")
names := []string{}
for key := range collections {
names = append(names, key)
}
prompt := promptui.Select{
Label: "Select ATT&CK",
Items: names,
}
prompt2 := promptui.Select{
Label: "How many elements for attack phase",
Items: []string{"1", "2", "3", "4", "5"},
}
_, result, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("You choose %q\n", result)
url := collections[result]
dataset := fetchAttackPatterns(url)
_, result, err = prompt2.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
//fmt.Println(dataset)
intVar, err := strconv.Atoi(result)
finalData := getRandomsAttacks(intVar, dataset)
color.Cyan("== PlayBook ==")
for key,value := range finalData {
color.Red(key)
for i:=0;i<len(value);i++ {
fmt.Println(value[i])
}
}
}