├── .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 | ![MFA.png](documentation/MFA.png) 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 | --------------------------------------------------------------------------------