Skip to content

Commit 165ef71

Browse files
Merge pull request #52 from step-security/process-tree
Add process tree
2 parents 97b3f2e + 9d9430e commit 165ef71

File tree

9 files changed

+108
-34
lines changed

9 files changed

+108
-34
lines changed

apiclient.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type DNSRecord struct {
1717
type Tool struct {
1818
Name string `json:"name"`
1919
SHA256 string `json:"sha256"`
20+
Parent *Tool `json:"parent"`
2021
}
2122

2223
type FileEvent struct {
@@ -59,28 +60,28 @@ func (apiclient *ApiClient) sendDNSRecord(correlationId, repo, domainName, ipAdd
5960
return apiclient.sendApiRequest("POST", url, dnsRecord)
6061
}
6162

62-
func (apiclient *ApiClient) sendNetConnection(correlationId, repo, ipAddress, port, status string, timestamp time.Time, tool, toolChecksum string) error {
63+
func (apiclient *ApiClient) sendNetConnection(correlationId, repo, ipAddress, port, status string, timestamp time.Time, tool Tool) error {
6364

6465
networkConnection := &NetworkConnection{}
6566

6667
networkConnection.IPAddress = ipAddress
6768
networkConnection.Port = port
6869
networkConnection.Status = status
6970
networkConnection.TimeStamp = timestamp
70-
networkConnection.Tool = Tool{Name: tool, SHA256: toolChecksum}
71+
networkConnection.Tool = tool
7172

7273
url := fmt.Sprintf("%s/github/%s/actions/jobs/%s/networkconnection", apiclient.APIURL, repo, correlationId)
7374

7475
return apiclient.sendApiRequest("POST", url, networkConnection)
7576
}
7677

77-
func (apiclient *ApiClient) sendFileEvent(correlationId, repo, fileType string, timestamp time.Time, tool, toolChecksum string) error {
78+
func (apiclient *ApiClient) sendFileEvent(correlationId, repo, fileType string, timestamp time.Time, tool Tool) error {
7879

7980
fileEvent := &FileEvent{}
8081

8182
fileEvent.FileType = fileType
8283
fileEvent.TimeStamp = timestamp
83-
fileEvent.Tool = Tool{Name: tool, SHA256: toolChecksum}
84+
fileEvent.Tool = tool
8485

8586
url := fmt.Sprintf("%s/github/%s/actions/jobs/%s/fileevent", apiclient.APIURL, repo, correlationId)
8687

apiclient_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ func Test_sendNetConnection(t *testing.T) {
8181
port string
8282
status string
8383
timestamp time.Time
84-
tool string
85-
toolChecksum string
84+
tool Tool
8685
}
8786

8887
apiclient := &ApiClient{Client: &http.Client{}, APIURL: agentApiBaseUrl}
@@ -102,7 +101,8 @@ func Test_sendNetConnection(t *testing.T) {
102101
}
103102
for _, tt := range tests {
104103
t.Run(tt.name, func(t *testing.T) {
105-
if err := apiclient.sendNetConnection(tt.args.correlationId, tt.args.repo, tt.args.ipAddress, tt.args.port, tt.args.status, tt.args.timestamp, tt.args.tool, tt.args.toolChecksum); (err != nil) != tt.wantErr {
104+
if err := apiclient.sendNetConnection(tt.args.correlationId, tt.args.repo, tt.args.ipAddress,
105+
tt.args.port, tt.args.status, tt.args.timestamp, tt.args.tool); (err != nil) != tt.wantErr {
106106
t.Errorf("sendNetConnection() error = %v, wantErr %v", err, tt.wantErr)
107107
}
108108
})

eventhandler.go

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type EventHandler struct {
2626
ProcessMap map[string]*Process
2727
netMutex sync.RWMutex
2828
fileMutex sync.RWMutex
29+
procMutex sync.RWMutex
2930
}
3031

3132
var classAPrivateSubnet, classBPrivateSubnet, classCPrivateSubnet, loopBackSubnet *net.IPNet
@@ -56,18 +57,27 @@ func (eventHandler *EventHandler) handleFileEvent(event *Event) {
5657
}
5758

5859
if fileType != "" {
59-
toolChecksum, _ := getProgramChecksum(event.Exe)
60-
exe := filepath.Base(event.Exe)
61-
eventHandler.ApiClient.sendFileEvent(eventHandler.CorrelationId, eventHandler.Repo, fileType, event.Timestamp, exe, toolChecksum)
60+
tool := *eventHandler.GetToolChain(event.PPid, event.Exe)
61+
eventHandler.ApiClient.sendFileEvent(eventHandler.CorrelationId, eventHandler.Repo, fileType, event.Timestamp, tool)
6262
eventHandler.ProcessFileMap[event.Pid] = true
6363
}
6464
}
6565

6666
eventHandler.fileMutex.Unlock()
6767
}
68-
func (eventHandler *EventHandler) handleProcessEvent() {
6968

69+
func (eventHandler *EventHandler) handleProcessEvent(event *Event) {
70+
eventHandler.procMutex.Lock()
71+
72+
_, found := eventHandler.ProcessMap[event.Pid]
73+
74+
if !found {
75+
eventHandler.ProcessMap[event.Pid] = &Process{PID: event.Pid, PPid: event.PPid, Exe: event.Exe, Arguments: event.ProcessArguments}
76+
}
77+
78+
eventHandler.procMutex.Unlock()
7079
}
80+
7181
func (eventHandler *EventHandler) handleNetworkEvent(event *Event) {
7282
eventHandler.netMutex.Lock()
7383

@@ -82,23 +92,18 @@ func (eventHandler *EventHandler) handleNetworkEvent(event *Event) {
8292

8393
if !found {
8494
//writeLog(fmt.Sprintf("handleNetworkEvent %v", event))
95+
tool := Tool{}
8596
image := GetContainerByPid(event.Pid)
86-
checksum := ""
87-
exe := ""
8897
if image == "" {
89-
9098
if event.Exe != "" {
91-
checksum, _ = getProgramChecksum(event.Exe)
92-
99+
tool = *eventHandler.GetToolChain(event.PPid, event.Exe)
93100
}
94-
exe = filepath.Base(event.Exe)
101+
95102
} else {
96-
event.Exe = image
97-
checksum = image
98-
exe = image
103+
tool = Tool{Name: image, SHA256: image} // TODO: Set container image checksum
99104
}
100105

101-
eventHandler.ApiClient.sendNetConnection(eventHandler.CorrelationId, eventHandler.Repo, event.IPAddress, event.Port, "", event.Timestamp, exe, checksum)
106+
eventHandler.ApiClient.sendNetConnection(eventHandler.CorrelationId, eventHandler.Repo, event.IPAddress, event.Port, "", event.Timestamp, tool)
102107
eventHandler.ProcessConnectionMap[cacheKey] = true
103108
}
104109
}
@@ -113,7 +118,7 @@ func (eventHandler *EventHandler) HandleEvent(event *Event) {
113118
case fileMonitorTag:
114119
eventHandler.handleFileEvent(event)
115120
case processMonitorTag:
116-
eventHandler.handleProcessEvent()
121+
eventHandler.handleProcessEvent(event)
117122
}
118123
}
119124

@@ -160,6 +165,34 @@ func getProgramChecksum(path string) (string, error) {
160165
return fmt.Sprintf("%x", h.Sum(nil)), nil
161166
}
162167

168+
func (eventHandler *EventHandler) GetToolChain(ppid, exe string) *Tool {
169+
checksum, _ := getProgramChecksum(exe)
170+
tool := Tool{Name: filepath.Base(exe), SHA256: checksum}
171+
172+
// In some cases the process has already exited, so get from map first
173+
parentProcess, found := eventHandler.ProcessMap[ppid]
174+
175+
if found {
176+
tool.Parent = eventHandler.GetToolChain(parentProcess.PPid, parentProcess.Exe)
177+
return &tool
178+
}
179+
180+
// If not in map, may be long running, so get from OS
181+
parentProcessId, err := getParentProcessId(ppid)
182+
if err != nil {
183+
return &tool
184+
}
185+
186+
path, err := getProcessExe(ppid)
187+
if err != nil {
188+
return &tool
189+
}
190+
191+
tool.Parent = eventHandler.GetToolChain(fmt.Sprintf("%d", parentProcessId), path)
192+
193+
return &tool
194+
}
195+
163196
func isPrivateIPAddress(ipAddress string) bool {
164197
if classAPrivateSubnet == nil {
165198
_, classAPrivateSubnet, _ = net.ParseCIDR(classAPrivateAddressRange)

go.mod

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ require (
3636
github.com/docker/docker v20.10.9+incompatible
3737
github.com/google/go-cmp v0.5.6 // indirect
3838
github.com/mdlayher/netlink v1.1.0 // indirect
39-
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
4039
golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect
41-
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
4240
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
4341
)

go.sum

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,6 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE
431431
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
432432
github.com/mdlayher/netlink v1.1.0 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkfg=
433433
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
434-
github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg=
435-
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
436434
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
437435
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
438436
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
@@ -639,14 +637,11 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
639637
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
640638
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
641639
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
642-
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
643640
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
644641
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
645642
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
646643
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
647644
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
648-
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
649-
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
650645
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
651646
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
652647
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -697,7 +692,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
697692
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
698693
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
699694
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
700-
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
701695
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
702696
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
703697
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -757,8 +751,6 @@ golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7w
757751
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
758752
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
759753
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
760-
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
761-
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
762754
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
763755
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
764756
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -833,7 +825,6 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
833825
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
834826
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
835827
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
836-
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
837828
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
838829
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
839830
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

netmon.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"github.com/pkg/errors"
1111
)
1212

13+
const Unknown = "Unknown"
14+
1315
type NetworkMonitor struct {
1416
CorrelationId string
1517
Repo string
@@ -88,7 +90,7 @@ func (netMonitor *NetworkMonitor) handlePacket(attrs nflog.Attribute) {
8890

8991
if isSYN {
9092
netMonitor.ApiClient.sendNetConnection(netMonitor.CorrelationId, netMonitor.Repo,
91-
ipv4.DstIP.String(), port, netMonitor.Status, timestamp, "Unknown", "Unknown")
93+
ipv4.DstIP.String(), port, netMonitor.Status, timestamp, Tool{Name: Unknown, SHA256: Unknown})
9294
}
9395
}
9496

procmon.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type ProcessMonitor struct {
2424

2525
type Process struct {
2626
PID string
27+
PPid string
2728
Exe string
2829
WorkingDirectory string
2930
Arguments []string

procmon_darwin.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@
33

44
package main
55

6+
import (
7+
"fmt"
8+
)
9+
610
func (p *ProcessMonitor) MonitorProcesses(errc chan error) {
711
writeLog("Monitor Processes called")
812
}
13+
14+
func getParentProcessId(pid string) (int, error) {
15+
return -1, fmt.Errorf("not implemented")
16+
}
17+
func getProcessExe(pid string) (string, error) {
18+
return "", fmt.Errorf("not implemented")
19+
}

procmon_linux.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import (
1111
"github.com/elastic/go-libaudit/v2/rule"
1212
"github.com/elastic/go-libaudit/v2/rule/flags"
1313
"github.com/pkg/errors"
14+
"io/ioutil"
15+
"os"
16+
"strings"
1417
)
1518

1619
func (p *ProcessMonitor) MonitorProcesses(errc chan error) {
@@ -139,3 +142,37 @@ func (p *ProcessMonitor) receive(r *libaudit.AuditClient) error {
139142

140143
}
141144
}
145+
146+
func getParentProcessId(pid string) (int, error) {
147+
statPath := fmt.Sprintf("/proc/%s/stat", pid)
148+
dataBytes, err := ioutil.ReadFile(statPath)
149+
if err != nil {
150+
return -1, err
151+
}
152+
153+
data := string(dataBytes)
154+
binStart := strings.IndexRune(data, '(') + 1
155+
binEnd := strings.IndexRune(data[binStart:], ')')
156+
157+
var ppid, pgrp, sid int
158+
var state rune
159+
160+
// Move past the image name and start parsing the rest
161+
data = data[binStart+binEnd+2:]
162+
_, err = fmt.Sscanf(data,
163+
"%c %d %d %d",
164+
&state,
165+
&ppid,
166+
&pgrp,
167+
&sid)
168+
169+
return ppid, err
170+
}
171+
172+
func getProcessExe(pid string) (string, error) {
173+
path, err := os.Readlink(fmt.Sprintf("/proc/%s/exe", pid))
174+
if err != nil {
175+
return "", err
176+
}
177+
return path, nil
178+
}

0 commit comments

Comments
 (0)