b0b0haha opened a new issue, #2383:
URL: https://github.com/apache/apisix-ingress-controller/issues/2383

   ### Current Behavior
   
   Target
   Apache APISIX ingress controller 
(https://github.com/apache/apisix-ingress-controller) Reporting channel: 
[email protected] (https://security.apache.org/projects/)
   
   Affected Versions
   Latest version 019d719ef421e97ab47e68c0f34724010b05f1e0
   Exploitation Prerequisites
   To exploit this vulnerability, an attacker needs:
   1. Control over the ApisixResourceVersion parameter: 
     1. The attacker needs to control s.opts.ApisixResourceVersion, which is 
used in the replacement
     2. This is the critical input that would contain the malicious payload 
with newlines
   2. Access to the test scaffold environment: 
     1. This vulnerability exists in the test/e2e package, suggesting this is 
test code
     2. An attacker would need to: 
       - Access the testing environment
       - Find a way to trigger the tests with controlled parameters
       - Or find similar code patterns in the production code
   3. Kubernetes API access: 
     1. For the injected Pod (or other resources) to be created, the test 
scaffold must have permissions to create such resources in the target 
Kubernetes cluster
     2. The service account running the tests needs sufficient RBAC permissions
   Exploitation Impact
   By controlling s.opts.ApisixResourceVersion to inject malicious YAML 
fragments, it's possible to create arbitrary resources that could lead to 
privilege escalation.
   
   Root Cause Analysis
   
   In test\e2e\scaffold\consumer.go: The call site of s.replaceApiVersion:
   ```
   func (s *Scaffold) CreateVersionedApisixResource(yml string) error {
       kindValue := s.getKindValue(yml)
       if _, ok := createVersionedApisixResourceMap[kindValue]; ok { // 
Validation done here
           resource := s.replaceApiVersion(yml, s.opts.ApisixResourceVersion) 
// Direct replacement to another resource
           return s.CreateResourceFromString(resource)
       }
       return fmt.Errorf("the resource %s does not support", kindValue)
   }
   func (s *Scaffold) ApisixConsumerBasicAuthCreated(name, username, password 
string) error {
     ac := fmt.Sprintf(_apisixConsumerBasicAuth, s.opts.ApisixResourceVersion, 
name, username, password)
     return s.CreateVersionedApisixResource(ac)
   }
   
     _apisixConsumerBasicAuth = `
   apiVersion: %s
   kind: ApisixConsumer
   metadata:
     name: %s
   spec:
     authParameter:
       basicAuth:
         value:
           username: %s
           password: %s
   `
   ```
   In test\e2e\scaffold\scaffold.go:
   ```
   var (
       versionRegex = regexp.MustCompile(
   apiVersion: apisix.apache.org/v.*?\n
   )
       kindRegex    = regexp.MustCompile(
   kind: (.*?)\n
   )
   )
   func (s *Scaffold) replaceApiVersion(yml, ver string) string {
       return versionRegex.ReplaceAllString(yml, "apiVersion: "+ver+"\n")
   }
   ```
   In test\e2e\scaffold\k8s.go:  
   ```
   CreateResourceFromString calls k8s.KubectlApplyFromStringE, the function 
finally uses kubectl to apply the resouce yaml.
   func (s *Scaffold) CreateResourceFromString(yaml string) error {
       err := k8s.KubectlApplyFromStringE(s.t, s.kubectlOptions, yaml)
       // if the error raised, it may be a &shell.ErrWithCmdOutput, which is 
useless in debug
       if err != nil {
           err = fmt.Errorf(err.Error())
       }
       return err
   }
   func KubectlApplyFromStringE(t testing.TestingT, options *KubectlOptions, 
configData string) error {
       tmpfile, err := StoreConfigToTempFileE(t, configData)
       if err != nil {
           return err
       }
       defer os.Remove(tmpfile)
       return KubectlApplyE(t, options, tmpfile)
   }
   func KubectlApplyE(t testing.TestingT, options *KubectlOptions, configPath 
string) error {
       return RunKubectlE(t, options, "apply", "-f", configPath)
   }
   
   func RunKubectlE(t testing.TestingT, options *KubectlOptions, args 
...string) error {
       _, err := RunKubectlAndGetOutputE(t, options, args...)
       return err
   }
   func RunKubectlAndGetOutputE(t testing.TestingT, options *KubectlOptions, 
args ...string) (string, error) {
       cmdArgs := []string{}
       if options.ContextName != "" {
           cmdArgs = append(cmdArgs, "--context", options.ContextName)
       }
       if options.ConfigPath != "" {
           cmdArgs = append(cmdArgs, "--kubeconfig", options.ConfigPath)
       }
       if options.Namespace != "" {
           cmdArgs = append(cmdArgs, "--namespace", options.Namespace)
       }
       cmdArgs = append(cmdArgs, args...)
       command := shell.Command{
           Command: "kubectl",
           Args:    cmdArgs,
           Env:     options.Env,
       }
       return shell.RunCommandAndGetOutputE(t, command)
   }
   ```
   
   The vulnerability has two main causes:
   1. Regular Expression Matching Flaw The original regex apiVersion: 
apisix.apache.org/v.*?\n with .*? allows matching any characters (including 
spaces before the newline), but doesn't filter the ver input:
   ```
   // Regex allows version numbers to contain newlines
   versionRegex.ReplaceAllString(yml, "apiVersion: "+ver+"\n") 
   ```
   
   2. If ver contains \n, it directly breaks the YAML structure. For example, 
if an attacker sets s.opts.ApisixResourceVersion to:
   "v2\nkind: Pod\nspec:\n  containers:\n  - name: evil-container\n    image: 
evil/rootkit"
   1The replaced yaml content becomes:
   ```
   - apiVersion: v2
   kind: Pod
   spec:
     containers:
   name: evil-container
   image: evil/rootkit
   metadata:
     name: %s
   spec:
     authParameter:...
   ```
   1. At this point, Kubernetes will prioritize parsing kind: Pod rather than 
the original ApisixConsumer, resulting in the creation of a malicious Pod.
   2. Dynamic Resource Creation Logic Flaw The CreateVersionedApisixResource 
method only validates the original kind field without performing secondary 
validation on the replaced content:
   ```
   // Original logic only checks the original kind type
   if _, ok := createVersionedApisixResourceMap[kindValue]; ok { ... }
   ```
   
   
   
   ### Expected Behavior
   
   There should be sanitizer for  s.opts.ApisixResourceVersion
   
   ### Error Logs
   
   _No response_
   
   ### Steps to Reproduce
   
   Exploitation Process
   The most likely attack path is:
   1. Inject the following payload into s.opts.ApisixResourceVersion: v2\nkind: 
Pod\nspec:\n containers:\n - name: evil-container\n image: evil/rootkit\n 
hostNetwork: true\n#
   2. When ApisixConsumerBasicAuthCreated or another function using 
CreateVersionedApisixResource is called, the malicious YAML will be created
   3. Instead of creating an ApisixConsumer resource, a Pod with elevated 
privileges will be created, potentially allowing the attacker to compromise the 
cluster
   
   ### Environment
   
   - APISIX Ingress controller version (run `apisix-ingress-controller version 
--long`)
   - Kubernetes cluster version (run `kubectl version`)
   - OS version if running APISIX Ingress controller in a bare-metal 
environment (run `uname -a`)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to