voxpupuli / hiera-eyaml

A backend for Hiera that provides per-value asymmetric encryption of sensitive data
MIT License
527 stars 132 forks source link

Remote Code Execution vulnerability in the hiera-eyaml tool #354

Closed rflj closed 1 year ago

rflj commented 1 year ago

I would like to bring to your attention a security vulnerability that I recently discovered in the hiera-eyaml tool.

This vulnerability has the potential to enable remote code execution, posing a serious risk to the security of affected systems. I have provided detailed information about the issue below.

Overview: By encrypting a command within backticks (e.g., `id`) and subsequently decrypting the created cipher, the encrypted command is executed on the victim’s device. This vulnerability affects the local computers of users who share secrets and perform encryption/decryption tasks collaboratively during their daily work. Additionally, systems utilizing hiera-eyaml, such as puppetmasters, are also vulnerable.

Proof of Concept (PoC):

  1. Encrypt the payload using the eyaml encrypt command, for example:

    eyaml encrypt -s “`id`“
  2. Take the encrypted value and decrypt it using eyaml, as shown below:

    eyaml decrypt -s “ENC[PKCS7,MII(...)“

Instead of displaying the encrypted string, the executed command’s result will be printed.

Recommendations: To address this vulnerability and mitigate the associated risks, I recommend the following measures:

  1. Implement thorough input validation and sanitization mechanisms to ensure that user-supplied input is properly validated and sanitized before further processing.
  2. Develop and enforce a mechanism that prevents command execution during the decryption process.
ekohl commented 1 year ago

I suspect the replacement actually happens in the shell when you encrypt it.

This is my environment:

$ git clone https://github.com/voxpupuli/hiera-eyaml
$ cd hiera-eyaml
$ bundle install
$ bundle exec eyaml createkeys
[hiera-eyaml-core] Created key directory: ./keys
[hiera-eyaml-core] Keys created OK

Then I run your reproducer:

$ bundle exec eyaml encrypt -s "`id`"
string: ENC[PKCS7,MIICCwYJKoZIhvcNAQcDoIIB/DCCAfgCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAGOgabQdw5hymYDbvL9FV6BWbeXLPawaaHQcRW1kt3L69R1QLnHhHtLXQf+TcfbVk7y1bXyVe02klHUjZFWUqWZ8yTQBL7rN94z+LgtHwhvfRZt2rwu2I8qg9f8kNCJ34qEHL5lPoILw/RnbO/DX7qedtSDHgPeU11eQkaoBagupRBlyYvkes/qXgS/N+EJsxVagLsocyQg0sCH1YvLxWDQ3nRx9vDWGzV6+zEM/y/J+Ok/KEeFRv0djZ3t5ckVVdtLxarxYIKXlmzolsd2jw1PpcMwW5E/PYPDFtr4IXR+zBQfVtZ8xSHOdFS+6XXJwm3+W+4xeQ02/iDiGvsPDxAzCBzQYJKoZIhvcNAQcBMB0GCWCGSAFlAwQBKgQQCt8gTDebGAgBTAoqqj5dDoCBoEQaUb8Fj1KMqxg3Hx6rCFNUXxFoje7olBKDiwAJ1S9xBwW+PhyEYnSbQzfaZqeb/AtfduVe+6lu5zuqdj+DaEZlqNDa+5qi2tyCcUIkiVpB9vvvRFoWYQpwyzWFxLxcmVbuZ5+mx9PGw/+FHNk5hiyNGyT2pHlguepSdL+qkQufdk5xidEzGD+lOAhogB9oFl7elBMu8O0aEDfBz8bjacY=]

And when I decrypt that, I see the output. However, if I run:

$ bundle exec eyaml encrypt -s '`id`'
string: ENC[PKCS7,MIIBeQYJKoZIhvcNAQcDoIIBajCCAWYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAHo4dtp6M8RHNhP4SpfYhVWRK1MpV8WGDVbtV/gTN3cq3OeQbwEAdnGZo7+DSKg1fP6wdfx/GbuGopS7pOfOLl0JTiErnCzJFImhzcsnn258alvM2RLXmTw544tP2bDqMfrldMOOThFirZbivR6uL01uBW8s6oyNCkxH+CRVtAUGbGWoJ+ii5YJsnCPzByRrufjPm9btn/JrIOEm5bQk594rrJHgznuzEc1s1y0vb/jiVg4ZUk/C5VbAJcZKNfeTc2Ofarf388XiYXG2gmtfNqcR9pdnsA8xJvWL1tDVRZRhutFVZpWsP2RYSErbVJKGyBQaPrBfRRp5+H/FbTJXf9jA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCReO8vVXtmpbOkbUNiIDIsgBATQym5a8VqieGH1FEwxE5T]

OR

block: >
  ENC[PKCS7,MIIBeQYJKoZIhvcNAQcDoIIBajCCAWYCAQAxggEhMIIBHQIBAD
  AFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAHo4dtp6M8RHNhP4SpfYhVWRK1M
  pV8WGDVbtV/gTN3cq3OeQbwEAdnGZo7+DSKg1fP6wdfx/GbuGopS7pOfOLl0
  JTiErnCzJFImhzcsnn258alvM2RLXmTw544tP2bDqMfrldMOOThFirZbivR6
  uL01uBW8s6oyNCkxH+CRVtAUGbGWoJ+ii5YJsnCPzByRrufjPm9btn/JrIOE
  m5bQk594rrJHgznuzEc1s1y0vb/jiVg4ZUk/C5VbAJcZKNfeTc2Ofarf388X
  iYXG2gmtfNqcR9pdnsA8xJvWL1tDVRZRhutFVZpWsP2RYSErbVJKGyBQaPrB
  fRRp5+H/FbTJXf9jA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCReO8vVX
  tmpbOkbUNiIDIsgBATQym5a8VqieGH1FEwxE5T]

And then decrypt it:

$ bundle exec eyaml decrypt -s 'ENC[PKCS7,MIIBeQYJKoZIhvcNAQcDoIIBajCCAWYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAHo4dtp6M8RHNhP4SpfYhVWRK1MpV8WGDVbtV/gTN3cq3OeQbwEAdnGZo7+DSKg1fP6wdfx/GbuGopS7pOfOLl0JTiErnCzJFImhzcsnn258alvM2RLXmTw544tP2bDqMfrldMOOThFirZbivR6uL01uBW8s6oyNCkxH+CRVtAUGbGWoJ+ii5YJsnCPzByRrufjPm9btn/JrIOEm5bQk594rrJHgznuzEc1s1y0vb/jiVg4ZUk/C5VbAJcZKNfeTc2Ofarf388XiYXG2gmtfNqcR9pdnsA8xJvWL1tDVRZRhutFVZpWsP2RYSErbVJKGyBQaPrBfRRp5+H/FbTJXf9jA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCReO8vVXtmpbOkbUNiIDIsgBATQym5a8VqieGH1FEwxE5T]'
`id`

So what happens is that your shell interprets the backticks and then passes it to the command. If you use single quotes, your shell doesn't and it gets encrypted as you'd expect.

Another way to demonstrate this is using date:

$ bundle exec eyaml encrypt -s "`date`"
string: ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAYW2VvuU6pY1xqge59qttmlQbtFQdCKOGL4LGTaPj6oPSuNidcSVZA4rcdEtC+lK7UVLE8XERM4c9qhtGE+tjhjmdPCGThHlW9Yk6+4WXef71QbqacaKNmUktUtA2zomlZLOiPJsnMFXphNJM26lhXILY+fjdBh4CqYTNImyYqlhGL4aa9eOlSlWVJeNAeO6TrYC/myf93GqkWN+Xpfsd49oClFcsvZ0qxx1BXWqJZxUWD0FEj16a2smaH5SBZpiZUGG/AfA/CCZ5YPpw2OQe79e77zxneq12xQRSwlVKEKaNkwmr4jNEuwk7qzXVDHGITv+s9ZCH0NR93h4nn6XtajBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBE2YcXRiH4GLOY98gIjzHggCBATsu05bC9awK7+AXJ7TdYmBckc9srcgoSyDB2RQKEsA==]

