aql

package
v0.0.0-...-0274333 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 3, 2024 License: AGPL-3.0 Imports: 21 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	DefaultQuerySettings = QueryDefinition{
		MaxDepth: 99,
	}
)
View Source
var Keywords []string // The keyword tokens
View Source
var Lexer *lexmachine.Lexer // The lexer object. Use this to construct a Scanner
View Source
var Literals []string // The tokens representing literal strings
View Source
var (
	PredefinedQueries = []QueryDefinition{
		{
			Name:    "Who owns your AD? (Reach Domain Admin etc)",
			Query:   "ACYCLIC start:(&(dataLoader=Active Directory)(type=Group)(|(objectSid=S-1-5-32-544)(objectSid=S-1-5-21-*-512)(objectSid=S-1-5-21-*-519)))<-[]{1,10}-end:(type=Person)",
			Default: true,
		},
		{
			Name:  "Who can DCsync?",
			Query: "ACYCLIC start:(&(name=DCsync)(type=Callable-Service-Point))<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "How to reach machines that have computer accounts with unconstrained delegation (non-DCs)",
			Query: "ACYCLIC start:(tag=unconstrained)<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "What can accounts with no Kerberos preauth requirement reach? (ASREPROAST)",
			Query: "ACYCLIC start:(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=4194304)(tag=account_active))-[]{1,10}->end:()",
		},
		{
			Name:  "Who can pwn your AD by sideloading a custom DLL on your DC? (Old DCs only)",
			Query: "ACYCLIC start:(distinguishedname=CN=MicrosoftDNS,CN=System,DC=*)<-[]{1,15}-end:(type=Person)",
		},
		{
			Name:  "Who can dump SAM/SYSTEM or your ntds.dit remotely or via RDP? (Server and Backup Operators)",
			Query: "ACYCLIC start:(&(dataLoader=Active Directory)(|(objectSid=S-1-5-32-551)(objectSid=S-1-5-32-549)))<-[]{1,10}-end:()",
		},
		{
			Name:  "Enroll in ESC1 vulnerable certificate templates (client auth + pose as anyone)",
			Query: "ACYCLIC start:(&(type=PKI-Certificate-Template)(msPKI-Certificate-Name-Flag:and:=1)(|(pKIExtendedKeyUsage=1.3.6.1.5.5.7.3.2)(pKIExtendedKeyUsage=1.3.5.1.5.2.3.4)(pKIExtendedKeyUsage=1.3.6.1.4.1.311.20.2.2)(pKIExtendedKeyUsage=2.5.29.37.0)(pKIExtendedKeyUsage:count:=0)))<-[CertificateEnroll]-()-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "Enroll in ESC15 vulnerable certificate templates (v1 + pose as anyone)",
			Query: "ACYCLIC start:(&(type=PKI-Certificate-Template)(msPKI-Certificate-Name-Flag:and:=1)(msPKI-Template-Schema-Version=1))<-[CertificateEnroll]-()-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "What can Domain Users, Authenticated Users and Everyone do?",
			Query: "ACYCLIC start:(&(dataLoader=Active Directory)(|(objectSid=S-1-5-21-*-513)(objectSid=S-1-5-11)(objectSid=S-1-1-0)))-[]{1,10}->end:()",
		},
		{
			Name:  "Who can dump a virtual DC? (hypervisor/SAN sounding groups)",
			Query: "ACYCLIC start:(&(dataLoader=Active Directory)(type=Group)(|(name=*vcenter*)(name=*vmware*)(name=*esxi*)(name=*vsan*)(name=*simplivity*)))<-[]{1,10}-end:()",
		},
		{
			Name:  "Who can wipe or access your backups? (backup sounding groups)",
			Query: "ACYCLIC start:(&(dataLoader=Active Directory)(type=Group)(|(name=*backup*)(name=*veeam*)(name=*tsm*)(name=*tivoli storage*)(name=*rubrik*)(name=*commvault*)))<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "Who can change GPOs?",
			Query: "ACYCLIC start:(&(dataLoader=Active Directory)(type=Group-Policy-Container))<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "What can users not required to have a password reach?",
			Query: "ACYCLIC start:(&(dataLoader=Active Directory)(type=Person)(userAccountControl:1.2.840.113556.1.4.803:=32))-[]{1,10}->end:()",
		},
		{
			Name:  "What can users that can't change password reach?",
			Query: "ACYCLIC start:(&(type=Person)(userAccountControl:1.2.840.113556.1.4.803:=64))-[]{1,10}->end:()",
		},
		{
			Name:  "What can users with never expiring passwords reach?",
			Query: "ACYCLIC start:(&(type=Person)(userAccountControl:1.2.840.113556.1.4.803:=65536))-[]{1,10}->end:()",
		},
		{
			Name:  "What can accounts that have a password older than 5 years reach?",
			Query: "ACYCLIC start:(&(objectClass=Person)(!(pwdLastSet=0))(pwdLastSet:since:<-5Y)(!(userAccountControl:and:=2)))-[]{1,10}->end:()",
		},
		{
			Name:  "What can accounts that have never set a password reach?",
			Query: "ACYCLIC start:(&(dataLoader=Active Directory)(objectClass=Person)(pwdLastSet=0)(|(logonCount=0)(!(logonCount=*)))(!(userAccountControl:and:=2)))-[]{1,10}->end:()",
		},
		{
			Name:  "Who can control Protected Users?",
			Query: "ACYCLIC start:(&(type=Group)(distinguishedName=CN=Protected Users,*))<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "What can kerberoastable user accounts reach?",
			Query: "ACYCLIC start:(&(type=Person)(servicePrincipalName=*)(tag=account_active))<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "What can large groups (more than 100 members) reach?",
			Query: "ACYCLIC start:(&(type=Group)(member:count:>100))-[]{1,10}->end:()",
		},
		{
			Name:  "Who can reach Domain Controllers?",
			Query: "ACYCLIC start:(&(type=Machine)(out=MachineAccount,(&(type=Computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))))<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "Who can reach Read-Only Domain Controllers? (RODC)",
			Query: "ACYCLIC start:(&(type=Machine)(out=MachineAccount,(&(type=Computer)(primaryGroupId=521))))<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "Who can reach computers with unconstrained delegation (non DCs)?",
			Query: "ACYCLIC start:(&(type=Computer)(userAccountControl:1.2.840.113556.1.4.803:=524288)(!userAccountControl:1.2.840.113556.1.4.803:=8192))<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "Who can reach computers with constrained delegation (non DCs)?",
			Query: "ACYCLIC start:(&(objectCategory=computer)(msds-allowedtodelegateto=*)(!userAccountControl:1.2.840.113556.1.4.803:=8192))<-[]{1,10}-end:(type=Person)",
		},
		{
			Name:  "Users that are members of more than 25 groups",
			Query: "ACYCLIC start:(&(type=Person)(memberOf:count:>10))",
		},
		{
			Name:  "Give me 100 random machines",
			Query: "ACYCLIC start:(&(type=Machine)(out=MachineAccount,(userAccountControl:1.2.840.113556.1.4.803:=4096))) LIMIT 100",
		},
	}
)
View Source
var StaticLexers = map[string]TokenID{
	"\\(": LParan,
	"\\)": RParan,

	"\\[": LBracket,
	"\\]": RBracket,

	"\\{": LBrace,
	"\\}": RBrace,

	"\\~":    Tilde,
	"\\=":    Equals,
	"\\<":    LessThan,
	"\\<\\=": LessThanEquals,
	"\\>":    GreaterThan,
	"\\>\\=": GreaterThanEquals,

	"\\&": BinaryAnd,
	"\\|": BinaryOr,
	"\\^": BinaryNot,

	"AND": And,
	"OR":  Or,
	"NOT": Not,

	"TRUE":  True,
	"FALSE": False,

	"\\*":    Star,
	"\\/":    Slash,
	"\\!":    Exclamation,
	"\\.":    Dot,
	"\\.\\.": Dotdot,
	"\\,":    Comma,

	"\\:":    Colon,
	"\\-":    EdgeAnyDirection,
	"\\-\\>": EdgeOut,
	"\\<\\-": EdgeIn,

	"MATCH":    Match,
	"IS":       Is,
	"WHERE":    Where,
	"SKIP":     Skip,
	"OFFSET":   Offset,
	"LIMIT":    Limit,
	"ORDER BY": OrderBy,
	"DESC":     Desc,
	"UNION":    Union,

	`//[^\n]*\n?`: Comment,
	`/\*([^*]|\r|\n|(\*+([^*/]|\r|\n)))*\*+/`: Comment,
	`([a-zA-Z]|_)([a-zA-Z0-9]|_|-)*`:          Identifier,
	`\\#([a-zA-Z]|_)([a-zA-Z0-9]|_)+`:         HashIdentifier,
	`\\@([a-zA-Z]|_)([a-zA-Z0-9]|_)+`:         AtIdentifier,

	`( |\t|\n|\r)+`: Whitespace,
}
View Source
var TokenIds map[string]int // A map from the token names to their int ids
View Source
var Tokens []string // All of the tokens (including literals and keywords)

