-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feat] support capture dash and zsh command
- Loading branch information
Showing
15 changed files
with
1,217 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright 2022 CFC4N <cfc4n.cs@gmail.com>. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package cmd | ||
|
||
import ( | ||
"github.com/gojue/ecapture/user/config" | ||
"github.com/gojue/ecapture/user/module" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var dc = config.NewDashConfig() | ||
|
||
// dashCmd represents the dash command | ||
var dashCmd = &cobra.Command{ | ||
Use: "dash", | ||
Short: "capture dash command", | ||
Long: `eCapture capture dash commands for dash security audit, | ||
Auto find the dash of the current env as the capture target.`, | ||
Run: dashCommandFunc, | ||
} | ||
|
||
func init() { | ||
dashCmd.PersistentFlags().StringVar(&dc.Dashpath, "dash", "", "$SHELL file path, eg: /bin/dash , will automatically find it from $ENV default.") | ||
dashCmd.PersistentFlags().StringVar(&dc.Readline, "readlineso", "", "readline.so file path, will automatically find it from $BASH_PATH default.") | ||
dashCmd.Flags().IntVarP(&dc.ErrNo, "errnumber", "e", module.DashErrnoDefault, "only show the command which exec reulst equals err number.") | ||
rootCmd.AddCommand(dashCmd) | ||
|
||
// Here you will define your flags and configuration settings. | ||
|
||
// Cobra supports Persistent Flags which will work for this command | ||
// and all subcommands, e.g.: | ||
// dashCmd.PersistentFlags().String("foo", "", "A help for foo") | ||
|
||
// Cobra supports local flags which will only run when this command | ||
// is called directly, e.g.: | ||
// dashCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") | ||
} | ||
|
||
// dashCommandFunc executes the "dash" command. | ||
func dashCommandFunc(command *cobra.Command, args []string) { | ||
runModule(module.ModuleNameDash, dc) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright 2022 CFC4N <cfc4n.cs@gmail.com>. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package cmd | ||
|
||
import ( | ||
"github.com/gojue/ecapture/user/config" | ||
"github.com/gojue/ecapture/user/module" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var zc = config.NewZshConfig() | ||
|
||
// zshCmd represents the zsh command | ||
var zshCmd = &cobra.Command{ | ||
Use: "zsh", | ||
Short: "capture zsh command", | ||
Long: `eCapture capture zsh commands for zsh security audit, | ||
Auto find the zsh of the current env as the capture target.`, | ||
Run: zshCommandFunc, | ||
} | ||
|
||
func init() { | ||
zshCmd.PersistentFlags().StringVar(&zc.Zshpath, "zsh", "", "$SHELL file path, eg: /bin/zsh , will automatically find it from $ENV default.") | ||
zshCmd.PersistentFlags().StringVar(&zc.Readline, "readlineso", "", "readline.so file path, will automatically find it from $BASH_PATH default.") | ||
zshCmd.Flags().IntVarP(&zc.ErrNo, "errnumber", "e", module.ZshErrnoDefault, "only show the command which exec reulst equals err number.") | ||
rootCmd.AddCommand(zshCmd) | ||
|
||
// Here you will define your flags and configuration settings. | ||
|
||
// Cobra supports Persistent Flags which will work for this command | ||
// and all suzcommands, e.g.: | ||
// zshCmd.PersistentFlags().String("foo", "", "A help for foo") | ||
|
||
// Cobra supports local flags which will only run when this command | ||
// is called directly, e.g.: | ||
// zshCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") | ||
} | ||
|
||
// zshCommandFunc executes the "zsh" command. | ||
func zshCommandFunc(command *cobra.Command, args []string) { | ||
runModule(module.ModuleNameZsh, zc) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2022 CFC4N <cfc4n.cs@gmail.com>. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#include "ecapture.h" | ||
|
||
struct event { | ||
u32 type; | ||
u32 pid; | ||
u32 uid; | ||
char comm[TASK_COMM_LEN]; | ||
u8 line[MAX_DATA_SIZE_DASH]; | ||
}; | ||
|
||
struct { | ||
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); | ||
__uint(key_size, sizeof(u32)); | ||
__uint(value_size, sizeof(u32)); | ||
__uint(max_entries, 1024); | ||
} events SEC(".maps"); | ||
|
||
// Force emitting struct event into the ELF. | ||
const struct event *unused __attribute__((unused)); | ||
|
||
SEC("uprobe/dash_read") | ||
int uprobe_dash_read(struct pt_regs *ctx) { | ||
u64 pid_tgid = bpf_get_current_pid_tgid(); | ||
u32 pid = pid_tgid >> 32; | ||
u64 current_uid_gid = bpf_get_current_uid_gid(); | ||
u32 uid = current_uid_gid; | ||
#ifndef KERNEL_LESS_5_2 | ||
// if target_ppid is 0 then we target all pids | ||
if (target_pid != 0 && target_pid != pid) { | ||
return 0; | ||
} | ||
if (target_uid != 0 && target_uid != uid) { | ||
return 0; | ||
} | ||
#endif | ||
u32 fd = (u32)PT_REGS_PARM1(ctx); | ||
if (fd == 0) { | ||
struct event event = {}; | ||
event.pid = pid; | ||
event.uid = uid; | ||
event.type = DASH_EVENT_TYPE_READLINE; | ||
bpf_get_current_comm(&event.comm, sizeof(event.comm)); | ||
bpf_probe_read_user(&event.line, sizeof(event.line),(void *)PT_REGS_PARM2(ctx)); | ||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(struct event)); | ||
} | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#include "ecapture.h" | ||
|
||
struct event { | ||
u32 type; | ||
u32 pid; | ||
u32 uid; | ||
char comm[TASK_COMM_LEN]; | ||
u8 line[MAX_DATA_SIZE_DASH]; | ||
}; | ||
|
||
struct { | ||
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); | ||
__uint(key_size, sizeof(u32)); | ||
__uint(value_size, sizeof(u32)); | ||
__uint(max_entries, 1024); | ||
} events SEC(".maps"); | ||
|
||
// Force emitting struct event into the ELF. | ||
const struct event *unused __attribute__((unused)); | ||
|
||
SEC("uretprobe/zsh_zleentry") | ||
int uretprobe_zsh_zleentry(struct pt_regs *ctx) { | ||
u64 pid_tgid = bpf_get_current_pid_tgid(); | ||
u32 pid = pid_tgid >> 32; | ||
u64 current_uid_gid = bpf_get_current_uid_gid(); | ||
u32 uid = current_uid_gid; | ||
#ifndef KERNEL_LESS_5_2 | ||
// if target_ppid is 0 then we target all pids | ||
if (target_pid != 0 && target_pid != pid) { | ||
return 0; | ||
} | ||
if (target_uid != 0 && target_uid != uid) { | ||
return 0; | ||
} | ||
#endif | ||
struct event event = {}; | ||
event.pid = pid; | ||
event.uid = uid; | ||
event.type = ZSH_EVENT_TYPE_READLINE; | ||
bpf_get_current_comm(&event.comm, sizeof(event.comm)); | ||
bpf_probe_read_user(&event.line, sizeof(event.line),(void *)PT_REGS_RC(ctx)); | ||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(struct event)); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// Copyright 2022 CFC4N <cfc4n.cs@gmail.com>. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package config | ||
|
||
import ( | ||
"debug/elf" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"strings" | ||
) | ||
|
||
// DashConfig Dashpath 与 readline 两个参数,使用时二选一 | ||
type DashConfig struct { | ||
BaseConfig | ||
Dashpath string `json:"dashpath"` //dash的文件路径 | ||
Readline string `json:"readline"` | ||
ErrNo int | ||
ElfType uint8 // | ||
ReadlineFuncName string | ||
} | ||
|
||
func NewDashConfig() *DashConfig { | ||
config := &DashConfig{} | ||
config.PerCpuMapSize = DefaultMapSizePerCpu | ||
return config | ||
} | ||
|
||
func (bc *DashConfig) Check() error { | ||
var binaryPath string | ||
switch bc.ElfType { | ||
case ElfTypeBin: | ||
binaryPath = bc.Dashpath | ||
case ElfTypeSo: | ||
binaryPath = bc.Readline | ||
default: | ||
binaryPath = "/bin/dash" | ||
} | ||
|
||
file, err := elf.Open(binaryPath) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
symbols, err := file.DynamicSymbols() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
bc.ReadlineFuncName = "read" | ||
|
||
targetSymbol := "read" | ||
for _, sym := range symbols { | ||
if sym.Name == targetSymbol { | ||
return nil | ||
} | ||
} | ||
|
||
return fmt.Errorf("symbol [%s] not found in [%s]", targetSymbol, binaryPath) | ||
} | ||
|
||
func (bc *DashConfig) checkElf() error { | ||
// 如果readline 配置,且存在,则直接返回。 | ||
if bc.Readline != "" || len(strings.TrimSpace(bc.Readline)) > 0 { | ||
_, e := os.Stat(bc.Readline) | ||
if e != nil { | ||
return e | ||
} | ||
bc.ElfType = ElfTypeSo | ||
return nil | ||
} | ||
|
||
//如果配置 dash的地址,且存在,则直接返回 | ||
if bc.Dashpath != "" || len(strings.TrimSpace(bc.Dashpath)) > 0 { | ||
_, e := os.Stat(bc.Dashpath) | ||
if e != nil { | ||
return e | ||
} | ||
bc.ElfType = ElfTypeBin | ||
return nil | ||
} | ||
|
||
//如果没配置,则自动查找。 | ||
dash, b := os.LookupEnv("SHELL") | ||
if b { | ||
soPath, e := getDynPathByElf(dash, "libreadline.so") | ||
if e != nil { | ||
bc.Dashpath = dash | ||
bc.ElfType = ElfTypeBin | ||
} else { | ||
bc.Dashpath = soPath | ||
bc.ElfType = ElfTypeSo | ||
} | ||
|
||
} else { | ||
return errors.New("cant found $SHELL path.") | ||
} | ||
return nil | ||
} | ||
|
||
func (bc *DashConfig) Bytes() []byte { | ||
b, e := json.Marshal(bc) | ||
if e != nil { | ||
return []byte{} | ||
} | ||
return b | ||
} |
Oops, something went wrong.