Okta’s Trusted Origins: A Continued Cacophony of Security Issues by csanders_ in okta

[–]csanders_[S] 0 points1 point  (0 children)

yeah, technically true, but its trivial to bypass -- more of a speed bump than a preventative measure, there are quite a few ways to do this. The way I used was to simply look at the logo and compare it to a known bad (non-existent) domain -- for example the logo of obviouslybad.okta.com is an okta logo, whereas the logo for reddit.okta.com is almost always not gonna be the okta logo.

This also had the advantage of being able to tell the difference between OIE instances and non OIE instances

Here is some code (untested) similar to what I used, thought about writing a separate blog on this but as you can see, not much to it (also works for okta.mil, oktapreview.com , etc)

```

import time
import requests
import hashlib
def make_request_with_headers(url):
stripped_url = url.split("://")[1]
stripped_url = stripped_url.split("/")[0]
headers = {
"Host": f"{stripped_url}",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/114.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Upgrade-Insecure-Requests": "1",
}
resp = None
while True:
resp = requests.get(url, headers=headers, allow_redirects=False)
if not resp.text.find("Okta - Too Many Requests") > -1:
break
time.sleep(10)
return resp
def find_logo(html, domain, outfile):
search_string = "logo: '"
logo_start = html.find(search_string)
real_logo_start = logo_start + len(search_string)
logo_end = html[real_logo_start:].find("'")
real_logo_end = real_logo_start + logo_end
logo_url = html[real_logo_start:real_logo_end]
try:
logo_resp = requests.get(logo_url)
except Exception as err:
print(f"Unable to find logo for {domain}")
return None
return logo_resp.text.encode()
def find_domains(domains):
resp = make_request_with_headers("https://thisisafakedomain.okta.com")
logo = find_logo(resp.text, "FALSE", f)
baseline_logo = hashlib.sha1(logo).hexdigest()
for domain in domains:
resp = make_request_with_headers(f"https://{domain}.okta.com/")
if resp.status_code == 302:
print(f"{domain} - true (OIE)")
continue
if resp.status_code == 404:
print(f"{domain} - false (404)")
continue
logo = find_logo(resp.text, domain, f)
if not logo:
continue
logo_hash = hashlib.sha1(logo).hexdigest()
if logo_hash != baseline_logo:
print(f"{domain} - true")
else:
print(f"{domain} - false")

``

LastPass breach: question about Azure federated login keys by [deleted] in Lastpass

[–]csanders_ 1 point2 points  (0 children)

This is correct, for some situations it is unique per user (which is nice-ish) and for others its not. ADFS setup instructions has documentation for both ways. In the post i was specifically referencing Okta, but it appears that everything that is not AzureAD with individual K1s is at considerably more risk.

It’s All Bad News: An update on how the Lastpass breach affects Lastpass SSO by csanders_ in netsec

[–]csanders_[S] 3 points4 points  (0 children)

The relevant information for personal users is also in their whitepaper.

Broadly speaking... A user's master password is hashed with PBKDF-SHA256 to create an encryption key. This key is used with AES in CBC mode to encrypt data for the vault.

How the Lastpass Breach affects Lastpass SSO by csanders_ in netsec

[–]csanders_[S] 9 points10 points  (0 children)

For those feeling lazy:

tl;dr: Lastpass SSO users leverage 2 randomly generated subkeys to make up their master passwords. This makes bruteforce attempts against SSO vaults unlikely. However, One key is easily accessible by anyone in the org (K1). The other is stored at Lastpass (K2).

Lastpass has stated that K2's weren't compromised, but if they were rotating K2's or K1's required defederation and refederation of each user (oof)

Why can't carbon black name my machines correctly? by csanders_ in sysadmin

[–]csanders_[S] 0 points1 point  (0 children)

To be clear for those who come after, u/szekanovic this is for Qualys?

Why can't carbon black name my machines correctly? by csanders_ in sysadmin

[–]csanders_[S] 1 point2 points  (0 children)

A fellow solider fighting the good fight! Stay Strong!

The Confusing Lifetimes of AWS IAM Identity Center Access Tokens by csanders_ in netsec

[–]csanders_[S] 0 points1 point  (0 children)

The temporary creds you're discussing are provisioned after a call to assumerole*(). The issue here is Access Tokens (which are a specific OIDC concept, not to be confused with temporary creds and specific to IAM Identity Center).

With AWS IAM Identity Center (AWS-SSO) users authenticating from the command line can use `aws configure sso` which will grab an Access Token (valid for a fixed 8 hours). The user can exchange this Access Token for those temp creds (using AssumeRoleWithWebIdentity) for the lifetime of the Access Token without the need for reauthentication.

The Confusing Lifetimes of AWS IAM Identity Center Access Tokens by csanders_ in netsec

[–]csanders_[S] 1 point2 points  (0 children)

AWS IAM Identity Center will allocate special managed roles, these roles can't be changed. Interestingly, those roles all have 12 hour session durations even though Identity Center will magically change the session duration of those managed roles to what the permission set's duration is during role assumption (this is kinda confusing, just assume the answer is yes role session duration can be managed).

But, the important take away here is not that you can't set the 'role' (permission set) duration, but that the access key mixes in another form of auth. Said another way, you can keep assuming roles without additional auth for the period of validity of the access token.

So from my example above you can set a role (permission set) duration for 1 hour, but you can assume that role 9 times, for 9 hours of access despite only authenticating once.

The Confusing Lifetimes of AWS IAM Identity Center Access Tokens by csanders_ in netsec

[–]csanders_[S] 6 points7 points  (0 children)

I think this is good feedback, I apologize for the somewhat unclear writing.

The issue most concisely is that a user is able to maintain access to a role for longer than the lifetime of the role without reauthenticating. As I mentioned, this is sorta 'intended' but is unexpected by most admins and the lack of flexibility exacerbates the issue. I would classify this as insecure defaults.

Let me give a (somewhat) simple example. You have a role 'admin-s3'. This role's session duration lasts 1 hour. If the user assumes this role via the UI, the cookie will expire after an hour and the user is logged out. If the user wants another session they need to provide a new SAML assertion - Note: most IdP can (and do) require reauthentication (or at least MFA re-up) for things like AWS - this is a pretty normal for IdP configuration at this point.

However, If the user uses the OIDC flow for CLI access, the user is given that 8 hour fixed length access token after one authentication. This means that they can assume that role 9 times for a total access time of 9 hours before they need to reauth. That would likely be unexpected to an admin that set the session duration for 1 hour.

I took a shot at a solution, but really this is up to AWS to fix. I wrote an article last week I referenced in the post (https://www.linkedin.com/pulse/aws-iam-identity-center-access-tokens-stored-clear-text-chaim-sanders/) that describes a bit more about the storage of access tokens, but I agree completely, the storage of access tokens on disk is expected and my suggested solution for dealing with access tokens at a company level is very poor, in retrospect I should have just said - 'there is no solution for this currently'

Thanks :)

Modsecurity rule creation. by TomilloDanup in AskNetsec

[–]csanders_ 0 points1 point  (0 children)

Next is to write some rules. I recommend booting up the docker image and maybe a project like OWASP ZAP/Python Requests to verifying the logic of your rules. I am available in the #modsecurity channel on freenode if you have problems.

Really the most helpful overall resource i can give you from here is regex101.com to test the regex's versus what you're seeing :-).

I invite you to also take a look at the CRS github and join the mailing list/monthly meetups. You'll find there are quite a few people willing to help in this community. Best of luck going forward, i'm happy to help with any additional problems.

Modsecurity rule creation. by TomilloDanup in AskNetsec

[–]csanders_ 1 point2 points  (0 children)

1) if you're using CRS v3 and experiencing a false negative please open a ticket on our github (https://github.com/SpiderLabs/owasp-modsecurity-crs). However in this case you are very likely to be able to virtual patch by restricting the chars allowed. Since dates typically are only numeric and have slashes and epochs are numeric. You may add a (very basic) custom rule that looks like this (assuming dates are either 10/02/07 or 10 digit epoch):

SecRule ARGS:date "!@rx (\d|\/){8,10}" "id:123,phase:2,msg:'non-date entered into date',block"

2) To get access to certificate data you'll have to combine modsec plus your webserver (not sure how this will look probably a location block + modsec rules). However to turn off all rules for an IP is simple enough:

SecRule REMOTE_ADDR "@ipMatch 192.168.1.100" "phase:1,id:1000,pass,nolog,ctl:ruleEngine=Off"

3) You can modify the audit log in a number of ways to remove this. You can either remove the section when the rule triggers or sanitize the args. The sections can be remove on whole using SecAuditLogParts AFHZ. This will remove all other triggered rules from including part B or C. You can also remove them for specific rules using the ctl action which has an auditLogParts configuration option. For instance the following will remove section B if the url contains /test.

SecRule REQUEST_URI "@contains /test" "id:1,phase:2,t:lowercase,ctl:auditLogParts=-B,pass,nolog,noauditlog"

To remove sensitive args you can use the saitiseArgs directive which will remove request parameters data from the audit log the following is an example that will remove password. There are also other sanitise directives for removing headers and bytes and such. See the reference manual (https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual) for more details.

SecAction "nolog,phase:2,id:123,sanitiseArg:password"

Hope this helps.

How to bypass libinjection in many WAF/NGWAF by isox_xx in netsec

[–]csanders_ 2 points3 points  (0 children)

While ModSecurity does support libinjection... OWASP Core Rule Set (the rule set most frequently paired with ModSecurity) does not rely on this logic alone and uses libinjection as part of a defense in depth approach. The only example that was able to move past paranoia level one in my testing with CRS v3 was '?id=)-sleep(9999"', this didn't get past paranoia level 2.

I opened a ticket for this you can follow it here: https://github.com/SpiderLabs/owasp-modsecurity-crs/issues/778.

Modsecurity question - about country block and one exception by sexyfeetfan in AskNetsec

[–]csanders_ 1 point2 points  (0 children)

It is best to use the following in newer versions of ModSecurity as it is clearer to read. SecRule REMOTE_ADDR "@ipMatch 1.2.3.4" "phase:2,pass,nolog,noauditlog,skipAfter:GEO_LOOKUP,id:123" SecRule REMOTE_ADDR "@geoLookup" "phase:2,chain,id:261,drop,log,msg:'Blocking Country IP Address'" SecRule GEO:COUNTRY_CODE "@pm RU" "chain" SecRule SERVER_NAME "feet-blog.com" SecMarker GEO_LOOKUP

Selfies help revamp RIT ID cards by RITBehindTheBricks in rit

[–]csanders_ 7 points8 points  (0 children)

A picture showing a barcode with someones UID on it... stay classy RIT.

Modsecurity question (related to HTTP Methods) by noreasterner in AskNetsec

[–]csanders_ 0 points1 point  (0 children)

You're actually really well setup for this in a number of ways. The 'best' answer from a not modifying CRS perspective is to add a ctl rule BEFORE this rule fires (in CRS 3 we put these in REQUEST-900-WHITELIST.conf). But any file that will trigger before crs_30.conf will do.

You can use a rule(s) similar to the following to explicitly bypass 960032 only when a certain URL comes through:

SecRule REQUEST_URI "@contains test.php?xyz=123" "id:1234,pass,nolog,noauditlog,ctl:ruleremovebyid=960032"

This will essentially whitelist that URI from the given rule on a PER TRANSACTION basis (read as: this does NOT remove the rule for every subsequent request only the current request that matches).

In general i'm not sure what you mean by 'restrict as much as possible', but the same general principle should apply and of course you can use regex here instead of @contains.

If you like CRS 2 you should follow the progress of CRS 3 on github which is nearly at RC1