Functions

func GetComparator

func GetComparator(ts *TokenStream) (query.ComparatorType, error)

func ParseObjectTypeStrings

func ParseObjectTypeStrings(typeslice []string) (map[engine.ObjectType]struct{}, error)

func ResolveWithOptions

func ResolveWithOptions(resolver AQLresolver, opts ResolverOptions) (*graph.Graph[*engine.Object, engine.EdgeBitmap], error)

func TokenIDStrings

func TokenIDStrings() []string

TokenIDStrings returns a slice of all String values of the enum

Types

type AQLquery

type AQLquery struct {
	Sources []NodeQuery // count is n

	Next               []EdgeSearcher // count is n-1
	Mode               QueryMode
	Shortest           bool
	OverAllProbability engine.Probability
	// contains filtered or unexported fields
}

func (AQLquery) Resolve

type AQLqueryUnion

type AQLqueryUnion struct {
	// contains filtered or unexported fields
}

Union of multiple queries

func (AQLqueryUnion) Resolve

type AQLresolver

type AQLresolver interface {
	Resolve(ResolverOptions) (*graph.Graph[*engine.Object, engine.EdgeBitmap], error)
}

func ParseAQLQuery

func ParseAQLQuery(s string, ao *engine.Objects) (AQLresolver, error)

