Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FALSE-NEGATIVE] cookies-without-httponly.yaml does not find cookies missing HttpOnly attribute if there are any cookies with HttpOnly set #11288

Open
rjcoleman-tg opened this issue Dec 2, 2024 · 4 comments · May be fixed by #11382
Assignees
Labels
Done Ready to merge false-negative Nuclei template missing valid results

Comments

@rjcoleman-tg
Copy link

rjcoleman-tg commented Dec 2, 2024

Template IDs or paths

https://github.com/projectdiscovery/nuclei-templates/blob/main/http/misconfiguration/cookies-without-httponly.yaml

Environment

- OS: MacOs Sequoia 15.1.1 
- Nuclei: Nuclei Engine Version: v3.3.7
- Go: go version go1.22.5 darwin/amd64

Steps To Reproduce

Expected results

This template should return findings if any cookies set by a target are missing HttpOnly.

Actual results:

This template only works if none of the cookies set by a target have HttpOnly set.

If there are any cookies with HttpOnly set, even if there are also cookies without HttpOnly set, this template does not return any results.

If there are no cookies with HttpOnly set, it returns results, although not the correct number of results.

Run nuclei -t http/misconfiguration/cookies-without-httponly.yaml -u https://www.target.com/ --debug .
observe no cookies with HttpOnly
Set-Cookie: TealeafAkaSid=xxxxxxxxxxxxxxxxxxx; Expires=Wed, 01 Jan 2025 15:34:35 GMT; Path=/; Domain=target.com;
Set-Cookie: visitorId=xxxxxxxxxxxxxxxxxxx; Max-Age=63072000; Expires=Wed, 02 Dec 2026 15:34:35 GMT; Path=/; Domain=.target.com; Secure; SameSite=None
Set-Cookie: sapphire=xxxxxxxxxxxxxxxxxxx; Max-Age=2629746; Expires=Thu, 02 Jan 2025 02:03:41 GMT; Path=/; Domain=.target.com; Secure; SameSite=None
Set-Cookie: TealeafAkaSid=xxxxxxxxxxxxxxxxxxx; Expires=Wed, 01 Jan 2025 15:34:35 GMT; Path=/; Domain=target.com;
Set-Cookie: GuestLocation=xxxxxxxxxxxxxxxxxxx;Path=/; Secure; Max-Age=86400
observe findings (there should be 5, not sure why there are only 2):
[cookies-without-httponly:word-1] [http] [info] https://www.target.com/
[cookies-without-httponly:word-2] [http] [info] https://www.target.com/
Run nuclei -t http/misconfiguration/cookies-without-httponly.yaml -u https://heinztohome.co.uk/ --debug
observe several cookies without HttpOnly
Set-Cookie: JSESSIONID=xxxxxxxxxxxxxxxxxxx; Path=/; Secure; HttpOnly; SameSite=Lax
Set-Cookie: chumewe_user=xxxxxxxxxxxxxxxxxxx; Version=1; Domain=.heinztohome.co.uk; Path=/; Max-Age=157784630; Expires=Sun, 02-Dec-2029 20:30:55 GMT; SameSite=None; Secure
Set-Cookie: chumewe_sess=xxxxxxxxxxxxxxxxxxx; Version=1; Domain=.heinztohome.co.uk; Path=/; Max-Age=14400; Expires=Mon, 02-Dec-2024 19:27:05 GMT; SameSite=None; Secure
Set-Cookie: locale_V6=xxxxxxxxxxxxxxxxxxx; Version=1; Domain=.heinztohome.co.uk; Path=/; Max-Age=31556926; Expires=Tue, 02-Dec-2025 21:15:51 GMT; SameSite=None; Secure
Set-Cookie: csrf_token=xxxxxxxxxxxxxxxxxxx; Version=1; Path=/; SameSite=None; HttpOnly; Secure
Set-Cookie: NSC_mc_wtsw_efgbvmu_xfctsw_8010_S=xxxxxxxxxxxxxxxxxxx;expires=Mon, 02-Dec-2024 18:26:59 GMT;path=/;secure;httponly
observe findings

[INF] No results found. Better luck next time!

Relevant dumped responses

Anything else?

I tried to write a template to do this correctly, but got as far as finding out Go does not support negative look aheads in regular expressions. Here is the template I am currently working with:

id: missing-httponly-cookie

info:
  name: Missing HttpOnly in Set-Cookie Header
  author: rjcoleman-tg
  severity: info
  description: |
    This template checks for Set-Cookie headers that do not include the HttpOnly flag, which can expose cookies to client-side scripts.

