Skip to content

Commit

Permalink
Added the ability to run javascript within VQL (Velocidex#118)
Browse files Browse the repository at this point in the history
Implemented currently is calling JS from VQL. Still to do calling VQL
from JS.
  • Loading branch information
scudette authored Oct 9, 2019
1 parent 83ede96 commit 8fba241
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 35 deletions.
6 changes: 3 additions & 3 deletions artifacts/assets/ab0x.go

Large diffs are not rendered by default.

56 changes: 29 additions & 27 deletions artifacts/definitions/Windows/EventLogs/Kerbroasting.yaml
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
name: Windows.EventLogs.Kerbroasting
description: |
This Artifact will return all successful Kerberos TGS Ticket events for
Service Accounts (SPN attribute) implemented with weak encryption. These
tickets are vulnerable to brute force attack and this event is an indicator
of a Kerbroasting attack.
**ATT&CK**: [T1208 - Kerbroasting](https://attack.mitre.org/techniques/T1208/)
Typical attacker methodology is to firstly request accounts in the domain
with SPN attributes, then request an insecure TGS ticket for brute forcing.
This attack is particularly effective as any domain credentials can be used
to implement the attack and service accounts often have elevated privileges.
Kerbroasting can be used for privilege escalation or persistence by adding a
SPN attribute to an unexpected account.
**Reference**: [The Art of Detecting Kerberoast Attacks](https://www.trustedsec.com/2018/05/art_of_kerberoast/)
**Log Source**: Windows Security Event Log (Domain Controllers)
**Event ID**: 4769
**Status**: 0x0 (Audit Success)
**Ticket Encryption**: 0x17 (RC4)
**Service Name**: NOT krbtgt or NOT a system account (account name ends in $)
**TargetUserName**: NOT a system account (*$@*)
Monitor and alert on unusual events with these conditions from an unexpected
IP.
Note: There are potential false positives so whitelist normal source IPs and
This Artifact will return all successful Kerberos TGS Ticket events for
Service Accounts (SPN attribute) implemented with weak encryption. These
tickets are vulnerable to brute force attack and this event is an indicator
of a Kerbroasting attack.
**ATT&CK**: [T1208 - Kerbroasting](https://attack.mitre.org/techniques/T1208/)
Typical attacker methodology is to firstly request accounts in the domain
with SPN attributes, then request an insecure TGS ticket for brute forcing.
This attack is particularly effective as any domain credentials can be used
to implement the attack and service accounts often have elevated privileges.
Kerbroasting can be used for privilege escalation or persistence by adding a
SPN attribute to an unexpected account.
**Reference**: [The Art of Detecting Kerberoast Attacks](https://www.trustedsec.com/2018/05/art_of_kerberoast/)
**Log Source**: Windows Security Event Log (Domain Controllers)
**Event ID**: 4769
**Status**: 0x0 (Audit Success)
**Ticket Encryption**: 0x17 (RC4)
**Service Name**: NOT krbtgt or NOT a system account (account name ends in $)
**TargetUserName**: NOT a system account (*$@*)
Monitor and alert on unusual events with these conditions from an unexpected
IP.
Note: There are potential false positives so whitelist normal source IPs and
manage risk of insecure ticket generation.
Expand All @@ -34,6 +34,8 @@ author: Matt Green - @mgreen27
parameters:
- name: eventLog
default: C:\Windows\system32\winevt\logs\Security.evtx
- name: accessor
default: file

sources:
- name: Kerbroasting
Expand Down Expand Up @@ -62,10 +64,10 @@ sources:
SELECT *
FROM parse_evtx(filename=FullPath)
WHERE System.EventID.Value = 4769
AND EventData.TicketEncryptionType = 23
AND EventData.TicketEncryptionType = 23
AND EventData.Status = 0
AND NOT EventData.ServiceName =~ "krbtgt|\\$$"
AND NOT EventData.TargetUserName =~ "\\$@"
AND NOT EventData.TargetUserName =~ "\\$@"
})
reports:
Expand Down
5 changes: 3 additions & 2 deletions artifacts/testdata/server/testcases/binary_blobs.in.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
Queries:
- SELECT * FROM switch(
b={SELECT Complete FROM execve(argv=["rm", "-f", "/tmp/autorunsc64.exe"])
b={SELECT Complete FROM execve(argv=["rm", "-f", "/tmp/autorunsc64.exe",
"/tmp/winpmem_v3.3.rc2.exe"])
WHERE Complete},
a={SELECT Complete FROM execve(argv=[
"cmd.exe", "/c", "del /Q C:\\tmp\\autorunsc64.exe"])
"cmd.exe", "/c", "del /Q C:\\tmp\\autorunsc64.exe C:\\tmp\\winpmem_v3.3.rc2.exe"])
WHERE Complete})

# First time we retrive the tool we should bring it from the server.
Expand Down
2 changes: 1 addition & 1 deletion artifacts/testdata/server/testcases/binary_blobs.out.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SELECT * FROM switch( b={SELECT Complete FROM execve(argv=["rm", "-f", "/tmp/autorunsc64.exe"]) WHERE Complete}, a={SELECT Complete FROM execve(argv=[ "cmd.exe", "/c", "del /Q C:\\tmp\\autorunsc64.exe"]) WHERE Complete})[
SELECT * FROM switch( b={SELECT Complete FROM execve(argv=["rm", "-f", "/tmp/autorunsc64.exe", "/tmp/winpmem_v3.3.rc2.exe"]) WHERE Complete}, a={SELECT Complete FROM execve(argv=[ "cmd.exe", "/c", "del /Q C:\\tmp\\autorunsc64.exe C:\\tmp\\winpmem_v3.3.rc2.exe"]) WHERE Complete})[
{
"Complete": true
}
Expand Down
3 changes: 3 additions & 0 deletions artifacts/testdata/server/testcases/js.in.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Queries:
- LET _ <= SELECT js(js='function foo(x, y) { return (1+x+y) }') FROM scope()
- SELECT js_call(func='foo', args=[5, 6]) FROM scope()
5 changes: 5 additions & 0 deletions artifacts/testdata/server/testcases/js.out.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LET _ <= SELECT js(js='function foo(x, y) { return (1+x+y) }') FROM scope()[]SELECT js_call(func='foo', args=[5, 6]) FROM scope()[
{
"js_call(func='foo', args= [5, 6])": 12
}
]
4 changes: 2 additions & 2 deletions config/ab0x.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ require (
github.com/prometheus/client_golang v1.1.0
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 // indirect
github.com/sebdah/goldie v0.0.0-20190531093107-d313ffb52c77
github.com/sergi/go-diff v1.0.0
Expand All @@ -87,6 +88,7 @@ require (
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737
gopkg.in/russross/blackfriday.v2 v2.0.1
gopkg.in/sourcemap.v1 v1.0.5 // indirect
gopkg.in/yaml.v2 v2.2.4 // indirect
www.velocidex.com/golang/evtx v0.0.0-20191004145223-59043b7b3e15
www.velocidex.com/golang/go-ntfs v0.0.0-20191007011621-167c7fa020db
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURm
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d h1:1VUlQbCfkoSGv7qP7Y+ro3ap1P1pPZxgdGVqiTVy5C4=
github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 h1:J4AOUcOh/t1XbQcJfkEqhzgvMJ2tDxdCVvmHxW5QXao=
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM=
Expand Down Expand Up @@ -445,6 +447,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737 h1:NvePS/smRcFQ4bMtTddFtknbGCtoBkJxGmpSpVRafCc=
gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
Expand Down
148 changes: 148 additions & 0 deletions vql/tools/js.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package tools

import (
"context"
"errors"
"reflect"
"runtime/debug"

"github.com/robertkrimen/otto"
_ "github.com/robertkrimen/otto/underscore"
vql_subsystem "www.velocidex.com/golang/velociraptor/vql"
"www.velocidex.com/golang/vfilter"
)

var halt = errors.New("Halt")

type JSCompileArgs struct {
JS string `vfilter:"required,field=js,doc=The body of the javascript code."`
Key string `vfilter:"optional,field=key,doc=If set use this key to cache the JS VM."`
}

func logIfPanic(scope *vfilter.Scope) {
err := recover()
if err == halt {
return
}

if err != nil {
scope.Log("PANIC %v: %v\n", err, string(debug.Stack()))
}
}

type JSCompile struct{}

func getVM(ctx context.Context,
scope *vfilter.Scope,
key string) *otto.Otto {
if key == "" {
key = "__jscontext"
}

vm, ok := vql_subsystem.CacheGet(scope, key).(*otto.Otto)
if !ok {
vm = otto.New()
vm.Interrupt = make(chan func(), 1)
go func() {
<-ctx.Done()
vm.Interrupt <- func() {
panic(halt)
}
}()
vql_subsystem.CacheSet(scope, key, vm)
}

return vm
}

func (self *JSCompile) Call(ctx context.Context,
scope *vfilter.Scope,
args *vfilter.Dict) vfilter.Any {
arg := &JSCompileArgs{}
err := vfilter.ExtractArgs(scope, args, arg)
if err != nil {
scope.Log("js: %s", err.Error())
return vfilter.Null{}
}

defer logIfPanic(scope)

vm := getVM(ctx, scope, arg.Key)
_, err = vm.Run(arg.JS)
if err != nil {
scope.Log("js: %s", err.Error())
return vfilter.Null{}
}

return vfilter.Null{}
}

func (self JSCompile) Info(scope *vfilter.Scope,
type_map *vfilter.TypeMap) *vfilter.FunctionInfo {
return &vfilter.FunctionInfo{
Name: "js",
Doc: "Compile and run javascript code.",
ArgType: type_map.AddType(scope, &JSCompileArgs{}),
}
}

type JSCallArgs struct {
Func string `vfilter:"required,field=func,doc=JS function to call."`
Args vfilter.Any `vfilter:"required,field=args,doc=Positional args for the function."`
Key string `vfilter:"optional,field=key,doc=If set use this key to cache the JS VM."`
}

type JSCall struct{}

func (self *JSCall) Call(ctx context.Context,
scope *vfilter.Scope,
args *vfilter.Dict) vfilter.Any {
arg := &JSCallArgs{}
err := vfilter.ExtractArgs(scope, args, arg)
if err != nil {
scope.Log("js_call: %s", err.Error())
return vfilter.Null{}
}

defer logIfPanic(scope)

var call_args []interface{}
slice := reflect.ValueOf(arg.Args)

// A slice of strings.
if slice.Type().Kind() != reflect.Slice {
call_args = append(call_args, arg.Args)
} else {
for i := 0; i < slice.Len(); i++ {
value := slice.Index(i).Interface()
call_args = append(call_args, value)
}
}

vm := getVM(ctx, scope, arg.Key)
value, err := vm.Call(arg.Func, nil, call_args...)
if err != nil {
scope.Log("js_call: %s", err.Error())
return vfilter.Null{}
}

result, _ := value.Export()
if result == nil {
result = vfilter.Null{}
}
return result
}

func (self JSCall) Info(scope *vfilter.Scope,
type_map *vfilter.TypeMap) *vfilter.FunctionInfo {
return &vfilter.FunctionInfo{
Name: "js_call",
Doc: "Compile and run javascript code.",
ArgType: type_map.AddType(scope, &JSCallArgs{}),
}
}

func init() {
vql_subsystem.RegisterFunction(&JSCall{})
vql_subsystem.RegisterFunction(&JSCompile{})
}

0 comments on commit 8fba241

Please sign in to comment.