├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── cmd
├── ssh-probe-auth
│ ├── main.go
│ └── run
│ │ ├── cmd.go
│ │ ├── options.go
│ │ └── ssh_probe_register.go
├── ssh-probe-register
│ ├── main.go
│ └── run
│ │ ├── cmd.go
│ │ ├── options.go
│ │ └── ssh_probe_register.go
└── ssh-probe
│ ├── main.go
│ └── run
│ ├── cmd.go
│ ├── options.go
│ └── ssh_probe.go
├── docker-compose.yml
├── documentation
├── MFA.png
└── sstic
│ ├── .gitignore
│ ├── Dockerfile
│ ├── Makefile
│ ├── MonitoringAndProtectingSSHSessionsWithEBPF.pdf
│ ├── MonitoringAndProtectingSSHSessionsWithEBPF.tmp.out
│ ├── MonitoringAndProtectingSSHSessionsWithEBPF
│ ├── biblio.bib
│ ├── img
│ │ └── mfa.png
│ ├── master.tex
│ └── metadata.mk
│ ├── README
│ ├── RuntimeSecurityMonitoringWithEBPF.pdf
│ ├── RuntimeSecurityMonitoringWithEBPF.tmp.out
│ ├── RuntimeSecurityMonitoringWithEBPF
│ ├── biblio.bib
│ ├── img
│ │ ├── bw-rsa.png
│ │ └── rsa.png
│ ├── master.tex
│ └── metadata.mk
│ ├── _frontmatter
│ ├── comites.tex
│ ├── logos
│ │ ├── airbus.pdf
│ │ ├── anssi.png
│ │ ├── cea.pdf
│ │ ├── centrale-supelec.png
│ │ ├── dga.pdf
│ │ ├── lsti.pdf
│ │ ├── orange.pdf
│ │ ├── thales.png
│ │ └── tsp.jpg
│ ├── partenaires.tex
│ └── preface.tex
│ ├── _master.tex
│ ├── _standalone.tex
│ ├── llncs.cls
│ ├── sstic.cls
│ └── tex4ht.env
├── ebpf
├── .gitignore
├── bpf
│ ├── bpf.h
│ ├── bpf_helpers.h
│ └── bpf_map.h
├── events
│ ├── execve.h
│ ├── file_access.h
│ ├── kill.h
│ ├── open.h
│ ├── os_level_protections.h
│ ├── performance_monitoring.h
│ ├── privilege_elevation.h
│ ├── process_level_protections.h
│ ├── socket.h
│ ├── stat.h
│ └── unlink_and_move.h
├── main.c
├── session
│ ├── process_lineage.h
│ ├── session.h
│ └── tracker.h
└── utils
│ ├── action.h
│ ├── const.h
│ ├── process.h
│ └── tail_call.h
├── go.mod
├── go.sum
├── pkg
├── assets
│ └── probe.go
├── model
│ ├── const.go
│ ├── kill.go
│ ├── notifications.go
│ ├── otp.go
│ └── profiles.go
├── ssh-probe
│ ├── datadog.go
│ ├── kill.go
│ ├── manager.go
│ ├── notifications.go
│ ├── otp.go
│ ├── profiles.go
│ └── ssh_probe.go
└── utils
│ ├── byteorder.go
│ ├── rand.go
│ └── utils.go
├── profiles
└── vagrant.yaml
└── static
└── datadog-agent
└── conf.d
└── ssh-probe.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea/
3 | bin/
4 | vendor/
5 | profiles/qr.png
6 | secrets.sh
7 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: build-ebpf build run
2 |
3 | build-ebpf:
4 | mkdir -p ebpf/bin
5 | clang -D__KERNEL__ -D__ASM_SYSREG_H \
6 | -Wno-unused-value \
7 | -Wno-pointer-sign \
8 | -Wno-compare-distinct-pointer-types \
9 | -Wunused \
10 | -Wall \
11 | -Werror \
12 | -I/lib/modules/$$(uname -r)/build/include \
13 | -I/lib/modules/$$(uname -r)/build/include/uapi \
14 | -I/lib/modules/$$(uname -r)/build/include/generated/uapi \
15 | -I/lib/modules/$$(uname -r)/build/arch/x86/include \
16 | -I/lib/modules/$$(uname -r)/build/arch/x86/include/uapi \
17 | -I/lib/modules/$$(uname -r)/build/arch/x86/include/generated \
18 | -O2 -emit-llvm \
19 | ebpf/main.c \
20 | -c -o - | llc -march=bpf -filetype=obj -o ebpf/bin/probe.o
21 | go-bindata -pkg assets -prefix "ebpf/bin" -o "pkg/assets/probe.go" "ebpf/bin/probe.o"
22 |
23 | build:
24 | go build -o bin/ ./...
25 |
26 | run:
27 | sudo --preserve-env=SSH_PROBE_SECRETS ./bin/ssh-probe --profiles ./profiles/vagrant.yaml --access-control-events-level allow --agent-url "127.0.0.1:10518"
28 |
29 | install:
30 | sudo cp ./bin/ssh-probe* /usr/bin/
31 |
32 | run_agent:
33 | docker-compose up
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## SSH Probe
2 |
3 | ssh-probe helps monitor and protect SSH sessions. Relying on predefined security profiles for each user, ssh-probe introduces a new access control layer that can restrict what a user can do on a server, including the root user. On a technical standpoint, ssh-probe relies on eBPF to collect runtime data on up to 127 hook points in the kernel. When an action diverges from the security profile of a session, ssh-probe relies on its syscall hook points to grant or deny access to specific operations. Access can also be granted through an MFA verification.
4 |
5 | Monitoring and enforcement are organized in categories:
6 |
7 | - File integrity monitoring: defines a list of files that should be monitored.
8 | - Process monitoring: defines a list of binaries that should be monitored.
9 | - Process level protections: includes protections against process injection techniques, etc.
10 | - Privilege elevation: covers the system calls used to change the credentials of a process.
11 | - OS level protections: includes protections for the kernel and prevents the modification of sensitive parameters, etc.
12 | - Socket creation: grants or denies socket creation, this category is used to control if the processes of a session should have access to the network (doesn't affect the sshd daemon).
13 | - Kill: isolates the kill system calls so that you can grant access to them for a short period of time.
14 | - Performance monitoring: isolates the system calls required for performance monitoring so that you can grant access to them for a short period of time.
15 |
16 | For each category (and each file for FIM and process monitoring), 4 options are available to define how ssh-probe should react:
17 |
18 | - `allow`: ssh-probe will allow the operation to go through.
19 | - `block`: ssh-probe will block the operation, no matter what Linux's "normal" access control decided. This essentially means that it will block a standard user as well as a privileged user.
20 | - `mfa`: an MFA verification for the requested category (called `scope`) is required for ssh-probe to grant access to the operation
21 | - `kill`: ssh-probe will block access to the operation and kill the SSH session that is responsible for trying to execute it
22 |
23 | You can find a profile example in [profiles/vagrant.yaml](profiles/vagrant.yaml).
24 |
25 | ### Getting started
26 |
27 | This project includes 3 binaries:
28 |
29 | - `ssh-probe` is the main executable of the project, it is responsible for loading eBPF programs and maps into the kernel, loading profiles in the kernel and collecting security alerts (and forwarding them to the Datadog agent if you configured it).
30 | - `ssh-probe-auth` is used for MFA verification. If you configured your profiles to use MFA, you should make sure that this binary is allowed in your profile.
31 | - `ssh-probe-register` is used to generate new MFA secrets so that you can register a new app to work with `ssh-probe`. The output of the binary is a QR code that you can scan with your MFA app, and a secret token for the newly registered user, that you need to share with `ssh-probe` using the `SSH_PROBE_SECRETS` environment variable.
32 |
33 | #### System requirements
34 |
35 | - golang 1.13+
36 | - This project was developed on an Ubuntu Focal machine (Linux Kernel 5.4) but should be compatible with 4.13+ kernels (not tested).
37 | - Kernel headers are expected to be installed in `lib/modules/$(uname -r)`, update the `Makefile` with their location otherwise.
38 | - clang & llvm (developed with 11.0.1)
39 |
40 | #### Build
41 |
42 | 1) If you need to rebuild the eBPF programs, use the following command:
43 |
44 | ```shell script
45 | # ~ make build-ebpf
46 | ```
47 |
48 | 2) To build ssh-probe, ssh-probe-auth and ssh-probe-register, run:
49 |
50 | ```shell script
51 | # ~ make build
52 | ```
53 |
54 | 3) To install ssh-probe (copy all 3 binaries to /usr/bin/) run:
55 | ```shell script
56 | # ~ make install
57 | ```
58 |
59 | ### Using the default example profile
60 |
61 | [profiles/vagrant.yaml](profiles/vagrant.yaml) contains the default security profiles for the `vagrant` and `root` users. Those profiles were written to showcase the different capabilities of ssh-probe and can be used as a starting point to write new ones.
62 |
63 | 1) Write a security profile for the user you wish to protect. If you use the default profiles, don't forget to edit the username as needed.
64 |
65 | 2) (optionnal) In order to use the MFA feature of ssh-probe, you need to link ssh-probe with your One-Time Password (OTP) application (like Google Authenticator or Duo Mobile). Use `ssh-probe-register` to generate a new secret token for each user you wish to protect. `ssh-probe-register` will generate a QR code that can be used to configure your OTP app with ssh-probe.
66 |
67 | ```shell script
68 | # ~ ssh-probe-register -h
69 | ssh-probe-register is used to generate MFA secrets and QR codes so that you can configure an OTP app with ssh-probe.
70 |
71 | Usage:
72 | ssh-probe-register [flags]
73 |
74 | Flags:
75 | -h, --help help for ssh-probe-register
76 | -o, --output string Output .png file for the QR code image. (default "/tmp/qr.png")
77 | -u, --user string Username for which the secret is being generated.
78 |
79 | # ~ ssh-probe-register --user my_user --output /tmp/qr.png
80 | INFO[2021-03-16T14:25:11Z] new secret generated for "my_user": [212 20 117 31 107 37 205 35 233 97] (base32: 2QKHKH3LEXGSH2LB)
81 | INFO[2021-03-16T14:25:11Z] export the following environment variable to ssh-probe (comma separated list): SSH_PROBE_SECRETS="my_user:2QKHKH3LEXGSH2LB"
82 | INFO[2021-03-16T14:25:11Z] MFA URL: otpauth://totp/SSHProbe:my_user@ssh-probe.com?issuer=SSHProbe&secret=2QKHKH3LEXGSH2LB
83 | INFO[2021-03-16T14:25:11Z] QR code was generated in /tmp/qr.png. Please scan it into MFA app.
84 | ```
85 |
86 | 3) (optionnal) Start the Datadog Agent with the following command:
87 |
88 | ```shell script
89 | # ~ export DD_API_KEY=[your_api_key]
90 | # ~ docker-compose up
91 | ```
92 |
93 | 4) To start `ssh-probe`, run the following command:
94 | ```shell script
95 | # ~ ssh-probe -h
96 | ssh-probe is a ssh session tracker based on eBPF, that can be used to mitigate the impact of stolen credentials. ssh-probe relies on eBPF to track ssh sessions at runtime.
97 |
98 | Usage:
99 | ssh-probe [flags]
100 |
101 | Flags:
102 | -a, --access-control-events-level string defines the access control events level that should be reported to Datadog, available options are: allow, block, mfa, kill (default "mfa")
103 | -u, --agent-url string Datadog agent URL used to forward logs to Datadog
104 | -g, --disable-mfa-global-scope Disable MFA tokens with global scope
105 | -h, --help help for ssh-probe
106 | -l, --log-level string log level, options: panic, fatal, error, warn, info, debug or trace (default "info")
107 | -p, --profiles string path to the file containing the security profiles for each user
108 |
109 | # ~ sudo --preserve-env=SSH_PROBE_SECRETS ./bin/ssh-probe --profiles ./profiles/vagrant.yaml --access-control-events-level allow --agent-url "127.0.0.1:10518"
110 | INFO[2021-03-16T15:06:35Z] ssh-probe successfully started
111 | ```
112 |
113 | 5) ssh into your instance with one of the users for which you wrote a profile. The session will automatically be tracked and protected by ssh-probe according to the profile your provided (note that existing SSH sessions won't be tracked). In order to access a resource protected by MFA, you need to provide a one-time password to `ssh-probe-auth`. Once authentication is successful, you'll be able to perform the sensitive action within the provided time limit. Should you fail your MFA authentication too many times, your session will be immediately killed.
114 |
115 | ```shell script
116 | # ~ ssh-probe-auth -h
117 | ssh-probe-auth is used to authenticate your session with a one time password, in order to approve sensitive administrative operations.
118 |
119 | Usage:
120 | ssh-probe-auth [flags]
121 |
122 | Flags:
123 | -h, --help help for ssh-probe-auth
124 | -s, --scope string scope of the MFA access. Options are: fim, process_monitoring, unknown_binary, socket_creation, deletes_and_moves, privilege_elevation, os_level_protections, process_level_protections, performance_monitoring, kill, global. (default "global")
125 | -t, --timeout duration MFA access timeout. Only values between 0 and 10m0s are allowed. (default 10s)
126 |
127 | # ~ ssh-probe-auth -s process_monitoring -t 10s
128 | INFO[2021-03-16T15:19:09Z] Enter your one time password (or q to quit):
129 | 592408
130 | INFO[2021-03-16T15:19:17Z] Authentication successful (token expires in 10s)
131 |
132 | # ~ // perform the sensitive action
133 | ```
134 |
135 | ### MFA verification with eBPF
136 |
137 | In order to temporarily grant access to sensitive resources, we have implemented an MFA verification mechanism using eBPF. The reason why we used eBPF is twofold: first we have implemented the access control with eBPF, which means that our eBPF programs need to know if an access should be granted or not; second, we couldn't expose a local endpoint since the we wanted to enforce network access by blocking the creation of sockets (the local endpoint solution would have required sockets, thus forcing the profile to always grant network access for other resources to be protected with MFA).
138 |
139 | In other words, we used eBPF to retrieve the MFA token from the `ssh-probe-auth` binary, without having to connect to a local endpoint. We decided to place a kprobe on the `stat` syscall, and use the file path provided to that syscall in order to push the one time password to kernel space. If our eBPF program recognizes the MFA declaration pattern in the path provided to `stat`, then the token, scope and timeout are parsed and sent to user space for verification. Once the token is accepted, `ssh-probe` pushes a temporary token to kernel space, so that our eBPF programs know which session should be granted temporary access to which resources. In the meantime, a signal is sent to `ssh-probe-auth` to let the user know that the MFA verification was successful (or not). Should the authentication fail too many times, the session is immediately killed.
140 |
141 | 
142 |
--------------------------------------------------------------------------------
/cmd/ssh-probe-auth/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package main
17 |
18 | import (
19 | "github.com/sirupsen/logrus"
20 |
21 | "github.com/Gui774ume/ssh-probe/cmd/ssh-probe-auth/run"
22 | )
23 |
24 | func main() {
25 | logrus.SetFormatter(&logrus.TextFormatter{
26 | FullTimestamp: true,
27 | TimestampFormat: "2006-01-02T15:04:05Z",
28 | DisableLevelTruncation: true,
29 | })
30 | run.SSHProbeAuthCmd.Execute()
31 | }
32 |
--------------------------------------------------------------------------------
/cmd/ssh-probe-auth/run/cmd.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package run
17 |
18 | import (
19 | "fmt"
20 | "time"
21 |
22 | "github.com/spf13/cobra"
23 |
24 | "github.com/Gui774ume/ssh-probe/pkg/model"
25 | )
26 |
27 | // SSHProbeAuthCmd represents the base command of ssh-probe-auth
28 | var SSHProbeAuthCmd = &cobra.Command{
29 | Use: "ssh-probe-auth",
30 | Short: "ssh-probe-auth is used to authenticate your session with a one time password, in order to approve sensitive administrative operations",
31 | Long: "ssh-probe-auth is used to authenticate your session with a one time password, in order to approve sensitive administrative operations",
32 | RunE: runSSHProbeAuthCmd,
33 | Example: "ssh-probe-auth --timeout 30s",
34 | }
35 |
36 | var options CLIOptions
37 |
38 | func init() {
39 | SSHProbeAuthCmd.Flags().DurationVarP(
40 | &options.Timeout,
41 | "timeout",
42 | "t",
43 | 10*time.Second,
44 | fmt.Sprintf("MFA access timeout. Only values between 0 and %v are allowed.", model.MaxOTPTimeout))
45 | SSHProbeAuthCmd.Flags().VarP(
46 | NewScopeSanitizer(&options.Scope),
47 | "scope",
48 | "s",
49 | "scope of the MFA access. Options are: fim, process_monitoring, unknown_binary, socket_creation, deletes_and_moves, privilege_elevation, os_level_protections, process_level_protections, performance_monitoring, kill, global.")
50 | }
51 |
--------------------------------------------------------------------------------
/cmd/ssh-probe-auth/run/options.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package run
17 |
18 | import (
19 | "fmt"
20 | "time"
21 |
22 | "github.com/Gui774ume/ssh-probe/pkg/model"
23 | )
24 |
25 | // CLIOptions are the command line options of ssh-probe
26 | type CLIOptions struct {
27 | Timeout time.Duration
28 | Scope model.Category
29 | }
30 |
31 | type ScopeSanitizer struct {
32 | scope *model.Category
33 | }
34 |
35 | // NewKernelNotifLevelSanitizer creates a new instance of KernelNotifLevelSanitizer.
36 | func NewScopeSanitizer(sanitizedScope *model.Category) *ScopeSanitizer {
37 | *sanitizedScope = model.CategoryGlobal
38 | return &ScopeSanitizer{
39 | scope: sanitizedScope,
40 | }
41 | }
42 |
43 | func (ss *ScopeSanitizer) String() string {
44 | return fmt.Sprintf("%s", *ss.scope)
45 | }
46 |
47 | func (ss *ScopeSanitizer) Set(val string) error {
48 | category, err := model.GetCategory(val)
49 | if err != nil {
50 | return err
51 | }
52 | *ss.scope = category
53 | return nil
54 | }
55 |
56 | func (ss *ScopeSanitizer) Type() string {
57 | return "string"
58 | }
59 |
--------------------------------------------------------------------------------
/cmd/ssh-probe-auth/run/ssh_probe_register.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package run
17 |
18 | import (
19 | "fmt"
20 | "os"
21 | "os/signal"
22 | "syscall"
23 | "time"
24 |
25 | "github.com/pkg/errors"
26 | "github.com/sirupsen/logrus"
27 | "github.com/spf13/cobra"
28 |
29 | "github.com/Gui774ume/ssh-probe/pkg/model"
30 | )
31 |
32 | var (
33 | sshProbeTimeout = 10 * time.Second
34 | )
35 |
36 | func runSSHProbeAuthCmd(cmd *cobra.Command, args []string) error {
37 | stop := make(chan os.Signal, 1)
38 | success := make(chan os.Signal, 1)
39 | failed := make(chan os.Signal, 1)
40 | signal.Notify(stop, os.Interrupt, os.Kill)
41 | signal.Notify(success, syscall.SIGUSR1)
42 | signal.Notify(failed, syscall.SIGUSR2)
43 | var timer *time.Timer
44 |
45 | // Set log level
46 | logrus.SetLevel(logrus.DebugLevel)
47 |
48 | // check timeout value
49 | if options.Timeout > model.MaxOTPTimeout || options.Timeout < 0 {
50 | return errors.New("Timeout must be between 0 and 10 minutes")
51 | }
52 |
53 | // scan for otp token
54 | for {
55 | var token string
56 | logrus.Info("Enter your one time password (or q to quit): ")
57 | _, err := fmt.Scanln(&token)
58 | if err != nil {
59 | return errors.Wrap(err, "Failed to read user input")
60 | }
61 | if token == "q" {
62 | return nil
63 | }
64 |
65 | // check OTP
66 | if err := checkOTP(token, options.Timeout.Microseconds(), options.Scope); err != nil {
67 | logrus.Error(err)
68 | return nil
69 | }
70 |
71 | // Wait for an answer from ssh-probe
72 | timer = time.NewTimer(sshProbeTimeout)
73 | select {
74 | case <-timer.C:
75 | logrus.Error("ssh-probe took too long to answer")
76 | return nil
77 | case <-stop:
78 | fmt.Println()
79 | return nil
80 | case <-success:
81 | logrus.Infof("Authentication successful (token expires in %v)", options.Timeout)
82 | return nil
83 | case <-failed:
84 | logrus.Warn("Authentication failed, try again ...")
85 | continue
86 | }
87 | }
88 | }
89 |
90 | func checkOTP(token string, timeout int64, scope model.Category) error {
91 | // craft a special stat request that ssh-probe will catch to check the OTP
92 | otpStr := fmt.Sprintf("otp://%s:%v@%s", scope.String(), timeout, token)
93 | if _, err := os.Stat(otpStr); err != nil {
94 | if os.IsNotExist(err) {
95 | return errors.New("ssh-probe didn't answer properly. Is ssh-probe running ?")
96 | }
97 | }
98 | return nil
99 | }
100 |
--------------------------------------------------------------------------------
/cmd/ssh-probe-register/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package main
17 |
18 | import (
19 | "github.com/sirupsen/logrus"
20 |
21 | "github.com/Gui774ume/ssh-probe/cmd/ssh-probe-register/run"
22 | )
23 |
24 | func main() {
25 | logrus.SetFormatter(&logrus.TextFormatter{
26 | FullTimestamp: true,
27 | TimestampFormat: "2006-01-02T15:04:05Z",
28 | DisableLevelTruncation: true,
29 | })
30 | run.SSHProbeRegisterCmd.Execute()
31 | }
32 |
--------------------------------------------------------------------------------
/cmd/ssh-probe-register/run/cmd.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package run
17 |
18 | import (
19 | "github.com/spf13/cobra"
20 | )
21 |
22 | // SSHProbe represents the base command of ssh-probe
23 | var SSHProbeRegisterCmd = &cobra.Command{
24 | Use: "ssh-probe-register",
25 | Short: "ssh-probe-register is used to generate MFA secrets and QR codes so that you can configure an OTP app with ssh-probe",
26 | Long: "ssh-probe-register is used to generate MFA secrets and QR codes so that you can configure an OTP app with ssh-probe",
27 | RunE: runSSHProbeRegisterCmd,
28 | Example: "ssh-probe-register -o /tmp/ -u vagrant",
29 | }
30 |
31 | var options CLIOptions
32 |
33 | func init() {
34 | SSHProbeRegisterCmd.Flags().StringVarP(
35 | &options.Output,
36 | "output",
37 | "o",
38 | "/tmp/qr.png",
39 | "Output .png file for the QR code image.")
40 | SSHProbeRegisterCmd.Flags().StringVarP(
41 | &options.User,
42 | "user",
43 | "u",
44 | "",
45 | "Username for which the secret is being generated.")
46 | SSHProbeRegisterCmd.MarkFlagRequired("user")
47 | }
48 |
--------------------------------------------------------------------------------
/cmd/ssh-probe-register/run/options.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package run
17 |
18 | // CLIOptions are the command line options of ssh-probe
19 | type CLIOptions struct {
20 | User string
21 | Output string
22 | }
23 |
--------------------------------------------------------------------------------
/cmd/ssh-probe-register/run/ssh_probe_register.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package run
17 |
18 | import (
19 | "encoding/base32"
20 | "fmt"
21 | "io/ioutil"
22 | "net/url"
23 | "rsc.io/qr"
24 |
25 | "github.com/sirupsen/logrus"
26 | "github.com/spf13/cobra"
27 |
28 | "github.com/Gui774ume/ssh-probe/pkg/utils"
29 | )
30 |
31 | func runSSHProbeRegisterCmd(cmd *cobra.Command, args []string) error {
32 | // Set log level
33 | logrus.SetLevel(logrus.DebugLevel)
34 |
35 | // Generate random secret instead of using the test value above.
36 | secret, err := utils.NewMFASecret()
37 | if err != nil {
38 | logrus.Fatalf("failed to generate a new MFA secret: %v", err)
39 | }
40 | secretBase32 := base32.StdEncoding.EncodeToString(secret)
41 | logrus.Infof("new secret generated for \"%s\": %v (base32: %v)", options.User, secret, secretBase32)
42 | logrus.Infof("export the following environment variable to ssh-probe (comma separated list): SSH_PROBE_SECRETS=\"%s:%s\"", options.User, secretBase32)
43 |
44 | // Generate MFA URL
45 | url, err := generateURL(secretBase32)
46 | if err != nil {
47 | logrus.Fatalf("failed to generate the MFA URL: %v", err)
48 | }
49 | logrus.Infof("MFA URL: %s", url)
50 |
51 | if len(options.Output) > 0 {
52 | if err := generateQRCode(url); err != nil {
53 | logrus.Fatalf("failed to generate QR code: %v", err)
54 | }
55 | }
56 | logrus.Infof("QR code was generated in %s. Please scan it into MFA app.\n", options.Output)
57 | return nil
58 | }
59 |
60 | func generateURL(secret string) (string, error) {
61 | account := fmt.Sprintf("%s@ssh-probe.com", options.User)
62 | issuer := "SSHProbe"
63 |
64 | URL, err := url.Parse("otpauth://totp")
65 | if err != nil {
66 | return "", err
67 | }
68 |
69 | URL.Path += "/" + url.PathEscape(issuer) + ":" + url.PathEscape(account)
70 |
71 | params := url.Values{}
72 | params.Add("secret", secret)
73 | params.Add("issuer", issuer)
74 |
75 | URL.RawQuery = params.Encode()
76 | return URL.String(), nil
77 | }
78 |
79 | func generateQRCode(url string) error {
80 | code, err := qr.Encode(url, qr.Q)
81 | if err != nil {
82 | return err
83 | }
84 | b := code.PNG()
85 | err = ioutil.WriteFile(options.Output, b, 0600)
86 | if err != nil {
87 | return err
88 | }
89 | return nil
90 | }
91 |
--------------------------------------------------------------------------------
/cmd/ssh-probe/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package main
17 |
18 | import (
19 | "github.com/sirupsen/logrus"
20 |
21 | "github.com/Gui774ume/ssh-probe/cmd/ssh-probe/run"
22 | )
23 |
24 | func main() {
25 | logrus.SetFormatter(&logrus.TextFormatter{
26 | FullTimestamp: true,
27 | TimestampFormat: "2006-01-02T15:04:05Z",
28 | DisableLevelTruncation: true,
29 | })
30 | run.SSHProbeCmd.Execute()
31 | }
32 |
--------------------------------------------------------------------------------
/cmd/ssh-probe/run/cmd.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package run
17 |
18 | import (
19 | "github.com/spf13/cobra"
20 | )
21 |
22 | // SSHProbe represents the base command of ssh-probe
23 | var SSHProbeCmd = &cobra.Command{
24 | Use: "ssh-probe",
25 | Short: "ssh-probe is a ssh session tracker based on eBPF, that can be used to mitigate the impact of stolen credentials",
26 | Long: `ssh-probe is a ssh session tracker based on eBPF, that can be used to mitigate the impact of stolen credentials
27 |
28 | ssh-probe relies on eBPF to track ssh sessions at runtime. More information about the project can be found on github: https://github.com/Gui774ume/ssh-probe`,
29 | RunE: runSSHProbeCmd,
30 | Example: "sudo ssh-probe --profiles /tmp/profiles",
31 | }
32 |
33 | var options CLIOptions
34 |
35 | func init() {
36 | SSHProbeCmd.Flags().VarP(
37 | NewLogLevelSanitizer(&options.LogLevel),
38 | "log-level",
39 | "l",
40 | `log level, options: panic, fatal, error, warn, info, debug or trace`)
41 | SSHProbeCmd.Flags().VarP(
42 | NewPathSanitizer(&options.Profiles),
43 | "profiles",
44 | "p",
45 | `path to the file containing the security profiles for each user`)
46 | SSHProbeCmd.Flags().VarP(
47 | NewAccessControlEventsLevelSanitizer(&options.AccessControlEventsLevel),
48 | "access-control-events-level",
49 | "a",
50 | `defines the access control events level that should be reported to Datadog, available options are: allow, block, mfa, kill`)
51 | SSHProbeCmd.Flags().BoolVarP(
52 | &options.DisableGlobalMFAScope,
53 | "disable-mfa-global-scope",
54 | "g",
55 | false,
56 | `Disable MFA tokens with global scope`)
57 | SSHProbeCmd.Flags().StringVarP(
58 | &options.AgentURL,
59 | "agent-url",
60 | "u",
61 | "",
62 | `Datadog agent URL used to forward logs to Datadog`)
63 | SSHProbeCmd.MarkFlagRequired("profiles")
64 | }
65 |
--------------------------------------------------------------------------------
/cmd/ssh-probe/run/options.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package run
17 |
18 | import (
19 | "fmt"
20 | "os"
21 |
22 | "github.com/pkg/errors"
23 | "github.com/sirupsen/logrus"
24 |
25 | "github.com/Gui774ume/ssh-probe/pkg/model"
26 | )
27 |
28 | // CLIOptions are the command line options of ssh-probe
29 | type CLIOptions struct {
30 | AccessControlEventsLevel model.Action
31 | LogLevel logrus.Level
32 | Profiles string
33 | DisableGlobalMFAScope bool
34 | AgentURL string
35 | }
36 |
37 | // LogLevelSanitizer is a log level sanitizer that ensures that the provided log level exists
38 | type LogLevelSanitizer struct {
39 | logLevel *logrus.Level
40 | }
41 |
42 | // NewLogLevelSanitizer creates a new instance of LogLevelSanitizer. The sanitized level will be written in the provided
43 | // logurs level
44 | func NewLogLevelSanitizer(sanitizedLevel *logrus.Level) *LogLevelSanitizer {
45 | *sanitizedLevel = logrus.InfoLevel
46 | return &LogLevelSanitizer{
47 | logLevel: sanitizedLevel,
48 | }
49 | }
50 |
51 | func (lls *LogLevelSanitizer) String() string {
52 | return fmt.Sprintf("%v", *lls.logLevel)
53 | }
54 |
55 | func (lls *LogLevelSanitizer) Set(val string) error {
56 | sanitized, err := logrus.ParseLevel(val)
57 | if err != nil {
58 | return err
59 | }
60 | *lls.logLevel = sanitized
61 | return nil
62 | }
63 |
64 | func (lls *LogLevelSanitizer) Type() string {
65 | return "string"
66 | }
67 |
68 | // PathSanitizer is a path sanitizer that ensures that the provided path exists
69 | type PathSanitizer struct {
70 | path *string
71 | }
72 |
73 | // NewPathSanitizer creates a new instance of PathSanitizer. The sanitized path will be written in the provided string
74 | func NewPathSanitizer(sanitizedPath *string) *PathSanitizer {
75 | return &PathSanitizer{
76 | path: sanitizedPath,
77 | }
78 | }
79 |
80 | func (ps *PathSanitizer) String() string {
81 | return fmt.Sprintf("%v", *ps.path)
82 | }
83 |
84 | func (ps *PathSanitizer) Set(val string) error {
85 | if len(val) == 0 {
86 | return errors.New("empty path")
87 | }
88 | if _, err := os.Stat(val); err != nil {
89 | return err
90 | }
91 | *ps.path = val
92 | return nil
93 | }
94 |
95 | func (ps *PathSanitizer) Type() string {
96 | return "string"
97 | }
98 |
99 | type AccessControlEventsLevelSanitizer struct {
100 | level *model.Action
101 | }
102 |
103 | // NewAccessControlEventsLevelSanitizer creates a new instance of AccessControlEventsLevelSanitizer.
104 | func NewAccessControlEventsLevelSanitizer(sanitizedLevel *model.Action) *AccessControlEventsLevelSanitizer {
105 | *sanitizedLevel = model.MFA
106 | return &AccessControlEventsLevelSanitizer{
107 | level: sanitizedLevel,
108 | }
109 | }
110 |
111 | func (knls *AccessControlEventsLevelSanitizer) String() string {
112 | return fmt.Sprintf("%v", *knls.level)
113 | }
114 |
115 | func (knls *AccessControlEventsLevelSanitizer) Set(val string) error {
116 | action := model.Action(val)
117 | if err := action.Sanitize(); err != nil {
118 | return err
119 | }
120 | *knls.level = action
121 | return nil
122 | }
123 |
124 | func (knls *AccessControlEventsLevelSanitizer) Type() string {
125 | return "string"
126 | }
127 |
--------------------------------------------------------------------------------
/cmd/ssh-probe/run/ssh_probe.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package run
17 |
18 | import (
19 | "fmt"
20 | "github.com/pkg/errors"
21 | "os"
22 | "os/signal"
23 |
24 | "github.com/sirupsen/logrus"
25 | "github.com/spf13/cobra"
26 |
27 | ssh_probe "github.com/Gui774ume/ssh-probe/pkg/ssh-probe"
28 | )
29 |
30 | func runSSHProbeCmd(cmd *cobra.Command, args []string) error {
31 | // Set log level
32 | logrus.SetLevel(options.LogLevel)
33 |
34 | if len(os.Getenv("SSH_PROBE_SECRETS")) == 0 {
35 | return errors.New("couldn't find any secret in SSH_PROBE_SECRETS environment variable")
36 | }
37 |
38 | // creates a new instance of ssh-probe
39 | sshp, err := ssh_probe.NewSSHProbe(options.Profiles, options.AccessControlEventsLevel, options.DisableGlobalMFAScope, options.AgentURL)
40 | if err != nil {
41 | logrus.Fatalf("%v", err)
42 | }
43 | if err := sshp.Start(); err != nil {
44 | logrus.Fatalf("%v", err)
45 | }
46 |
47 | logrus.Info("ssh-probe successfully started")
48 | wait()
49 |
50 | // stops ssh-probe
51 | if err := sshp.Stop(); err != nil {
52 | logrus.Fatalf("%v", err)
53 | }
54 | logrus.Info("ssh-probe stopped")
55 | return nil
56 | }
57 |
58 | // wait stops the main goroutine until an interrupt or kill signal is sent
59 | func wait() {
60 | sig := make(chan os.Signal, 1)
61 | signal.Notify(sig, os.Interrupt, os.Kill)
62 | <-sig
63 | fmt.Println()
64 | }
65 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | agent:
5 | container_name: agent
6 | image: datadog/agent:latest
7 | restart: unless-stopped
8 | ports:
9 | - "8126:8126/tcp"
10 | - "8125:8125/udp"
11 | - "10518:10518/udp"
12 | - "10518:10518/tcp"
13 | environment:
14 | - "DD_API_KEY=${DD_API_KEY}"
15 | - "DD_LOGS_ENABLED=true"
16 | - "DD_APM_ENABLED=true"
17 | - "DD_PROCESS_AGENT_ENABLED=true"
18 | - "DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true"
19 | - "DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL=true"
20 | # - "DD_SITE=datadoghq.eu"
21 | volumes:
22 | - "/var/run/docker.sock:/var/run/docker.sock:ro"
23 | - "/proc/:/host/proc/:ro"
24 | - "/sys/fs/cgroup/:/host/sys/fs/cgroup:ro"
25 | - "./static/datadog-agent/conf.d/:/conf.d:ro"
26 |
--------------------------------------------------------------------------------
/documentation/MFA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/MFA.png
--------------------------------------------------------------------------------
/documentation/sstic/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.aux
3 | *.bbl
4 | *.blg
5 | *.idx
6 | *.ilg
7 | *.ind
8 | *.log
9 | *.toc
10 | _articles.tex
11 | Makefile.standalone-targets
12 | *.tmp.tex
13 | *.tmp.pdf
14 | *.ebook.tex
15 | *.ebook.css
16 | *.ebook.dvi
17 | *.ebook.html
18 | *.ebook.4ct
19 | *.ebook.4tc
20 | *.ebook.idv
21 | *.ebook.lg
22 | *.ebook.pdf
23 | *.ebook.tmp
24 | *.ebook.xref
25 |
--------------------------------------------------------------------------------
/documentation/sstic/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:20.04
2 |
3 | RUN apt-get update && apt-get install -y --no-install-recommends \
4 | ca-certificates \
5 | ghostscript \
6 | libfontconfig \
7 | make \
8 | perl \
9 | wget \
10 | && wget -qO- "https://github.com/yihui/tinytex/raw/master/tools/install-unx.sh" | sh \
11 | && chmod a+x /root \
12 | && chmod -R a+r /root/.TinyTeX \
13 | && chmod -R a+rx /root/.TinyTeX/bin \
14 | && mv /root/bin/* /usr/local/bin/. \
15 | && tlmgr install \
16 | acronym \
17 | algorithmicx \
18 | algorithms \
19 | babel-english \
20 | babel-french \
21 | bigfoot \
22 | bytefield \
23 | caption \
24 | carlisle \
25 | cite \
26 | colortbl \
27 | environ \
28 | eurosym \
29 | fancyhdr \
30 | fnpct \
31 | hyphen-english \
32 | hyphen-french \
33 | koma-script \
34 | listings \
35 | microtype \
36 | multirow \
37 | nth \
38 | pdflscape \
39 | pdfpages \
40 | pgfplots \
41 | pmboxdraw \
42 | psnfss \
43 | siunitx \
44 | subfigure \
45 | tcolorbox \
46 | tikz-inet \
47 | tikz-qtree \
48 | translations \
49 | trimspaces \
50 | truncate \
51 | ucs \
52 | xstring \
53 | && apt-get purge -y --autoremove \
54 | ca-certificates \
55 | libfontconfig \
56 | perl \
57 | wget \
58 | && rm -rf /var/lib/apt/lists/*
59 |
60 | WORKDIR /SSTIC
61 |
62 | ENTRYPOINT ["make"]
63 | CMD []
64 |
--------------------------------------------------------------------------------
/documentation/sstic/Makefile:
--------------------------------------------------------------------------------
1 | #####################
2 | # General Variables #
3 | #####################
4 |
5 | SRC=actes.tmp.tex $(wildcard */*.tex)
6 | LATEX?=pdflatex
7 | LFLAGS?=-halt-on-error
8 |
9 | GSFLAGS=-sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -dEmbedAllFonts=true -dCompatibilityLevel=1.4 -dNOPAUSE -dBATCH -dSubsetFonts=true -dOptimize=true -dNOPLATFONTS -dDOPDFMARKS -dSAFER -dSTRICT -dConvertCMYKImagesToRGB=false -dProcessColorModel=/DeviceCMYK -dDetectDuplicateImages=true
10 |
11 | BIB_MISSING = 'No file.*\.bbl|Citation.*undefined'
12 | REFERENCE_UNDEFINED='(There were undefined references|Rerun to get (cross-references|the bars) right)'
13 |
14 |
15 |
16 | ###################
17 | # Ebook Variables #
18 | ###################
19 |
20 | HTLATEX=htlatex
21 | HTFLAGS?="xhtml,charset=utf-8" " -cunihtf -utf8"
22 |
23 | # ebook metadata
24 | CALFLAGS+=--book-producer STIC --publisher STIC
25 | CALFLAGS+=--series SSTIC2020 --language fr
26 |
27 | # IMGPDFS=$(wildcard */img/*.pdf */img/**/*.pdf)
28 | # IMGEPSS=$(foreach img, $(IMGPDFS), $(img:pdf=eps))
29 | # IMGJPGS=$(wildcard */img/*.jpg */img/**/*.jpg)
30 | # IMGPNGS=$(foreach img, $(IMGJPGS), $(img:jpg=png))
31 |
32 |
33 |
34 | ###################
35 | # Generic targets #
36 | ###################
37 |
38 | .PHONY: default export clean from_docker from_docker_su
39 |
40 |
41 | default: Makefile.standalone-targets
42 |
43 | from_docker_su:
44 | sudo docker run --rm -it -v ${PWD}:/SSTIC:rw -u `id -u`:`id -g` sstic/actes
45 |
46 | from_docker:
47 | docker run --rm -it -v ${PWD}:/SSTIC:rw -u `id -u`:`id -g` sstic/actes
48 | # "-v ${PWD}:/SSTIC:rw" mount the current directory inside of the container (with RW access)
49 | # "-u `id -u`:`id -g`" ensures that files written by the container belong to the current user
50 |
51 | export: Makefile.standalone-targets
52 |
53 |
54 | clean:
55 | rm -f *.aux *.bbl *.blg *.idx *.ilg *.ind *.log *.toc *.out
56 | rm -f _master.pdf
57 | rm -f _articles.tex
58 | rm -f Makefile.standalone-targets
59 | rm -f *.tmp.tex *.tmp.pdf
60 | rm -f *.ebook.tex *.ebook.css *.ebook.dvi *.ebook.html *.ebook.4ct *.ebook.4tc
61 | rm -f *.ebook.idv *.ebook.lg *.ebook.pdf *.ebook.tmp *.ebook.xref
62 | rm -f actes.pdf actes-online.pdf
63 |
64 |
65 |
66 | #######################
67 | # Compilation helpers #
68 | #######################
69 |
70 | %.tmp.pdf: %.tmp.tex sstic.cls llncs.cls
71 | @rm -f $(@:.pdf=.aux) $(@:.pdf=.idx)
72 | $(LATEX) $(LFLAGS) $<
73 | bibtex $(@:.pdf=.aux) > /dev/null || true
74 | makeindex $(@:.pdf=.idx) > /dev/null 2> /dev/null || true
75 | $(LATEX) $(LFLAGS) $<
76 | @grep -Eqc $(BIB_MISSING) $(@:.pdf=.log) && $(LATEX) $< ; true
77 | @grep -Eqc $(REFERENCE_UNDEFINED) $(@:.pdf=.log) && $(LATEX) $<; true
78 | -grep --color '\(Warning\|Overful\).*' $(@:.pdf=.log) || true
79 |
80 | %.pdf: %.tmp.pdf
81 | gs -sOutputFile=$@ $(GSFLAGS) $< < /dev/null > /dev/null
82 |
83 | %-online.pdf: %-online.tmp.pdf
84 | gs -sOutputFile=$@ $(GSFLAGS) -dPrinted=false $< < /dev/null > /dev/null
85 |
86 | %.tgz: %.pdf %
87 | @tar czf $@ $(@:.tgz=)/ $(@:.tgz=.pdf)
88 | @echo "Created $@." >&2; \
89 |
90 |
91 |
92 | #######################
93 | # Proceedings targets #
94 | #######################
95 |
96 | actes-online.tmp.tex: _master.tex
97 | cp $< $@
98 |
99 | actes-online.tmp.pdf: _articles.tex $(SRC)
100 |
101 |
102 | actes.tmp.pdf: _articles.tex $(SRC)
103 |
104 | actes.tmp.tex: _master.tex
105 | @sed 's/{sstic}/[paper]{sstic}/' $< > $@
106 |
107 |
108 |
109 | #################
110 | # Ebook helpers #
111 | #################
112 |
113 | %.eps: %.pdf
114 | pdftocairo -eps $< $@
115 |
116 | # TODO: Re-add stg to protect from GS attacks (restricted policy.xml)
117 | #%.png: %.jpg
118 | # convert $< $@
119 |
120 | %.ebook.html: %.ebook.tex sstic.cls llncs.cls
121 | @rm -f $(@:.html=.aux)
122 | $(HTLATEX) $< $(HTFLAGS) > /dev/null
123 | bibtex $(@:.html=) ||true
124 | $(LATEX) $(LFLAGS) $(@:.html=.tex)
125 | $(HTLATEX) $< $(HTFLAGS) > /dev/null
126 | -grep --color '\(Warning\|Overful\).*' $(@:.html=.log)
127 | @grep -Eqc $(BIB_MISSING) $(@:.html=.log) && $(HTLATEX) $< $(HTFLAGS) > /dev/null ; true
128 | @grep -Eqc $(REFERENCE_UNDEFINED) $(@:.html=.log) && $(HTLATEX) $< $(HTFLAGS) > /dev/null; true
129 |
130 |
131 | # TODO: Re-add a way to include authors metadata properly, if needed
132 | # TODO: What about the title?
133 | # -include article/metadata.mk
134 | # AUTHORS?=SSTIC
135 | # CALFLAGS+=--authors $(AUTHORS)
136 |
137 | %.epub: %.ebook.html
138 | ebook-convert $< $@ $(CALFLAGS)
139 |
140 | %.mobi: %.ebook.html
141 | ebook-convert $< $@ $(CALFLAGS)
142 |
143 | %.azw3: %.epub
144 | # ebook-convert doesn't rasterize svgs for azw3, but Kindle svg parser seems
145 | # buggy, so instead of doing html -> azw3 we do html -> epub -> azw3.
146 | ebook-convert $< $@ $(CALFLAGS)
147 |
148 |
149 |
150 | ###############################
151 | # Specific standalone targets #
152 | ###############################
153 |
154 | _articles.tex: $(SRC) Makefile
155 | @for d in [^_]*/; do \
156 | i=$$(basename "$$d"); \
157 | check_i=$$(echo "$$i" | tr -cd "a-zA-Z0-9_+-"); \
158 | if [ "$$i" = "$$check_i" ]; then \
159 | echo "\inputarticle{$$i}"; \
160 | fi; \
161 | done > $@
162 |
163 | Makefile.standalone-targets: $(SRC) Makefile
164 | @for d in [^_]*/; do \
165 | i=$$(basename "$$d"); \
166 | [ -f "$$i/master.tex" ] || continue; \
167 | check_i=$$(echo "$$i" | tr -cd "a-zA-Z0-9_+-"); \
168 | if [ "$$i" = "$$check_i" ]; then \
169 | echo "# Targets for $$i"; \
170 | echo; \
171 | echo "$$i.tmp.tex: _standalone.tex"; \
172 | echo " @sed 's/@@DIRECTORY@@/\$$(@:.tmp.tex=)/' _standalone.tex > \$$@"; \
173 | echo; \
174 | echo "$$i.ebook.tex: $$i.tmp.tex"; \
175 | echo " @sed 's/{sstic}/[ebook]{sstic}/' \$$< > \$$@"; \
176 | echo; \
177 | printf "%s" "$$i.tmp.pdf: $$i.tmp.tex $$(echo $$i/*.tex)"; \
178 | ls $$i/*.bib > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/*.bib)"; \
179 | ls $$i/img/*.jpg > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.jpg)"; \
180 | ls $$i/img/*.png > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.png)"; \
181 | ls $$i/img/*.eps > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.eps)"; \
182 | ls $$i/img/*.pdf > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.pdf)"; \
183 | echo; \
184 | echo; \
185 | printf "%s" "$$i.ebook.html: $$i.ebook.tex $$(echo $$i/*.tex)"; \
186 | ls $$i/*.bib > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/*.bib)"; \
187 | ls $$i/img/*.jpg > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.jpg)"; \
188 | ls $$i/img/*.png > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.png)"; \
189 | ls $$i/img/*.eps > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.eps)"; \
190 | ls $$i/img/*.pdf > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.pdf)"; \
191 | echo; \
192 | echo; \
193 | printf "%s" "actes.tmp.pdf: $$i.tmp.tex $$(echo $$i/*.tex)"; \
194 | ls $$i/*.bib > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/*.bib)"; \
195 | ls $$i/img/*.jpg > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.jpg)"; \
196 | ls $$i/img/*.png > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.png)"; \
197 | ls $$i/img/*.eps > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.eps)"; \
198 | ls $$i/img/*.pdf > /dev/null 2> /dev/null && printf "%s" " $$(echo $$i/img/*.pdf)"; \
199 | echo; \
200 | echo; \
201 | echo "$$i-clean:"; \
202 | echo " rm -f $$i.pdf $$i.azw3 $$i.epub $$i.mobi"; \
203 | echo; \
204 | echo "default: $$i.pdf"; \
205 | echo "clean: $$i-clean"; \
206 | echo "export: $$i.tgz"; \
207 | echo "Created targets for $$i." >&2; \
208 | echo; \
209 | echo; \
210 | echo; \
211 | else \
212 | echo "Ignoring invalid dir name ($$i)." >&2; \
213 | fi \
214 | done > Makefile.standalone-targets
215 |
216 | -include Makefile.standalone-targets
217 |
--------------------------------------------------------------------------------
/documentation/sstic/MonitoringAndProtectingSSHSessionsWithEBPF.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/MonitoringAndProtectingSSHSessionsWithEBPF.pdf
--------------------------------------------------------------------------------
/documentation/sstic/MonitoringAndProtectingSSHSessionsWithEBPF.tmp.out:
--------------------------------------------------------------------------------
1 | \BOOKMARK [0][-]{chapter.1}{Monitoring and protecting SSH sessions with eBPF}{}% 1
2 |
--------------------------------------------------------------------------------
/documentation/sstic/MonitoringAndProtectingSSHSessionsWithEBPF/biblio.bib:
--------------------------------------------------------------------------------
1 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:DBIR,
2 | author = {Verizon},
3 | title = {{Data breach investigations report (DBIR)}},
4 | howpublished = {\url{https://enterprise.verizon.com/resources/reports/2020-data-breach-investigations-report.pdf}},
5 | year = {2020},
6 | }
7 |
8 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Ext4,
9 | author = {IOVisor},
10 | title = {{Summarize ext4 operation latency distribution as a histogram}},
11 | howpublished = {\url{https://github.com/iovisor/bcc/blob/master/tools/ext4dist.py}},
12 | }
13 |
14 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Ubuntu2FA,
15 | author = {Ubuntu},
16 | title = {{Configure SSH to use two-factor authentication}},
17 | howpublished = {\url{https://ubuntu.com/tutorials/configure-ssh-2fa}},
18 | }
19 |
20 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Twitter,
21 | author = {Brian Barrett},
22 | title = {{How Twitter CEO Jack Dorsey's Account Was Hacked}},
23 | howpublished = {\url{https://www.wired.com/story/jack-dorsey-twitter-hacked}},
24 | year = {2020},
25 | }
26 |
27 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:MITRE,
28 | author = {MITRE},
29 | title = {{Sim card swap}},
30 | howpublished = {\url{https://attack.mitre.org/techniques/T1451}},
31 | }
32 |
33 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Proofpoint,
34 | author = {Or Safran and Itir Clarke},
35 | title = {{New Vulnerabilities Bypass Multi-Factor Authentication for Microsoft 365}},
36 | howpublished = {\url{https://www.proofpoint.com/us/blog/cloud-security/new-vulnerabilities-bypass-multi-factor-authentication-microsoft-365}},
37 | year = {2020},
38 | }
39 |
40 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:WSTrust,
41 | author = {OASIS},
42 | title = {{WS-Trust 1.4}},
43 | howpublished = {\url{http://docs.oasis-open.org/ws-sx/ws-trust/v1.4/ws-trust.html}},
44 | year = {2012},
45 | }
46 |
47 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Titan,
48 | author = {Victor Lomne and Thomas Roche},
49 | title = {{A side journey to Titan}},
50 | howpublished = {\url{https://ninjalab.io/wp-content/uploads/2021/01/a_side_journey_to_titan.pdf}},
51 | year = {2021},
52 | }
53 |
54 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Chrome,
55 | author = {Andy Greenberg},
56 | title = {{Chrome lets hackers phish even “Unphishable” Yubikey users}},
57 | howpublished = {\url{https://www.wired.com/story/chrome-yubikey-phishing-webusb}},
58 | year = {2018},
59 | }
60 |
61 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:GoDaddy,
62 | author = {Davey Winder},
63 | title = {{GoDaddy Confirms Data Breach: What Customers Need To Know}},
64 | howpublished = {\url{https://www.forbes.com/sites/daveywinder/2020/05/05/godaddy-confirms-data-breach-what-19-million-customers-need-to-know}},
65 | year = {2020},
66 | }
67 |
68 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:KernelCapabilities,
69 | author = {Linux},
70 | title = {{Kernel capabilities man pages}},
71 | howpublished = {\url{https://man7.org/linux/man-pages/man7/capabilities.7.html}},
72 | }
73 |
74 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:SSHProbe,
75 | author = {Guillaume Fournier},
76 | title = {{ssh-probe source code}},
77 | howpublished = {\url{https://github.com/Gui774ume/ssh-probe}},
78 | year = {2021},
79 | }
80 |
81 | @article{MonitoringAndProtectingSSHSessionsWithEBPF:BrendanGregg,
82 | author = {Brendan Gregg},
83 | title = {{BPF Performance Tools: Linux System and Application Observability}},
84 | year = {December 2019}
85 | }
86 |
87 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:SSH,
88 | author = {IETF},
89 | title = {{SSH protocol architecture}},
90 | howpublished = {\url{https://tools.ietf.org/html/rfc4251}},
91 | }
92 |
93 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Facebook,
94 | author = {Marlon Dutra},
95 | title = {{Scalable and secure access with SSH}},
96 | howpublished = {\url{https://engineering.fb.com/2016/09/12/security/scalable-and-secure-access-with-ssh}},
97 | year = {2016},
98 | }
99 |
100 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Chef,
101 | author = {Chef},
102 | title = {{Chef Infrastructure Automation}},
103 | howpublished = {\url{https://www.chef.io/products/chef-infra}},
104 | }
105 |
106 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Ansible,
107 | author = {Red Hat Software},
108 | title = {{Ansible IT automation}},
109 | howpublished = {\url{https://github.com/ansible/ansible}},
110 | }
111 |
112 | @article{MonitoringAndProtectingSSHSessionsWithEBPF:LorenzoDavid,
113 | author = {Lorenzo Fontana and David Calavera},
114 | title = {{Linux Observability with BPF}},
115 | year = {November 2019}
116 | }
117 |
118 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Kprobes,
119 | author = {Linux},
120 | title = {{Kprobe documentation}},
121 | howpublished = {\url{https://www.kernel.org/doc/Documentation/kprobes.txt}},
122 | }
123 |
124 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Tracepoints,
125 | author = {Linux},
126 | title = {{Tracepoint Documentation}},
127 | howpublished = {\url{https://www.kernel.org/doc/html/latest/trace/tracepoints.html}},
128 | }
129 |
130 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Uprobes,
131 | author = {Linux},
132 | title = {{Uprobe Documentation}},
133 | howpublished = {\url{https://www.kernel.org/doc/Documentation/trace/uprobetracer.txt}},
134 | }
135 |
136 | @article{MonitoringAndProtectingSSHSessionsWithEBPF:NSP,
137 | author = {Guillaume Fournier},
138 | title = {{Process level network security monitoring and enforcement with eBPF}},
139 | journal = {SSTIC},
140 | year = {2020}
141 | }
142 |
143 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:Profile,
144 | author = {Guillaume Fournier},
145 | title = {{ssh-probe profile example}},
146 | howpublished = {\url{https://github.com/Gui774ume/ssh-probe/blob/c7364d7eef0a76bc67e35ff3d85768467862a99d/profiles/vagrant.yaml}},
147 | year = {2021}
148 | }
149 |
150 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:OverrideReturn,
151 | author = {Jonathan Corbet},
152 | title = {{BPF-based error injection for the kernel}},
153 | howpublished = {\url{https://lwn.net/Articles/740146/}},
154 | year = {2017}
155 | }
156 |
157 | @misc{MonitoringAndProtectingSSHSessionsWithEBPF:KRSI,
158 | author = {KP Singh},
159 | title = {{Kernel Runtime Security Instrumentation}},
160 | howpublished = {\url{https://lwn.net/Articles/798918}},
161 | year = {2019},
162 | }
163 |
--------------------------------------------------------------------------------
/documentation/sstic/MonitoringAndProtectingSSHSessionsWithEBPF/img/mfa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/MonitoringAndProtectingSSHSessionsWithEBPF/img/mfa.png
--------------------------------------------------------------------------------
/documentation/sstic/MonitoringAndProtectingSSHSessionsWithEBPF/metadata.mk:
--------------------------------------------------------------------------------
1 | # Ajouter ici les prénoms et noms des auteurs
2 | AUTHORS?="Guillaume Fournier,Sylvain Afchain,Sylvain Baubeau"
3 |
--------------------------------------------------------------------------------
/documentation/sstic/README:
--------------------------------------------------------------------------------
1 | Template LaTeX pour SSTIC
2 |
3 |
4 | Utilisation
5 | ===========
6 |
7 | Pour compiler les sources, c'est aussi simple que :
8 | $ make from_docker
9 |
10 | Cette commande nécessite [docker](https://docs.docker.com/engine/install/).
11 | Si vous préférez utiliser votre distribution LaTeX locale:
12 | $ make
13 |
14 | Par défaut, cette commande compile tous les articles présents dans le
15 | répertoire (un article est un répertoire dont le nom est composé de
16 | caractères alphanumériques et qui contient un fichier master.tex).
17 |
18 | Avec le modèle fraichement téléchargé, cela produira l'article
19 | MonNom.pdf et l'article MyName.pdf.
20 |
21 | Pour que la compilation fonctionne, il faut installer un certain
22 | nombre de packages LaTeX. Sous Debian, les packages nécessaires sont
23 | texlive, texlive-latex-extra, texlive-science, texlive-lang-french,
24 | cm-super et gs.
25 |
26 |
27 | Ce modèle doit être la base commune à tous les articles LaTeX pour
28 | SSTIC.
29 |
30 | Un exemple d'article est disponible dans le répertoire MonNom/ En
31 | tant qu'auteur, vous devez copier ce répertoire et l'adapter.
32 |
33 | $ cp -r MonNom SecuriteDesTrotinettes
34 |
35 | Important : Vous ne devez modifier que les fichiers contenus dans le
36 | répertoire correspondant à votre article.
37 |
38 | Nous avons déjà sélectionné quelques packages. D'ailleurs, ces
39 | derniers nécessitent parfois d'installer des paquetages systèmes
40 | (lmodern pour lmodern, cm-super pour microtype). Si vous avez besoin
41 | d'ajouter un nouveau package, veuillez contacter l'équipe (actes (AT)
42 | lists.sstic.org) car nous devons vérifier la compatibilité des modules
43 | entre eux.
44 |
45 |
46 | Publication
47 | ===========
48 |
49 | Vous devez envoyer les *sources* à actes (AT) lists.sstic.org avant
50 | la date butoir. Les sources peuvent être exportées via la commande:
51 | $ make export
52 |
53 | L'auteur consciencieux prendre soin de corriger tous les warnings
54 | avant de nous envoyer ses sources :). En particulier, les erreurs les
55 | plus importantes sont signalées en rouge par la commande make.
56 |
57 | Vous pouvez envoyer une archive complète de votre répertoire
58 | (.tar.gz), ou encore un patch, ou un lien vers votre repository git
59 | dérivé du dépôt d'origine, etc.
60 |
61 |
62 | Découpage en sections
63 | =====================
64 |
65 | La classe llcns ne définit que 4 niveaux de sections :
66 |
67 | * section
68 | * subsection
69 | * subsubsection
70 | * paragraph
71 |
72 | Merci de *ne pas* redéfinir la profondeur des sections, en particulier
73 | le compteur secnumdepth.
74 |
75 |
76 | Si vous souhaitez mettre des annexes, vous pouvez utiliser \appendix :
77 | toutes les sections qui suivront cette commande seront identifiées par
78 | des lettres.
79 |
80 |
81 | Images, figures
82 | ===============
83 |
84 | Les images doivent être placées dans un environnement figure de
85 | façon à pouvoir y associer facilement une légende et un label.
86 |
87 | Quelques notes:
88 |
89 | * La légende doit être en dessous de l'image
90 |
91 | * Le placement de l'image par LaTeX ne garantit pas que votre
92 | image sera exactement où vous souhaitiez. Remplacez donc les
93 | formules "l'image ci-dessous" mais plutôt par sa référence
94 | ("l'image~\ref{fig:monnom:archi}")
95 |
96 | * il ne *FAUT* pas utiliser [H] pour forcer les figures flottantes
97 | à une position donnée. Cela n'est pas une option standard et
98 | elle est incompatible avec certains packages.
99 |
100 | * Format de l'image
101 | - Compatible pdflatex : PNG, PDF
102 | - Les formats vectoriels (comme PDF) sont recommandés car il
103 | arrive de devoir redimensionner les images à l'édition
104 | finale
105 |
106 | * Il est demandé de créér un répertoire img/ dédié à toutes les
107 | images
108 |
109 | * La référence doit être préfixée par votre fig:monnom pour éviter les
110 | collisions entre articles dans la version consolidée des actes.
111 |
112 | * Prenez soin de vos couleurs, SSTIC n'imprime qu'en dégradé de
113 | gris, votre choix de couleurs doit donc respecter cette
114 | contrainte.
115 |
116 | Votre article sera publié sur papier et sur notre site Web. Vous
117 | pouvez donc fournir les images dans les deux « formats » si cela
118 | vous semble nécessaire.
119 |
120 | Pour cela, le plus simple est d'inclure l'image aux deux formats
121 | (bw-archi.png et archi.png) dans le répertoire img/ et d'utiliser la
122 | macro \ifssticbw pour choisir la version à utiliser lors de la
123 | compilation. Un illutstration de cette macro est donnée dans
124 | l'article donné en exemple.
125 |
126 |
127 | Citations
128 | =========
129 |
130 | * Les références de citation doivent être préfixées par votre nom
131 | afin d'empêcher les collisions entre auteurs.
132 |
133 | * La bibliographie doit être dans contenue dans le fichier
134 | biblio.bib au format BibTeX
135 | Référence des types : http://newton.ex.ac.uk/tex/pack/bibtex/btxdoc/node6.html
136 | Référence des champs: http://newton.ex.ac.uk/tex/pack/bibtex/btxdoc/node7.html
137 |
138 | * Faites attention aux warning indiquant les références manquantes
139 |
140 | * Attention à la syntaxe lorsque vous citez plusieurs références:
141 | MAUVAIS: \cite{foo, bar}
142 | MAUVAIS: \cite{foo}\cite{bar}
143 | CORRECT: \cite{foo,bar}
144 |
145 | * BibTeX a la fâcheuse tendance de ne pas conserver les majuscules
146 | dans les titres des références. Pour éviter le problème, le plus
147 | simple est d'entourer la valeur du champ title avec deux accolades
148 | (le fichier MonNom/biblio.bib est un exemple)
149 |
150 |
151 | Texte verbatim
152 | ==============
153 |
154 | lstlisting
155 | ----------
156 |
157 | Documentation complète : http://mirrors.ctan.org/macros/latex/contrib/listings/listings.pdf
158 |
159 | Options utiles :
160 |
161 | * ``language'' peut prendre les valeurs suivantes:
162 | Python Java PHP [x86masm]Assembler
163 | C Perl HTML [Sharp]C
164 |
165 | * ``numbers'' pour numéroter les ligne (``stepnumbers'' pour contrôler l'incrément)
166 |
167 | * ``basicstyle'' pour modifier la police utilisée (exemple: basicstyle=\tiny)
168 |
169 |
170 | Mise en page
171 | ============
172 |
173 | De manière générale, LaTeX fait un bon travail de mise en page. Dans
174 | tous les cas, ne vous souciez de la mise en page qu'à *la fin* de la
175 | rédaction.
176 |
177 | Si la mise en page ne vous plaît pas, évitez d'utiliser des sauts de
178 | ligne (\\) ou de page (\newpage) car cela casse la mise en page et
179 | nuit au rendu global. On préférera, si vraiment c'est nécessaire, les
180 | commandes \smallskip, \medskip et \bigskip qui s'intègrent mieux à
181 | l'algorithme de mise en page.
182 |
183 | En cas de doute, n'hésitez pas à poser la question à
184 | actes@lists.sstic.org. En effet, l'objetif est d'avoir un document
185 | unique contenant les différents articles. Homogénéiser les styles rend
186 | la lecture des actes plus agréables.
187 |
188 | Concernant les figures flottantes, elles ont vocation, comme leur nom
189 | l'indique, à *flotter*. Ne vous étonnez donc pas qu'elles
190 | n'apparaissent pas au bon endroit. Il est possible d'indiquer à LaTeX
191 | où on souhaiterait voir ces images. Lorsque vous en avez peu, les
192 | indications [ht] ou [h] suffisent. En revanche, si vous avez beaucoup
193 | de figures, LaTeX peut refuser d'en mettre plus de deux par
194 | page. Auquel cas vous pouvez essayer [p], qui regroupe les figures sur
195 | de pages dédiées. Le rendu obtenu est souvent préférable à ce que l'on
196 | obtient en forçant le positionnement des figures. En aucun cas il ne
197 | faut utiliser [H] dans vos articles.
198 |
199 |
200 | E-Book
201 | ======
202 |
203 | La compilation des actes sous forme d'e-book se fait en appelant la
204 | commande make sstic-actes.{azw3,mobi,epub} (choisir l'extension
205 | voulue.
206 |
207 | Elle nécessite, en plus des paquetages LaTeX décrits ci-dessus, les
208 | outils suivants (les paquetages Debian sont indiqués entre parenthèses)
209 | - htlatex (tex4ht)
210 | - ebook-convert (calibre)
211 | - cssselect (python-cssselect)
212 |
213 | La génération des e-books est un procédé incertain et fragile,
214 | utilisant divers formats (HTML entre autres) et de nombreux outils qui
215 | ne fonctionnent que grâce à la magie noire et à beaucoup de
216 | scotch. Cela explique que la commande peut échouer, ou ne pas rendre
217 | les actes de manière fidèle.
218 |
219 |
220 | FAQ
221 | ===
222 |
223 | Q) docker: Got permission denied while trying to connect to the Docker daemon socket
224 |
225 | R) Il faut configuer docker pour être utilisable par un utilisateur non-privilégié:
226 | https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user
227 | ou utiliser la cible from_docker_su:
228 | $ make from_docker_su
229 |
230 |
231 | Q) Quels packages LaTeX sont nécessaires/autorisés ?
232 |
233 | R) La liste est disponible dans le fichier Dockerfile, utilisé pour
234 | générer l'image invoquée par la cible from_docker.
235 |
236 |
237 | Q) LaTeX m'insulte avec le message sordide suivant :
238 | ! pdfTeX error (font expansion): auto expansion is only possible with scalable fonts.
239 |
240 | R) Afin de fonctionner les packages séléctionnés nécessitent
241 | l'installation du paquetage Debian cm-super.
242 |
243 |
244 | Q) LaTeX m'insulte au sujet d'utf8x.def / algorithm.sty / du support
245 | du français. Que faire ?
246 |
247 | R) Il faut respectivement installer texlive-latex-extra,
248 | texlive-sciences et texlive-lang-french.
249 |
250 |
251 | Q) Autre message sordide de LaTeX:
252 | LaTeX Warning: Characters dropped after `\end{verbatim}'
253 |
254 | R) Le package verbatim change la façon dont les environnements du même
255 | nom sont interprétés. En particulier, si vous avez une ligne de la
256 | forme '\end{verbatim}}', la seconde accolade est ignorée, ce qui mène
257 | logiquement à une problème plus loin.
258 |
259 |
260 | Q) Puis-je redéfinir des commandes (newcommand / renewcommand) ?
261 |
262 | R) Si ce sont des commandes perso (TODO, myurl), vous pouvez, mais
263 | pensez à préfixer ces commandes par votre nom ou quelque chose les
264 | rendant uniques (pour éviter la collision avec d'autres articles). Si
265 | ce sont des commandes standard (labelitemi, secnumdepth), la réponse
266 | est *non*. En effet, cela nuit à l'homogénéité des actes, et surtout,
267 | cela risque de casser les articles suivant le vôtre dans le
268 | document. En cas de doute, contactez la liste actes@.
269 |
--------------------------------------------------------------------------------
/documentation/sstic/RuntimeSecurityMonitoringWithEBPF.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/RuntimeSecurityMonitoringWithEBPF.pdf
--------------------------------------------------------------------------------
/documentation/sstic/RuntimeSecurityMonitoringWithEBPF.tmp.out:
--------------------------------------------------------------------------------
1 | \BOOKMARK [0][-]{chapter.1}{Runtime Security Monitoring with eBPF}{}% 1
2 |
--------------------------------------------------------------------------------
/documentation/sstic/RuntimeSecurityMonitoringWithEBPF/biblio.bib:
--------------------------------------------------------------------------------
1 | @misc{RuntimeSecurityMonitoringWithEBPF:CNCF,
2 | author = {Cloud Native Computing Foundation (CNCF)},
3 | title = {{CNCF Survey 2020}},
4 | howpublished = {\url{https://www.cncf.io/wp-content/uploads/2020/11/CNCF_Survey_Report_2020.pdf}},
5 | year = {2020},
6 | }
7 |
8 | @article{RuntimeSecurityMonitoringWithEBPF:DevopsWithK8s,
9 | author = {John Arundel and Justin Domingus},
10 | title = {{Cloud Native DevOps with Kubernetes}},
11 | year = {March 2019}
12 | }
13 |
14 | @misc{RuntimeSecurityMonitoringWithEBPF:PCI,
15 | author = {Payment Card Industry (PCI)},
16 | title = {{PCI security standards}},
17 | howpublished = {\url{https://www.pcisecuritystandards.org/documents/Prioritized-Approach-for-PCI_DSS-v3_2.pdf}},
18 | year = {2019},
19 | }
20 |
21 | @misc{RuntimeSecurityMonitoringWithEBPF:GOAudit,
22 | author = {Slack},
23 | title = {{go-audit project source code}},
24 | howpublished = {\url{https://github.com/slackhq/go-audit}},
25 | year = {2016},
26 | }
27 |
28 | @misc{RuntimeSecurityMonitoringWithEBPF:LinuxAudit,
29 | author = {Linux},
30 | title = {{Linux Audit Documentation}},
31 | howpublished = {\url{https://github.com/linux-audit/audit-documentation/wiki}},
32 | }
33 |
34 | @misc{RuntimeSecurityMonitoringWithEBPF:Wazuh,
35 | author = {Wazuh},
36 | title = {{Wazuh runtime detection source code}},
37 | howpublished = {\url{https://github.com/wazuh/wazuh}},
38 | }
39 |
40 | @misc{RuntimeSecurityMonitoringWithEBPF:AuditBeat,
41 | author = {Elastic},
42 | title = {{Auditbeat File Integrity Module source code}},
43 | howpublished = {\url{https://github.com/elastic/beats/tree/master/auditbeat/module/file_integrity}},
44 | }
45 |
46 | @misc{RuntimeSecurityMonitoringWithEBPF:FilesystemNotification,
47 | author = {Michael Kerrisk},
48 | title = {{Filesystem Notification}},
49 | howpublished = {\url{https://lwn.net/Articles/604686}},
50 | year = {2014},
51 | }
52 |
53 | @misc{RuntimeSecurityMonitoringWithEBPF:Fanotify,
54 | author = {Linux},
55 | title = {{Fanotify manual page}},
56 | howpublished = {\url{https://man7.org/linux/man-pages/man7/fanotify.7.html}},
57 | }
58 |
59 | @misc{RuntimeSecurityMonitoringWithEBPF:KprobeDocumentation,
60 | author = {Linux},
61 | title = {{Kprobe documentation}},
62 | howpublished = {\url{https://www.kernel.org/doc/Documentation/kprobes.txt}},
63 | }
64 |
65 | @misc{RuntimeSecurityMonitoringWithEBPF:GOAuditContainerID,
66 | author = {Slack},
67 | title = {{go-audit procfs fallback to resolve a container ID}},
68 | howpublished = {\url{https://github.com/slackhq/go-audit/blob/d3dd09bab49077bb4f6998609acbed71fb659fdd/extras_containers_capsule8.go}},
69 | }
70 |
71 | @misc{RuntimeSecurityMonitoringWithEBPF:Graboid,
72 | author = {Palo Alto Networks},
73 | title = {{Graboid crypto jacking worm}},
74 | howpublished = {\url{https://unit42.paloaltonetworks.com/graboid-first-ever-cryptojacking-worm-found-in-images-on-docker-hub}},
75 | }
76 |
77 | @misc{RuntimeSecurityMonitoringWithEBPF:KprobeInterface,
78 | author = {Linux},
79 | title = {{Kprobe Interface source code}},
80 | howpublished = {\url{https://elixir.bootlin.com/linux/v5.11.3/source/kernel/trace/trace_probe.c#L551}},
81 | }
82 |
83 | @misc{RuntimeSecurityMonitoringWithEBPF:VerifierScalability,
84 | author = {Alexei Starovoitov},
85 | title = {{BPF: Improve verifier scalability}},
86 | howpublished = {\url{https://lwn.net/Articles/784571}},
87 | year = {2019},
88 | }
89 |
90 | @article{RuntimeSecurityMonitoringWithEBPF:NSP,
91 | author = {Guillaume Fournier},
92 | title = {{Process level network security monitoring and enforcement with eBPF}},
93 | journal = {SSTIC},
94 | year = {2020}
95 | }
96 |
97 | @misc{RuntimeSecurityMonitoringWithEBPF:VirtualMachine,
98 | author = {Jonathan Corbet},
99 | title = {{BPF: the universal in-kernel virtual machine}},
100 | howpublished = {\url{https://lwn.net/Articles/599755}},
101 | year = {2014},
102 | }
103 |
104 | @misc{RuntimeSecurityMonitoringWithEBPF:Firewall,
105 | author = {Jonathan Corbet},
106 | title = {{BPF comes to firewalls}},
107 | howpublished = {\url{https://lwn.net/Articles/747551}},
108 | year = {2018},
109 | }
110 |
111 | @misc{RuntimeSecurityMonitoringWithEBPF:KRSI,
112 | author = {KP Singh},
113 | title = {{Kernel Runtime Security Instrumentation}},
114 | howpublished = {\url{https://lwn.net/Articles/798918}},
115 | year = {2019},
116 | }
117 |
118 | @misc{RuntimeSecurityMonitoringWithEBPF:SecurityModule,
119 | author = {Jonathan Corbet},
120 | title = {{Writing your own security module}},
121 | howpublished = {\url{https://lwn.net/Articles/674949}},
122 | year = {2016},
123 | }
124 |
125 | @misc{RuntimeSecurityMonitoringWithEBPF:Loops,
126 | author = {Marta Rybczyńska},
127 | title = {{Bounded loops in eBPF}},
128 | howpublished = {\url{https://lwn.net/Articles/794934}},
129 | year = {2019},
130 | }
131 |
132 | @misc{RuntimeSecurityMonitoringWithEBPF:CORE,
133 | author = {Facebook},
134 | title = {{Compile Once - Run Everywhere}},
135 | howpublished = {\url{https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html}},
136 | year = {2020},
137 | }
138 |
139 | @misc{RuntimeSecurityMonitoringWithEBPF:BTF,
140 | author = {Linux},
141 | title = {{BTF type format}},
142 | howpublished = {\url{https://www.kernel.org/doc/html/latest/bpf/btf.html}},
143 | }
144 |
145 | @misc{RuntimeSecurityMonitoringWithEBPF:OpenByHandleAt,
146 | author = {Linux},
147 | title = {{open\_by\_handle\_at syscall manual page}},
148 | howpublished = {\url{https://man7.org/linux/man-pages/man2/open_by_handle_at.2.html}},
149 | }
150 |
151 | @misc{RuntimeSecurityMonitoringWithEBPF:IOUring,
152 | author = {Linux},
153 | title = {{io\_uring\_enter syscall manual page}},
154 | howpublished = {\url{https://manpages.debian.org/unstable/liburing-dev/io_uring_enter.2.en.html}},
155 | }
156 |
157 | @misc{RuntimeSecurityMonitoringWithEBPF:Tracepoints,
158 | author = {Linux},
159 | title = {{Tracepoint Documentation}},
160 | howpublished = {\url{https://www.kernel.org/doc/html/latest/trace/tracepoints.html}},
161 | }
162 |
163 | @misc{RuntimeSecurityMonitoringWithEBPF:Perf,
164 | author = {Linux},
165 | title = {{perf: Linux profiling with performance counters}},
166 | howpublished = {\url{https://perf.wiki.kernel.org/index.php/Main_Page}},
167 | }
168 |
169 | @misc{RuntimeSecurityMonitoringWithEBPF:Dentry,
170 | author = {Linux},
171 | title = {{Dentry structure definition in the Linux kernel}},
172 | howpublished = {\url{https://elixir.bootlin.com/linux/latest/ident/dentry}},
173 | }
174 |
175 | @misc{RuntimeSecurityMonitoringWithEBPF:Inode,
176 | author = {Linux},
177 | title = {{Inode structure definition in the Linux kernel}},
178 | howpublished = {\url{https://elixir.bootlin.com/linux/latest/ident/inode}},
179 | }
180 |
181 | @misc{RuntimeSecurityMonitoringWithEBPF:DatadogAgent,
182 | author = {Datadog},
183 | title = {{Datadog Runtime Security Agent source code}},
184 | howpublished = {\url{https://github.com/DataDog/datadog-agent}},
185 | }
186 |
187 | @misc{RuntimeSecurityMonitoringWithEBPF:DatadogEBPFLib,
188 | author = {Datadog},
189 | title = {{Datadog eBPF library}},
190 | howpublished = {\url{https://github.com/DataDog/ebpf/blob/ea64821c979335c97a9c935bafaf3981828ba0e9/manager/manager.go#L158}},
191 | }
192 |
193 | @misc{RuntimeSecurityMonitoringWithEBPF:Falco,
194 | author = {Sysdig},
195 | title = {{The Falco Project}},
196 | howpublished = {\url{https://falco.org/}},
197 | }
198 |
199 | @misc{RuntimeSecurityMonitoringWithEBPF:SecurityAgentPolicies,
200 | author = {Datadog},
201 | title = {{Datadog Runtime Security Agent default policies}},
202 | howpublished = {\url{https://github.com/DataDog/security-agent-policies/blob/master/runtime/default.policy}},
203 | }
204 |
205 | @article{RuntimeSecurityMonitoringWithEBPF:BrendanGregg,
206 | author = {Brendan Gregg},
207 | title = {{BPF Performance Tools: Linux System and Application Observability}},
208 | year = {December 2019}
209 | }
210 |
211 | @article{RuntimeSecurityMonitoringWithEBPF:LorenzoDavid,
212 | author = {Lorenzo Fontana and David Calavera},
213 | title = {{Linux Observability with BPF}},
214 | year = {November 2019}
215 | }
216 |
217 | @misc{RuntimeSecurityMonitoringWithEBPF:SignedBPF,
218 | author = {Jonathan Corbet},
219 | title = {{Toward signed BPF programs}},
220 | howpublished = {\url{https://lwn.net/Articles/853489}},
221 | year = {2019},
222 | }
223 |
--------------------------------------------------------------------------------
/documentation/sstic/RuntimeSecurityMonitoringWithEBPF/img/bw-rsa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/RuntimeSecurityMonitoringWithEBPF/img/bw-rsa.png
--------------------------------------------------------------------------------
/documentation/sstic/RuntimeSecurityMonitoringWithEBPF/img/rsa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/RuntimeSecurityMonitoringWithEBPF/img/rsa.png
--------------------------------------------------------------------------------
/documentation/sstic/RuntimeSecurityMonitoringWithEBPF/metadata.mk:
--------------------------------------------------------------------------------
1 | # Ajouter ici les prénoms et noms des auteurs
2 | AUTHORS?="Guillaume Fournier,Sylvain Afchain,Sylvain Baubeau"
3 |
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/comites.tex:
--------------------------------------------------------------------------------
1 | \section*{Comit\'e d'organisation}
2 | \begin{tabular}{@{}p{5cm}@{}p{6.5cm}@{}}
3 | Mathieu \textsc{Blanc} & CEA/DAM \\
4 | Aurélien \textsc{Bordes} & ANSSI \\
5 | Jean-Marie \textsc{Borello} & Thales \\
6 | Pierre \textsc{Capillon} & ANSSI \\
7 | Olivier \textsc{Courtay} & DGA-MI \\
8 | Isabelle \textsc{Kraemer} & Orange \\
9 | Olivier \textsc{Levillain} & Télécom SudParis \\
10 | Camille \textsc{Mougey} & CEA/DAM \\
11 | Benjamin \textsc{Morin} & ANSSI \\
12 | Nicolas \textsc{Prigent} & LSTI \\
13 | Raphaël \textsc{Rigo} & Airbus \\
14 | Frédéric \textsc{Tronel} & CentraleSupélec \\
15 | Sarah \textsc{Zennou} & Airbus \\
16 | \end{tabular}
17 |
18 | \clearpage
19 |
20 |
21 | \section*{Comité de programme}
22 | \begin{tabular}{@{}p{5cm}@{}p{6.5cm}@{}}
23 | Jean-Baptiste \textsc{Bédrune} & Ledger \\
24 | Mathieu \textsc{Blanc} & CEA/DAM \\
25 | Jean-Marie \textsc{Borello} & Thales \\
26 | Pierre \textsc{Capillon} & ANSSI \\
27 | Olivier \textsc{Courtay} & DGA-MI \\
28 | Marion \textsc{Daubignard} & ANSSI \\
29 | Géraud \textsc{De Drouas} & Présidence de la République \\
30 | Renaud \textsc{Dubourguais} & Synacktiv \\
31 | Ninon \textsc{Eyrolles} & \\
32 | Alexandre \textsc{Gazet} & Airbus \\
33 | Isabelle \textsc{Kraemer} & Orange \\
34 | Olivier \textsc{Levillain} & Télécom SudParis \\
35 | Thierry \textsc{Marinier} & Lexfo \\
36 | Clémentine \textsc{Maurice} & CNRS \\
37 | Xavier \textsc{Mehrenberger} & Airbus \\
38 | Benoît \textsc{Michau} & P1 security \\
39 | Benjamin \textsc{Morin} & ANSSI \\
40 | Camille \textsc{Mougey} & CEA/DAM \\
41 | Sarah \textsc{Nataf} & Orange \\
42 | Nicolas \textsc{Prigent} & LSTI \\
43 | Pierre-Michel \textsc{Ricordel} & ANSSI \\
44 | Raphaël \textsc{Rigo} & Airbus \\
45 | Philippe \textsc{Teuwen} & Quarkslab \\
46 | Frédéric \textsc{Tronel} & CentraleSupélec \\
47 | Valérie \textsc{Viet Triem Tong}& CentraleSupélec \\
48 | Gabrielle \textsc{Viala} & Quarkslab \\
49 | Sarah \textsc{Zennou} & Airbus \\
50 | \end{tabular}
51 |
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/logos/airbus.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/_frontmatter/logos/airbus.pdf
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/logos/anssi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/_frontmatter/logos/anssi.png
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/logos/cea.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/_frontmatter/logos/cea.pdf
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/logos/centrale-supelec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/_frontmatter/logos/centrale-supelec.png
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/logos/dga.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/_frontmatter/logos/dga.pdf
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/logos/lsti.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/_frontmatter/logos/lsti.pdf
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/logos/orange.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/_frontmatter/logos/orange.pdf
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/logos/thales.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/_frontmatter/logos/thales.png
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/logos/tsp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gui774ume/ssh-probe/f0a4a60cf4334bbbd89c6349824289e609537b16/documentation/sstic/_frontmatter/logos/tsp.jpg
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/partenaires.tex:
--------------------------------------------------------------------------------
1 | \noindent
2 | L'association STIC tient à remercier les employeurs des membres du
3 | comité d'organisation qui ont soutenu leur participation au CO.
4 |
5 | \begin{center}
6 | Airbus - ANSSI - CEA - CentraleSup\'elec - DGA - LSTI - Orange - Télécom SudParis - Thales
7 | \end{center}
8 |
9 | \vspace*{\stretch{1}}
10 |
11 | \begin{figure}[h]
12 | \begin{center}
13 | \parbox{3cm}{\includegraphics[width=3cm]{_frontmatter/logos/airbus}}
14 | \hfill
15 | \parbox{3cm}{\includegraphics[width=3cm]{_frontmatter/logos/anssi}}
16 | \hfill
17 | \parbox{3cm}{\includegraphics[width=3cm]{_frontmatter/logos/cea}}
18 | \end{center}
19 | \vfill
20 | \begin{center}
21 | \parbox{3cm}{\includegraphics[width=3cm]{_frontmatter/logos/centrale-supelec}}
22 | \hfill
23 | \parbox{3cm}{\includegraphics[width=3cm]{_frontmatter/logos/dga}}
24 | \hfill
25 | \parbox{3cm}{\includegraphics[width=3cm]{_frontmatter/logos/lsti}}
26 | \end{center}
27 | \vfill
28 | \begin{center}
29 | \parbox{3cm}{\includegraphics[width=3cm]{_frontmatter/logos/orange}}
30 | \hfill
31 | \parbox{3cm}{\includegraphics[width=3cm]{_frontmatter/logos/tsp}}
32 | \hfill
33 | \parbox{3cm}{\includegraphics[width=3cm]{_frontmatter/logos/thales}}
34 | \end{center}
35 | \end{figure}
36 |
37 | \vspace*{\stretch{1}}
38 |
--------------------------------------------------------------------------------
/documentation/sstic/_frontmatter/preface.tex:
--------------------------------------------------------------------------------
1 |
2 |
3 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
4 | eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
5 | minim veniam, quis nostrud exercitation ullamco laboris nisi ut
6 | aliquip ex ea commodo consequat. Duis aute irure dolor in
7 | reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
8 | pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
9 | culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum
10 | dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
11 | incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
12 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
13 | commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
14 | velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
15 | occaecat cupidatat non proident, sunt in culpa qui officia deserunt
16 | mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur
17 | adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore
18 | magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
19 | ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
20 | irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
21 | fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
22 | sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem
23 | ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
24 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
25 | veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
26 | ea commodo consequat. Duis aute irure dolor in reprehenderit in
27 | voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
28 | sint occaecat cupidatat non proident, sunt in culpa qui officia
29 | deserunt mollit anim id est laborum.
30 |
31 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
32 | eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
33 | minim veniam, quis nostrud exercitation ullamco laboris nisi ut
34 | aliquip ex ea commodo consequat. Duis aute irure dolor in
35 | reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
36 | pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
37 | culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum
38 | dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
39 | incididunt ut labore et dolore magna aliqua.
40 |
41 | \begin{flushright} Bon symposium,
42 |
43 | Gérard Menvussa, pour le comit{\'e} d'Organisation.
44 | \end{flushright}
45 |
--------------------------------------------------------------------------------
/documentation/sstic/_master.tex:
--------------------------------------------------------------------------------
1 | \documentclass{sstic}
2 |
3 |
4 |
5 | \begin{document}
6 |
7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9 | % FRONT MATTER
10 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12 |
13 | \pagestyle{empty}
14 | \frontmatter
15 |
16 | \ifssticpaper
17 | ~
18 |
19 | \cleardoublepage
20 | \fi
21 |
22 | \begin{titlepage}
23 | \vspace*{\stretch{1}}
24 | \noindent
25 |
26 | % TODO: A remplacer par la couverture intérieure en noir et blanc
27 | \centering
28 | (Couverture intérieure)
29 |
30 | \vspace*{\stretch{1}}
31 | \end{titlepage}
32 |
33 | \clearpage
34 |
35 | \vspace*{\stretch{1}}
36 | \begin{center}
37 | % TODO: A renseigner
38 | ISBN: XXX-X-XXXXXXX-X-X
39 | \end{center}
40 |
41 |
42 | \clearpage
43 | \phantomsection
44 | \pdfbookmark{Pr\'eface}{preface}
45 | \chapter*{Préface}
46 |
47 | \input{_frontmatter/preface}
48 | \clearpage
49 |
50 | \input{_frontmatter/comites}
51 | \clearpage
52 |
53 | \input{_frontmatter/partenaires}
54 | \myclearpage
55 |
56 |
57 |
58 |
59 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61 | % MAIN MATTER
62 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 |
65 | \tableofcontents
66 | \mainmatter
67 | \pagestyle{fancy}
68 |
69 | \renewcommand{\thelstlisting}{\arabic{lstlisting}}
70 |
71 | \part*{Conférences}
72 | \addcontentsline{toc}{part}{Conférences}
73 |
74 | \input{_articles}
75 |
76 |
77 |
78 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80 | % BACK MATTER
81 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83 |
84 |
85 | \myclearpage
86 | \phantomsection
87 | \cleardoublepage
88 | \newpage
89 | \renewcommand{\indexname}{Index des auteurs}
90 | \addcontentsline{toc}{chapter}{\indexname}
91 | \printindex
92 |
93 |
94 | % TODO: Ajout d'une éventuelle postface
95 | % \cleardoublepage
96 |
97 | % \centerline{\Large \textbf{Postface}}
98 | % \vspace{2cm}
99 | % (Eventuelle postface)
100 | % \vspace{1mm}
101 |
102 | % \hfill Le comité d'organisation de SSTIC 20XX
103 |
104 |
105 |
106 | \pagestyle{empty}
107 |
108 | \clearpage
109 |
110 | \vspace*{\stretch{1}}
111 | \begin{center}
112 | \footnotesize
113 | Achevé d'imprimer par l'imprimerie de l'université de Rennes 1 en mai 2018.
114 |
115 | Dépôt légal: juin 20XX
116 |
117 | Éditeur: association STIC
118 | \end{center}
119 |
120 | \ifssticpaper
121 | \cleardoublepage
122 | \fi
123 |
124 | \end{document}
125 |
--------------------------------------------------------------------------------
/documentation/sstic/_standalone.tex:
--------------------------------------------------------------------------------
1 | \documentclass{sstic}
2 |
3 | \begin{document}
4 | \selectlanguage{french}
5 |
6 | \input{@@DIRECTORY@@/master}
7 |
8 | \end{document}
9 |
--------------------------------------------------------------------------------
/documentation/sstic/tex4ht.env:
--------------------------------------------------------------------------------
1 |
2 | G.png
3 | Gdvipng -T tight -D 212 -bg Transparent -pp %%2:%%2 %%1 -o %%3
4 | G.gif
5 | Gdvipng -T tight -D 212 -bg Transparent -gif -pp %%2:%%2 %%1 -o %%3
6 | G.
7 | Gdvips -Ppdf -mode ibmvga -D 110 -f %%1 -pp %%2 > zz%%4.ps
8 | Gconvert -crop 0x0 -density 110x110 -transparent '#FFFFFF' zz%%4.ps %%3
9 | Grm zz%%4.ps
10 |
11 |
--------------------------------------------------------------------------------
/ebpf/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 |
--------------------------------------------------------------------------------
/ebpf/bpf/bpf_helpers.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef __BPF_HELPERS_H
9 | #define __BPF_HELPERS_H
10 |
11 | #define bpf_printk(fmt, ...) \
12 | ({ \
13 | char ____fmt[] = fmt; \
14 | bpf_trace_printk(____fmt, sizeof(____fmt), \
15 | ##__VA_ARGS__); \
16 | })
17 |
18 | /* helper macro to place programs, maps, license in
19 | * different sections in elf_bpf file. Section names
20 | * are interpreted by elf_bpf loader
21 | */
22 | #define SEC(NAME) __attribute__((section(NAME), used))
23 |
24 | /* helper functions called from eBPF programs written in C */
25 | static void *(*bpf_map_lookup_elem)(void *map, void *key) =
26 | (void *)BPF_FUNC_map_lookup_elem;
27 | static int (*bpf_map_update_elem)(void *map, void *key, void *value,
28 | unsigned long long flags) =
29 | (void *)BPF_FUNC_map_update_elem;
30 | static int (*bpf_map_delete_elem)(void *map, void *key) =
31 | (void *)BPF_FUNC_map_delete_elem;
32 | static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) =
33 | (void *)BPF_FUNC_probe_read;
34 | static unsigned long long (*bpf_ktime_get_ns)(void) =
35 | (void *)BPF_FUNC_ktime_get_ns;
36 | static void (*bpf_tail_call)(void *ctx, void *map, int index) =
37 | (void *)BPF_FUNC_tail_call;
38 | static int (*bpf_override_return)(void *ctx, unsigned long rc) =
39 | (void *) BPF_FUNC_override_return;
40 | static int (*bpf_probe_read_str)(void *dst, int size, void *unsafe_ptr) =
41 | (void *)BPF_FUNC_probe_read_str;
42 | static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
43 | (void *)BPF_FUNC_trace_printk;
44 | static unsigned long long (*bpf_get_smp_processor_id)(void) =
45 | (void *)BPF_FUNC_get_smp_processor_id;
46 | static unsigned long long (*bpf_get_current_pid_tgid)(void) =
47 | (void *)BPF_FUNC_get_current_pid_tgid;
48 | static unsigned long long (*bpf_get_current_uid_gid)(void) =
49 | (void *)BPF_FUNC_get_current_uid_gid;
50 | static int (*bpf_get_current_comm)(void *buf, int buf_size) =
51 | (void *)BPF_FUNC_get_current_comm;
52 | static int (*bpf_perf_event_read)(void *map, int index) =
53 | (void *)BPF_FUNC_perf_event_read;
54 | static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) =
55 | (void *)BPF_FUNC_clone_redirect;
56 | static unsigned long long (*bpf_get_current_task)(void) =
57 | (void *)BPF_FUNC_get_current_task;
58 | static int (*bpf_redirect)(int ifindex, int flags) =
59 | (void *)BPF_FUNC_redirect;
60 | static int (*bpf_perf_event_output)(void *ctx, void *map,
61 | unsigned long long flags, void *data,
62 | int size) =
63 | (void *)BPF_FUNC_perf_event_output;
64 | static int (*bpf_skb_get_tunnel_key)(void *ctx, void *key, int size, int flags) =
65 | (void *)BPF_FUNC_skb_get_tunnel_key;
66 | static int (*bpf_skb_set_tunnel_key)(void *ctx, void *key, int size, int flags) =
67 | (void *)BPF_FUNC_skb_set_tunnel_key;
68 | static unsigned long long (*bpf_get_prandom_u32)(void) =
69 | (void *)BPF_FUNC_get_prandom_u32;
70 |
71 | /* llvm builtin functions that eBPF C program may use to
72 | * emit BPF_LD_ABS and BPF_LD_IND instructions
73 | */
74 | struct sk_buff;
75 | unsigned long long load_byte(void *skb,
76 | unsigned long long off) asm("llvm.bpf.load.byte");
77 | unsigned long long load_half(void *skb,
78 | unsigned long long off) asm("llvm.bpf.load.half");
79 | unsigned long long load_word(void *skb,
80 | unsigned long long off) asm("llvm.bpf.load.word");
81 |
82 | /* a helper structure used by eBPF C program
83 | * to describe map attributes to elf_bpf loader
84 | */
85 | #define BUF_SIZE_MAP_NS 256
86 |
87 | // struct bpf_map_def
88 | // {
89 | // unsigned int type;
90 | // unsigned int key_size;
91 | // unsigned int value_size;
92 | // unsigned int max_entries;
93 | // unsigned int map_flags;
94 | // unsigned int pinning;
95 | // char namespace[BUF_SIZE_MAP_NS];
96 | // };
97 |
98 | static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) =
99 | (void *)BPF_FUNC_skb_store_bytes;
100 | static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) =
101 | (void *)BPF_FUNC_l3_csum_replace;
102 | static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) =
103 | (void *)BPF_FUNC_l4_csum_replace;
104 |
105 | #if defined(__x86_64__)
106 |
107 | #define PT_REGS_PARM1(x) ((x)->di)
108 | #define PT_REGS_PARM2(x) ((x)->si)
109 | #define PT_REGS_PARM3(x) ((x)->dx)
110 | #define PT_REGS_PARM4(x) ((x)->cx)
111 | #define PT_REGS_PARM5(x) ((x)->r8)
112 | #define PT_REGS_RET(x) ((x)->sp)
113 | #define PT_REGS_FP(x) ((x)->bp)
114 | #define PT_REGS_RC(x) ((x)->ax)
115 | #define PT_REGS_SP(x) ((x)->sp)
116 | #define PT_REGS_IP(x) ((x)->ip)
117 |
118 | #elif defined(__s390x__)
119 |
120 | #define PT_REGS_PARM1(x) ((x)->gprs[2])
121 | #define PT_REGS_PARM2(x) ((x)->gprs[3])
122 | #define PT_REGS_PARM3(x) ((x)->gprs[4])
123 | #define PT_REGS_PARM4(x) ((x)->gprs[5])
124 | #define PT_REGS_PARM5(x) ((x)->gprs[6])
125 | #define PT_REGS_RET(x) ((x)->gprs[14])
126 | #define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */
127 | #define PT_REGS_RC(x) ((x)->gprs[2])
128 | #define PT_REGS_SP(x) ((x)->gprs[15])
129 | #define PT_REGS_IP(x) ((x)->ip)
130 |
131 | #elif defined(__aarch64__)
132 |
133 | #define PT_REGS_PARM1(x) ((x)->regs[0])
134 | #define PT_REGS_PARM2(x) ((x)->regs[1])
135 | #define PT_REGS_PARM3(x) ((x)->regs[2])
136 | #define PT_REGS_PARM4(x) ((x)->regs[3])
137 | #define PT_REGS_PARM5(x) ((x)->regs[4])
138 | #define PT_REGS_RET(x) ((x)->regs[30])
139 | #define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */
140 | #define PT_REGS_RC(x) ((x)->regs[0])
141 | #define PT_REGS_SP(x) ((x)->sp)
142 | #define PT_REGS_IP(x) ((x)->pc)
143 |
144 | #elif defined(__powerpc__)
145 |
146 | #define PT_REGS_PARM1(x) ((x)->gpr[3])
147 | #define PT_REGS_PARM2(x) ((x)->gpr[4])
148 | #define PT_REGS_PARM3(x) ((x)->gpr[5])
149 | #define PT_REGS_PARM4(x) ((x)->gpr[6])
150 | #define PT_REGS_PARM5(x) ((x)->gpr[7])
151 | #define PT_REGS_RC(x) ((x)->gpr[3])
152 | #define PT_REGS_SP(x) ((x)->sp)
153 | #define PT_REGS_IP(x) ((x)->nip)
154 |
155 | #endif
156 |
157 | #ifdef __powerpc__
158 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; })
159 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
160 | #else
161 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); })
162 | #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ bpf_probe_read(&(ip), sizeof(ip), \
163 | (void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
164 | #endif
165 |
166 | #endif
167 |
--------------------------------------------------------------------------------
/ebpf/bpf/bpf_map.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #define BUF_SIZE_MAP_NS 256
9 |
10 | typedef struct bpf_map_def {
11 | unsigned int type;
12 | unsigned int key_size;
13 | unsigned int value_size;
14 | unsigned int max_entries;
15 | unsigned int map_flags;
16 | unsigned int inner_map_idx;
17 | unsigned int pinning;
18 | char namespace[BUF_SIZE_MAP_NS];
19 | } bpf_map_def;
20 |
21 | enum bpf_pin_type {
22 | PIN_NONE = 0,
23 | PIN_OBJECT_NS,
24 | PIN_GLOBAL_NS,
25 | PIN_CUSTOM_NS,
26 | };
27 |
--------------------------------------------------------------------------------
/ebpf/events/execve.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _EXECVE_H_
9 | #define _EXECVE_H_
10 |
11 | struct binary_path_key_t
12 | {
13 | u32 profile_cookie;
14 | char path[PATH_MAX_LEN];
15 | };
16 |
17 | struct binary_path_action_t {
18 | u64 inode;
19 | u8 action;
20 | };
21 |
22 | struct bpf_map_def SEC("maps/allowed_binaries") allowed_binaries = {
23 | .type = BPF_MAP_TYPE_HASH,
24 | .key_size = sizeof(struct binary_path_key_t),
25 | .value_size = sizeof(u8),
26 | .max_entries = 1500,
27 | };
28 |
29 | struct process_monitoring_notification_t {
30 | struct notification_t notification;
31 | struct binary_path_key_t key;
32 | };
33 |
34 | // trace_execve assesses an execve call
35 | __attribute__((always_inline)) static int trace_execve(struct pt_regs *ctx, char *filename)
36 | {
37 | u64 pid_tgid = bpf_get_current_pid_tgid();
38 | u32 pid = pid_tgid >> 32;
39 |
40 | // get binary context
41 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
42 | if (binary_ctx == NULL) {
43 | // the process is not part of an ssh session, ignore
44 | return 0;
45 | }
46 |
47 | // generate new binary cookie
48 | binary_ctx->binary_cookie = bpf_get_prandom_u32();
49 |
50 | // select profile cookie
51 | u32 cookie = binary_ctx->session_cookie;
52 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
53 | if (session == NULL) {
54 | return 0;
55 | }
56 |
57 | // select binary action
58 | struct process_monitoring_notification_t notif = {};
59 | notif.key.profile_cookie = session->profile_cookie;
60 | int real_len = bpf_probe_read_str(¬if.key.path, PATH_MAX_LEN, filename);
61 | u8 *action = bpf_map_lookup_elem(&allowed_binaries, ¬if.key);
62 | int blocked;
63 | u32 sent_action;
64 | if (action == NULL) {
65 | sent_action = session->unknown_binary_default;
66 | blocked = override_return(ctx, session->unknown_binary_default, session, CATEGORY_PROCESS_MONITORING);
67 | } else {
68 | sent_action = *action;
69 | blocked = override_return(ctx, sent_action, session, CATEGORY_PROCESS_MONITORING);
70 | }
71 | if (blocked && sent_action == ACTION_MFA) {
72 | sent_action = ACTION_BLOCK;
73 | }
74 | if (should_notify(sent_action)) {
75 | notif.notification.timestamp = bpf_ktime_get_ns();
76 | notif.notification.session_login_timestamp = session->login_timestamp;
77 | notif.notification.profile_cookie = notif.key.profile_cookie;
78 | notif.notification.session_cookie = binary_ctx->session_cookie;
79 | notif.notification.category = CATEGORY_PROCESS_MONITORING;
80 | notif.notification.action = sent_action;
81 | fill_process_context(¬if.notification);
82 |
83 | u32 cpu = bpf_get_smp_processor_id();
84 | bpf_perf_event_output(ctx, ¬ifications, cpu, ¬if, sizeof(struct notification_t) + sizeof(u32) + (real_len & PATH_MAX_LEN));
85 | }
86 | return 0;
87 | }
88 |
89 | SEC("kprobe/__x64_sys_execve")
90 | int kprobe_execve(struct pt_regs *kprobe_ctx) {
91 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
92 | char *filename;
93 | bpf_probe_read(&filename, sizeof(filename), &PT_REGS_PARM1(ctx));
94 | return trace_execve(kprobe_ctx, filename);
95 | }
96 |
97 | SEC("kprobe/__x64_sys_execveat")
98 | int kprobe_execveat(struct pt_regs *kprobe_ctx) {
99 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
100 | char *filename;
101 | bpf_probe_read(&filename, sizeof(filename), &PT_REGS_PARM2(ctx));
102 | return trace_execve(kprobe_ctx, filename);
103 | }
104 |
105 | #endif
106 |
--------------------------------------------------------------------------------
/ebpf/events/file_access.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _FILE_ACCESS_H_
9 | #define _FILE_ACCESS_H_
10 |
11 | // trace_fd_access assesses a file descriptor access
12 | __attribute__((always_inline)) static int trace_fd_access(struct pt_regs *ctx, int fd1, int fd2)
13 | {
14 | u32 pid = bpf_get_current_pid_tgid();
15 |
16 | // select binary context
17 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
18 | if (binary_ctx == NULL) {
19 | // the process is not part of an ssh session, ignore
20 | return 0;
21 | }
22 |
23 | // select profile cookie
24 | u32 cookie = binary_ctx->session_cookie;
25 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
26 | if (session == NULL) {
27 | return 0;
28 | }
29 |
30 | // select file descriptor
31 | struct illegal_fd_t selector = {};
32 | selector.binary_cookie = binary_ctx->binary_cookie;
33 | if (fd1 != 0) {
34 | selector.fd = fd1;
35 | u8 *action = bpf_map_lookup_elem(&illegal_fds, &selector);
36 | if (action != NULL) {
37 | return override_return(ctx, *action, session, CATEGORY_FIM);
38 | }
39 | }
40 | if (fd2 != 0) {
41 | selector.fd = fd2;
42 | u8 *action = bpf_map_lookup_elem(&illegal_fds, &selector);
43 | if (action != NULL) {
44 | return override_return(ctx, *action, session, CATEGORY_FIM);
45 | }
46 | }
47 | return 0;
48 | }
49 |
50 | // read
51 |
52 | SEC("kprobe/__x64_sys_read")
53 | int kprobe_read(struct pt_regs *kprobe_ctx) {
54 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
55 | int fd;
56 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
57 | return trace_fd_access(kprobe_ctx, fd, 0);
58 | }
59 |
60 | SEC("kprobe/__x64_sys_readv")
61 | int kprobe_readv(struct pt_regs *kprobe_ctx) {
62 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
63 | int fd;
64 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
65 | return trace_fd_access(kprobe_ctx, fd, 0);
66 | }
67 |
68 | SEC("kprobe/__x64_sys_preadv")
69 | int kprobe_preadv(struct pt_regs *kprobe_ctx) {
70 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
71 | int fd;
72 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
73 | return trace_fd_access(kprobe_ctx, fd, 0);
74 | }
75 |
76 | SEC("kprobe/__x64_sys_preadv2")
77 | int kprobe_preadv2(struct pt_regs *kprobe_ctx) {
78 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
79 | int fd;
80 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
81 | return trace_fd_access(kprobe_ctx, fd, 0);
82 | }
83 |
84 | SEC("kprobe/__x64_sys_pread64")
85 | int kprobe_pread64(struct pt_regs *kprobe_ctx) {
86 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
87 | int fd;
88 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
89 | return trace_fd_access(kprobe_ctx, fd, 0);
90 | }
91 |
92 | SEC("kprobe/__x64_sys_readdir")
93 | int kprobe_readdir(struct pt_regs *kprobe_ctx) {
94 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
95 | int fd;
96 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
97 | return trace_fd_access(kprobe_ctx, fd, 0);
98 | }
99 |
100 | SEC("kprobe/__x64_sys_readahead")
101 | int kprobe_readahead(struct pt_regs *kprobe_ctx) {
102 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
103 | int fd;
104 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
105 | return trace_fd_access(kprobe_ctx, fd, 0);
106 | }
107 |
108 | // Write
109 |
110 | SEC("kprobe/__x64_sys_write")
111 | int kprobe_write(struct pt_regs *kprobe_ctx) {
112 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
113 | int fd;
114 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
115 | return trace_fd_access(kprobe_ctx, fd, 0);
116 | }
117 |
118 | SEC("kprobe/__x64_sys_writev")
119 | int kprobe_writev(struct pt_regs *kprobe_ctx) {
120 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
121 | int fd;
122 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
123 | return trace_fd_access(kprobe_ctx, fd, 0);
124 | }
125 |
126 | SEC("kprobe/__x64_sys_pwritev")
127 | int kprobe_pwritev(struct pt_regs *kprobe_ctx) {
128 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
129 | int fd;
130 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
131 | return trace_fd_access(kprobe_ctx, fd, 0);
132 | }
133 |
134 | SEC("kprobe/__x64_sys_pwritev2")
135 | int kprobe_pwritev2(struct pt_regs *kprobe_ctx) {
136 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
137 | int fd;
138 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
139 | return trace_fd_access(kprobe_ctx, fd, 0);
140 | }
141 |
142 | SEC("kprobe/__x64_sys_pwrite64")
143 | int kprobe_pwrite64(struct pt_regs *kprobe_ctx) {
144 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
145 | int fd;
146 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
147 | return trace_fd_access(kprobe_ctx, fd, 0);
148 | }
149 |
150 | // mmap
151 |
152 | SEC("kprobe/__x64_sys_mmap")
153 | int kprobe_mmap(struct pt_regs *kprobe_ctx) {
154 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
155 | int fd;
156 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM5(ctx));
157 | return trace_fd_access(kprobe_ctx, fd, 0);
158 | }
159 |
160 | SEC("kprobe/__x64_sys_pipe")
161 | int kprobe_pipe(struct pt_regs *kprobe_ctx) {
162 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
163 | int fds[2];
164 | bpf_probe_read(&fds, sizeof(fds), &PT_REGS_PARM1(ctx));
165 | return trace_fd_access(kprobe_ctx, fds[0], fds[1]);
166 | }
167 |
168 | SEC("kprobe/__x64_sys_dup")
169 | int kprobe_dup(struct pt_regs *kprobe_ctx) {
170 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
171 | int fd;
172 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
173 | return trace_fd_access(kprobe_ctx, fd, 0);
174 | }
175 |
176 | SEC("kprobe/__x64_sys_dup2")
177 | int kprobe_dup2(struct pt_regs *kprobe_ctx) {
178 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
179 | int fd1;
180 | bpf_probe_read(&fd1, sizeof(fd1), &PT_REGS_PARM1(ctx));
181 | int fd2;
182 | bpf_probe_read(&fd2, sizeof(fd2), &PT_REGS_PARM2(ctx));
183 | return trace_fd_access(kprobe_ctx, fd1, fd2);
184 | }
185 |
186 | SEC("kprobe/__x64_sys_dup3")
187 | int kprobe_dup3(struct pt_regs *kprobe_ctx) {
188 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
189 | int fd1;
190 | bpf_probe_read(&fd1, sizeof(fd1), &PT_REGS_PARM1(ctx));
191 | int fd2;
192 | bpf_probe_read(&fd2, sizeof(fd2), &PT_REGS_PARM2(ctx));
193 | return trace_fd_access(kprobe_ctx, fd1, fd2);
194 | }
195 |
196 | SEC("kprobe/__x64_sys_sendfile")
197 | int kprobe_sendfile(struct pt_regs *kprobe_ctx) {
198 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
199 | int fd1;
200 | bpf_probe_read(&fd1, sizeof(fd1), &PT_REGS_PARM1(ctx));
201 | int fd2;
202 | bpf_probe_read(&fd2, sizeof(fd2), &PT_REGS_PARM2(ctx));
203 | return trace_fd_access(kprobe_ctx, fd1, fd2);
204 | }
205 |
206 | SEC("kprobe/__x64_sys_sendfile64")
207 | int kprobe_sendfile64(struct pt_regs *kprobe_ctx) {
208 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
209 | int fd1;
210 | bpf_probe_read(&fd1, sizeof(fd1), &PT_REGS_PARM1(ctx));
211 | int fd2;
212 | bpf_probe_read(&fd2, sizeof(fd2), &PT_REGS_PARM2(ctx));
213 | return trace_fd_access(kprobe_ctx, fd1, fd2);
214 | }
215 |
216 | SEC("kprobe/__x64_sys_fcntl")
217 | int kprobe_fcntl(struct pt_regs *kprobe_ctx) {
218 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
219 | int fd;
220 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
221 | return trace_fd_access(kprobe_ctx, fd, 0);
222 | }
223 |
224 | SEC("kprobe/__x64_sys_flock")
225 | int kprobe_flock(struct pt_regs *kprobe_ctx) {
226 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
227 | int fd;
228 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
229 | return trace_fd_access(kprobe_ctx, fd, 0);
230 | }
231 |
232 | SEC("kprobe/__x64_sys_fsync")
233 | int kprobe_fsync(struct pt_regs *kprobe_ctx) {
234 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
235 | int fd;
236 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
237 | return trace_fd_access(kprobe_ctx, fd, 0);
238 | }
239 |
240 | SEC("kprobe/__x64_sys_fdatasync")
241 | int kprobe_fdatasync(struct pt_regs *kprobe_ctx) {
242 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
243 | int fd;
244 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
245 | return trace_fd_access(kprobe_ctx, fd, 0);
246 | }
247 |
248 | SEC("kprobe/__x64_sys_syncfs")
249 | int kprobe_syncfs(struct pt_regs *kprobe_ctx) {
250 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
251 | int fd;
252 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
253 | return trace_fd_access(kprobe_ctx, fd, 0);
254 | }
255 |
256 | SEC("kprobe/__x64_sys_sync_file_range")
257 | int kprobe_sync_file_range(struct pt_regs *kprobe_ctx) {
258 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
259 | int fd;
260 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
261 | return trace_fd_access(kprobe_ctx, fd, 0);
262 | }
263 |
264 | SEC("kprobe/__x64_sys_sync_fallocate")
265 | int kprobe_fallocate(struct pt_regs *kprobe_ctx) {
266 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
267 | int fd;
268 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
269 | return trace_fd_access(kprobe_ctx, fd, 0);
270 | }
271 |
272 | SEC("kprobe/__x64_sys_splice")
273 | int kprobe_splice(struct pt_regs *kprobe_ctx) {
274 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
275 | int fd1;
276 | bpf_probe_read(&fd1, sizeof(fd1), &PT_REGS_PARM1(ctx));
277 | int fd2;
278 | bpf_probe_read(&fd2, sizeof(fd2), &PT_REGS_PARM3(ctx));
279 | return trace_fd_access(kprobe_ctx, fd1, fd2);
280 | }
281 |
282 | SEC("kprobe/__x64_sys_tee")
283 | int kprobe_tee(struct pt_regs *kprobe_ctx) {
284 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
285 | int fd1;
286 | bpf_probe_read(&fd1, sizeof(fd1), &PT_REGS_PARM1(ctx));
287 | int fd2;
288 | bpf_probe_read(&fd2, sizeof(fd2), &PT_REGS_PARM2(ctx));
289 | return trace_fd_access(kprobe_ctx, fd1, fd2);
290 | }
291 |
292 | SEC("kprobe/__x64_sys_vmsplice")
293 | int kprobe_vmsplice(struct pt_regs *kprobe_ctx) {
294 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
295 | int fd;
296 | bpf_probe_read(&fd, sizeof(fd), &PT_REGS_PARM1(ctx));
297 | return trace_fd_access(kprobe_ctx, fd, 0);
298 | }
299 |
300 | #endif
301 |
--------------------------------------------------------------------------------
/ebpf/events/kill.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _KILL_H_
9 | #define _KILL_H_
10 |
11 | // trace_kill assesses a kill call
12 | __attribute__((always_inline)) static int trace_kill(struct pt_regs *ctx, int sig, int syscall) {
13 | u32 pid = bpf_get_current_pid_tgid();
14 |
15 | // Only stop SIGKILL, SIGALRM, SIGTERM or SIGSTOP
16 | if (sig != 9 && sig != 14 && sig != 15 && sig != 19) {
17 | return 0;
18 | }
19 |
20 | // get binary context
21 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
22 | if (binary_ctx == NULL) {
23 | // the process is not part of an ssh session, ignore
24 | return 0;
25 | }
26 |
27 | // select profile cookie
28 | u32 cookie = binary_ctx->session_cookie;
29 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
30 | if (session == NULL) {
31 | return 0;
32 | }
33 | u32 cached_profile_cookie = session->profile_cookie;
34 |
35 | int blocked = override_return(ctx, session->kill, session, CATEGORY_KILL);
36 | u32 action = session->kill;
37 | if (blocked && action == ACTION_MFA) {
38 | action = ACTION_BLOCK;
39 | }
40 | if (should_notify(action)) {
41 | struct syscall_notification_t notif = {};
42 | notif.notification.timestamp = bpf_ktime_get_ns();
43 | notif.notification.session_login_timestamp = session->login_timestamp;
44 | notif.notification.profile_cookie = cached_profile_cookie;
45 | notif.notification.session_cookie = binary_ctx->session_cookie;
46 | notif.notification.category = CATEGORY_KILL;
47 | notif.notification.action = action;
48 | notif.syscall = syscall;
49 | fill_process_context(¬if.notification);
50 |
51 | u32 cpu = bpf_get_smp_processor_id();
52 | bpf_perf_event_output(ctx, ¬ifications, cpu, ¬if, sizeof(notif));
53 | }
54 | return 0;
55 | }
56 |
57 | SEC("kprobe/__x64_sys_kill")
58 | int kprobe_kill(struct pt_regs *kprobe_ctx) {
59 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
60 | int sig;
61 | bpf_probe_read(&sig, sizeof(sig), &PT_REGS_PARM2(ctx));
62 | return trace_kill(kprobe_ctx, sig, 62);
63 | }
64 |
65 | SEC("kprobe/__x64_sys_tkill")
66 | int kprobe_tkill(struct pt_regs *kprobe_ctx) {
67 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
68 | int sig;
69 | bpf_probe_read(&sig, sizeof(sig), &PT_REGS_PARM2(ctx));
70 | return trace_kill(kprobe_ctx, sig, 200);
71 | }
72 |
73 | SEC("kprobe/__x64_sys_tgkill")
74 | int kprobe_tgkill(struct pt_regs *kprobe_ctx) {
75 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
76 | int sig;
77 | bpf_probe_read(&sig, sizeof(sig), &PT_REGS_PARM2(ctx));
78 | return trace_kill(kprobe_ctx, sig, 234);
79 | }
80 |
81 | #endif
82 |
--------------------------------------------------------------------------------
/ebpf/events/open.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _OPEN_H_
9 | #define _OPEN_H_
10 |
11 | #define IS_ILLEGAL_OPEN 1
12 |
13 | struct open_context_t {
14 | u64 inode;
15 | int flags;
16 | u8 action;
17 | };
18 |
19 | struct bpf_map_def SEC("maps/open_context") open_context = {
20 | .type = BPF_MAP_TYPE_LRU_HASH,
21 | .key_size = sizeof(u64),
22 | .value_size = sizeof(struct open_context_t),
23 | .max_entries = 15000,
24 | };
25 |
26 | // trace_open_ret assesses an open return call
27 | __attribute__((always_inline)) static int trace_open(struct pt_regs *ctx, int flags)
28 | {
29 | u64 pid_tgid = bpf_get_current_pid_tgid();
30 | u32 pid = pid_tgid >> 32;
31 |
32 | // get session context
33 | struct session_context_t *session = get_session(pid);
34 | if (session == NULL) {
35 | return 0;
36 | }
37 |
38 | // cache open flag
39 | struct open_context_t open_ctx = {};
40 | open_ctx.flags = flags;
41 | open_ctx.action = ACTION_ALLOW;
42 | bpf_map_update_elem(&open_context, &pid_tgid, &open_ctx, BPF_ANY);
43 | return 0;
44 | }
45 |
46 | SEC("kprobe/__x64_sys_open")
47 | int kprobe_open(struct pt_regs *kprobe_ctx) {
48 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
49 | int flags;
50 | bpf_probe_read(&flags, sizeof(flags), &PT_REGS_PARM2(ctx));
51 | return trace_open(kprobe_ctx, flags);
52 | }
53 |
54 | SEC("kprobe/__x64_sys_openat")
55 | int kprobe_openat(struct pt_regs *kprobe_ctx) {
56 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
57 | int flags;
58 | bpf_probe_read(&flags, sizeof(flags), &PT_REGS_PARM3(ctx));
59 | return trace_open(kprobe_ctx, flags);
60 | }
61 |
62 |
63 | struct illegal_fd_t {
64 | u32 fd;
65 | u32 binary_cookie;
66 | };
67 |
68 | struct bpf_map_def SEC("maps/illegal_fds") illegal_fds = {
69 | .type = BPF_MAP_TYPE_LRU_HASH,
70 | .key_size = sizeof(struct illegal_fd_t),
71 | .value_size = sizeof(u8),
72 | .max_entries = 15000,
73 | };
74 |
75 | // get_inode_ino returns the inode number of an inode structure
76 | __attribute__((always_inline)) unsigned long get_inode_ino(struct inode *inode)
77 | {
78 | unsigned long ino;
79 | bpf_probe_read(&ino, sizeof(inode), &inode->i_ino);
80 | return ino;
81 | }
82 |
83 | // get_dentry_ino returns the inode number of the inode of the provided dentry
84 | __attribute__((always_inline)) unsigned long get_dentry_ino(struct dentry *dentry)
85 | {
86 | struct inode *d_inode;
87 | bpf_probe_read(&d_inode, sizeof(d_inode), &dentry->d_inode);
88 | return get_inode_ino(d_inode);
89 | }
90 |
91 | // get_path_ino returns the inode number of the inode of the dentry of the provided path
92 | __attribute__((always_inline)) unsigned long __attribute__((always_inline)) get_path_ino(struct path *path) {
93 | struct dentry *dentry;
94 | bpf_probe_read(&dentry, sizeof(dentry), &path->dentry);
95 |
96 | if (dentry) {
97 | return get_dentry_ino(dentry);
98 | }
99 | return 0;
100 | }
101 |
102 | struct inode_selector_t {
103 | u64 inode;
104 | u32 profile_cookie;
105 | u8 access_right;
106 | };
107 |
108 | struct bpf_map_def SEC("maps/inodes") inodes = {
109 | .type = BPF_MAP_TYPE_HASH,
110 | .key_size = sizeof(struct inode_selector_t),
111 | .value_size = sizeof(u8),
112 | .max_entries = 150000,
113 | };
114 |
115 | SEC("kprobe/vfs_open")
116 | int kprobe_vfs_open(struct pt_regs *ctx) {
117 | struct path *path = (struct path *)PT_REGS_PARM1(ctx);
118 | u64 pid_tgid = bpf_get_current_pid_tgid();
119 | u32 pid = pid_tgid >> 32;
120 |
121 | // select open context
122 | struct open_context_t *open_ctx = bpf_map_lookup_elem(&open_context, &pid_tgid);
123 | if (open_ctx == NULL) {
124 | // this call to vfs_open did not come from an open syscall, ignore
125 | return 0;
126 | }
127 |
128 | // select binary context
129 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
130 | if (binary_ctx == NULL) {
131 | // the process is not part of an ssh session, ignore
132 | return 0;
133 | }
134 |
135 | // select profile cookie
136 | u32 cookie = binary_ctx->session_cookie;
137 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
138 | if (session == NULL) {
139 | return 0;
140 | }
141 |
142 | // check if the inode is in the list of watched files
143 | struct inode_selector_t selector = {};
144 | selector.inode = get_path_ino(path);
145 | selector.profile_cookie = session->profile_cookie;
146 | open_ctx->inode = selector.inode;
147 |
148 | if ((open_ctx->flags & 1)) {
149 | selector.access_right = WRITE_ACCESS;
150 | } else {
151 | selector.access_right = READ_ACCESS;
152 | }
153 |
154 | u8 *action = bpf_map_lookup_elem(&inodes, &selector);
155 | if (action == NULL) {
156 | // allow by default
157 | open_ctx->action = session->unknown_file_default;
158 | } else {
159 | open_ctx->action = *action;
160 | }
161 |
162 | return 0;
163 | }
164 |
165 | struct fim_notification_t {
166 | struct notification_t notification;
167 | u64 inode;
168 | int syscall;
169 | };
170 |
171 | // trace_open_ret assesses an open return call
172 | __attribute__((always_inline)) static int trace_open_ret(struct pt_regs *ctx, int fd, int syscall)
173 | {
174 | u64 pid_tgid = bpf_get_current_pid_tgid();
175 | u32 pid = pid_tgid >> 32;
176 |
177 | // select binary context
178 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
179 | if (binary_ctx == NULL) {
180 | // the process is not part of an ssh session, ignore
181 | return 0;
182 | }
183 |
184 | // select profile cookie
185 | u32 cookie = binary_ctx->session_cookie;
186 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
187 | if (session == NULL) {
188 | return 0;
189 | }
190 | u32 cached_profile_cookie = session->profile_cookie;
191 |
192 | // check if the file descriptor is illegal
193 | struct open_context_t *open_ctx = bpf_map_lookup_elem(&open_context, &pid_tgid);
194 | if (open_ctx == NULL) {
195 | // shouldn't happen, ignore
196 | return 0;
197 | }
198 | if (open_ctx->action != ACTION_ALLOW) {
199 | struct illegal_fd_t fd_selector = {};
200 | fd_selector.fd = fd;
201 | fd_selector.binary_cookie = binary_ctx->binary_cookie;
202 | u8 action = open_ctx->action;
203 | bpf_map_update_elem(&illegal_fds, &fd_selector, &action, BPF_ANY);
204 |
205 | int blocked = override_return(ctx, action, session, CATEGORY_FIM);
206 | if (blocked && action == ACTION_MFA) {
207 | action = ACTION_BLOCK;
208 | }
209 | if (should_notify(action)) {
210 | struct fim_notification_t notif = {};
211 | notif.notification.timestamp = bpf_ktime_get_ns();
212 | notif.notification.session_login_timestamp = session->login_timestamp;
213 | notif.notification.profile_cookie = cached_profile_cookie;
214 | notif.notification.session_cookie = binary_ctx->session_cookie;
215 | notif.notification.category = CATEGORY_FIM;
216 | notif.notification.action = action;
217 | notif.inode = open_ctx->inode;
218 | notif.syscall = syscall;
219 | fill_process_context(¬if.notification);
220 |
221 | u32 cpu = bpf_get_smp_processor_id();
222 | bpf_perf_event_output(ctx, ¬ifications, cpu, ¬if, sizeof(notif));
223 | }
224 | }
225 | return 0;
226 | }
227 |
228 | SEC("kretprobe/__x64_sys_open")
229 | int kretprobe_open(struct pt_regs *ctx) {
230 | int fd = (int) PT_REGS_RC(ctx);
231 | return trace_open_ret(ctx, fd, 2);
232 | }
233 |
234 | SEC("kretprobe/__x64_sys_openat")
235 | int kretprobe_openat(struct pt_regs *ctx) {
236 | int fd = (int) PT_REGS_RC(ctx);
237 | return trace_open_ret(ctx, fd, 257);
238 | }
239 |
240 | #endif
241 |
--------------------------------------------------------------------------------
/ebpf/events/os_level_protections.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _OS_LEVEL_PROTECTIONS_H_
9 | #define _OS_LEVEL_PROTECTIONS_H_
10 |
11 | // trace_os_level_protections assesses an OS level protection call
12 | __attribute__((always_inline)) static int trace_os_level_protections(struct pt_regs *ctx, int syscall) {
13 | u32 pid = bpf_get_current_pid_tgid();
14 |
15 | // get binary context
16 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
17 | if (binary_ctx == NULL) {
18 | // the process is not part of an ssh session, ignore
19 | return 0;
20 | }
21 |
22 | // select profile cookie
23 | u32 cookie = binary_ctx->session_cookie;
24 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
25 | if (session == NULL) {
26 | return 0;
27 | }
28 | u32 cached_profile_cookie = session->profile_cookie;
29 |
30 | int blocked = override_return(ctx, session->os_level_protections, session, CATEGORY_OS_LEVEL_PROTECTIONS);
31 | u32 action = session->os_level_protections;
32 | if (blocked && action == ACTION_MFA) {
33 | action = ACTION_BLOCK;
34 | }
35 | if (should_notify(action)) {
36 | struct syscall_notification_t notif = {};
37 | notif.notification.timestamp = bpf_ktime_get_ns();
38 | notif.notification.session_login_timestamp = session->login_timestamp;
39 | notif.notification.profile_cookie = cached_profile_cookie;
40 | notif.notification.session_cookie = binary_ctx->session_cookie;
41 | notif.notification.category = CATEGORY_OS_LEVEL_PROTECTIONS;
42 | notif.notification.action = action;
43 | notif.syscall = syscall;
44 | fill_process_context(¬if.notification);
45 |
46 | u32 cpu = bpf_get_smp_processor_id();
47 | bpf_perf_event_output(ctx, ¬ifications, cpu, ¬if, sizeof(notif));
48 | }
49 | return 0;
50 | }
51 |
52 | SYSCALL(create_module, trace_os_level_protections, 174)
53 | SYSCALL(delete_module, trace_os_level_protections, 176)
54 | SYSCALL(query_module, trace_os_level_protections, 178)
55 | SYSCALL(init_module, trace_os_level_protections, 175)
56 | SYSCALL(finit_module, trace_os_level_protections, 313)
57 | SYSCALL(reboot, trace_os_level_protections, 169)
58 | SYSCALL(settimeofday, trace_os_level_protections, 164)
59 | SYSCALL(clock_settime, trace_os_level_protections, 227)
60 | SYSCALL(clock_adjtime, trace_os_level_protections, 305)
61 | SYSCALL(stime, trace_os_level_protections, 160)
62 | SYSCALL(setrlimit, trace_os_level_protections, 160)
63 | SYSCALL(sysinfo, trace_os_level_protections, 99)
64 | SYSCALL(syslog, trace_os_level_protections, 103)
65 | SYSCALL(getrusage, trace_os_level_protections, 98)
66 | SYSCALL(add_key, trace_os_level_protections, 248)
67 | SYSCALL(keyctl, trace_os_level_protections, 250)
68 | SYSCALL(request_key, trace_os_level_protections, 249)
69 | SYSCALL(unshare, trace_os_level_protections, 272)
70 | SYSCALL(get_kernel_syms, trace_os_level_protections, 177)
71 | SYSCALL(get_mempolicy, trace_os_level_protections, 239)
72 | SYSCALL(set_mempolicy, trace_os_level_protections, 238)
73 | SYSCALL(mbind, trace_os_level_protections, 237)
74 | SYSCALL(move_pages, trace_os_level_protections, 279)
75 | SYSCALL(migrate_pages, trace_os_level_protections, 256)
76 | SYSCALL(kexec_load, trace_os_level_protections, 246)
77 | SYSCALL(kexec_file_load, trace_os_level_protections, 320)
78 | SYSCALL(lookup_dcookie, trace_os_level_protections, 212)
79 | SYSCALL(mount, trace_os_level_protections, 165)
80 | SYSCALL(umount, trace_os_level_protections, 166)
81 | SYSCALL(umount2, trace_os_level_protections, 166)
82 | SYSCALL(name_to_handle_at, trace_os_level_protections, 303)
83 | SYSCALL(open_by_handle_at, trace_os_level_protections, 304)
84 | SYSCALL(nfsservctl, trace_os_level_protections, 180)
85 | SYSCALL(pivot_root, trace_os_level_protections, 155)
86 | SYSCALL(swapon, trace_os_level_protections, 167)
87 | SYSCALL(swapoff, trace_os_level_protections, 168)
88 | SYSCALL(sysfs, trace_os_level_protections, 139)
89 | SYSCALL(_sysctl, trace_os_level_protections, 156)
90 | SYSCALL(uselib, trace_os_level_protections, 134)
91 | SYSCALL(ustat, trace_os_level_protections, 136)
92 | SYSCALL(chroot, trace_os_level_protections, 161)
93 | SYSCALL(sethostname, trace_os_level_protections, 170)
94 | SYSCALL(setdomainname, trace_os_level_protections, 171)
95 | SYSCALL(iopl, trace_os_level_protections, 172)
96 | SYSCALL(ioperm, trace_os_level_protections, 173)
97 |
98 | #endif
99 |
--------------------------------------------------------------------------------
/ebpf/events/performance_monitoring.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _PERFORMANCE_MONITORING_H_
9 | #define _PERFORMANCE_MONITORING_H_
10 |
11 | // trace_performance_monitoring assesses a performance monitoring call
12 | __attribute__((always_inline)) static int trace_performance_monitoring(struct pt_regs *ctx, int syscall) {
13 | u32 pid = bpf_get_current_pid_tgid();
14 |
15 | // get binary context
16 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
17 | if (binary_ctx == NULL) {
18 | // the process is not part of an ssh session, ignore
19 | return 0;
20 | }
21 |
22 | // select profile cookie
23 | u32 cookie = binary_ctx->session_cookie;
24 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
25 | if (session == NULL) {
26 | return 0;
27 | }
28 | u32 cached_profile_cookie = session->profile_cookie;
29 |
30 | int blocked = override_return(ctx, session->performance_monitoring, session, CATEGORY_PERFORMANCE_MONITORING);
31 | u32 action = session->performance_monitoring;
32 | if (blocked && action == ACTION_MFA) {
33 | action = ACTION_BLOCK;
34 | }
35 | if (should_notify(action)) {
36 | struct syscall_notification_t notif = {};
37 | notif.notification.timestamp = bpf_ktime_get_ns();
38 | notif.notification.session_login_timestamp = session->login_timestamp;
39 | notif.notification.profile_cookie = cached_profile_cookie;
40 | notif.notification.session_cookie = binary_ctx->session_cookie;
41 | notif.notification.category = CATEGORY_PERFORMANCE_MONITORING;
42 | notif.notification.action = action;
43 | notif.syscall = syscall;
44 | fill_process_context(¬if.notification);
45 |
46 | u32 cpu = bpf_get_smp_processor_id();
47 | bpf_perf_event_output(ctx, ¬ifications, cpu, ¬if, sizeof(notif));
48 | }
49 | return 0;
50 | }
51 |
52 | SYSCALL(bpf, trace_performance_monitoring, 321)
53 | SYSCALL(perf_event_open, trace_performance_monitoring, 298)
54 |
55 | #endif
56 |
--------------------------------------------------------------------------------
/ebpf/events/privilege_elevation.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _PRIVILEGE_ELEVATION_H_
9 | #define _PRIVILEGE_ELEVATION_H_
10 |
11 | // trace_privilege_elevation assesses a privilege elevation call
12 | __attribute__((always_inline)) static int trace_privilege_elevation(struct pt_regs *ctx, int syscall) {
13 | u32 pid = bpf_get_current_pid_tgid();
14 |
15 | // get binary context
16 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
17 | if (binary_ctx == NULL) {
18 | // the process is not part of an ssh session, ignore
19 | return 0;
20 | }
21 |
22 | // select profile cookie
23 | u32 cookie = binary_ctx->session_cookie;
24 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
25 | if (session == NULL) {
26 | return 0;
27 | }
28 | u32 cached_profile_cookie = session->profile_cookie;
29 |
30 | int blocked = override_return(ctx, session->privilege_elevation, session, CATEGORY_PRIVILEGE_ELEVATION);
31 | u32 action = session->privilege_elevation;
32 | if (blocked && action == ACTION_MFA) {
33 | action = ACTION_BLOCK;
34 | }
35 | if (should_notify(action)) {
36 | struct syscall_notification_t notif = {};
37 | notif.notification.timestamp = bpf_ktime_get_ns();
38 | notif.notification.session_login_timestamp = session->login_timestamp;
39 | notif.notification.profile_cookie = cached_profile_cookie;
40 | notif.notification.session_cookie = binary_ctx->session_cookie;
41 | notif.notification.category = CATEGORY_PRIVILEGE_ELEVATION;
42 | notif.notification.action = action;
43 | notif.syscall = syscall;
44 | fill_process_context(¬if.notification);
45 |
46 | u32 cpu = bpf_get_smp_processor_id();
47 | bpf_perf_event_output(ctx, ¬ifications, cpu, ¬if, sizeof(notif));
48 | }
49 | return 0;
50 | }
51 |
52 | SEC("kprobe/__x64_sys_setuid")
53 | int kprobe_setuid(struct pt_regs *kprobe_ctx) {
54 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
55 | int id;
56 | bpf_probe_read(&id, sizeof(id), &PT_REGS_PARM1(ctx));
57 | if (id == 0) {
58 | return trace_privilege_elevation(kprobe_ctx, 105);
59 | }
60 | return 0;
61 | }
62 |
63 | SEC("kprobe/__x64_sys_setgid")
64 | int kprobe_setgid(struct pt_regs *kprobe_ctx) {
65 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
66 | int id;
67 | bpf_probe_read(&id, sizeof(id), &PT_REGS_PARM1(ctx));
68 | if (id == 0) {
69 | return trace_privilege_elevation(kprobe_ctx, 106);
70 | }
71 | return 0;
72 | }
73 |
74 | SEC("kprobe/__x64_sys_setfsuid")
75 | int kprobe_setfsuid(struct pt_regs *kprobe_ctx) {
76 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
77 | int id;
78 | bpf_probe_read(&id, sizeof(id), &PT_REGS_PARM1(ctx));
79 | if (id == 0) {
80 | return trace_privilege_elevation(kprobe_ctx, 122);
81 | }
82 | return 0;
83 | }
84 |
85 | SEC("kprobe/__x64_sys_setfsgid")
86 | int kprobe_setfsgid(struct pt_regs *kprobe_ctx) {
87 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
88 | int id;
89 | bpf_probe_read(&id, sizeof(id), &PT_REGS_PARM1(ctx));
90 | if (id == 0) {
91 | return trace_privilege_elevation(kprobe_ctx, 123);
92 | }
93 | return 0;
94 | }
95 |
96 | SEC("kprobe/__x64_sys_setreuid")
97 | int kprobe_setreuid(struct pt_regs *kprobe_ctx) {
98 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
99 | int rid;
100 | bpf_probe_read(&rid, sizeof(rid), &PT_REGS_PARM1(ctx));
101 | int eid;
102 | bpf_probe_read(&eid, sizeof(eid), &PT_REGS_PARM2(ctx));
103 | if (rid == 0 || eid == 0) {
104 | return trace_privilege_elevation(kprobe_ctx, 113);
105 | }
106 | return 0;
107 | }
108 |
109 | SEC("kprobe/__x64_sys_setregid")
110 | int kprobe_setregid(struct pt_regs *kprobe_ctx) {
111 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
112 | int rid;
113 | bpf_probe_read(&rid, sizeof(rid), &PT_REGS_PARM1(ctx));
114 | int eid;
115 | bpf_probe_read(&eid, sizeof(eid), &PT_REGS_PARM2(ctx));
116 | if (rid == 0 || eid == 0) {
117 | return trace_privilege_elevation(kprobe_ctx, 114);
118 | }
119 | return 0;
120 | }
121 |
122 | SEC("kprobe/__x64_sys_setresgid")
123 | int kprobe_setresgid(struct pt_regs *kprobe_ctx) {
124 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
125 | int rgid;
126 | bpf_probe_read(&rgid, sizeof(rgid), &PT_REGS_PARM1(ctx));
127 | int egid;
128 | bpf_probe_read(&egid, sizeof(egid), &PT_REGS_PARM2(ctx));
129 | int sgid;
130 | bpf_probe_read(&sgid, sizeof(sgid), &PT_REGS_PARM2(ctx));
131 | if (rgid == 0 || egid == 0 || sgid == 0) {
132 | return trace_privilege_elevation(kprobe_ctx, 119);
133 | }
134 | return 0;
135 | }
136 |
137 | SEC("kprobe/__x64_sys_setresuid")
138 | int kprobe_setresuid(struct pt_regs *kprobe_ctx) {
139 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
140 | int ruid;
141 | bpf_probe_read(&ruid, sizeof(ruid), &PT_REGS_PARM1(ctx));
142 | int euid;
143 | bpf_probe_read(&euid, sizeof(euid), &PT_REGS_PARM2(ctx));
144 | int suid;
145 | bpf_probe_read(&suid, sizeof(suid), &PT_REGS_PARM2(ctx));
146 | if (ruid == 0 || euid == 0 || suid == 0) {
147 | return trace_privilege_elevation(kprobe_ctx, 117);
148 | }
149 | return 0;
150 | }
151 |
152 | SEC("kprobe/__x64_sys_setpgid")
153 | int kprobe_setpgid(struct pt_regs *kprobe_ctx) {
154 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
155 | int pid;
156 | bpf_probe_read(&pid, sizeof(pid), &PT_REGS_PARM1(ctx));
157 | int pgid;
158 | bpf_probe_read(&pgid, sizeof(pgid), &PT_REGS_PARM2(ctx));
159 | if (pid == 0 || pgid == 0) {
160 | return trace_privilege_elevation(kprobe_ctx, 109);
161 | }
162 | return 0;
163 | }
164 |
165 | SYSCALL(setns, trace_privilege_elevation, 308)
166 | SYSCALL(setsid, trace_privilege_elevation, 112)
167 | SYSCALL(capset, trace_privilege_elevation, 126)
168 | SYSCALL(personality, trace_privilege_elevation, 135)
169 | SYSCALL(setpriority, trace_privilege_elevation, 141)
170 | SYSCALL(sched_setparam, trace_privilege_elevation, 142)
171 | SYSCALL(sched_setscheduler, trace_privilege_elevation, 144)
172 | SYSCALL(sched_setaffinity, trace_privilege_elevation, 203)
173 | SYSCALL(set_tid_address, trace_privilege_elevation, 218)
174 | SYSCALL(set_thread_area, trace_privilege_elevation, 205)
175 | SYSCALL(ioprio_set, trace_privilege_elevation, 251)
176 | SYSCALL(acct, trace_privilege_elevation, 163)
177 | SYSCALL(quotactl, trace_privilege_elevation, 179)
178 |
179 | #endif
180 |
--------------------------------------------------------------------------------
/ebpf/events/process_level_protections.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _PROCESS_LEVEL_PROTECTIONS_H_
9 | #define _PROCESS_LEVEL_PROTECTIONS_H_
10 |
11 | // trace_process_level_protections assesses a process level protection call
12 | __attribute__((always_inline)) static int trace_process_level_protections(struct pt_regs *ctx, int syscall) {
13 | u32 pid = bpf_get_current_pid_tgid();
14 |
15 | // get binary context
16 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
17 | if (binary_ctx == NULL) {
18 | // the process is not part of an ssh session, ignore
19 | return 0;
20 | }
21 |
22 | // select profile cookie
23 | u32 cookie = binary_ctx->session_cookie;
24 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
25 | if (session == NULL) {
26 | return 0;
27 | }
28 | u32 cached_profile_cookie = session->profile_cookie;
29 |
30 | int blocked = override_return(ctx, session->process_level_protections, session, CATEGORY_PROCESS_LEVEL_PROTECTIONS);
31 | u32 action = session->process_level_protections;
32 | if (blocked && action == ACTION_MFA) {
33 | action = ACTION_BLOCK;
34 | }
35 | if (should_notify(action)) {
36 | struct syscall_notification_t notif = {};
37 | notif.notification.timestamp = bpf_ktime_get_ns();
38 | notif.notification.session_login_timestamp = session->login_timestamp;
39 | notif.notification.profile_cookie = cached_profile_cookie;
40 | notif.notification.session_cookie = binary_ctx->session_cookie;
41 | notif.notification.category = CATEGORY_PROCESS_LEVEL_PROTECTIONS;
42 | notif.notification.action = action;
43 | notif.syscall = syscall;
44 | fill_process_context(¬if.notification);
45 |
46 | u32 cpu = bpf_get_smp_processor_id();
47 | bpf_perf_event_output(ctx, ¬ifications, cpu, ¬if, sizeof(notif));
48 | }
49 | return 0;
50 | }
51 |
52 | SYSCALL(ptrace, trace_process_level_protections, 101)
53 | SYSCALL(memfd_create, trace_process_level_protections, 319)
54 | SYSCALL(kcmp, trace_process_level_protections, 312)
55 | SYSCALL(process_vm_readv, trace_process_level_protections, 310)
56 | SYSCALL(process_vm_writev, trace_process_level_protections, 311)
57 | SYSCALL(userfaultfd, trace_process_level_protections, 323)
58 | SYSCALL(modify_ldt, trace_process_level_protections, 154)
59 |
60 | #endif
61 |
--------------------------------------------------------------------------------
/ebpf/events/socket.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _UN_SOCKET_H_
9 | #define _UN_SOCKET_H_
10 |
11 | // trace_socket assesses a socket creation call
12 | __attribute__((always_inline)) static int trace_socket(struct pt_regs *ctx, int syscall) {
13 | u32 pid = bpf_get_current_pid_tgid();
14 |
15 | // get binary context
16 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
17 | if (binary_ctx == NULL) {
18 | // the process is not part of an ssh session, ignore
19 | return 0;
20 | }
21 |
22 | // select profile cookie
23 | u32 cookie = binary_ctx->session_cookie;
24 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
25 | if (session == NULL) {
26 | return 0;
27 | }
28 | u32 cached_profile_cookie = session->profile_cookie;
29 |
30 | int blocked = override_return(ctx, session->socket_creation, session, CATEGORY_SOCKET_CREATION);
31 | u32 action = session->socket_creation;
32 | if (blocked && action == ACTION_MFA) {
33 | action = ACTION_BLOCK;
34 | }
35 | if (should_notify(action)) {
36 | struct syscall_notification_t notif = {};
37 | notif.notification.timestamp = bpf_ktime_get_ns();
38 | notif.notification.session_login_timestamp = session->login_timestamp;
39 | notif.notification.profile_cookie = cached_profile_cookie;
40 | notif.notification.session_cookie = binary_ctx->session_cookie;
41 | notif.notification.category = CATEGORY_SOCKET_CREATION;
42 | notif.notification.action = action;
43 | notif.syscall = syscall;
44 | fill_process_context(¬if.notification);
45 |
46 | u32 cpu = bpf_get_smp_processor_id();
47 | bpf_perf_event_output(ctx, ¬ifications, cpu, ¬if, sizeof(notif));
48 | }
49 | return 0;
50 | }
51 |
52 | SYSCALL(socket, trace_socket, 41)
53 | SYSCALL(socketpair, trace_socket, 53)
54 | SYSCALL(socketcall, trace_socket, 400)
55 |
56 | #endif
57 |
--------------------------------------------------------------------------------
/ebpf/events/stat.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _STAT_H_
9 | #define _STAT_H_
10 |
11 | #define OTP_REQUEST_SIZE 75
12 |
13 | struct otp_request_t {
14 | u64 timestamp;
15 | u64 session_login_timestamp;
16 | u32 profile_cookie;
17 | u32 session_init_pid;
18 | u32 request_pid;
19 | u32 session_cookie;
20 | char otp[OTP_REQUEST_SIZE];
21 | };
22 |
23 | // otp_requests is the perf ring buffer used to send OTP requests to user space
24 | struct bpf_map_def SEC("maps/otp_requests") otp_requests = {
25 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
26 | .key_size = 0,
27 | .value_size = 0,
28 | .max_entries = 0,
29 | .pinning = PIN_NONE,
30 | .namespace = "",
31 | };
32 |
33 | /*
34 | * kprobe_stat is used to track ssh-probe-auth instance for OTP verification
35 | */
36 | SEC("kprobe/__x64_sys_newfstatat")
37 | int kprobe_stat(struct pt_regs *kprobe_ctx)
38 | {
39 | // retrieve stat filename
40 | struct pt_regs *ctx = (struct pt_regs *) PT_REGS_PARM1(kprobe_ctx);
41 | char *filename;
42 | bpf_probe_read(&filename, sizeof(filename), &PT_REGS_PARM2(ctx));
43 |
44 | u32 pid = bpf_get_current_pid_tgid();
45 |
46 | // get session cookie
47 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
48 | if (binary_ctx == NULL) {
49 | // the process is not part of an ssh session, ignore
50 | return 0;
51 | }
52 |
53 | // select session context
54 | u32 cookie = binary_ctx->session_cookie;
55 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
56 | if (session == NULL) {
57 | return 0;
58 | }
59 |
60 | // create OTP request
61 | struct otp_request_t request = {};
62 | bpf_probe_read_str(&request.otp, OTP_REQUEST_SIZE, filename);
63 | if (request.otp[0] != 'o' || request.otp[1] != 't' || request.otp[2] != 'p' || request.otp[3] != ':' || request.otp[4] != '/' || request.otp[5] != '/') {
64 | // this is not an OTP request, ignore
65 | return 0;
66 | }
67 | // send OTP request to the backend
68 | request.timestamp = bpf_ktime_get_ns();
69 | request.session_login_timestamp = session->login_timestamp;
70 | request.profile_cookie = session->profile_cookie;
71 | request.session_init_pid = session->init_pid;
72 | request.request_pid = pid;
73 | request.session_cookie = binary_ctx->session_cookie;
74 |
75 | u32 cpu = bpf_get_smp_processor_id();
76 | bpf_perf_event_output(kprobe_ctx, &otp_requests, cpu, &request, sizeof(request));
77 |
78 | // notify ssh-probe-auth that ssh-probe received the OTP request
79 | return override_return(kprobe_ctx, ACTION_BLOCK, session, 0);
80 | }
81 |
82 | #endif
83 |
--------------------------------------------------------------------------------
/ebpf/events/unlink_and_move.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _UNLINK_AND_MOVE_H_
9 | #define _UNLINK_AND_MOVE_H_
10 |
11 | // trace_deletions_and_moves assesses an unlink or rename call
12 | __attribute__((always_inline)) static int trace_deletions_and_moves(struct pt_regs *ctx, int syscall) {
13 | u32 pid = bpf_get_current_pid_tgid();
14 |
15 | // get binary context
16 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
17 | if (binary_ctx == NULL) {
18 | // the process is not part of an ssh session, ignore
19 | return 0;
20 | }
21 |
22 | // select profile cookie
23 | u32 cookie = binary_ctx->session_cookie;
24 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
25 | if (session == NULL) {
26 | return 0;
27 | }
28 | u32 cached_profile_cookie = session->profile_cookie;
29 |
30 | int blocked = override_return(ctx, session->deletions_and_moves, session, CATEGORY_DELETIONS_AND_MOVES);
31 | u32 action = session->deletions_and_moves;
32 | if (blocked && action == ACTION_MFA) {
33 | action = ACTION_BLOCK;
34 | }
35 | if (should_notify(action)) {
36 | struct syscall_notification_t notif = {};
37 | notif.notification.timestamp = bpf_ktime_get_ns();
38 | notif.notification.session_login_timestamp = session->login_timestamp;
39 | notif.notification.profile_cookie = cached_profile_cookie;
40 | notif.notification.session_cookie = binary_ctx->session_cookie;
41 | notif.notification.category = CATEGORY_DELETIONS_AND_MOVES;
42 | notif.notification.action = action;
43 | notif.syscall = syscall;
44 | fill_process_context(¬if.notification);
45 |
46 | u32 cpu = bpf_get_smp_processor_id();
47 | bpf_perf_event_output(ctx, ¬ifications, cpu, ¬if, sizeof(notif));
48 | }
49 | return 0;
50 | }
51 |
52 | SYSCALL(unlink, trace_deletions_and_moves, 87)
53 | SYSCALL(unlinkat, trace_deletions_and_moves, 263)
54 | SYSCALL(rmdir, trace_deletions_and_moves, 84)
55 | SYSCALL(rename, trace_deletions_and_moves, 82)
56 | SYSCALL(renameat, trace_deletions_and_moves, 264)
57 | SYSCALL(renameat2, trace_deletions_and_moves, 316)
58 | SYSCALL(truncate, trace_deletions_and_moves, 76)
59 | SYSCALL(ftruncate, trace_deletions_and_moves, 77)
60 |
61 | #endif
62 |
--------------------------------------------------------------------------------
/ebpf/main.c:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #pragma clang diagnostic push
9 | #pragma clang diagnostic ignored "-Waddress-of-packed-member"
10 | #pragma clang diagnostic ignored "-Warray-bounds"
11 | #pragma clang diagnostic ignored "-Wunused-label"
12 | #pragma clang diagnostic ignored "-Wgnu-variable-sized-type-not-at-end"
13 | #pragma clang diagnostic ignored "-Wframe-address"
14 | #include
15 | #include
16 |
17 | /* In Linux 5.4 asm_inline was introduced, but it's not supported by clang.
18 | * Redefine it to just asm to enable successful compilation.
19 | */
20 | #ifdef asm_inline
21 | #undef asm_inline
22 | #define asm_inline asm
23 | #endif
24 | /* Before bpf_helpers.h is included, uapi bpf.h has been
25 | * included, which references linux/types.h. This may bring
26 | * in asm_volatile_goto definition if permitted based on
27 | * compiler setup and kernel configs.
28 | *
29 | * clang does not support "asm volatile goto" yet.
30 | * So redefine asm_volatile_goto to some invalid asm code.
31 | * If asm_volatile_goto is actually used by the bpf program,
32 | * a compilation error will appear.
33 | */
34 | #ifdef asm_volatile_goto
35 | #undef asm_volatile_goto
36 | #endif
37 | #define asm_volatile_goto(x...) asm volatile("invalid use of asm_volatile_goto")
38 |
39 | #include
40 | #include
41 | #include
42 | #include
43 | #pragma clang diagnostic pop
44 |
45 | // Custom eBPF helpers
46 | #include "bpf/bpf.h"
47 | #include "bpf/bpf_map.h"
48 | #include "bpf/bpf_helpers.h"
49 |
50 | #include "session/session.h"
51 |
52 | // utils
53 | #include "utils/tail_call.h"
54 | #include "utils/const.h"
55 | #include "utils/action.h"
56 | //#include "utils/process.h"
57 |
58 | // Session tracking probes
59 | #include "session/tracker.h"
60 | #include "session/process_lineage.h"
61 |
62 | // Events
63 | #include "events/execve.h"
64 | #include "events/unlink_and_move.h"
65 | #include "events/stat.h"
66 | #include "events/socket.h"
67 | #include "events/privilege_elevation.h"
68 | #include "events/process_level_protections.h"
69 | #include "events/kill.h"
70 | #include "events/os_level_protections.h"
71 | #include "events/open.h"
72 | #include "events/file_access.h"
73 | #include "events/performance_monitoring.h"
74 |
75 | char _license[] SEC("license") = "GPL";
76 | __u32 _version SEC("version") = 0xFFFFFFFE;
77 |
--------------------------------------------------------------------------------
/ebpf/session/process_lineage.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _PROCESS_LINEAGE_H_
9 | #define _PROCESS_LINEAGE_H_
10 |
11 | struct sched_process_fork_args
12 | {
13 | unsigned short common_type;
14 | unsigned char common_flags;
15 | unsigned char common_preempt_count;
16 | int common_pid;
17 |
18 | char parent_comm[16];
19 | pid_t parent_pid;
20 | char child_comm[16];
21 | pid_t child_pid;
22 | };
23 |
24 | /*
25 | * tracepoint__sched__sched_process_fork is used to track child processes and inherit session cookies
26 | */
27 | SEC("tracepoint/sched/sched_process_fork")
28 | int tracepoint__sched__sched_process_fork(struct sched_process_fork_args *ctx)
29 | {
30 | u32 pid = bpf_get_current_pid_tgid();
31 |
32 | // get binary context
33 | struct binary_context_t *parent_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
34 | if (parent_ctx == NULL) {
35 | // the process is not part of an ssh session, ignore
36 | return 0;
37 | }
38 |
39 | // inherit session cookie
40 | u32 child_pid = (u32) ctx->child_pid;
41 | struct binary_context_t child_ctx = {};
42 | child_ctx.session_cookie = parent_ctx->session_cookie;
43 | child_ctx.binary_cookie = parent_ctx->binary_cookie;
44 | bpf_map_update_elem(&pid_binary_context, &child_pid, &child_ctx, BPF_ANY);
45 | return 0;
46 | }
47 |
48 | #endif
49 |
--------------------------------------------------------------------------------
/ebpf/session/session.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _SESSION_H_
9 | #define _SESSION_H_
10 |
11 | struct session_context_t
12 | {
13 | u64 login_timestamp;
14 | u32 profile_cookie;
15 | u32 session_cookie;
16 | u32 init_pid;
17 |
18 | u8 unknown_binary_default;
19 | u8 deletions_and_moves;
20 | u8 socket_creation;
21 | u8 privilege_elevation;
22 | u8 os_level_protections;
23 | u8 process_level_protections;
24 | u8 performance_monitoring;
25 | u8 kill;
26 | u8 unknown_file_default;
27 | };
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/ebpf/session/tracker.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _TRACKER_H_
9 | #define _TRACKER_H_
10 |
11 | struct bpf_map_def SEC("maps/user_profile_cookie") user_profile_cookie = {
12 | .type = BPF_MAP_TYPE_HASH,
13 | .key_size = USERNAME_MAX_LENGTH,
14 | .value_size = sizeof(u32),
15 | .max_entries = 5000,
16 | };
17 |
18 | struct bpf_map_def SEC("maps/session_context") session_context = {
19 | .type = BPF_MAP_TYPE_HASH,
20 | .key_size = sizeof(u32),
21 | .value_size = sizeof(struct session_context_t),
22 | .max_entries = 5000,
23 | };
24 |
25 | struct binary_context_t {
26 | u32 session_cookie;
27 | u32 binary_cookie;
28 | };
29 |
30 | struct bpf_map_def SEC("maps/pid_binary_context") pid_binary_context = {
31 | .type = BPF_MAP_TYPE_HASH,
32 | .key_size = sizeof(u32),
33 | .value_size = sizeof(struct binary_context_t),
34 | .max_entries = 32000,
35 | };
36 |
37 | // get_session returns the session in which the provided pid lives
38 | __attribute__((always_inline)) static struct session_context_t *get_session(u32 pid)
39 | {
40 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
41 | if (binary_ctx == NULL) {
42 | // the process is not part of an ssh session, ignore
43 | return 0;
44 | }
45 |
46 | // select profile cookie
47 | u32 cookie = binary_ctx->session_cookie;
48 | struct session_context_t *session = bpf_map_lookup_elem(&session_context, &cookie);
49 | if (session == NULL) {
50 | return 0;
51 | }
52 | return session;
53 | }
54 |
55 | struct action_key {
56 | u32 profile_cookie;
57 | u32 category;
58 | };
59 |
60 | struct bpf_map_def SEC("maps/actions") actions = {
61 | .type = BPF_MAP_TYPE_HASH,
62 | .key_size = sizeof(struct action_key),
63 | .value_size = sizeof(u8),
64 | .max_entries = 5000,
65 | };
66 |
67 | // fill_session_context fills the provided section context with its actions
68 | __attribute__((always_inline)) static void fill_session_context(struct session_context_t *session)
69 | {
70 | u8 *action;
71 | struct action_key key = {};
72 | key.profile_cookie = session->profile_cookie;
73 | u8 default_action = load_unknown_user_default();
74 |
75 | // unknown binary
76 | key.category = CATEGORY_UNKNOWN_BINARY;
77 | action = bpf_map_lookup_elem(&actions, &key);
78 | if (action == NULL) {
79 | session->unknown_binary_default = default_action;
80 | } else {
81 | session->unknown_binary_default = *action;
82 | }
83 |
84 | // unknown file
85 | key.category = CATEGORY_UNKNOWN_FILE;
86 | action = bpf_map_lookup_elem(&actions, &key);
87 | if (action == NULL) {
88 | session->unknown_file_default = default_action;
89 | } else {
90 | session->unknown_file_default = *action;
91 | }
92 |
93 | // socket creation
94 | key.category = CATEGORY_SOCKET_CREATION;
95 | action = bpf_map_lookup_elem(&actions, &key);
96 | if (action == NULL) {
97 | session->socket_creation = default_action;
98 | } else {
99 | session->socket_creation = *action;
100 | }
101 |
102 | // kill
103 | key.category = CATEGORY_KILL;
104 | action = bpf_map_lookup_elem(&actions, &key);
105 | if (action == NULL) {
106 | session->kill = default_action;
107 | } else {
108 | session->kill = *action;
109 | }
110 |
111 | // privilege_elevation
112 | key.category = CATEGORY_PRIVILEGE_ELEVATION;
113 | action = bpf_map_lookup_elem(&actions, &key);
114 | if (action == NULL) {
115 | session->privilege_elevation = default_action;
116 | } else {
117 | session->privilege_elevation = *action;
118 | }
119 |
120 | // deletions_and_moves
121 | key.category = CATEGORY_DELETIONS_AND_MOVES;
122 | action = bpf_map_lookup_elem(&actions, &key);
123 | if (action == NULL) {
124 | session->deletions_and_moves = default_action;
125 | } else {
126 | session->deletions_and_moves = *action;
127 | }
128 |
129 | // os level protections
130 | key.category = CATEGORY_OS_LEVEL_PROTECTIONS;
131 | action = bpf_map_lookup_elem(&actions, &key);
132 | if (action == NULL) {
133 | session->os_level_protections = default_action;
134 | } else {
135 | session->os_level_protections = *action;
136 | }
137 |
138 | // process level protections
139 | key.category = CATEGORY_PROCESS_LEVEL_PROTECTIONS;
140 | action = bpf_map_lookup_elem(&actions, &key);
141 | if (action == NULL) {
142 | session->process_level_protections = default_action;
143 | } else {
144 | session->process_level_protections = *action;
145 | }
146 |
147 | // process level protections
148 | key.category = CATEGORY_PERFORMANCE_MONITORING;
149 | action = bpf_map_lookup_elem(&actions, &key);
150 | if (action == NULL) {
151 | session->performance_monitoring = default_action;
152 | } else {
153 | session->performance_monitoring = *action;
154 | }
155 | }
156 |
157 | /*
158 | * uprobe_setlogin is used to track new logins in the ssh daemon
159 | */
160 | SEC("uprobe/setlogin")
161 | int uprobe_setlogin(struct pt_regs *ctx)
162 | {
163 | char *username = (void *)PT_REGS_PARM1(ctx);
164 | struct session_context_t session = {};
165 | char login[USERNAME_MAX_LENGTH] = {};
166 |
167 | // Select the profile cookie of the provided username
168 | bpf_probe_read_str(&login, USERNAME_MAX_LENGTH, username);
169 | u32 *profile_cookie = bpf_map_lookup_elem(&user_profile_cookie, login);
170 | if (profile_cookie == NULL) {
171 | session.profile_cookie = UNKNOWN_USER_NAME;
172 | } else {
173 | session.profile_cookie = *profile_cookie;
174 | }
175 |
176 | // Generate a random session cookie for this new ssh session
177 | u32 session_cookie = bpf_get_prandom_u32();
178 | session.login_timestamp = bpf_ktime_get_ns();
179 | u32 pid = bpf_get_current_pid_tgid();
180 | session.init_pid = pid;
181 | session.session_cookie = session_cookie;
182 | fill_session_context(&session);
183 |
184 | // Update the session cookie <-> session context mapping
185 | bpf_map_update_elem(&session_context, &session_cookie, &session, BPF_ANY);
186 |
187 | // Update the pid <-> session mapping
188 | struct binary_context_t *binary_ctx = bpf_map_lookup_elem(&pid_binary_context, &pid);
189 | if (binary_ctx == NULL) {
190 | struct binary_context_t new_binary_ctx = {};
191 | new_binary_ctx.session_cookie = session_cookie;
192 | bpf_map_update_elem(&pid_binary_context, &pid, &new_binary_ctx, BPF_ANY);
193 | } else {
194 | binary_ctx->session_cookie = session_cookie;
195 | }
196 | return 0;
197 | };
198 |
199 | #endif
200 |
--------------------------------------------------------------------------------
/ebpf/utils/action.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _ACTION_H_
9 | #define _ACTION_H_
10 |
11 | struct kill_request_t {
12 | u64 timestamp;
13 | u64 session_login_timestamp;
14 | u32 profile_cookie;
15 | u32 session_init_pid;
16 | u32 session_cookie;
17 | u32 padding;
18 | };
19 |
20 | // kill_requests is the perf ring buffer used to send kill requests to user space
21 | struct bpf_map_def SEC("maps/kill_requests") kill_requests = {
22 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
23 | .key_size = 0,
24 | .value_size = 0,
25 | .max_entries = 0,
26 | .pinning = PIN_NONE,
27 | .namespace = "",
28 | };
29 |
30 | struct notification_t {
31 | u64 timestamp;
32 | u64 session_login_timestamp;
33 | u32 profile_cookie;
34 | u32 session_cookie;
35 | u32 category;
36 | u32 action;
37 | u32 pid;
38 | u32 tid;
39 | char comm[TASK_COMM_LEN];
40 | };
41 |
42 | struct syscall_notification_t {
43 | struct notification_t notification;
44 | int syscall;
45 | };
46 |
47 | // notification is the perf ring buffer used to send notifications (other than kill requests) to user space
48 | struct bpf_map_def SEC("maps/notifications") notifications = {
49 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
50 | .key_size = 0,
51 | .value_size = 0,
52 | .max_entries = 0,
53 | .pinning = PIN_NONE,
54 | .namespace = "",
55 | };
56 |
57 | struct mfa_selector_t {
58 | u32 profile_cookie;
59 | u32 session_cookie;
60 | u8 scope;
61 | };
62 |
63 | // mfa_tokens contains the mfa tokens and expiration dates of admin operations that require mfa
64 | struct bpf_map_def SEC("maps/mfa_tokens") mfa_tokens = {
65 | .type = BPF_MAP_TYPE_HASH,
66 | .key_size = sizeof(struct mfa_selector_t),
67 | .value_size = sizeof(u64),
68 | .max_entries = 5000,
69 | };
70 |
71 | // load_unknown_user_default returns the default action for unknown users
72 | __attribute__((always_inline)) static u8 load_unknown_user_default() {
73 | u64 unknown_user_default = 0;
74 | LOAD_CONSTANT("unknown_user_default", unknown_user_default);
75 | return (u8) unknown_user_default;
76 | }
77 |
78 | // load_access_control_events_level returns the access control events level
79 | __attribute__((always_inline)) static u8 load_access_control_events_level() {
80 | u64 access_control_events_level = 0;
81 | LOAD_CONSTANT("access_control_events_level", access_control_events_level);
82 | return (u8) access_control_events_level;
83 | }
84 |
85 | // should_notify returns 1 if a notification should be sent, based on the current notification level parameter
86 | __attribute__((always_inline)) static int should_notify(u8 action) {
87 | u64 access_control_events_level = load_access_control_events_level();
88 | return (access_control_events_level <= action);
89 | }
90 |
91 | // fill_process_context fills the provided notification with the process context available from eBPF
92 | __attribute__((always_inline)) static u64 fill_process_context(struct notification_t *notif)
93 | {
94 | // Comm
95 | bpf_get_current_comm(¬if->comm, sizeof(notif->comm));
96 |
97 | // Pid & Tid
98 | u64 id = bpf_get_current_pid_tgid();
99 | notif->pid = id >> 32;
100 | notif->tid = id;
101 | return id;
102 | }
103 |
104 | // override_return overrides the return value of a syscall based on the provided action. Returns 1 if the action was blocked
105 | __attribute__((always_inline)) static int override_return(struct pt_regs *ctx, u8 action, struct session_context_t *session, u8 scope)
106 | {
107 | switch (action) {
108 | case 0:
109 | {
110 | break;
111 | }
112 | case 1:
113 | {
114 | // override return
115 | bpf_override_return(ctx, OVERRIDE_RETURN_VALUE);
116 | break;
117 | }
118 | case 2:
119 | {
120 | struct mfa_selector_t selector = {};
121 | selector.profile_cookie = session->profile_cookie;
122 | selector.session_cookie = session->session_cookie;
123 | selector.scope = scope;
124 | u64 *expires_at = bpf_map_lookup_elem(&mfa_tokens, &selector);
125 | if (expires_at != NULL) {
126 | u64 now = bpf_ktime_get_ns();
127 | if (now <= *expires_at) {
128 | break;
129 | }
130 | }
131 | // override return
132 | bpf_override_return(ctx, OVERRIDE_RETURN_VALUE);
133 | return 1;
134 | }
135 | case 3:
136 | {
137 | // override return
138 | bpf_override_return(ctx, OVERRIDE_RETURN_VALUE);
139 |
140 | // send kill order
141 | struct kill_request_t kr = {};
142 | kr.timestamp = bpf_ktime_get_ns();
143 | kr.session_login_timestamp = session->login_timestamp;
144 | kr.profile_cookie = session->profile_cookie;
145 | kr.session_cookie = session->session_cookie;
146 | kr.session_init_pid = session->init_pid;
147 |
148 | // dissociate session from profile token
149 | session->profile_cookie = UNKNOWN_USER_NAME;
150 |
151 | u32 cpu = bpf_get_smp_processor_id();
152 | bpf_perf_event_output(ctx, &kill_requests, cpu, &kr, sizeof(kr));
153 | return 1;
154 | }
155 | }
156 | return 0;
157 | }
158 |
159 | #endif
160 |
--------------------------------------------------------------------------------
/ebpf/utils/const.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _CONST_H_
9 | #define _CONST_H_
10 |
11 | // PATH_MAX_LEN is limited to 255 as we won't really need more. The theoretical limit of the kernel is 4096 but
12 | // as we control which path is supposed to be provided, we don't need to go as far as 4096. The paths provided
13 | // for the allowed processes feature are also automatically added to the list of watched files. Any attempt to modify
14 | // them will be stopped.
15 | #define PATH_MAX_LEN 255
16 |
17 | // USERNAME_MAX_LENGTH is the maximum length of a user name.
18 | #define USERNAME_MAX_LENGTH 32
19 |
20 | // UNKNOWN_USER_NAME When a user is unknown, its profile cookie will have the following value.
21 | #define UNKNOWN_USER_NAME 42
22 |
23 | // Actions define how a return value should be overridden
24 | #define ACTION_ALLOW 0
25 | #define ACTION_BLOCK 1
26 | #define ACTION_MFA 2
27 | #define ACTION_KILL 3
28 |
29 | // OVERRIDE_RETURN_VALUE is the return value used to override the answer of the kernel. 13 = EACCESS = Permission denied
30 | #define OVERRIDE_RETURN_VALUE -13
31 |
32 | // Category identification keys are used to identify the right action in the actions map
33 | #define CATEGORY_FIM 1
34 | #define CATEGORY_PROCESS_MONITORING 2
35 | #define CATEGORY_UNKNOWN_BINARY 3
36 | #define CATEGORY_SOCKET_CREATION 4
37 | #define CATEGORY_DELETIONS_AND_MOVES 5
38 | #define CATEGORY_PRIVILEGE_ELEVATION 6
39 | #define CATEGORY_OS_LEVEL_PROTECTIONS 7
40 | #define CATEGORY_PROCESS_LEVEL_PROTECTIONS 8
41 | #define CATEGORY_PERFORMANCE_MONITORING 9
42 | #define CATEGORY_KILL 10
43 | #define CATEGORY_GLOBAL 11
44 | #define CATEGORY_FAILED_MFA 12
45 | #define CATEGORY_UNKNOWN_FILE 13
46 |
47 | // *_ACCESS flags are used to define open access rights
48 | #define READ_ACCESS 1
49 | #define WRITE_ACCESS 2
50 | #define ANY_ACCESS 3
51 |
52 | // Notification levels
53 | #define NOTIFY_ALLOW 1
54 | #define NOTIFY_BLOCK 2
55 | #define NOTIFY_MFA 3
56 | #define NOTIFY_KILL 4
57 |
58 | // LOAD_CONSTANT is a macro used to prepare constant edition at runtime
59 | #define LOAD_CONSTANT(param, var) asm("%0 = " param " ll" : "=r"(var))
60 |
61 | // Macros used to list syscall hook points
62 |
63 | #include "../bpf/bpf_helpers.h"
64 |
65 | #define SYSCALL_PREFIX "__x64_sys_"
66 | #define SYSCALL(syscall, func, id) SEC("kprobe/" SYSCALL_PREFIX #syscall) int kprobe__sys_##syscall(struct pt_regs *ctx) { return func(ctx, id); }
67 | #define SYSCALL_RET(syscall, func, id) SEC("kretprobe/" SYSCALL_PREFIX #syscall) int kretprobe__sys_##syscall(struct pt_regs *ctx) { return func(ctx, id); }
68 |
69 | #endif
70 |
--------------------------------------------------------------------------------
/ebpf/utils/process.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _PROCESS_H_
9 | #define _PROCESS_H_
10 |
11 | // TTY_NAME_LEN is the maximum length of the TTY name
12 | #define TTY_NAME_LEN 16
13 |
14 | // process_ctx_t Contains all the process context collected for a file system event
15 | struct process_ctx_t
16 | {
17 | // Process data
18 | u64 timestamp;
19 | u32 pid;
20 | u32 tid;
21 | u32 uid;
22 | u32 gid;
23 | char tty_name[TTY_NAME_LEN];
24 | char comm[TASK_COMM_LEN];
25 | };
26 |
27 | // fill_process_data fills the provided process_ctx_t with the process context available from eBPF
28 | __attribute__((always_inline)) static u64 fill_process_data(struct process_ctx_t *data)
29 | {
30 | // Process data
31 | struct task_struct *task = (struct task_struct *)bpf_get_current_task();
32 |
33 | // TTY
34 | struct signal_struct *signal;
35 | bpf_probe_read(&signal, sizeof(signal), &task->signal);
36 | struct tty_struct *tty;
37 | bpf_probe_read(&tty, sizeof(tty), &signal->tty);
38 | bpf_probe_read_str(data->tty_name, TTY_NAME_LEN, tty->name);
39 |
40 | // Comm
41 | bpf_get_current_comm(&data->comm, sizeof(data->comm));
42 |
43 | // Pid & Tid
44 | u64 id = bpf_get_current_pid_tgid();
45 | data->pid = id >> 32;
46 | data->tid = id;
47 |
48 | // UID & GID
49 | u64 userid = bpf_get_current_uid_gid();
50 | data->uid = userid >> 32;
51 | data->gid = userid;
52 | return id;
53 | }
54 |
55 | #endif
56 |
--------------------------------------------------------------------------------
/ebpf/utils/tail_call.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 | /* Copyright (c) 2020
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of version 2 of the GNU General Public
6 | * License as published by the Free Software Foundation.
7 | */
8 | #ifndef _TAIL_CALL_H_
9 | #define _TAIL_CALL_H_
10 |
11 | #define PROCESS_MONITORING_TAILCALL 1
12 |
13 | struct bpf_map_def SEC("maps/prog_array") prog_array = {
14 | .type = BPF_MAP_TYPE_PROG_ARRAY,
15 | .key_size = 4,
16 | .value_size = 4,
17 | .max_entries = 3,
18 | };
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/Gui774ume/ssh-probe
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/DataDog/ebpf v0.0.0-20200813173322-0c621fa94637
7 | github.com/DataDog/gopsutil v0.0.0-20200624212600-1b53412ef321
8 | github.com/Gui774ume/ebpf v0.0.0-20200411100314-4233cdb60f05 // indirect
9 | github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3
10 | github.com/pkg/errors v0.9.1
11 | github.com/sirupsen/logrus v1.6.0
12 | github.com/spf13/cobra v1.0.0
13 | golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
14 | gopkg.in/yaml.v2 v2.2.2
15 | rsc.io/qr v0.2.0
16 | )
17 |
--------------------------------------------------------------------------------
/pkg/model/kill.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package model
17 |
18 | import (
19 | "time"
20 |
21 | "github.com/Gui774ume/ssh-probe/pkg/utils"
22 | )
23 |
24 | type KillRequest struct {
25 | Timestamp time.Time
26 | SessionLoginTimestamp time.Time
27 | ProfileCookie uint32
28 | SessionCookie uint32
29 | SessionInitPid uint32
30 | }
31 |
32 | func (kr *KillRequest) UnmarshalBinary(data []byte, bootTime time.Time) (int, error) {
33 | if len(data) < 20 {
34 | return 0, ErrNotEnoughData
35 | }
36 | kr.Timestamp = bootTime.Add(time.Duration(utils.ByteOrder.Uint64(data[0:8])) * time.Nanosecond)
37 | kr.SessionLoginTimestamp = bootTime.Add(time.Duration(utils.ByteOrder.Uint64(data[8:16])) * time.Nanosecond)
38 | kr.ProfileCookie = utils.ByteOrder.Uint32(data[16:20])
39 | kr.SessionInitPid = utils.ByteOrder.Uint32(data[20:24])
40 | kr.SessionCookie = utils.ByteOrder.Uint32(data[24:28])
41 | return 28, nil
42 | }
43 |
--------------------------------------------------------------------------------
/pkg/model/notifications.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package model
17 |
18 | import (
19 | "C"
20 | "bytes"
21 | "fmt"
22 | "strings"
23 | "time"
24 |
25 | "github.com/Gui774ume/ssh-probe/pkg/utils"
26 | )
27 |
28 | type Resolver interface {
29 | CacheInode(path string) (uint64, error)
30 | ResolveInode(ino uint64) (string, bool)
31 | }
32 |
33 | type Notification struct {
34 | Timestamp time.Time `json:"timestamp"`
35 | SessionLoginTimestamp time.Time `json:"session_login_timestamp"`
36 | User string `json:"profile_user"`
37 | ProfileCookie uint32 `json:"profile_cookie"`
38 | SessionCookie uint32 `json:"session_cookie"`
39 | Category Category `json:"category"`
40 | Action Action `json:"action"`
41 | Pid uint32 `json:"pid"`
42 | Tid uint32 `json:"tid"`
43 | Comm string `json:"comm"`
44 |
45 | Syscall *Syscall `json:"syscall,omitempty"`
46 | BinaryPath *BinaryPath `json:"binary_path,omitempty"`
47 | FIM *FIM `json:"fim,omitempty"`
48 | OTPRequest *OTPRequest `json:"otp,omitempty"`
49 | }
50 |
51 | func (n *Notification) String() string {
52 | p := fmt.Sprintf(
53 | "%s session:%v user:%s category:%s pid:%d comm:%s",
54 | strings.ToUpper(string(n.Action)),
55 | n.SessionCookie,
56 | n.User,
57 | n.Category,
58 | n.Pid,
59 | n.Comm)
60 |
61 | switch n.Category {
62 | case CategorySocketCreation, CategoryDeletionsAndMoves, CategoryPrivilegeElevation, CategoryOSLevelProtections, CategoryProcessLevelProtections, CategoryPerformanceMonitoring, CategoryKill:
63 | p = fmt.Sprintf("%s %s", p, n.Syscall.String())
64 | case CategoryProcessMonitoring:
65 | p = fmt.Sprintf("%s %s", p, n.BinaryPath.String())
66 | case CategoryFim:
67 | p = fmt.Sprintf("%s %s %s", p, n.Syscall.String(), n.FIM.String())
68 | }
69 | return p
70 | }
71 |
72 | func (n *Notification) UnmarshalBinary(data []byte, bootTime time.Time, resolver Resolver) (int, error) {
73 | if len(data) < 32 {
74 | return 0, ErrNotEnoughData
75 | }
76 | n.Timestamp = bootTime.Add(time.Duration(utils.ByteOrder.Uint64(data[0:8])) * time.Nanosecond)
77 | n.SessionLoginTimestamp = bootTime.Add(time.Duration(utils.ByteOrder.Uint64(data[8:16])) * time.Nanosecond)
78 | n.ProfileCookie = utils.ByteOrder.Uint32(data[16:20])
79 | n.SessionCookie = utils.ByteOrder.Uint32(data[20:24])
80 | n.Category = Category(utils.ByteOrder.Uint32(data[24:28]))
81 | n.Action = ActionFromKernelValue(uint8(utils.ByteOrder.Uint32(data[28:32])))
82 | n.Pid = utils.ByteOrder.Uint32(data[32:36])
83 | n.Tid = utils.ByteOrder.Uint32(data[36:40])
84 | n.Comm = bytes.NewBuffer(bytes.Trim(data[40:56], "\x00")).String()
85 |
86 | cursor := 56
87 |
88 | switch n.Category {
89 | case CategorySocketCreation, CategoryDeletionsAndMoves, CategoryPrivilegeElevation, CategoryOSLevelProtections, CategoryProcessLevelProtections, CategoryPerformanceMonitoring, CategoryKill:
90 | n.Syscall = &Syscall{}
91 | return n.Syscall.UnmarshalBinary(data[cursor:])
92 | case CategoryProcessMonitoring:
93 | n.BinaryPath = &BinaryPath{}
94 | return n.BinaryPath.UnmarshalBinary(data[cursor:])
95 | case CategoryFim:
96 | n.FIM = &FIM{}
97 | read, err := n.FIM.UnmarshalBinary(data[cursor:], resolver)
98 | if err != nil {
99 | return 0, err
100 | }
101 | cursor += read
102 | n.Syscall = &Syscall{}
103 | return n.Syscall.UnmarshalBinary(data[cursor:])
104 | }
105 | return 32, nil
106 | }
107 |
108 | type Syscall struct {
109 | ID uint32 `json:"id"`
110 | Name string `json:"name"`
111 | }
112 |
113 | func (sn *Syscall) String() string {
114 | return fmt.Sprintf("syscall:%d syscall_name:%s", sn.ID, sn.Name)
115 | }
116 |
117 | func (sn *Syscall) UnmarshalBinary(data []byte) (int, error) {
118 | if len(data) < 4 {
119 | return 0, ErrNotEnoughData
120 | }
121 | sn.ID = utils.ByteOrder.Uint32(data[0:4])
122 | sn.Name = GetSyscallName(sn.ID)
123 | return 4, nil
124 | }
125 |
126 | type BinaryPath struct {
127 | ProfileCookie uint32 `json:"-"`
128 | BinaryPath string `json:"binary_path"`
129 | }
130 |
131 | func (bp *BinaryPath) String() string {
132 | return fmt.Sprintf("binary_path:%s", bp.BinaryPath)
133 | }
134 |
135 | func (bp *BinaryPath) UnmarshalBinary(data []byte) (int, error) {
136 | if len(data) < 4 {
137 | return 0, ErrNotEnoughData
138 | }
139 | bp.ProfileCookie = utils.ByteOrder.Uint32(data[0:4])
140 | bp.BinaryPath = bytes.NewBuffer(bytes.Trim(data[4:], "\x00")).String()
141 | return len(data), nil
142 | }
143 |
144 | type FIM struct {
145 | Inode uint64 `json:"inode"`
146 | Path string `json:"path"`
147 | }
148 |
149 | func (f *FIM) String() string {
150 | return fmt.Sprintf("inode:%d path:%s", f.Inode, f.Path)
151 | }
152 |
153 | func (f *FIM) UnmarshalBinary(data []byte, resolver Resolver) (int, error) {
154 | if len(data) < 8 {
155 | return 0, ErrNotEnoughData
156 | }
157 | f.Inode = utils.ByteOrder.Uint64(data[0:8])
158 | f.Path, _ = resolver.ResolveInode(f.Inode)
159 | return 8, nil
160 | }
161 |
--------------------------------------------------------------------------------
/pkg/model/otp.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package model
17 |
18 | import (
19 | "bytes"
20 | "encoding/binary"
21 | "fmt"
22 | "strconv"
23 | "strings"
24 | "time"
25 | "unsafe"
26 |
27 | "github.com/pkg/errors"
28 |
29 | "github.com/Gui774ume/ssh-probe/pkg/utils"
30 | )
31 |
32 | // OTPRequest is used to represent an OTP request from the ssh-probe-auth
33 | type OTPRequest struct {
34 | Timestamp time.Time `json:"-"`
35 | SessionLoginTimestamp time.Time `json:"-"`
36 | ProfileCookie uint32 `json:"-"`
37 | SessionCookie uint32 `json:"-"`
38 | SessionInitPid uint32 `json:"-"`
39 | RequestPid uint32 `json:"-"`
40 | OTPRequest string `json:"-"`
41 | OTPRequestRaw [OTPRequestSize]byte `json:"-"`
42 |
43 | OTPTimeout time.Duration `json:"timeout"`
44 | OTPToken string `json:"-"`
45 | OTPScope Category `json:"scope"`
46 | }
47 |
48 | // UnmarshalBinary parses raw bytes into an OTPRequest instance
49 | func (or *OTPRequest) UnmarshalBinary(data []byte, bootTime time.Time) (int, error) {
50 | if len(data) < 24+OTPRequestSize {
51 | return 0, ErrNotEnoughData
52 | }
53 | or.Timestamp = bootTime.Add(time.Duration(utils.ByteOrder.Uint64(data[0:8])) * time.Nanosecond)
54 | or.SessionLoginTimestamp = bootTime.Add(time.Duration(utils.ByteOrder.Uint64(data[8:16])) * time.Nanosecond)
55 | or.ProfileCookie = utils.ByteOrder.Uint32(data[16:20])
56 | or.SessionInitPid = utils.ByteOrder.Uint32(data[20:24])
57 | or.RequestPid = utils.ByteOrder.Uint32(data[24:28])
58 | or.SessionCookie = utils.ByteOrder.Uint32(data[28:32])
59 | if err := binary.Read(bytes.NewBuffer(data[32:32+OTPRequestSize]), utils.ByteOrder, &or.OTPRequestRaw); err != nil {
60 | return 32, err
61 | }
62 | or.OTPRequest = string(bytes.Trim(or.OTPRequestRaw[:], "\x00"))
63 | // Extract token and requested access timeout
64 | if err := or.ParseOTPRequest(); err != nil {
65 | return 32 + OTPRequestSize, err
66 | }
67 | return 32 + OTPRequestSize, nil
68 | }
69 |
70 | // ParseOTPRequest parses an OTP request to extract the timeout and the one time password
71 | func (or *OTPRequest) ParseOTPRequest() error {
72 | var err error
73 |
74 | // Parse scope
75 | req := strings.Replace(or.OTPRequest, "otp://", "", 1)
76 | splitScope := strings.Split(req, ":")
77 | if len(splitScope) != 2 {
78 | return fmt.Errorf("invalid OTP request format: %s", or.OTPRequest)
79 | }
80 | or.OTPScope, err = GetCategory(splitScope[0])
81 | if err != nil {
82 | return errors.Wrapf(err, "invalid OTP request %s", or.OTPRequest)
83 | }
84 |
85 | // Parse timeout
86 | splitTimeout := strings.Split(splitScope[1], "@")
87 | if len(splitTimeout) != 2 {
88 | return fmt.Errorf("invalid OTP request format: %s", or.OTPRequest)
89 | }
90 | timeoutMill, err := strconv.Atoi(splitTimeout[0])
91 | if err != nil {
92 | return fmt.Errorf("invalid OTP timeout format: %s", or.OTPRequest)
93 | }
94 | or.OTPTimeout = time.Duration(timeoutMill) * time.Microsecond
95 |
96 | // Parse token
97 | or.OTPToken = splitTimeout[1]
98 | return nil
99 | }
100 |
101 | type MFASelector struct {
102 | ProfileCookie uint32
103 | SessionCookie uint32
104 | Scope Category
105 | }
106 |
107 | // GetMFASelectorKey returns a kernel ready representation of an MFASelector instance
108 | func (mfa MFASelector) GetMFASelectorKey() (unsafe.Pointer, error) {
109 | key, err := utils.InterfaceToBytes(mfa)
110 | if err != nil {
111 | return nil, err
112 | }
113 | return unsafe.Pointer(&key[0]), nil
114 | }
115 |
--------------------------------------------------------------------------------
/pkg/ssh-probe/datadog.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package ssh_probe
17 |
18 | import (
19 | "encoding/json"
20 | "fmt"
21 | "net"
22 | "sync"
23 |
24 | "github.com/sirupsen/logrus"
25 | )
26 |
27 | // DatadogLogs Datadog logs forwarder
28 | type DatadogLogs struct {
29 | wg *sync.WaitGroup
30 | stop chan struct{}
31 | EventChan chan interface{}
32 | conn net.Conn
33 | }
34 |
35 | // Start sets up communication with the Datadog agent
36 | func (dl *DatadogLogs) Start(agentURL string) error {
37 | dl.wg = &sync.WaitGroup{}
38 | dl.EventChan = make(chan interface{}, 1000)
39 | dl.stop = make(chan struct{})
40 |
41 | if agentURL == "" {
42 | return nil
43 | }
44 |
45 | // Prepare connection with the agent
46 | var err error
47 | dl.conn, err = net.Dial("udp", agentURL)
48 | if err != nil {
49 | return fmt.Errorf("couldn't connect to the agent: %v", err)
50 | }
51 | go dl.listen()
52 | return nil
53 | }
54 |
55 | // listen waits for events and sends them
56 | func (dl *DatadogLogs) listen() {
57 | dl.wg.Add(1)
58 | var event interface{}
59 | var ok bool
60 | for {
61 | select {
62 | case <-dl.stop:
63 | dl.wg.Done()
64 | return
65 | case event, ok = <-dl.EventChan:
66 | if !ok {
67 | dl.wg.Done()
68 | return
69 | }
70 | // Send to Datadog
71 | data, err := json.Marshal(event)
72 | if err != nil {
73 | logrus.Errorf("couldn't marshal event: %v", err)
74 | continue
75 | }
76 | if _, err := dl.conn.Write(data); err != nil {
77 | logrus.Errorf("couldn't send event to the agent: %v", err)
78 | }
79 | }
80 | }
81 | }
82 |
83 | // Stop stops the logs forwarder
84 | func (dl *DatadogLogs) Stop() error {
85 | close(dl.stop)
86 | close(dl.EventChan)
87 | dl.wg.Wait()
88 | return nil
89 | }
90 |
--------------------------------------------------------------------------------
/pkg/ssh-probe/kill.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package ssh_probe
17 |
18 | import (
19 | "syscall"
20 |
21 | "github.com/DataDog/ebpf/manager"
22 | "github.com/sirupsen/logrus"
23 | "golang.org/x/sys/unix"
24 |
25 | "github.com/Gui774ume/ssh-probe/pkg/model"
26 | )
27 |
28 | // HandleKillRequests handles process kill requests from the kernel
29 | func (sshp *SSHProbe) HandleKillRequests(Cpu int, data []byte, perfMap *manager.PerfMap, m *manager.Manager) {
30 | request := &model.KillRequest{}
31 | if _, err := request.UnmarshalBinary(data, sshp.bootTime); err != nil {
32 | logrus.Warnf("error decoding KillRequest event: %v", err)
33 | return
34 | }
35 |
36 | // Select the corresponding user by its cookie
37 | profile := sshp.GetProfile(request.ProfileCookie)
38 | if profile == nil && request.ProfileCookie != model.UnknownUserNameCookie {
39 | logrus.Warnf("couldn't find profile for cookie %v", request.ProfileCookie)
40 | }
41 | if profile == nil {
42 | profile = &model.UnknownUserProfile
43 | }
44 |
45 | // kill the requested process
46 | if err := unix.Kill(int(request.SessionInitPid), syscall.SIGKILL); err != nil {
47 | if unix.ESRCH.Error() == err.Error() {
48 | // Session is already dead, ignore
49 | return
50 | }
51 | logrus.Errorf("couldn't kill session %v of user \"%s\" (login_timestamp: %v): %v", request.SessionCookie, profile.User, request.SessionLoginTimestamp, err)
52 | } else {
53 | logrus.Printf("session %v of user \"%s\" successfully killed (login_timestamp: %v)", request.SessionCookie, profile.User, request.SessionLoginTimestamp)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/pkg/ssh-probe/manager.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package ssh_probe
17 |
18 | import (
19 | "time"
20 |
21 | "github.com/DataDog/ebpf/manager"
22 | "github.com/DataDog/gopsutil/host"
23 |
24 | "github.com/Gui774ume/ssh-probe/pkg/model"
25 | )
26 |
27 | // ebpfManager holds the list of eBPF programs and maps of ssh-probe
28 | var ebpfManager = &manager.Manager{
29 | Probes: []*manager.Probe{
30 | {Section: "uprobe/setlogin", BinaryPath: "/usr/sbin/sshd"},
31 | {Section: "tracepoint/sched/sched_process_fork"},
32 | {Section: "kprobe/__x64_sys_execve"},
33 | {Section: "kprobe/__x64_sys_execveat"},
34 | {Section: "kprobe/__x64_sys_unlinkat"},
35 | {Section: "kprobe/__x64_sys_unlink"},
36 | {Section: "kprobe/__x64_sys_rmdir"},
37 | {Section: "kprobe/__x64_sys_rename"},
38 | {Section: "kprobe/__x64_sys_renameat"},
39 | {Section: "kprobe/__x64_sys_renameat2"},
40 | {Section: "kprobe/__x64_sys_truncate"},
41 | {Section: "kprobe/__x64_sys_ftruncate"},
42 | {Section: "kprobe/__x64_sys_newfstatat"},
43 | {Section: "kprobe/__x64_sys_socket"},
44 | {Section: "kprobe/__x64_sys_socketpair"},
45 | {Section: "kprobe/__x64_sys_socketcall"},
46 | {Section: "kprobe/__x64_sys_setuid"},
47 | {Section: "kprobe/__x64_sys_setgid"},
48 | {Section: "kprobe/__x64_sys_setfsuid"},
49 | {Section: "kprobe/__x64_sys_setfsgid"},
50 | {Section: "kprobe/__x64_sys_setreuid"},
51 | {Section: "kprobe/__x64_sys_setregid"},
52 | {Section: "kprobe/__x64_sys_setresgid"},
53 | {Section: "kprobe/__x64_sys_setresuid"},
54 | {Section: "kprobe/__x64_sys_setpgid"},
55 | {Section: "kprobe/__x64_sys_setns"},
56 | {Section: "kprobe/__x64_sys_setsid"},
57 | //{Section: "kprobe/__x64_sys_setgroups"},
58 | {Section: "kprobe/__x64_sys_capset"},
59 | {Section: "kprobe/__x64_sys_personality"},
60 | {Section: "kprobe/__x64_sys_setpriority"},
61 | {Section: "kprobe/__x64_sys_sched_setparam"},
62 | {Section: "kprobe/__x64_sys_sched_setscheduler"},
63 | {Section: "kprobe/__x64_sys_sched_setaffinity"},
64 | {Section: "kprobe/__x64_sys_set_tid_address"},
65 | {Section: "kprobe/__x64_sys_set_thread_area"},
66 | {Section: "kprobe/__x64_sys_ioprio_set"},
67 | {Section: "kprobe/__x64_sys_acct"},
68 | {Section: "kprobe/__x64_sys_quotactl"},
69 | {Section: "kprobe/__x64_sys_ptrace"},
70 | {Section: "kprobe/__x64_sys_memfd_create"},
71 | {Section: "kprobe/__x64_sys_kcmp"},
72 | {Section: "kprobe/__x64_sys_process_vm_readv"},
73 | {Section: "kprobe/__x64_sys_process_vm_writev"},
74 | {Section: "kprobe/__x64_sys_userfaultfd"},
75 | {Section: "kprobe/__x64_sys_modify_ldt"},
76 | {Section: "kprobe/__x64_sys_kill"},
77 | {Section: "kprobe/__x64_sys_tkill"},
78 | {Section: "kprobe/__x64_sys_tgkill"},
79 | {Section: "kprobe/__x64_sys_create_module"},
80 | {Section: "kprobe/__x64_sys_delete_module"},
81 | {Section: "kprobe/__x64_sys_query_module"},
82 | {Section: "kprobe/__x64_sys_init_module"},
83 | {Section: "kprobe/__x64_sys_finit_module"},
84 | {Section: "kprobe/__x64_sys_reboot"},
85 | {Section: "kprobe/__x64_sys_settimeofday"},
86 | {Section: "kprobe/__x64_sys_clock_settime"},
87 | {Section: "kprobe/__x64_sys_clock_adjtime"},
88 | {Section: "kprobe/__x64_sys_stime"},
89 | {Section: "kprobe/__x64_sys_setrlimit"},
90 | //{Section: "kprobe/__x64_sys_sysinfo"},
91 | {Section: "kprobe/__x64_sys_syslog"},
92 | {Section: "kprobe/__x64_sys_getrusage"},
93 | {Section: "kprobe/__x64_sys_add_key"},
94 | {Section: "kprobe/__x64_sys_keyctl"},
95 | {Section: "kprobe/__x64_sys_request_key"},
96 | {Section: "kprobe/__x64_sys_unshare"},
97 | {Section: "kprobe/__x64_sys_get_kernel_syms"},
98 | {Section: "kprobe/__x64_sys_get_mempolicy"},
99 | {Section: "kprobe/__x64_sys_set_mempolicy"},
100 | {Section: "kprobe/__x64_sys_mbind"},
101 | {Section: "kprobe/__x64_sys_move_pages"},
102 | {Section: "kprobe/__x64_sys_migrate_pages"},
103 | {Section: "kprobe/__x64_sys_kexec_load"},
104 | {Section: "kprobe/__x64_sys_kexec_file_load"},
105 | {Section: "kprobe/__x64_sys_lookup_dcookie"},
106 | {Section: "kprobe/__x64_sys_mount"},
107 | {Section: "kprobe/__x64_sys_umount"},
108 | {Section: "kprobe/__x64_sys_umount2"},
109 | {Section: "kprobe/__x64_sys_name_to_handle_at"},
110 | {Section: "kprobe/__x64_sys_open_by_handle_at"},
111 | {Section: "kprobe/__x64_sys_nfsservctl"},
112 | {Section: "kprobe/__x64_sys_pivot_root"},
113 | {Section: "kprobe/__x64_sys_swapon"},
114 | {Section: "kprobe/__x64_sys_swapoff"},
115 | {Section: "kprobe/__x64_sys_sysfs"},
116 | {Section: "kprobe/__x64_sys__sysctl"},
117 | {Section: "kprobe/__x64_sys_uselib"},
118 | {Section: "kprobe/__x64_sys_ustat"},
119 | {Section: "kprobe/__x64_sys_chroot"},
120 | {Section: "kprobe/__x64_sys_sethostname"},
121 | {Section: "kprobe/__x64_sys_setdomainname"},
122 | {Section: "kprobe/__x64_sys_iopl"},
123 | {Section: "kprobe/__x64_sys_ioperm"},
124 | {Section: "kprobe/__x64_sys_open"},
125 | {Section: "kprobe/__x64_sys_openat"},
126 | {Section: "kprobe/vfs_open"},
127 | {Section: "kretprobe/__x64_sys_open", KProbeMaxActive: 512},
128 | {Section: "kretprobe/__x64_sys_openat", KProbeMaxActive: 512},
129 | {Section: "kprobe/__x64_sys_read"},
130 | {Section: "kprobe/__x64_sys_readv"},
131 | {Section: "kprobe/__x64_sys_preadv"},
132 | {Section: "kprobe/__x64_sys_preadv2"},
133 | {Section: "kprobe/__x64_sys_pread64"},
134 | {Section: "kprobe/__x64_sys_readdir"},
135 | {Section: "kprobe/__x64_sys_readahead"},
136 | {Section: "kprobe/__x64_sys_write"},
137 | {Section: "kprobe/__x64_sys_writev"},
138 | {Section: "kprobe/__x64_sys_pwritev"},
139 | {Section: "kprobe/__x64_sys_pwritev2"},
140 | {Section: "kprobe/__x64_sys_pwrite64"},
141 | {Section: "kprobe/__x64_sys_mmap"},
142 | {Section: "kprobe/__x64_sys_pipe"},
143 | {Section: "kprobe/__x64_sys_dup"},
144 | {Section: "kprobe/__x64_sys_dup2"},
145 | {Section: "kprobe/__x64_sys_dup3"},
146 | {Section: "kprobe/__x64_sys_sendfile"},
147 | {Section: "kprobe/__x64_sys_sendfile64"},
148 | {Section: "kprobe/__x64_sys_fcntl"},
149 | {Section: "kprobe/__x64_sys_flock"},
150 | {Section: "kprobe/__x64_sys_fsync"},
151 | {Section: "kprobe/__x64_sys_fdatasync"},
152 | {Section: "kprobe/__x64_sys_syncfs"},
153 | {Section: "kprobe/__x64_sys_sync_file_range"},
154 | {Section: "kprobe/__x64_sys_sync_fallocate"},
155 | {Section: "kprobe/__x64_sys_splice"},
156 | {Section: "kprobe/__x64_sys_tee"},
157 | {Section: "kprobe/__x64_sys_vmsplice"},
158 | {Section: "kprobe/__x64_sys_bpf"},
159 | {Section: "kprobe/__x64_sys_perf_event_open"},
160 | },
161 | }
162 |
163 | // NewSSHProbe parses the provided profiles and creates a new ssh-probe instance
164 | func NewSSHProbe(profiles string, accessControlEventsLevel model.Action, disableGlobalScope bool, agentURL string) (*SSHProbe, error) {
165 | sshprobe := &SSHProbe{
166 | manager: ebpfManager,
167 | accessControlEventsLevel: accessControlEventsLevel,
168 | inodeCache: make(map[uint64]string),
169 | disableGlobalScope: disableGlobalScope,
170 | agentURL: agentURL,
171 | }
172 | // Get boot time
173 | bt, err := host.BootTime()
174 | if err != nil {
175 | return nil, err
176 | }
177 | sshprobe.bootTime = time.Unix(int64(bt), 0)
178 |
179 | // set perf ring buffer handlers
180 | sshprobe.manager.PerfMaps = []*manager.PerfMap{
181 | &manager.PerfMap{
182 | Map: manager.Map{
183 | Name: "otp_requests",
184 | },
185 | PerfMapOptions: manager.PerfMapOptions{
186 | DataHandler: sshprobe.HandleOTPRequests,
187 | LostHandler: sshprobe.LostHandler,
188 | },
189 | },
190 | &manager.PerfMap{
191 | Map: manager.Map{
192 | Name: "kill_requests",
193 | },
194 | PerfMapOptions: manager.PerfMapOptions{
195 | DataHandler: sshprobe.HandleKillRequests,
196 | LostHandler: sshprobe.LostHandler,
197 | },
198 | },
199 | &manager.PerfMap{
200 | Map: manager.Map{
201 | Name: "notifications",
202 | },
203 | PerfMapOptions: manager.PerfMapOptions{
204 | DataHandler: sshprobe.HandleNotifications,
205 | LostHandler: sshprobe.LostHandler,
206 | },
207 | },
208 | }
209 |
210 | // load profiles
211 | if err := sshprobe.loadProfiles(profiles); err != nil {
212 | return nil, err
213 | }
214 | return sshprobe, nil
215 | }
216 |
--------------------------------------------------------------------------------
/pkg/ssh-probe/notifications.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package ssh_probe
17 |
18 | import (
19 | "github.com/DataDog/ebpf/manager"
20 | "github.com/sirupsen/logrus"
21 |
22 | "github.com/Gui774ume/ssh-probe/pkg/model"
23 | )
24 |
25 | // HandleNotifications handls a new notification from the kernel
26 | func (sshp *SSHProbe) HandleNotifications(Cpu int, data []byte, perfMap *manager.PerfMap, m *manager.Manager) {
27 | notification := &model.Notification{}
28 | if _, err := notification.UnmarshalBinary(data, sshp.bootTime, sshp); err != nil {
29 | logrus.Warnf("error decoding Notification event: %v", err)
30 | return
31 | }
32 |
33 | // Select the corresponding user by its cookie
34 | profile := sshp.GetProfile(notification.ProfileCookie)
35 | if profile == nil && notification.ProfileCookie != model.UnknownUserNameCookie {
36 | logrus.Warnf("couldn't find profile for cookie %v", notification.ProfileCookie)
37 | }
38 | if profile == nil {
39 | profile = &model.UnknownUserProfile
40 | }
41 | notification.User = profile.User
42 |
43 | sshp.Forward(notification)
44 | logrus.Println(notification)
45 | }
46 |
--------------------------------------------------------------------------------
/pkg/ssh-probe/otp.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package ssh_probe
17 |
18 | import (
19 | "fmt"
20 | "syscall"
21 | "time"
22 |
23 | "github.com/DataDog/ebpf/manager"
24 | "github.com/sirupsen/logrus"
25 | "golang.org/x/sys/unix"
26 |
27 | "github.com/Gui774ume/ssh-probe/pkg/model"
28 | )
29 |
30 | // HandleOTPRequests handles OTP requests from ssh-probe-auth
31 | func (sshp *SSHProbe) HandleOTPRequests(Cpu int, data []byte, perfMap *manager.PerfMap, m *manager.Manager) {
32 | request := &model.OTPRequest{}
33 | if _, err := request.UnmarshalBinary(data, sshp.bootTime); err != nil {
34 | logrus.Warnf("error decoding OTPRequest: %v", err)
35 | sshp.sendAuthenticationResult(false, request, nil)
36 | return
37 | }
38 |
39 | // Check token validity
40 | if request.OTPTimeout > model.MaxOTPTimeout || request.OTPTimeout < 0 {
41 | err := fmt.Errorf("invalid OTP timeout requested (value must be between 0 and 10 minutes): %v", request.OTPTimeout)
42 | logrus.Warnf("invalid OTPRequest: %v", err)
43 | sshp.sendAuthenticationResult(false, request, nil)
44 | return
45 | }
46 | if request.OTPScope == model.CategoryGlobal && sshp.disableGlobalScope {
47 | logrus.Warnf("invalid OTPRequest: global MFA scope is disabled by config")
48 | sshp.sendAuthenticationResult(false, request, nil)
49 | return
50 | }
51 |
52 | // Select the right profile by its cookie
53 | profile := sshp.GetProfile(request.ProfileCookie)
54 | if profile == nil {
55 | logrus.Warnf("couldn't find profile for cookie %s", request.ProfileCookie)
56 | sshp.sendAuthenticationResult(false, request, profile)
57 | return
58 | }
59 |
60 | // Authenticate token
61 | if profile.OTPConfig == nil {
62 | logrus.Warnf("no OTPConfig available for \"%s\", did you provide a secret in SSH_PROBE_SECRETS for that user ?", profile.User)
63 | sshp.sendAuthenticationResult(false, request, profile)
64 | return
65 | }
66 | ok, err := profile.OTPConfig.Authenticate(request.OTPToken)
67 | if err != nil {
68 | logrus.Warnf("couldn't authenticate token \"%s\": %v", request.OTPToken, err)
69 | sshp.sendAuthenticationResult(false, request, profile)
70 | return
71 | }
72 |
73 | if ok {
74 | logrus.Printf("OTP authentication successful for user \"%s\" (session %v): token is valid for %v, scope is %s", profile.User, request.SessionCookie, request.OTPTimeout, request.OTPScope)
75 | if request.OTPScope == model.CategoryGlobal {
76 | for _, scope := range model.AllCategories {
77 | if err = sshp.insertMFAToken(profile.KernelCookie, request.SessionCookie, scope, request.OTPTimeout); err != nil {
78 | logrus.Errorf("failed to insert the OTP token in the kernel for user \"%s\" (session %v): %v", profile.User, request.SessionCookie, err)
79 | }
80 | }
81 | } else {
82 | if err = sshp.insertMFAToken(profile.KernelCookie, request.SessionCookie, request.OTPScope, request.OTPTimeout); err != nil {
83 | logrus.Errorf("failed to insert the OTP token in the kernel for user \"%s\" (session %v): %v", profile.User, request.SessionCookie, err)
84 | }
85 | }
86 | }
87 | sshp.sendAuthenticationResult(ok, request, profile)
88 | }
89 |
90 | func (sshp *SSHProbe) insertMFAToken(profileCookie uint32, sessionCookie uint32, scope model.Category, timeout time.Duration) error {
91 | // Insert access token
92 | selector := model.MFASelector{
93 | ProfileCookie: profileCookie,
94 | SessionCookie: sessionCookie,
95 | Scope: scope,
96 | }
97 | key, err := selector.GetMFASelectorKey()
98 | if err != nil {
99 | return err
100 | }
101 | ts := time.Now().Add(timeout).Sub(sshp.bootTime).Nanoseconds()
102 | if err := sshp.mfaTokens.Put(key, &ts); err != nil {
103 | return err
104 | }
105 | return nil
106 | }
107 |
108 | func (sshp *SSHProbe) sendAuthenticationResult(success bool, request *model.OTPRequest, profile *model.Profile) {
109 | // Send authentication result to ssh-probe-auth
110 | if success {
111 | profile.OTPFailedRequests = 0
112 | // send SIGUSR1
113 | if err := unix.Kill(int(request.RequestPid), syscall.SIGUSR1); err != nil {
114 | logrus.Warnf("failed to sent OTP authentication result to ssh-probe-auth: %v", err)
115 | }
116 |
117 | } else {
118 | if profile != nil {
119 | // Check number of failed attempts
120 | profile.OTPFailedRequests++
121 | if profile.OTPFailedRequests >= model.MaxOTPFailures {
122 | logrus.Printf("OTP authentication failed %v times, killing session %v of user \"%s\" ...", profile.OTPFailedRequests, request.SessionCookie, profile.User)
123 | if err := unix.Kill(int(request.SessionInitPid), syscall.SIGKILL); err != nil {
124 | logrus.Errorf("couldn't kill session %v for user \"%s\" (login_timestamp: %v): %v", request.SessionCookie, profile.User, request.SessionLoginTimestamp, err)
125 | }
126 |
127 | // send a notification
128 | notification := &model.Notification{
129 | Timestamp: request.Timestamp,
130 | SessionLoginTimestamp: request.SessionLoginTimestamp,
131 | ProfileCookie: request.ProfileCookie,
132 | SessionCookie: request.SessionCookie,
133 | User: profile.User,
134 | Category: model.CategoryFailedMFA,
135 | Action: model.Kill,
136 | Pid: request.RequestPid,
137 | OTPRequest: request,
138 | }
139 | sshp.Forward(notification)
140 | } else {
141 | logrus.Printf("OTP authentication failure for user \"%s\", %v attempt(s) left", profile.User, model.MaxOTPFailures-profile.OTPFailedRequests)
142 | }
143 | }
144 |
145 | // send SIGUSR2
146 | if err := unix.Kill(int(request.RequestPid), syscall.SIGUSR2); err != nil {
147 | logrus.Warnf("failed to sent OTP authentication result to ssh-probe-auth: %v", err)
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/pkg/ssh-probe/profiles.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package ssh_probe
17 |
18 | import (
19 | "fmt"
20 | "os"
21 | "strings"
22 |
23 | "github.com/dgryski/dgoogauth"
24 | "github.com/pkg/errors"
25 | "gopkg.in/yaml.v2"
26 |
27 | "github.com/Gui774ume/ssh-probe/pkg/model"
28 | "github.com/Gui774ume/ssh-probe/pkg/utils"
29 | )
30 |
31 | // insertProfiles calls insertProfile for user profile
32 | func (sshp *SSHProbe) insertProfiles() error {
33 | for _, profile := range sshp.profiles.UserProfiles {
34 | if err := sshp.insertProfile(profile); err != nil {
35 | return err
36 | }
37 | }
38 | return nil
39 | }
40 |
41 | // insertProfile inserts the provided profile in the kernel
42 | func (sshp *SSHProbe) insertProfile(profile *model.Profile) error {
43 | // generate random cookie
44 | profile.KernelCookie = utils.NewCookie()
45 |
46 | // insert allowed processes
47 | for path, action := range profile.Binaries {
48 | key, err := model.GetBinaryPathKey(profile.KernelCookie, path)
49 | if err != nil {
50 | return errors.Wrapf(err, "failed to generate binary path key for user \"%s\" and binary \"%s\"", profile.User, path)
51 | }
52 | if err := sshp.allowedBinariesMap.Put(key, action.KernelValue()); err != nil {
53 | return errors.Wrapf(err, "failed to insert process keys for user \"%s\" and binary \"%s\"", profile.User, path)
54 | }
55 | }
56 |
57 | // insert single actions
58 | if err := sshp.insertActions(profile); err != nil {
59 | return errors.Wrapf(err, "failed to insert actions for user \"%s\"", profile.User)
60 | }
61 |
62 | // insert FIM policies
63 | for _, f := range profile.FIM {
64 | for inode, path := range f.Inodes {
65 | sshp.inodeCache[inode] = path
66 | for _, ar := range f.AccessRight.KernelValues() {
67 | // prepare key
68 | key, err := model.GetInodeSelector(inode, profile.KernelCookie, ar)
69 | if err != nil {
70 | return errors.Wrapf(err, "failed to insert an inode for pattern %s", f.Pattern)
71 | }
72 | // insert
73 | if err := sshp.inodes.Put(key, f.Action.KernelValue()); err != nil {
74 | return errors.Wrapf(err, "failed to insert an inode <-> action for pattern %s", f.Pattern)
75 | }
76 | }
77 | }
78 | }
79 |
80 | // update username <-> profile cookie mapping
81 | usernameKey := profile.GetUserKey()
82 | if err := sshp.userProfileCookieMap.Put(usernameKey, profile.KernelCookie); err != nil {
83 | return errors.Wrapf(err, "failed to insert profile cookie for user \"%s\"", profile.User)
84 | }
85 | return nil
86 | }
87 |
88 | // insertActions inserts the single actions of the profile
89 | func (sshp *SSHProbe) insertActions(profile *model.Profile) error {
90 | ak := model.ActionKey{
91 | ProfileCookie: profile.KernelCookie,
92 | }
93 |
94 | // Unknown binary
95 | ak.Category = model.CategoryUnknownBinary
96 | key, err := ak.GetActionKey()
97 | if err != nil {
98 | return err
99 | }
100 | if err := sshp.actions.Put(key, profile.UnknownBinaryDefault.KernelValue()); err != nil {
101 | return errors.Wrapf(err, "failed to insert unknown binary default action")
102 | }
103 |
104 | // Unknown file
105 | ak.Category = model.CategoryUnknownFile
106 | key, err = ak.GetActionKey()
107 | if err != nil {
108 | return err
109 | }
110 | if err := sshp.actions.Put(key, profile.UnknownFile.KernelValue()); err != nil {
111 | return errors.Wrapf(err, "failed to insert unknown file default action")
112 | }
113 |
114 | // Deletions and moves
115 | ak.Category = model.CategoryDeletionsAndMoves
116 | key, err = ak.GetActionKey()
117 | if err != nil {
118 | return err
119 | }
120 | if err := sshp.actions.Put(key, profile.DeletionsAndMoves.KernelValue()); err != nil {
121 | return errors.Wrapf(err, "failed to insert deletions and moves action")
122 | }
123 |
124 | // Socket creation
125 | ak.Category = model.CategorySocketCreation
126 | key, err = ak.GetActionKey()
127 | if err != nil {
128 | return err
129 | }
130 | if err := sshp.actions.Put(key, profile.SocketCreation.KernelValue()); err != nil {
131 | return errors.Wrapf(err, "failed to insert socket creation action")
132 | }
133 |
134 | // Privilege elevation
135 | ak.Category = model.CategoryPrivilegeElevation
136 | key, err = ak.GetActionKey()
137 | if err != nil {
138 | return err
139 | }
140 | if err := sshp.actions.Put(key, profile.PrivilegeElevation.KernelValue()); err != nil {
141 | return errors.Wrapf(err, "failed to insert privilege elevation action")
142 | }
143 |
144 | // OS level protections
145 | ak.Category = model.CategoryOSLevelProtections
146 | key, err = ak.GetActionKey()
147 | if err != nil {
148 | return err
149 | }
150 | if err := sshp.actions.Put(key, profile.OSLevelProtections.KernelValue()); err != nil {
151 | return errors.Wrapf(err, "failed to insert os level protections action")
152 | }
153 |
154 | // Process level protections
155 | ak.Category = model.CategoryProcessLevelProtections
156 | key, err = ak.GetActionKey()
157 | if err != nil {
158 | return err
159 | }
160 | if err := sshp.actions.Put(key, profile.ProcessLevelProtections.KernelValue()); err != nil {
161 | return errors.Wrapf(err, "failed to insert process level protections action")
162 | }
163 |
164 | // Performance monitoring
165 | ak.Category = model.CategoryPerformanceMonitoring
166 | key, err = ak.GetActionKey()
167 | if err != nil {
168 | return err
169 | }
170 | if err := sshp.actions.Put(key, profile.PerformanceMonitoring.KernelValue()); err != nil {
171 | return errors.Wrapf(err, "failed to insert performance monitoring action")
172 | }
173 |
174 | // kill
175 | ak.Category = model.CategoryKill
176 | key, err = ak.GetActionKey()
177 | if err != nil {
178 | return err
179 | }
180 | if err := sshp.actions.Put(key, profile.Kill.KernelValue()); err != nil {
181 | return errors.Wrapf(err, "failed to insert kill action")
182 | }
183 | return nil
184 | }
185 |
186 | // loadProfiles loads the profile at the provided path
187 | func (sshp *SSHProbe) loadProfiles(profiles string) error {
188 | f, err := os.Open(profiles)
189 | if err != nil {
190 | return errors.WithStack(err)
191 | }
192 | defer f.Close()
193 | decoder := yaml.NewDecoder(f)
194 | if err := decoder.Decode(&sshp.profiles); err != nil {
195 | return errors.WithStack(err)
196 | }
197 |
198 | // add the profiles file in each profile
199 | for _, profile := range sshp.profiles.UserProfiles {
200 | profile.FIM = append(profile.FIM, &model.FilePolicy{
201 | Pattern: profiles,
202 | AccessRight: model.Any,
203 | Action: model.Block,
204 | })
205 | }
206 |
207 | // Sanitize profiles values
208 | if err := sshp.profiles.Sanitize(); err != nil {
209 | return err
210 | }
211 |
212 | // Fetch secrets
213 | secretsEnv := os.Getenv("SSH_PROBE_SECRETS")
214 | if len(secretsEnv) == 0 {
215 | return errors.New("couldn't find any secret in SSH_PROBE_SECRETS environment variable")
216 | }
217 | secrets := strings.Split(secretsEnv, ",")
218 | for _, secret := range secrets {
219 | split := strings.Split(secret, ":")
220 | if len(split) != 2 {
221 | return fmt.Errorf("invalid SSH_PROBE_SECRETS format: %s", secret)
222 | }
223 | for _, profile := range sshp.profiles.UserProfiles {
224 | if split[0] == profile.User {
225 | profile.OTPConfig = &dgoogauth.OTPConfig{
226 | Secret: split[1],
227 | WindowSize: 5,
228 | HotpCounter: 0,
229 | }
230 | }
231 | }
232 | }
233 |
234 | // Fetch inodes of each file pattern
235 | for _, profile := range sshp.profiles.UserProfiles {
236 | if err := sshp.loadProfileInodes(profile); err != nil {
237 | return errors.Wrapf(err, "failed to load profile inodes for user \"%s\"", profile.User)
238 | }
239 | }
240 | return nil
241 | }
242 |
243 | // loadProfileInodes loads the list of inodes that a profile will watch
244 | func (sshp *SSHProbe) loadProfileInodes(profile *model.Profile) error {
245 | // Expand patterns
246 | for _, f := range profile.FIM {
247 | if err := f.ExpandPattern(); err != nil {
248 | return errors.Wrapf(err, "couldn't expand pattern %s", f.Pattern)
249 | }
250 | }
251 | return nil
252 | }
253 |
--------------------------------------------------------------------------------
/pkg/ssh-probe/ssh_probe.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package ssh_probe
17 |
18 | import (
19 | "bytes"
20 | "strings"
21 | "time"
22 |
23 | "github.com/DataDog/ebpf"
24 | "github.com/DataDog/ebpf/manager"
25 | "github.com/pkg/errors"
26 | "github.com/sirupsen/logrus"
27 |
28 | "github.com/Gui774ume/ssh-probe/pkg/assets"
29 | "github.com/Gui774ume/ssh-probe/pkg/model"
30 | "github.com/Gui774ume/ssh-probe/pkg/utils"
31 | )
32 |
33 | // SSHProbe is the main structure of the ssh probe project
34 | type SSHProbe struct {
35 | profiles model.Profiles
36 | bootTime time.Time
37 | manager *manager.Manager
38 | accessControlEventsLevel model.Action
39 | disableGlobalScope bool
40 |
41 | userProfileCookieMap *ebpf.Map
42 | allowedBinariesMap *ebpf.Map
43 | mfaTokens *ebpf.Map
44 | actions *ebpf.Map
45 | inodes *ebpf.Map
46 |
47 | inodeCache map[uint64]string
48 |
49 | agentURL string
50 | logsForwarder *DatadogLogs
51 | }
52 |
53 | // Start ssh-probe
54 | func (sshp *SSHProbe) Start() error {
55 | // setup the datadog logs forwarder
56 | if sshp.agentURL != "" {
57 | sshp.logsForwarder = &DatadogLogs{}
58 | if err := sshp.logsForwarder.Start(sshp.agentURL); err != nil {
59 | return errors.Wrap(err, "couldn't start the Datadog logs forwarder")
60 | }
61 | }
62 |
63 | // Load eBPF programs and maps
64 | if err := sshp.initManager(); err != nil {
65 | return err
66 | }
67 |
68 | // Select eBPF maps
69 | if err := sshp.initKernelMaps(); err != nil {
70 | return errors.Wrap(err, "failed to select kernel maps")
71 | }
72 |
73 | // Insert profiles in eBPF maps
74 | if err := sshp.insertProfiles(); err != nil {
75 | return errors.Wrap(err, "failed to insert user profiles in the kernel")
76 | }
77 |
78 | // Start the eBPF manager
79 | if err := sshp.manager.Start(); err != nil {
80 | return errors.Wrap(err, "failed to start eBPF manager")
81 | }
82 | return nil
83 | }
84 |
85 | // initManager retrieves the eBPF bytecode assets and insert them in the kernel
86 | func (sshp *SSHProbe) initManager() error {
87 | buf, err := assets.Asset("/probe.o")
88 | if err != nil {
89 | return errors.Wrap(err, "failed to retrieve eBPF bytecode")
90 | }
91 | options := manager.Options{
92 | ConstantEditors: []manager.ConstantEditor{
93 | {
94 | Name: "unknown_user_default",
95 | Value: uint64(sshp.profiles.UnknownUserDefault.KernelValue()),
96 | },
97 | {
98 | Name: "access_control_events_level",
99 | Value: uint64(sshp.accessControlEventsLevel.KernelValue()),
100 | },
101 | },
102 | }
103 |
104 | // check which probes should be activated
105 | sshp.computeActivatedProbes(&options)
106 |
107 | if err := sshp.manager.InitWithOptions(bytes.NewReader(buf), options); err != nil {
108 | return errors.Wrap(err, "failed to init eBPF manager")
109 | }
110 | return nil
111 | }
112 |
113 | // computeActivatedProbes computes the list of probes that should be activated based on the available probes in the current kernel
114 | func (sshp *SSHProbe) computeActivatedProbes(options *manager.Options) {
115 | for _, probe := range sshp.manager.Probes {
116 | // activate uprobes and tracepoints
117 | if strings.HasPrefix(probe.Section, "uprobe/") || strings.HasPrefix(probe.Section, "tracepoint/") {
118 | options.ActivatedProbes = append(options.ActivatedProbes, probe.Section)
119 | continue
120 | }
121 |
122 | // check symbols for kprobes / kretprobes
123 | if strings.HasPrefix(probe.Section, "kprobe/") || strings.HasPrefix(probe.Section, "kretprobe/") {
124 | sym := strings.TrimPrefix(probe.Section, "kprobe/")
125 | sym = strings.TrimPrefix(sym, "kretprobe/")
126 | foundSym, err := manager.FindFilterFunction(sym)
127 | if err == nil && foundSym == sym {
128 | options.ActivatedProbes = append(options.ActivatedProbes, probe.Section)
129 | } else {
130 | logrus.Debugf("couldn't find symbol: %s", probe.Section)
131 | }
132 | }
133 | }
134 | }
135 |
136 | // initKernelMaps prepares the kernel maps that ssh-probe will use to communicate with the eBPF programs
137 | func (sshp *SSHProbe) initKernelMaps() error {
138 | var ok bool
139 | var err error
140 |
141 | // select user_profile_cookie map
142 | sshp.userProfileCookieMap, ok, err = sshp.manager.GetMap("user_profile_cookie")
143 | if err != nil {
144 | return err
145 | }
146 | if !ok {
147 | return errors.New("couldn't find user_profile_cookie map")
148 | }
149 |
150 | // select allowed_binaries map
151 | sshp.allowedBinariesMap, ok, err = sshp.manager.GetMap("allowed_binaries")
152 | if err != nil {
153 | return err
154 | }
155 | if !ok {
156 | return errors.New("couldn't find allowed_binaries map")
157 | }
158 |
159 | // select mfa_tokens map
160 | sshp.mfaTokens, ok, err = sshp.manager.GetMap("mfa_tokens")
161 | if err != nil {
162 | return err
163 | }
164 | if !ok {
165 | return errors.New("couldn't find mfa_tokens map")
166 | }
167 |
168 | // select actions map
169 | sshp.actions, ok, err = sshp.manager.GetMap("actions")
170 | if err != nil {
171 | return err
172 | }
173 | if !ok {
174 | return errors.New("couldn't find actions map")
175 | }
176 |
177 | // select inodes map
178 | sshp.inodes, ok, err = sshp.manager.GetMap("inodes")
179 | if err != nil {
180 | return err
181 | }
182 | if !ok {
183 | return errors.New("couldn't find inodes map")
184 | }
185 | return nil
186 | }
187 |
188 | // Forward forwards an event to Datadog
189 | func (sshp *SSHProbe) Forward(event interface{}) {
190 | if sshp.logsForwarder != nil {
191 | sshp.logsForwarder.EventChan <- event
192 | }
193 | }
194 |
195 | // GetProfile returns a profile by its cookie
196 | func (sshp *SSHProbe) GetProfile(cookie uint32) *model.Profile {
197 | for _, profile := range sshp.profiles.UserProfiles {
198 | if profile.KernelCookie == cookie {
199 | return profile
200 | }
201 | }
202 | return nil
203 | }
204 |
205 | // Stop ssh-probe
206 | func (sshp *SSHProbe) Stop() error {
207 | // Stop the eBPF manager
208 | if err := sshp.manager.Stop(manager.CleanAll); err != nil {
209 | return err
210 | }
211 | return nil
212 | }
213 |
214 | // CacheInode caches the inode of the provided path
215 | func (sshp *SSHProbe) CacheInode(path string) (uint64, error) {
216 | ino, err := utils.GetInode(path)
217 | if err != nil {
218 | return 0, err
219 | }
220 | sshp.inodeCache[ino] = path
221 | return ino, nil
222 | }
223 |
224 | // ResolveInode returns the path of the provided inode
225 | func (sshp *SSHProbe) ResolveInode(ino uint64) (string, bool) {
226 | p, ok := sshp.inodeCache[ino]
227 | return p, ok
228 | }
229 |
230 | // LostHandler logs lost samples from perf ring buffers
231 | func (sshp *SSHProbe) LostHandler(CPU int, count uint64, perfMap *manager.PerfMap, manager *manager.Manager) {
232 | logrus.Printf("lost %d samples from %s", count, perfMap.Name)
233 | }
234 |
--------------------------------------------------------------------------------
/pkg/utils/byteorder.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package utils
17 |
18 | import (
19 | "encoding/binary"
20 | "unsafe"
21 | )
22 |
23 | // GetHostByteOrder guesses the hosts byte order
24 | func GetHostByteOrder() binary.ByteOrder {
25 | var i int32 = 0x01020304
26 | u := unsafe.Pointer(&i)
27 | pb := (*byte)(u)
28 | b := *pb
29 | if b == 0x04 {
30 | return binary.LittleEndian
31 | }
32 |
33 | return binary.BigEndian
34 | }
35 |
36 | // ByteOrder holds the hosts byte order
37 | var ByteOrder binary.ByteOrder
38 |
39 | func init() {
40 | ByteOrder = GetHostByteOrder()
41 | }
42 |
--------------------------------------------------------------------------------
/pkg/utils/rand.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package utils
17 |
18 | import (
19 | "math/rand"
20 | "time"
21 | )
22 |
23 | func init() {
24 | rand.Seed(time.Now().UnixNano())
25 | }
26 |
27 | // NewCookie returns a new random cookie
28 | func NewCookie() uint32 {
29 | return rand.Uint32()
30 | }
31 |
32 | // NewMFASecret returns a new MFA secret
33 | func NewMFASecret() ([]byte, error) {
34 | secret := make([]byte, 10)
35 | _, err := rand.Read(secret)
36 | if err != nil {
37 | return nil, err
38 | }
39 | return secret, nil
40 | }
41 |
--------------------------------------------------------------------------------
/pkg/utils/utils.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2020 GUILLAUME FOURNIER
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package utils
17 |
18 | import (
19 | "bytes"
20 | "encoding/binary"
21 | "os"
22 | "syscall"
23 |
24 | "github.com/pkg/errors"
25 | )
26 |
27 | // InterfaceToBytes tranforms an interface into a C bytes array
28 | func InterfaceToBytes(data interface{}) ([]byte, error) {
29 | var buf bytes.Buffer
30 | if err := binary.Write(&buf, ByteOrder, data); err != nil {
31 | return []byte{}, err
32 | }
33 | return buf.Bytes(), nil
34 | }
35 |
36 | // GetInode returns the inode of the provided path
37 | func GetInode(path string) (uint64, error) {
38 | fi, err := os.Stat(path)
39 | if err != nil {
40 | return 0, err
41 | }
42 | pathStat, ok := fi.Sys().(*syscall.Stat_t)
43 | if !ok && pathStat == nil {
44 | return 0, errors.New("couldn't find inode")
45 | }
46 | return pathStat.Ino, nil
47 | }
48 |
--------------------------------------------------------------------------------
/static/datadog-agent/conf.d/ssh-probe.yaml:
--------------------------------------------------------------------------------
1 | ##Log section
2 | logs:
3 | - type: udp
4 | port: 10518
5 | service: ssh-probe
6 | source: ssh-probe
7 | tags:
8 | - ssh-probe:event
9 |
--------------------------------------------------------------------------------