-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Os
committed
Dec 22, 2021
0 parents
commit 67afa92
Showing
10 changed files
with
2,790 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
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,39 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/tidwall/gjson" | ||
"io/ioutil" | ||
"os" | ||
"regexp" | ||
) | ||
|
||
// *findContainerdContainers* cycles through our matches and checks to see goes through out matches and checks | ||
// to see if any are containerd containers (overlayfs). If so, it extracts the image name | ||
// and adds it to the record | ||
// this will match entries such as this: | ||
// /run/containerd/io.containerd.runtime.v2.task/k8s.io/dc2c9c214809f506283c917244cd126a9b056ac7274322d12b59c9196d95dd9b/rootfs/app/spring-boot-application.jar | ||
// I originally used the client https://pkg.go.dev/github.com/containerd/containerd but then decided to just parse on the on-disk file to remove the extra dependencies | ||
// To extract the image name: | ||
// # cat /run/containerd/io.containerd.runtime.v2.task/k8s.io/dc2c9c214809f506283c917244cd126a9b056ac7274322d12b59c9196d95dd9b/config.json | jq '.annotations."io.kubernetes.cri.image-name"' | ||
// "ghcr.io/christophetd/log4shell-vulnerable-app:latest" | ||
func findContainerdContainers() { | ||
re := regexp.MustCompile(`\/run\/containerd\/io.containerd.runtime.v2.task\/k8s.io\/(?P<Hash>\S{64})\/`) | ||
for i := range matches { | ||
res := re.FindStringSubmatch(matches[i].fullPath) | ||
if len(res) > 0 { | ||
match := &matches[i] | ||
match.isContainer = true | ||
hash := res[1] | ||
|
||
jsonFile, err := os.Open("/run/containerd/io.containerd.runtime.v2.task/k8s.io/" + hash + "/config.json") | ||
if err == nil { | ||
byteValue, _ := ioutil.ReadAll(jsonFile) | ||
imageName := gjson.GetBytes(byteValue, `annotations.io\.kubernetes\.cri\.image-name`) | ||
if imageName.String() != "" { | ||
match.containerImage = imageName.String() | ||
} | ||
} | ||
defer jsonFile.Close() | ||
} | ||
} | ||
} |
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 @@ | ||
package main | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"os/user" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/tidwall/gjson" | ||
) | ||
|
||
// *findCrioContainers* will check to see if any of our matches are CRI-O containers and | ||
// will append image-related metadata. If an image is not found, it will add the pod name | ||
// instead | ||
// the default client does not support extracting metadata as mentioned here: | ||
// https://github.com/cri-o/cri-o/issues/3567 so I resorted to parsing the on-disk files | ||
func findCrioContainers() { | ||
var containerImage string | ||
currUser, _ := user.Current() | ||
path := "/var/lib/containers/storage/overlay-containers/" | ||
filepath.Walk(path, func(path string, info os.FileInfo, err error) error { | ||
// suppress errors / ignore files we can't read | ||
if err == nil { | ||
if !info.IsDir() && (filepath.Base(path) == "config.json") { | ||
jsonFile, err := os.Open(path) | ||
if err == nil { | ||
byteValue, _ := ioutil.ReadAll(jsonFile) | ||
rootPath := gjson.GetBytes(byteValue, "root.path") | ||
imageName := gjson.GetBytes(byteValue, `annotations.io\.kubernetes\.cri-o\.ImageName`) | ||
podName := gjson.GetBytes(byteValue, `annotations.io\.kubernetes\.pod\.name`) | ||
// NOTE containers run directly from kubelet do not have a corresponding ImageName. As backup, | ||
// I'll use the pod name if there is no ImageName | ||
if imageName.String() == "" { | ||
containerImage = podName.String() | ||
} else { | ||
containerImage = imageName.String() | ||
} | ||
for i := range matches { | ||
match := &matches[i] | ||
if strings.Contains(match.fullPath, rootPath.String()) { | ||
match.containerImage = containerImage | ||
match.isContainer = true | ||
// check if it is running or not. Requires root + crictl | ||
// we ignore any containers that are not running | ||
if err == nil { | ||
if checkBinary("crictl") && currUser.Username == "root" { | ||
if crictlCheckContainer(match.containerImage) == false { | ||
match.ignore = true | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
defer jsonFile.Close() | ||
} | ||
} | ||
return nil | ||
}) | ||
} |
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,63 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"context" | ||
|
||
// "github.com/docker/docker/api/types" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
|
||
"github.com/docker/docker/client" | ||
) | ||
|
||
// *findDockerAufsContainers* will map Docker containers using the *aufs* storage driver to the corresponding | ||
// image | ||
// https://stackoverflow.com/questions/47400479/find-to-which-container-or-image-a-docker-aufs-diff-folder-belongs-to | ||
// it will match entries such as: /var/lib/docker/aufs/diff/b3e8f4a721f46384260c55daf33ae52e1026bf130a10bbe3150485a2de32d573/... | ||
func findDockerAufsContainers() { | ||
re := regexp.MustCompile(`\/var\/lib\/docker\/aufs\/diff\/(?P<Hash>\S{64})\/`) | ||
re_path := regexp.MustCompile(`\/var\/lib\/docker\/image\/aufs\/layerdb\/mounts\/(?P<Hash>\S{64})\/mount-id`) | ||
files, err := filepath.Glob("/var/lib/docker/image/aufs/layerdb/mounts/*/mount-id") | ||
if err != nil { | ||
return | ||
} | ||
|
||
// 1. iterate over the files | ||
for _, file := range files { | ||
f, err := os.Open(file) | ||
if err == nil { | ||
|
||
defer f.Close() | ||
scanner := bufio.NewScanner(f) | ||
|
||
// 2. for each file, open and read its contents (I'm not sure if I have to remove a newline or not) | ||
for scanner.Scan() { | ||
// 3. for each file, iterate over the matches | ||
for i := range matches { | ||
res := re.FindStringSubmatch(matches[i].fullPath) | ||
if len(res) > 0 { | ||
match := &matches[i] | ||
match.isContainer = true | ||
hash := res[1] | ||
fileTxt := scanner.Text() | ||
if fileTxt == hash { | ||
res_path := re_path.FindStringSubmatch(file) | ||
if len(res_path) > 0 { | ||
// 4. get container image | ||
cli, err := client.NewClientWithOpts(client.FromEnv) | ||
if err == nil { | ||
container, err := cli.ContainerInspect(context.Background(), res_path[1]) | ||
if err == nil { | ||
match.containerImage = container.Config.Image | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
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,49 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/docker/docker/api/types" | ||
"github.com/docker/docker/client" | ||
) | ||
|
||
// *findDockerOverContainers* goes through Docker containers backed by the | ||
// overlayfs storage driver and maps the corresponding container image | ||
// this will match entries such as this: /var/lib/docker/overlay2/3839945137d898a38d7c91666d06ca99324d2858667439288cf6978d2829be5d/... | ||
// https://docs.docker.com/storage/storagedriver/overlayfs-driver/ | ||
// https://pkg.go.dev/github.com/docker/[email protected]+incompatible/api/types#GraphDriverData | ||
// NOTE: | ||
// - you'll typically have two entries for a single container: | ||
// 1. the merged layer | ||
// 2. the diff layer | ||
// for example: | ||
// /var/lib/docker/overlay2/192768f471818601094bf4edd96d14bfc0e2b178a04a2efd00b2231ad4e46b33/merged/app/spring-boot-application.jar | ||
// /var/lib/docker/overlay2/9e570f0cec8dcff5662a940f205600b541f82bd7d5d9c9bea8975ecb072506f4/diff/app/spring-boot-application.jar | ||
// we'll match just the *merged* layer as this indicates a running container | ||
func findDockerOverlayContainers() { | ||
re := regexp.MustCompile(`\/var\/lib\/docker\/overlay2?\/(?P<Hash>\S{64})\/merged\/`) | ||
cli, err := client.NewClientWithOpts(client.FromEnv) | ||
if err == nil { | ||
for i := range matches { | ||
res := re.FindStringSubmatch(matches[i].fullPath) | ||
if len(res) > 0 { | ||
match := &matches[i] | ||
match.isContainer = true | ||
hash := res[1] | ||
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{}) | ||
if err == nil { | ||
for _, container := range containers { | ||
res, _ := cli.ContainerInspect(context.Background(), container.ID) | ||
// fmt.Sprint is ugly but does the job | ||
if strings.Contains(fmt.Sprint(res.GraphDriver), hash) { | ||
match.containerImage = container.Image | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
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,41 @@ | ||
module github.com/ossie-git/log4shell_sentinel | ||
|
||
go 1.17 | ||
|
||
require ( | ||
github.com/deckarep/golang-set v1.8.0 | ||
github.com/docker/docker v20.10.12+incompatible | ||
github.com/fatih/color v1.13.0 | ||
github.com/tidwall/gjson v1.12.1 | ||
github.com/urfave/cli/v2 v2.3.0 | ||
) | ||
|
||
require ( | ||
github.com/Microsoft/go-winio v0.4.17 // indirect | ||
github.com/containerd/containerd v1.5.8 // indirect | ||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect | ||
github.com/docker/distribution v2.7.1+incompatible // indirect | ||
github.com/docker/go-connections v0.4.0 // indirect | ||
github.com/docker/go-units v0.4.0 // indirect | ||
github.com/gogo/protobuf v1.3.2 // indirect | ||
github.com/golang/protobuf v1.5.0 // indirect | ||
github.com/gorilla/mux v1.8.0 // indirect | ||
github.com/mattn/go-colorable v0.1.9 // indirect | ||
github.com/mattn/go-isatty v0.0.14 // indirect | ||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect | ||
github.com/morikuni/aec v1.0.0 // indirect | ||
github.com/opencontainers/go-digest v1.0.0 // indirect | ||
github.com/opencontainers/image-spec v1.0.2 // indirect | ||
github.com/pkg/errors v0.9.1 // indirect | ||
github.com/russross/blackfriday/v2 v2.0.1 // indirect | ||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect | ||
github.com/sirupsen/logrus v1.8.1 // indirect | ||
github.com/tidwall/match v1.1.1 // indirect | ||
github.com/tidwall/pretty v1.2.0 // indirect | ||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect | ||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect | ||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect | ||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect | ||
google.golang.org/grpc v1.43.0 // indirect | ||
google.golang.org/protobuf v1.27.1 // indirect | ||
) |
Oops, something went wrong.