requests:
  - method: GET
    path:
      - "{{BaseURL}}"

    headers:
        User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0

    extractors:
      - type: regex
        name: all-cookies
        regex:
          - "Set-Cookie:.*"
        part: header

      - type: regex
        name: httponly-cookies
        regex:
          - "Set-Cookie:.*HttpOnly.*"
        part: header

#      - type: regex
#        name: missing-httponly
#        part: header
#        regex:
#          - "Set-Cookie:(?!.*\bHttpOnly\b).*"

the last extractor is commented out because it fails.

@rjcoleman-tg rjcoleman-tg added the false-negative Nuclei template missing valid results label Dec 2, 2024
@princechaddha
Copy link
Member

Hi @rjcoleman-tg, Thank you for taking the time to create this issue and for contributing to this project 🍻

We are currently rewriting this template using our JavaScript protocol to print all cookies without the HttpOnly flag, and we will apply the same to the cookies without secure attribute template

@rjcoleman-tg
Copy link
Author

rjcoleman-tg commented Dec 14, 2024

Hey @princechaddha

Ah thank you!

I think I have a working template for this problem, but am looking forward to seeing what you ultimately ship!

Here is what I've been using:

id: cookies-missing-httponly

info:
  name: Cookies set on target without HttpOnly
  author: RJ Coleman
  severity: info
  description: This template finds and reports all Cookies set by target without HttpOnly.

flow: |
    http()
    javascript()

http:
  - method: GET
    path:
      - "{{BaseURL}}"
    headers:
        User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0

javascript:
  - code: |
      content = template.http_all_headers
      const setCookieLines = content
        .split(/\r\n/) 
        .filter(line => line.trim().startsWith('Set-Cookie:')); 
      const nonHttpOnlyCookies = setCookieLines.filter(line => !line.includes('HttpOnly'));
      const cookieNames = nonHttpOnlyCookies.map(line => {
        const match = line.match(/Set-Cookie:\s*([^=]+)=/); 
        return match ? match[1] : null;
      }).filter(Boolean); 
      cookieNames

    extractors:
      - type: regex
        regex:
          - '[_-a-zA-Z0-9].*'

@rjcoleman-tg
Copy link
Author

Update above, changed the regex in the extractor and modified the js to find cookies with case insensitive search:

id: cookies-missing-httponly

info:
  name: Cookies set on target without HttpOnly
  author: RJ Coleman
  severity: info
  description: This template finds and reports all Cookies set by target without HttpOnly.

flow: |
    http()
    javascript()

http:
  - method: GET
    path:
      - "{{BaseURL}}"
    redirects: true
    max-redirects: 2
    headers:
        User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0

javascript:
  - code: |
      content = template.http_all_headers
      const setCookieLines = content
        .split(/\r\n/)
        .filter(line => line.trim().toLowerCase().startsWith('set-cookie:'));

      const nonHttpOnlyCookies = setCookieLines.filter(line => !line.toLowerCase().includes('httponly'));

      const cookieNames = nonHttpOnlyCookies.map(line => {
        const match = line.match(/set-cookie:\s*([^=]+)=/i);
        return match ? match[1] : null;
      }).filter(Boolean);
      cookieNames

    extractors:
      - type: regex
        regex:
          - '[a-zA-Z0-9_-]+'

This same pattern works for cookies-missing-secure as well:

id: cookies-missing-secure

info:
  name: Cookies set on target without Secure
  author: RJ Coleman
  severity: info
  description: This template finds and reports all Cookies set by target without Secure.

flow: |
    http()
    javascript()

http:
  - method: GET
    path:
      - "{{BaseURL}}"
    headers:
        User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0

javascript:
  - code: |
      content = template.http_all_headers
      const setCookieLines = content
        .split(/\r\n/)
        .filter(line => line.trim().toLowerCase().startsWith('set-cookie:'));
      const nonSecureCookies = setCookieLines.filter(line => !line.toLowerCase().includes('secure'));
      const cookieNames = nonSecureCookies.map(line => {
        const match = line.match(/set-cookie:\s*([^=]+)=/i);
        return match ? match[1] : null;
      }).filter(Boolean);
      cookieNames

    extractors:
      - type: regex
        regex:
          - '[a-zA-Z0-9_-]+'

@princechaddha
Copy link
Member

@rjcoleman-tg The templates you shared look great and function well. We will update our existing templates with the ones you provided. Thank you for making these updates.

@princechaddha princechaddha added the Done Ready to merge label Dec 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Done Ready to merge false-negative Nuclei template missing valid results
Projects
None yet
2 participants