lastpass / lastpass-cli

LastPass command line interface tool
GNU General Public License v2.0
2.85k stars 291 forks source link

lpass login returns 'unknown' - iterations.php behavior changed #604

Open mjbroekman opened 3 years ago

mjbroekman commented 3 years ago

Yesterday, I was using lastpass-cli (via Homebrew) fine, but this morning when I attempt to log in via lpass login <username>, I get the message "unknown" and prompted for the master password again.

$ lpass login username@email.com Please enter the LastPass master password for username@email.com.

unknown Master Password:

I am able to log in to both the browser extension and the website, so I know my credentials are correct. The only place I see 'unknown' that would be applicable to logging in is in the xml_error_cause function in xml.c, which gets called from endpoints-login.c

I also attempted to compile from the repo here and, following all the applicable steps for M1 and Homebrew, I get the same result.

Workaround

A workaround is to:

  1. Open Account Settings in your browser (Open My VaultAccount Settings)
  2. Press Show Advanced Settings
  3. Set General → Security → Password Iterations to exactly 100100

LastPass will ask for your Master password and re-encrypt your vault. After that using lpass should work again.

rkirkpat commented 3 years ago

I am getting the same error on a 2015 MacBookPro with MacOS 10.14 (Mojave), so it appears it is a lpass issue, or more likely the backend web APIs used by it.

brendanlong commented 3 years ago

Several people where I work are running into this on Ubuntu 20.04 as well.

brendanlong commented 3 years ago

I traced through the code and I think the problem is that iterations.php on the server is broken. The steps I see are:

  1. CLI does a request to lastpass.com/iterations.php with my username
  2. iterations.php returns 100100
  3. CLI does 100100 iterations of hashing
  4. CLI sends the result to lastpass.com/login.php with iterations=100100
  5. login.php returns an error saying iterations should be 5000

If I hack up cmd-login.c to hard-code iterations to 5000 I'm able to log in:

diff --git a/cmd-login.c b/cmd-login.c
index 27b2272..8e46232 100644
--- a/cmd-login.c
+++ b/cmd-login.c
@@ -96,6 +96,8 @@ int cmd_login(int argc, char **argv)

        username = argv[optind];
        iterations = lastpass_iterations(username);
+       printf("Server says %d iterations\n", iterations);
+       iterations = 5000;
        if (!iterations)
                die("Unable to fetch iteration count. Check your internet connection and be sure your username is valid.");

I'm not sure if this is the same for everyone, so to find this number you'll need to improve the error response handling:

diff --git a/xml.c b/xml.c
index 5961244..fecbde6 100644
--- a/xml.c
+++ b/xml.c
@@ -154,7 +154,9 @@ out:
        if (doc)
                xmlFreeDoc(doc);
        if (!result)
-               result = xstrdup("unknown");
+               xasprintf(&result, "unknown XML error: %s", buf);
+       if (!result)
+               result = xstrdup("unknown xml_error_cause");

        return result;
 }
brendanlong commented 3 years ago

Something weird is going on with iterations.php. If I click this link in my browser: https://lastpass.com/iterations.php?email=me@example.com it says 5000, but if I do curl https://lastpass.com/iterations.php?email=me@example.com it says 100100.

mjbroekman commented 3 years ago

Something weird is going on with iterations.php. If I click this link in my browser: https://lastpass.com/iterations.php?email=me@example.com it says 5000, but if I do curl https://lastpass.com/iterations.php?email=me@example.com it says 100100.

I get the same results for my email and it's entirely (and poorly) UserAgent based. If I run curl and specify a UserAgent, I'm able to get back 5000 or 100100 depending on how 'valid' the user agent string is:

$ curl -A 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36' 'https://lastpass.com/iterations.php?email=me@example.com'        
5000                                                                                                                                                                                                   
$ curl -A 'Mozilla/5.0' 'https://lastpass.com/iterations.php?email=me@example.com' 
100100                                                                                                                                                                                                 
$ curl -A 'AppleWebKit/537.36' 'https://lastpass.com/iterations.php?email=me@example.com'
5000                                                                                                                                                                                                   
$ curl -A 'Chrome' 'https://lastpass.com/iterations.php?email=me@example.com'
100100                                                                                                                                                                                                 
$ curl -A 'Chrome/90' 'https://lastpass.com/iterations.php?email=me@example.com'
5000                                                                                                                                                                                                   
$ curl -A 'Safari' 'https://lastpass.com/iterations.php?email=me@example.com'
100100                                                                                                                                                                                                 
$ curl -A 'Safari/1' 'https://lastpass.com/iterations.php?email=me@example.com'
5000                                                                                                                                                                                                   
$ curl -A 'Safari/537.36' 'https://lastpass.com/iterations.php?email=me@example.com'
5000                                                                                                                                                                                                   
$ curl -A 'Firefox/537.36' 'https://lastpass.com/iterations.php?email=me@example.com'
5000                                                                                                                                                                                                   
$ curl -A 'Firefox/' 'https://lastpass.com/iterations.php?email=me@example.com' 
100100                                                                                                                                                                                                 
$ curl -A 'Firefox/88.0' 'https://lastpass.com/iterations.php?email=me@example.com'
5000
brendanlong commented 3 years ago

I'm trying to set the user agent in lpass but even if I send a Firefox user agent it's still not working there.

brendanlong commented 3 years ago

Ah I think this is the other fun part: lpass always uses HTTP POST but iterations.php only seems to work with GET requests now:

$ curl -A 'Chrome/90' https://lastpass.com/iterations.php?email=me@example.com
5000
$ curl -A 'Chrome/90' https://lastpass.com/iterations.php -d 'email=me@example.com'
100100
$ curl -A 'Chrome/90' https://lastpass.com/iterations.php -d 'email=me%40example.com'
100100
$ curl -A 'Chrome/90' https://lastpass.com/iterations.php?email=me@example.com -d ''
100100
brendanlong commented 3 years ago

Ah cool, so the user agent lpass uses is fine, it's entirely the GET issue. I think this is the fix:

diff --git a/endpoints.c b/endpoints.c
index 8f5b7d8..8ad5a5c 100644
--- a/endpoints.c
+++ b/endpoints.c
@@ -48,9 +48,11 @@ unsigned int lastpass_iterations(const char *username)
 {
        _cleanup_free_ char *reply = NULL;
        _cleanup_free_ char *user_lower = NULL;
+       _cleanup_free_ char *path = NULL;

        user_lower = xstrlower(username);
-       reply = http_post_lastpass("iterations.php", NULL, NULL, "email", user_lower, NULL);
+       xasprintf(&path, "iterations.php?email=%s", user_lower);
+       reply = http_post_lastpass(path, NULL, NULL, NULL);

        if (!reply)
                return 0;

(I have no idea if this introduces a memory leak or not :shrug: )

mjbroekman commented 3 years ago

Hmm. With that code in place, lpass segfaults for me now.

brendanlong commented 3 years ago

Well that's fun.. :\ I hardly ever write C so it's possible I'm doing something wrong here but I don't know what it is since it works for me.

mjbroekman commented 3 years ago

Well that's fun.. :\ I hardly ever write C so it's possible I'm doing something wrong here but I don't know what it is since it works for me.

My guess is it's library related ... I'll keep poking at it on my end to see whether I can get it to work on my M1

mjbroekman commented 3 years ago

I realized I had re-pulled the repo but not re-implemented the fixes for homebrew in #513 . After fixing CMakeLists.txt for Homebrew, it compiled and runs fine.

The elimination of POST in iterations.php is annoying though.

rkirkpat commented 3 years ago

I can confirm that the pull request above works for me on MacOS 10.14 (Mojave) on an MacBookPro 2015 using brew. I was able to do this as follows:

$ curl https://patch-diff.githubusercontent.com/raw/lastpass/lastpass-cli/pull/605.diff | shasum -a 256
14ced28cc06a335cd8ae850ff49841c08562391dfbe6b1ad4a585607778ebd04 -

$ brew edit lastpass-cli
Editing /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/lastpass-cli.rb
...insert after depends_on and uses_from...

  # Apply fix for iterations.php breakage.
  patch do
    url "https://patch-diff.githubusercontent.com/raw/lastpass/lastpass-cli/pull/605.diff"
    sha256 "14ced28cc06a335cd8ae850ff49841c08562391dfbe6b1ad4a585607778ebd04"
  end

$ brew reinstall -s lastpass-cli
==> Downloading https://patch-diff.githubusercontent.com/raw/lastpass/lastpass-cli/pull/605.diff
######################################################################## 100.0%
==> Downloading https://github.com/lastpass/lastpass-cli/releases/download/v1.3.3/lastpass-cli-1.3.3.tar.gz
Already downloaded: /Users/.../Library/Caches/Homebrew/downloads/5881a7a4f1a6bc8a050d7ae9afb718019425c89631ca153d699413e9773385c0--lastpass-cli-1.3.3.tar.gz
==> Reinstalling lastpass-cli
==> Patching
==> Applying 605.diff
patching file endpoints.c
patching file xml.c
==> cmake .. -DCMAKE_INSTALL_MANDIR:PATH=/usr/local/Cellar/lastpass-cli/1.3.3_1/share/man
==> make install install-doc
==> Caveats
Bash completion has been installed to:
  /usr/local/etc/bash_completion.d
==> Summary
🍺  /usr/local/Cellar/lastpass-cli/1.3.3_1: 11 files, 225.9KB, built in 9 seconds

$ lpass login me@example.com
Success: Logged in as me@example.com

Used https://www.ralfebert.de/snippets/brew-apply-patch-to-package-formula/ as a guide for the above. Thank you all for running this down and providing at a work-in-progress fix, I can at least use LastPass from the command line again!

xdays commented 3 years ago
unknown XML error: <?xml version="1.0" encoding="UTF-8"?><response><error iterations="5000" /></response>

Got this error with patched version.

woopstar commented 3 years ago

I can confirm the steps for patching by @rkirkpat works for me too on MacBook Air (M1, 2020) using Big Sur 11.3.1 (20E241)

detunized commented 3 years ago

Hi everyone. I maintain a library that allows access to LastPass and other PMs. I noticed this issue as well. After some testing I see that the iterations.php endpoint is broken at the moment. Whatever it returns is not stable. Since it doesn't produce any error codes, it's hard to say if what it returned is a valid value or not. In the past, LastPass used to rely on the return value of login.php in order to resubmit with the correct iteration count again. This error looks like this: <response><error iterations="5000" /></response>. The value that is returned is the correct PBKDF2 iteration count. Why you see so much 5000s is that because this used to be the default values some years back. Now it's 100100. So it's normal to see that for older accounts where this setting has not been updated. Go set this value much higher, BTW.

In order to fix this issue, you'd need to login with the default value and parse the result and re-hash the password and re-login with the correct value returned from the server.

This is how I fixed in my library and it's tested and it works: https://github.com/detunized/password-manager-access/commit/bd2e31d3478309a2d9a5e171b0422a1a254c3a57

woopstar commented 3 years ago

@detunized

Go set this value much higher, BTW.

Where do you set this value?

detunized commented 3 years ago

@woopstar

Vault -> Account Settings -> General -> Show Advanced Settings -> Password Iterations

image

mjbroekman commented 3 years ago

I can confirm that the patch submitted by @brendanlong continues to function properly even if you update the number of iterations to something other than 5000 or 100100.

mattiasb commented 3 years ago

@mjbroekman Could you remove "MacBook Air M1 - " from the title of this issue since it happens elsewhere as well? :)

millerdrew commented 3 years ago