type EdgeMatcher

type EdgeMatcher struct {
	Bitmap      engine.EdgeBitmap
	Count       int64 // minimum number of edges to match
	Comparator  query.ComparatorType
	NoTrimEdges bool // don't trim edges to just the filter
}

type EdgeSearcher

type EdgeSearcher struct {
	PathNodeRequirement *NodeQuery // Nodes passed along the way must fulfill this filter

	FilterEdges                  EdgeMatcher // match any of these
	Direction                    engine.EdgeDirection
	MinIterations, MaxIterations int // there should be between min and max iterations in the chain
	ProbabilityValue             engine.Probability
	ProbabilityComparator        query.ComparatorType
	// contains filtered or unexported fields
}

type FirstLimiter

type FirstLimiter int

func (FirstLimiter) Limit

type IndexLookup

type IndexLookup struct {
	// contains filtered or unexported fields
}

type NodeLimiter

type NodeLimiter interface {
	Limit(engine.ObjectSlice) engine.ObjectSlice
}

type NodeMatcher

type NodeMatcher interface {
	Match(o *engine.Object) bool
}

type NodeQuery

type NodeQuery struct {
	IndexLookup   IndexLookup      // Possible start of search, quickly narrows it down
	Selector      query.NodeFilter // Where style boolean approval filter for objects
	OrderBy       NodeSorter       // Sorting
	ReferenceName string           // For cross result reference
	Skip          int              // Skipping
	Limit         int              // Limiting
}

func (NodeQuery) Populate

func (nq NodeQuery) Populate(ao *engine.Objects) *engine.Objects

type NodeSorter

type NodeSorter interface {
	Sort(engine.ObjectSlice) engine.ObjectSlice
}

type NodeSorterImpl

type NodeSorterImpl struct {
	Attr       engine.Attribute
	Descending bool
}

func (NodeSorterImpl) Sort

type QueryDefinition

type QueryDefinition struct {
	Name  string `json:"name"`
	Query string `json:"query,omitempty"`

	MaxDepth                  int                `json:"max_depth,omitempty"`
	MaxOutgoingConnections    int                `json:"max_outgoing_connections,omitempty"`
	Default                   bool               `json:"default,omitempty"`
	MinAccumulatedProbability engine.Probability `json:"min_accumulated_probability,omitempty"`

	UserDefined bool `json:"user_defined,omitempty"`
}