OR

block: >
  ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBAD
  AFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAYW2VvuU6pY1xqge59qttmlQbtF
  QdCKOGL4LGTaPj6oPSuNidcSVZA4rcdEtC+lK7UVLE8XERM4c9qhtGE+tjhj
  mdPCGThHlW9Yk6+4WXef71QbqacaKNmUktUtA2zomlZLOiPJsnMFXphNJM26
  lhXILY+fjdBh4CqYTNImyYqlhGL4aa9eOlSlWVJeNAeO6TrYC/myf93GqkWN
  +Xpfsd49oClFcsvZ0qxx1BXWqJZxUWD0FEj16a2smaH5SBZpiZUGG/AfA/CC
  Z5YPpw2OQe79e77zxneq12xQRSwlVKEKaNkwmr4jNEuwk7qzXVDHGITv+s9Z
  CH0NR93h4nn6XtajBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBE2YcXRi
  H4GLOY98gIjzHggCBATsu05bC9awK7+AXJ7TdYmBckc9srcgoSyDB2RQKEsA
  ==]
$ bundle exec eyaml decrypt -s 'ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAYW2VvuU6pY1xqge59qttmlQbtFQdCKOGL4LGTaPj6oPSuNidcSVZA4rcdEtC+lK7UVLE8XERM4c9qhtGE+tjhjmdPCGThHlW9Yk6+4WXef71QbqacaKNmUktUtA2zomlZLOiPJsnMFXphNJM26lhXILY+fjdBh4CqYTNImyYqlhGL4aa9eOlSlWVJeNAeO6TrYC/myf93GqkWN+Xpfsd49oClFcsvZ0qxx1BXWqJZxUWD0FEj16a2smaH5SBZpiZUGG/AfA/CCZ5YPpw2OQe79e77zxneq12xQRSwlVKEKaNkwmr4jNEuwk7qzXVDHGITv+s9ZCH0NR93h4nn6XtajBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBE2YcXRiH4GLOY98gIjzHggCBATsu05bC9awK7+AXJ7TdYmBckc9srcgoSyDB2RQKEsA==]'
Mon 29 May 16:36:24 CEST 2023
$ bundle exec eyaml decrypt -s 'ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAYW2VvuU6pY1xqge59qttmlQbtFQdCKOGL4LGTaPj6oPSuNidcSVZA4rcdEtC+lK7UVLE8XERM4c9qhtGE+tjhjmdPCGThHlW9Yk6+4WXef71QbqacaKNmUktUtA2zomlZLOiPJsnMFXphNJM26lhXILY+fjdBh4CqYTNImyYqlhGL4aa9eOlSlWVJeNAeO6TrYC/myf93GqkWN+Xpfsd49oClFcsvZ0qxx1BXWqJZxUWD0FEj16a2smaH5SBZpiZUGG/AfA/CCZ5YPpw2OQe79e77zxneq12xQRSwlVKEKaNkwmr4jNEuwk7qzXVDHGITv+s9ZCH0NR93h4nn6XtajBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBE2YcXRiH4GLOY98gIjzHggCBATsu05bC9awK7+AXJ7TdYmBckc9srcgoSyDB2RQKEsA==]'
Mon 29 May 16:36:24 CEST 2023

Note it shows the same timestamp twice, so it certainly doesn't happen while decrypting because then it'd change.

smortex commented 1 year ago

Yeah, shell substitution happen before command execution. To check what is actually run, just add echo in front of the command:

$ echo bundle exec eyaml encrypt -s "`date`"
bundle exec eyaml encrypt -s lun. 29 mai 2023 18:28:05 -10
$ echo bundle exec eyaml encrypt -s '`date`'
bundle exec eyaml encrypt -s `date`