I got this error as well with the patched version using the method from @rkirkpat (https://github.com/lastpass/lastpass-cli/issues/604#issuecomment-843612877):

unknown XML error: <?xml version="1.0" encoding="UTF-8"?><response><error iterations="5000" /></response>

But just changing my settings to a higher number like 200000 did not work. It only worked with changing my account settings to exactly 100100: image

Thanks all for the help!

mattiasb commented 3 years ago

I got this error as well with the patched version using the method from @rkirkpat (#604 (comment)):

unknown XML error: <?xml version="1.0" encoding="UTF-8"?><response><error iterations="5000" /></response>

But just changing my settings to a higher number like 200000 did not work. It only worked with changing my account settings to exactly 100100: image

Thanks all for the help!

I had issues building the branch in #605 because of the issue in #532 . I rebased #605 over #532 (manually) and could build but the the patched lpass still gave the same issue for me so I gave up.

Now after reading the above comment by @millerdrew I just went into settings and changed Password Iterations to 100100 and now the regular¹ lpass without any of the above patches works fine.

@millerdrew could you test with your old unpatched lpass and see if that works for you? That would mean we have a rather solid non-intrusive workaround until this is fixed proper.

1: "regular lpass" here means whatever version of lpass that happens to be shipped with Fedora 33 on my laptop :P

brendanlong commented 3 years ago

Hm I'm not sure why even with my patch sometimes the iterations.php result is wrong. A better solution would probably be to call login.php and use the iterations it returns, then rehash and call it again. The code to do that was just more complicated than I was planning to write when my fix worked for me.

It looks like for most people the simplest workaround is to set your password iterations to 100100 though. I'll be doing that myself.

brendanlong commented 3 years ago

I updated my patch in https://github.com/lastpass/lastpass-cli/pull/605 to use login.php to get the iteration count since hopefully that's more stable. I recommend that most people just update their password iterations to 100100 though (and probably a worthwhile security improvement on its own if yours is set to 5000).

millerdrew commented 3 years ago

@mattiasb I uninstalled lastpass-cli, and reinstalled without patching and I can confirm that it works now that my password iterations is set to 100100.

mattiasb commented 3 years ago

@mjbroekman could you append the following to your question (so that it's easily and immediately accessible to people coming here from the interwebs):

## Workaround

A workaround is to:
1. Open `Account Settings` in your browser (`Open My Vault` → `Account Settings`)
2. Press `Show Advanced Settings`
3. Set `General → Security → Password Iterations` to *exactly* `100100`

LastPass will ask for your Master password and re-encrypt your vault. After that using `lpass` *should*  work again.
mattiasb commented 3 years ago

@mjbroekman thanks! ❤️

mjbroekman commented 3 years ago

@mjbroekman thanks! ❤️

Thanks for pointing out the workaround.

strider4560 commented 3 years ago

I've had the same issue. Setting "Password Iterations" to 100100 also fixed it for me.

But I wanted to add something else to this bug description. I have the following script in my .zshrc: alias lp='lpass show -c --password $(lpass ls | fzf | awk '"'"'{print $(NF)}'"'"' | sed '"'"'s/\]//g'"'"')'

If I run "lpass login" I was unable to login. If I run "lp" I would be prompted for a login and I would be successfully authenticated. So there must be some different mechanism between "lpass login" and "lpass show" where the second one doesn't have this iterations issue.

mjbroekman commented 3 years ago

I've had the same issue. Setting "Password Iterations" to 100100 also fixed it for me.

But I wanted to add something else to this bug description. I have the following script in my .zshrc: alias lp='lpass show -c --password $(lpass ls | fzf | awk '"'"'{print $(NF)}'"'"' | sed '"'"'s/\]//g'"'"')'

If I run "lpass login" I was unable to login. If I run "lp" I would be prompted for a login and I would be successfully authenticated. So there must be some different mechanism between "lpass login" and "lpass show" where the second one doesn't have this iterations issue.

The only thing I can think of is that lpass show or lpass ls are hitting a different endpoint that grabs the 'correct' number of iterations for you to be able to login with a different number of iterations. Hopefully the devs will be able to information in this thread to fix it in a way that allows non-default iterations to work properly.

detunized commented 3 years ago

@strider4560, @mjbroekman It is likely that they save the iteration count with the blob and don't need it when refetch. Or they don't refetch at all and the password is only used to decrypt the already downloaded blob.

strider4560 commented 3 years ago

@detunized I think you're right. I logged in with lpass, killed my agent, changed my iterations count, and now "lp" can't log me back in. So it sounds like the iterations wasn't being refetched with "lpass show".

ghost commented 3 years ago

@brendanlong thank you for the fix! I applied the patch per @rkirkpat's splendid patching tips.

slmingol commented 3 years ago

@efx @brendanlong just trying to find out what the status of this issue is? Are we suppose to continue with the workaround 100100 or is there a proper fix coming down the pipe?

AndrewKassab commented 2 years ago

@efx @brendanlong just trying to find out what the status of this issue is? Are we suppose to continue with the workaround 100100 or is there a proper fix coming down the pipe?

I am wondering the same. Have they dropped support for this?

twilightsophie commented 2 years ago

I have run into this problem today, I've only just found this thread so I'll be trying the workaround. My version of lastpass-cli according to brew upgrade lastpass-cli is 1.3.3.1 and I'm using a 2015 MacBook Pro with MacOS Catalina 10.15.7.

ETA: Changing the number to 100100 let me login.

cjstaples commented 2 years ago

Still an issue as of today. Thankfully, the workaround of updating password iterations to 100100 still succeeds.

MartinPaulEve commented 2 years ago

Still hitting this issue as of today -- the workaround still works but strangely the problem was intermittent (as in I wasn't getting this error until a few days ago and then it suddenly hit)

mjbroekman commented 2 years ago

LastPass was having issues yesterday for a couple of hours during which it was impossible to access objects in your vaults.

atipi commented 1 year ago

Hello, yesterday night I started to have the issue logging in to LastPass even my password is correct but using biometric in my iPhone is working fine.

By following conversation above, I don't see 'Password Iterations' option at all in LastPass UI in browser. What should I do?

austinbutler commented 1 year ago

Go to "Account Settings", click "Advanced", scroll down to the "Security" section, then look for "Password Iterations".

lukens commented 1 year ago

Has this been fixed yet? I hit this issue today, having recently updated my iterations to 2000000. Switching to 100100 got it working again.

neirbowj commented 1 year ago

Is the LastPass CLI still considered a supported feature? In light of the fact that 100100 iterations is no longer considered sufficiently high, LastPass needs to resolve this issue one way or the other: enable CLI users to set iterations > 100100; or deprecate the CLI feature.

0xdevalias commented 1 year ago

Looks like May 1 2023 will be when this version of the CLI ceases to work correctly (unless they make required changes before then):

image

lukens commented 1 year ago

@0xdevalias - where did you see this announcement? I've not seen it anywhere. I wonder if it's specific to your company's settings (assuming you're using a business edition/account).

andrew-llh commented 1 year ago

@lukens According to the Apr 28, 2023 LastPass Security Bulletin at https://support.lastpass.com/s/document-item?language=en_US&bundleId=lastpass&topicId=LastPass/security-bulletin-recommended-actions-free-premium-families.html&_LANG=enus:

Note: In the coming months, we will be increasing the minimum required iterations value for existing customers to 600,000 rounds. When this change takes place, all newly created accounts will begin with the new minimum default of 600,000 rounds, and all existing accounts will be upgraded automatically to meet the new minimum value (no customer action required).

0xdevalias commented 1 year ago

@lukens That was in the LastPass desktop client, personal account, nothing to do with business/org accounts.

neirbowj commented 8 months ago

LastPass just notified me that it had changed the iterations on my "business user" account to 600,000. I confirmed in the advanced account settings area. I am still able to login and access secrets from the LastPass CLI (v1.3.7 on FreeBSD and on macOS via MacPorts).

$ curl -s "https://lastpass.com/iterations.php?email=$MYUSERNAME"
600000

Whew!