curl.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package rules
  2. import (
  3. "fmt"
  4. "github.com/zricethezav/gitleaks/v8/cmd/generate/config/utils"
  5. "github.com/zricethezav/gitleaks/v8/config"
  6. "github.com/zricethezav/gitleaks/v8/regexp"
  7. )
  8. // https://curl.se/docs/manpage.html#-u
  9. func CurlBasicAuth() *config.Rule {
  10. r := config.Rule{
  11. RuleID: "curl-auth-user",
  12. Description: "Discovered a potential basic authorization token provided in a curl command, which could compromise the curl accessed resource.",
  13. Regex: regexp.MustCompile(`\bcurl\b(?:.*|.*(?:[\r\n]{1,2}.*){1,5})[ \t\n\r](?:-u|--user)(?:=|[ \t]{0,5})("(:[^"]{3,}|[^:"]{3,}:|[^:"]{3,}:[^"]{3,})"|'([^:']{3,}:[^']{3,})'|((?:"[^"]{3,}"|'[^']{3,}'|[\w$@.-]+):(?:"[^"]{3,}"|'[^']{3,}'|[\w${}@.-]+)))(?:\s|\z)`),
  14. Keywords: []string{"curl"},
  15. Entropy: 2,
  16. Allowlists: []*config.Allowlist{
  17. {
  18. Regexes: []*regexp.Regexp{
  19. regexp.MustCompile(`[^:]+:(?:change(?:it|me)|pass(?:word)?|pwd|test|token|\*+|x+)`), // common placeholder passwords
  20. regexp.MustCompile(`['"]?<[^>]+>['"]?:['"]?<[^>]+>|<[^:]+:[^>]+>['"]?`), // <placeholder>
  21. regexp.MustCompile(`[^:]+:\[[^]]+]`), // [placeholder]
  22. regexp.MustCompile(`['"]?[^:]+['"]?:['"]?\$(?:\d|\w+|\{(?:\d|\w+)})['"]?`), // $1 or $VARIABLE
  23. regexp.MustCompile(`\$\([^)]+\):\$\([^)]+\)`), // $(cat login.txt)
  24. regexp.MustCompile(`['"]?\$?{{[^}]+}}['"]?:['"]?\$?{{[^}]+}}['"]?`), // ${{ secrets.FOO }} or {{ .Values.foo }}
  25. },
  26. },
  27. },
  28. }
  29. // validate
  30. tps := []string{
  31. // short
  32. `curl --cacert ca.crt -u elastic:P@ssw0rd$1 https://localhost:9200`, // same lines, no quotes
  33. `sh-5.0$ curl -k -X POST https://infinispan:11222/rest/v2/caches/default/hello \
  34. -H 'Content-type: text/plain' \
  35. -d 'world' \
  36. -u developer:yqDVtkqPECriaLRi`, // different line
  37. `curl -u ":d2LkV78zLx!t" https://localhost:9200`, // empty username
  38. `curl -u "d2LkV78zLx!t:" https://localhost:9200`, // empty password
  39. // long
  40. `curl -sw '%{http_code}' -X POST --user 'johns:h0pk1ns~21s' $GItHUB_API_URL/$GIT_COMMIT --data`,
  41. `curl --user roger23@gmail.com:pQ9wTxu4Fg https://www.dropbox.com/cli_link?host_id=abcdefg -v`, // same line, no quotes
  42. `curl -s --user 'api:d2LkV78zLx!t' \
  43. https://api.mailgun.net/v2/sandbox91d3515882ecfaa1c65be642.mailgun.org/messages`, // same line, single quotes
  44. `curl -s -v --user "j.smith:dB2yF6@qL9vZm1P#4J" "https://api.contoso.org/user/me"`, // same line, double quotes
  45. `curl -X POST --user "{acd3c08b-74e8-4f44-a2d0-80694le24f46}":"{ZqL5kVrX1n8tA2}" --header "Accept: application/json" --data "{\"text\":\"Hello, world\",\"source\":\"en\",\"target\":\"es\"}" https://gateway.watsonplatform.net/language-translator/api`,
  46. `curl --user kevin:'pRf7vG2h1L8nQkW9' -iX PATCH -H "Content-Type: application/json" -d`, // same line, mixed quoting
  47. `$ curl https://api.dropbox.com/oauth2/token \
  48. --user c28wlsosanujy2z:qgsnai0xokrw4j1 --data grant_type=authorization_code`, // different line
  49. // TODO
  50. //` curl -s --insecure --url "imaps://whatever.imap.server" --user\
  51. //"myuserid:mypassword" --request "STATUS INBOX (UNSEEN)"`,
  52. }
  53. fps := []string{
  54. // short
  55. `curl -i -u 'test:test'`,
  56. ` curl -sL --user "$1:$2" "$3" > "$4"`, // environment variable
  57. `curl -u <user:password> https://test.com/endpoint`, // placeholder
  58. `curl --user neo4j:[PASSWORD] http://[IP]:7474/db/data/`, // placeholder
  59. `curl -u "myusername" http://localhost:15130/api/check_user/`, // no password
  60. `curl -u username:token`,
  61. `curl -u "${_username}:${_password}"`,
  62. `curl -u "${username}":"${password}"`,
  63. `curl -k -X POST -I -u "SRVC_JENKINS:${APPID}"`,
  64. `curl -u ":" https://localhost:9200`, // empty username and password
  65. // long
  66. `curl -sw '%{http_code}' -X POST --user '$USERNAME:$PASSWORD' $GItHUB_API_URL/$GIT_COMMIT --data`,
  67. `curl --user "xxx:yyy"`,
  68. ` curl -sL --user "$GITHUB_USERNAME:$GITHUB_PASSWORD" "$GITHUB_URL" > "$TESTS_PATH"`, // environment variable
  69. // variable interpolation
  70. `curl --silent --fail {{- if and $.Values.username $.Values.password }} --user "{{ $.Values.username }}:{{ $.Values.password }}"`,
  71. `curl -XGET -i -u "${{ env.ELK_ID }}:${{ build.env.ELK_PASS }}"`,
  72. `curl -XGET -i -u "${{needs.vault.outputs.account_id}}:${{needs.vault.outputs.account_password}}"`,
  73. `curl -XGET -i -u "${{ steps.vault.outputs.account_id }}:${{ steps.vault.outputs.account_password }}"`,
  74. `curl -X POST --user "$(cat ./login.txt):$(cat ./password.txt)"`, // command
  75. `curl http://127.0.0.1:5000/file --user user:pass --digest # digest auth`, // placeholder
  76. ` curl -X GET --insecure --user "username:password" \`, // placeholder
  77. `curl --silent --insecure --user ${f5user}:${f5pass} \`, // placeholder
  78. `curl --insecure --ssl-reqd "smtps://smtp.gmail.com" --mail-from "src@gmail.com" --mail-rcpt "dst@gmail.com" --user "src@gmail.com" --upload-file out.txt`, // no password
  79. // different command
  80. `#HTTP command line test
  81. curl -X POST -H "Content-Type: application/json" -d '{"id":12345,"geo":{"latitude":28.50,"longitude":-81.14}}' http://<ip>:8080/serve
  82. #UDP command line test
  83. echo -n '{"type":"serve","channel":"/","data":{"site_id":8,"post_id":12345,"geo":{"lat":28.50,"long":-81.14}}}' >/dev/udp/127.0.0.1/41234
  84. #UDP Listener (for confirmation)
  85. nc -u -l 41234`,
  86. }
  87. return utils.Validate(r, tps, fps)
  88. }
  89. // https://curl.se/docs/manpage.html#-H
  90. func CurlHeaderAuth() *config.Rule {
  91. // language=regexp
  92. authPat := `(?i)(?:Authorization:[ \t]{0,5}(?:Basic[ \t]([a-z0-9+/]{8,}={0,3})|(?:Bearer|(?:Api-)?Token)[ \t]([\w=~@.+/-]{8,})|([\w=~@.+/-]{8,}))|(?:(?:X-(?:[a-z]+-)?)?(?:Api-?)?(?:Key|Token)):[ \t]{0,5}([\w=~@.+/-]{8,}))`
  93. r := config.Rule{
  94. RuleID: "curl-auth-header",
  95. Description: "Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.",
  96. Regex: regexp.MustCompile(
  97. // language=regexp
  98. fmt.Sprintf(`\bcurl\b(?:.*?|.*?(?:[\r\n]{1,2}.*?){1,5})[ \t\n\r](?:-H|--header)(?:=|[ \t]{0,5})(?:"%s"|'%s')(?:\B|\s|\z)`, authPat, authPat)),
  99. Entropy: 2.75,
  100. Keywords: []string{"curl"},
  101. //Allowlists: []*config.Allowlist{
  102. // {
  103. // Regexes: []*regexp.Regexp{},
  104. // },
  105. //},
  106. }
  107. tps := []string{
  108. `curl --header 'Authorization: 5eb4223e-5008-46e5-be67-c7b8f2732305'`,
  109. // Short flag.
  110. `curl -H 'Authorization: Basic YnJvd3Nlcjo=' \`, // same line, single quotes
  111. // TODO: Handle short flags combined.
  112. //`TOKEN=$(curl -sH "Authorization: Basic $BASIC_TOKEN" "https://$REGISTRY/oauth2/token?service=$REGISTRY&scope=repository:$REPO:pull" | jq -r .access_token)`,
  113. // Long flag.
  114. `curl -k -X POST --header "Authorization: Basic djJlNEpYa0NJUHZ5a2FWT0VRXzRqZmZUdDkwYTp2emNBZGFzZWpmlWZiUDc2VUJjNDNNVDExclVh" "https://api-qa.example.com:8243/token" -d "grant_type=client_credentials"`, // same line, double quotes
  115. // Basic auth.
  116. `curl -X POST -H "Content-Type: application/json" \
  117. -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" \
  118. -d '{"user":{"emailAddress":"test@example.com"}, "password":"password"}' \
  119. 'http://localhost:8080/oauth2-provider/v1.0/users'`, // different line, double quotes
  120. `#curl -X POST \
  121. # https://api.mailgun.net/v3/sandbox7dbcabccd4314c123e8b23599d35f5b6.mailgun.org/messages \
  122. # -H 'Authorization: Basic YXBpOmtleS1hN2MzNDJ3MzNhNWQxLTU2M2U3MjlwLTZhYjI3YzYzNzM0Ng==' \
  123. # -F from='Excited User <mailgun@sandbox7dbc123bccd4314c0aae8b23599d35f5b6.mailgun.org>' \
  124. # -F to='joe@example.com' \
  125. # -F subject='Hello' \
  126. # -F text='Testing some Mailgun awesomness!'`, // different line, single quotes
  127. // Bearer auth
  128. `# curl -X GET "http://localhost:3000/api/cron/status" -H "Authorization: Bearer cfcabd11c7ed9a41b1a3e063c32d5114"`, // same line, double quotes
  129. `curl -X PUT -H 'Authorization: Bearer jC+6TUUjCNHcVtAXpcqBCgxnA8r+qD6MatnYaf/+289y7HWpK0BWPyLHv/K4DMN32fufwmeVVjlo8zjgBh8kx3GfS6IqO70w1DVMSCTwX7fhEpiXaxzv0mhSMHDX9Kw63Q6DkavUWUV+MDNhCF5wGQrcdQNncVRF3YkuDHDT/xw2YWyZ/DX8k+gAYiC8gcD8Ueg0ljBVS1IDwPjuGoFPESJVxYr0MDPF2D8Pn2S5rq692U4D9ZLuluS46VA4DK6ig5P7QM5XVXi4V7vXM8qpN/zqneyz+w4PUh6NIX7QG6JczMhYd9maWRWVat5jDdyII63P6sNAy9QZjw+ClW211Q==' -d 'user={"account":"user@domain.com", "roles":["user"]}' http://127.0.0.1:8443/desks/1/occupy`, // same line, single quotes
  130. `curl https://api.openai.com/v1/chat/completions \
  131. -H "Content-Type: application/json" \
  132. -H "Authorization: Bearer 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" \`, // different line, double quotes
  133. `curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
  134. -H "Authorization: Bearer _FXNljbSRYMWx3TWrd7lgKhLtVZX6iskC8Wcbb4b" \
  135. -H "Content-Type:application/json"`,
  136. `curl -H "Authorization: Bearer sha256~bRLFnzd59Z3XpZH5_seJPHALOuvbWiKwbFKSsoALkgp"`,
  137. // Token auth
  138. `curl -H "Authorization: Api-Token 22cb987851bc5659l29114c62e60c79abd0d2c08" --request PUT https://appsecclass.report/api/use/635`, // token
  139. `curl -H "Authorization: Token 22cb987851bc5659229114c62e60c79abd0d2c08" --request PUT https://appsecclass.report/api/use/635`, // token
  140. // Nothing
  141. `curl -L -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb25maWRlbmNlIjowLjh9.kvRPfjAhLhtRTczoRgctVGp7KY1QVH3UBZM-gM0x8ec" service1.local -> correct jwt `, // no prefix
  142. `curl -L -H "Authorization: sha256~bRLFnzd5@=-.a+/hgdS"`, // no prefix
  143. // Non-authorization headers.
  144. `curl -X GET \
  145. -H "apikey: c4ed6c21-9dd5-4a05-8e3f-c56d1151cce8" \
  146. -H "Accept: application/json" \`, // apikey
  147. `curl -X POST --header "Api-Token: Sk94HG7f6KB"`, // api-token
  148. `curl -XPOST http://localhost:8080/api/tasks -H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" -H "Token: 3fea6af1349166ea" -d "content=hello-curl"`, // token
  149. `curl -X GET https://octopus.corp.net/
  150. -H "X-Octopus-ApiKey: 3a16750d-d363-41a4-8ebd-035408f7730f" \`, // X-$thing-ApiKey
  151. }
  152. fps := []string{
  153. // Placeholders
  154. `curl https://example.com/micropub -d h=entry -d "content=Hello World" -H "Authorization: Bearer XXXXXXXXXXXX"`,
  155. `curl -X POST https://accounts.spotify.com/api/token -d grant_type=client_credentials --header "Authorization: Basic ..."`,
  156. `curl \
  157. -H "Authorization: Bearer <Openverse API token>" \
  158. "https://api.openverse.org/v1/audio/?q=test"`,
  159. `curl -v -v -v -X POST https://domain/api/v1/authentication/sso/login-url/ \
  160. -H 'Content-Type: application/json' \
  161. -H "Authorization: Token **********" \
  162. -d '{"username": "test", "next": "/luna/"}'`,
  163. // Variables
  164. `curl -XPOST http://localhost:8080/api/token -H "Authorization: basic {base64(email:password[\n])}" => token`, // same line, invalid base64
  165. `curl -X GET \
  166. -H "apikey: $API_KEY" \
  167. -H "Accept: $FORMAT" \
  168. "$API_URL/rest/v1/stats_derniere_labellisation"`, // API Key placeholder
  169. `$ curl -X POST "http://localhost:8000/v1/chat/completions" \
  170. -H "Content-Type: application/json" \
  171. -H "Authorization: Bearer $API_KEY" \
  172. -d '{
  173. "model": "chatglm3-6b-32k",
  174. "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"}]
  175. }'`, // different line, placeholder
  176. `curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $(gcloud auth print-access-token)" https://workflowexecutions.googleapis.com/v1/projects/244283331594/locations/us-central1/workflows/sample-workflow/executions/43c925aa-514a-44c1-a0a4-a9f8f26fd2cb/callbacks/1705791f-d446-4e92-a6d0-a13622422e80_31864a51-8c13-4b03-ad4d-945cdc8d0631`, // script
  177. // Not valid BASIC
  178. `curl -X POST -H "Content-Type: application/json" -H "Authorization: Basic eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb25maWRlbmNlIjowLjh9.kvRPfjAhLhtRTczoRgctVGp7KY1QVH3UBZM-gM0x8ec" \`,
  179. }
  180. return utils.Validate(r, tps, fps)
  181. }