Can return built in queries and user defined persisted queries

func DefaultQueryDefinition

func DefaultQueryDefinition() QueryDefinition

func ParseQueryDefinitionFromPOST

func ParseQueryDefinitionFromPOST(ctx *gin.Context) (QueryDefinition, error)

func (QueryDefinition) ID

func (q QueryDefinition) ID() string

func (QueryDefinition) Resolver

func (qd QueryDefinition) Resolver(ao *engine.Objects) (AQLresolver, error)

Compiles the query and makes it a resolver

type QueryMode

type QueryMode int
const (
	Walk    QueryMode = iota // No Homomorphism
	Trail                    // Edge homomorphism (unique edges)
	Acyclic                  // Node homomorphism (unique nodes)
	Simple                   // Partial node-isomorphism
)

type ResolverOptions

type ResolverOptions struct {
	MaxDepth                  int                `json:"max_depth,omitempty"`
	MaxOutgoingConnections    int                `json:"max_outgoing_connections,omitempty"`
	MinEdgeProbability        engine.Probability `json:"min_edge_probability,omitempty"`
	MinAccumulatedProbability engine.Probability `json:"min_accumulated_probability,omitempty"`
	PruneIslands              bool               `json:"prune_islands,omitempty"`
	NodeLimit                 int                `json:"nodelimit,omitempty"`
}

func NewResolverOptions

func NewResolverOptions() ResolverOptions

type SkipLimiter

type SkipLimiter int

func (SkipLimiter) Limit

type Token

type Token struct {
	Native   any
	Value    string
	Position machines.Match
	Type     TokenID
}

func (Token) Is

func (t Token) Is(id TokenID) bool

func (Token) String

func (t Token) String() string

type TokenID

type TokenID int
const (
	Invalid TokenID = iota

	Star
	Slash
	Exclamation

	Dot
	Dotdot
	Comma
	Colon
	Equals
	Tilde
	LessThan
	LessThanEquals
	GreaterThan
	GreaterThanEquals

	And
	Or
	Xor
	Not

	BinaryAnd
	BinaryOr
	BinaryNot

	LParan // (
	RParan // )

	LBracket // [
	RBracket // ]

	LBrace // {
	RBrace // }

	EdgeAnyDirection // -
	EdgeIn           // <-
	EdgeOut          // ->

	Is
	Match
	Where
	Skip
	Offset
	Limit
	OrderBy
	Desc
	Union

	True
	False

	Literal
	Keyword

	Whitespace

	Integer
	Float

	UnquotedLDAPString
	QuotedString // Quoted string

	Identifier
	HashIdentifier
	AtIdentifier

	Comment

	MAXTOKEN = Comment
)

func TokenIDString

func TokenIDString(s string) (TokenID, error)

TokenIDString retrieves an enum value from the enum constants string name. Throws an error if the param is not part of the enum.

func TokenIDValues

func TokenIDValues() []TokenID

TokenIDValues returns all values of the enum

func (TokenID) IsATokenID

func (i TokenID) IsATokenID() bool

IsATokenID returns "true" if the value is listed in the enum definition. "false" otherwise

func (TokenID) String

func (i TokenID) String() string

type TokenStream

type TokenStream struct {
	// contains filtered or unexported fields
}

func Parse

func Parse(input string) (*TokenStream, error)

func (*TokenStream) EOF

func (ts *TokenStream) EOF() bool

func (*TokenStream) Next

func (ts *TokenStream) Next() bool

func (*TokenStream) NextIfIs

func (ts *TokenStream) NextIfIs(id TokenID) bool

func (*TokenStream) PeekNextRawToken

func (ts *TokenStream) PeekNextRawToken() Token

func (*TokenStream) PeekNextToken

func (ts *TokenStream) PeekNextToken() Token

func (*TokenStream) Prev

func (ts *TokenStream) Prev() bool

func (*TokenStream) SnarfTextUntil

func (ts *TokenStream) SnarfTextUntil(id TokenID) string

func (*TokenStream) Token

func (ts *TokenStream) Token() Token

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL