1 00:00:00,000 --> 00:00:05,180 Welcome to my talk, broadcasting from my quarantine bunker to yours. 2 00:00:05,260 --> 00:00:12,520 My talk is Yippee-ki-yay MFA-er, bypassing multi-factor authentication with real-time replay session instantiation attacks. 3 00:00:13,600 --> 00:00:15,960 So before we get started, I'll go ahead and introduce myself. 4 00:00:15,960 --> 00:00:17,220 My name is Hutch. 5 00:00:17,220 --> 00:00:20,460 I am the consulting services practice lead at a company called Set Solutions. 6 00:00:20,460 --> 00:00:29,520 I lead our government's risk and compliance, cyber incident response, threat hunting, penetration testing, and red teaming operations at the company. 7 00:00:29,520 --> 00:00:35,480 I have a master's in information systems and multiple different certifications on both the blue and red team side. 8 00:00:35,620 --> 00:00:46,580 I got into information security in the United States Air Force, did many years of consulting, went in industry for about three years into the financial sector, and now back at the consulting game. 9 00:00:47,160 --> 00:00:51,900 Aside from my hacktivity interests, I do enjoy spending time with my family. 10 00:00:51,900 --> 00:00:58,300 I enjoy country life and getting away from the big city, algorithmic trading, and last but not least, street tacos. 11 00:01:02,060 --> 00:01:12,740 So when I got started in penetration testing and red teaming, I remember that it was fairly easy to breach the perimeter and gain unauthorized access to an organization's network using usually one of two different attacks. 12 00:01:12,740 --> 00:01:25,840 One was password spray attacks, where we would take a large number of different potential usernames and we would iterate through those usernames and attempt a common guessable password. 13 00:01:25,840 --> 00:01:38,140 And we would usually do this multiple times every few hours in order to prevent any user accounts from being locked out, but still attempting thousands of different password attempts every hour. 14 00:01:38,880 --> 00:01:51,640 The other attack that we would commonly use would be credential harvesting attacks, where we would send a phishing email to our different targets, and we would set up a fake evil twin replica site of their login portal. 15 00:01:51,640 --> 00:02:03,920 We would use that site to harvest their usernames and passwords, and then we would subsequently use those compromised credentials to log into the actual site and gain remote access to those networks. 16 00:02:05,540 --> 00:02:12,080 So the security industry's response to these types of attacks was multi-factor authentication. 17 00:02:12,400 --> 00:02:20,220 And by multi-factor authentication, the common understanding is that that's any combination of at least two of the following authentication factors. 18 00:02:20,220 --> 00:02:22,600 Something you know, such as a password. 19 00:02:22,600 --> 00:02:26,480 Something you have, such as a mobile device or a hardware token. 20 00:02:26,480 --> 00:02:29,100 Or something you are, such as biometrics. 21 00:02:29,380 --> 00:02:49,520 So the thinking was that even if an attacker was to gain usernames and passwords through credential harvesting attacks, or even if they were to perform password sprays against an external service, they would not be able to gain access to the network because there would still be that second factor standing in their way. 22 00:02:52,020 --> 00:03:07,120 So now things become a lot harder for red teamers these days because they can no longer easily perform those basic password sprays or those credential harvesting attacks in order to immediately gain access to a corporate network. 23 00:03:07,120 --> 00:03:13,980 Multi-factor authentication stands in the way and is better securing remote access services. 24 00:03:15,780 --> 00:03:19,260 Fortunately, not all hope is lost for the red team. 25 00:03:19,260 --> 00:03:24,260 When it comes to multi-factor authentication implementation, the right way is also the hard way. 26 00:03:24,260 --> 00:03:31,680 And what I mean by the right way is FIDO compliant UTF or universal second factor authentication. 27 00:03:32,180 --> 00:03:52,540 And this is a legitimate form of something you have because the hardware device that you use for UTF authentication does an actual cryptographic authentication sequence within the same authentication sequence that you are performing to access the remote service. 28 00:03:53,520 --> 00:04:02,880 Unfortunately, the overhead involved in supporting a UTF program at an enterprise level is extremely difficult for most organizations to achieve. 29 00:04:02,880 --> 00:04:08,400 So the reality is most organizations are doing multi-factor authentication the wrong way. 30 00:04:08,400 --> 00:04:18,080 And the two most common types of multi-factor authentication that we're seeing for remote access services are either one-time pass or push-based notifications. 31 00:04:18,220 --> 00:04:28,900 And the reason that I say that this is the wrong way is because in reality, if you look at the way that these two different authentication sequences are performed, it's not actual multi-factor authentication. 32 00:04:28,920 --> 00:04:32,720 You have the factor of something that you know, which is the username and password. 33 00:04:33,340 --> 00:04:38,900 And the assumption is with these two types of authentication that the second factor is something that you have. 34 00:04:38,900 --> 00:04:40,360 But in reality, it's not. 35 00:04:40,360 --> 00:04:48,600 Because with the one-time pass, while you do have possession of the phone, the phone is not the thing that's actually authenticating to the service. 36 00:04:48,680 --> 00:04:53,780 Instead, you look at that one-time password that's good for, say, 30 seconds. 37 00:04:53,780 --> 00:05:01,900 And within that 30 seconds, that becomes something you know, and then you supply to the access portal in the same way that you would supply your username and password. 38 00:05:02,080 --> 00:05:07,720 And so it becomes subject to the same types of attacks that those username and passwords are. 39 00:05:07,720 --> 00:05:09,920 Specifically, they are replayable. 40 00:05:10,380 --> 00:05:23,520 And the other type of multi-factor authentication, which is push notifications, actually doesn't even operate within the same sequence as the original authentication sequence to access that service. 41 00:05:23,520 --> 00:05:27,300 Instead, it's done completely out of band, and your phone just receives a notification. 42 00:05:27,300 --> 00:05:30,060 So it makes it very easy to bypass as well. 43 00:05:30,740 --> 00:05:43,300 So because most organizations are using one-time pass or push-based notifications, this makes it very easy for the red team to still bypass these remote access services and gain access. 44 00:05:43,580 --> 00:05:53,500 So for an example to demonstrate the types of attacks that we're discussing, let's consider a scenario of a life and a guy of a typical white-collar office worker. 45 00:05:53,500 --> 00:05:57,340 For the sake of conversation, we will call this office worker Dwight Schrute. 46 00:05:57,560 --> 00:06:12,480 And Dwight, like most white-collar workers, occasionally does work from home, and when he does, he has to access his corporate remote access service by using his username and password to access corporate resources like email, file shares, or VPN. 47 00:06:13,920 --> 00:06:21,020 Unfortunately, for Dwight, there are bad people in this world that seek to do harm to business organizations. 48 00:06:21,280 --> 00:06:38,760 So let's suppose, hypothetically, that we have a hardened cyber criminal that has sent an email to our office worker, and this email indicates that Dunder Mifflin is making an update on its servers and that it needs the employee to log in in order to ensure that their account is migrated properly. 49 00:06:38,960 --> 00:06:51,120 Because this email looks like legitimate email, Dwight Schrute then clicks the link and browses to what is actually an evil twin clone server hosted by the hardened cyber criminal. 50 00:06:52,180 --> 00:07:00,100 And because this server looks identical to the original server that Dwight is accustomed to log into, he then supplies its credentials. 51 00:07:01,100 --> 00:07:11,280 So those credentials are then forwarded to the attacker, and the attacker is able to use those to log in to the legitimate remote access service. 52 00:07:13,700 --> 00:07:18,700 And so, unfortunately, without multi-factor authentication, this leaves our employees vulnerable. 53 00:07:19,580 --> 00:07:29,100 This gets the attention of upper-level management, who then decides to call in the IT department to implement multi-factor authentication. 54 00:07:29,100 --> 00:07:36,640 And now, when Dwight logs in to his remote access portal, he doesn't just have to supply a username and password, but he also has to supply a token passcode. 55 00:07:38,560 --> 00:07:44,440 So multi-factor authentication saves the day, the fortress is impenetrable, and all of the hackers have given up, right? 56 00:07:44,740 --> 00:07:45,780 Not exactly. 57 00:07:46,360 --> 00:07:51,300 So now let's talk about how this attack can be performed in order to bypass that multi-factor authentication. 58 00:07:51,600 --> 00:07:55,700 So, similarly, we have our white-collar worker, the attacker that sends the email. 59 00:07:56,110 --> 00:08:03,000 The email looks exactly the same, and the victim then clicks the update account link, 60 00:08:06,130 --> 00:08:10,040 accesses the site, and, similar to before, enters his credentials, 61 00:08:13,230 --> 00:08:17,990 and this time to include his multi-factor authentication token. 62 00:08:21,400 --> 00:08:36,700 So then, upon logging in to the malicious evil twin site, instead of sending those credentials to the cyber criminal, the site replays them in real time while that one-time pass multi-factor authentication token is still valid to the actual server. 63 00:08:36,700 --> 00:08:45,960 This establishes a legitimate login session with the actual server, and the session token is then forwarded back to the attacker. 64 00:08:46,140 --> 00:08:58,520 The attacker is then able to use that session token in his browser in order to assume the compromised session, and thereby gain remote access to the legitimate remote access server. 65 00:09:00,980 --> 00:09:03,140 So, now let's look through a couple demonstrations. 66 00:09:03,140 --> 00:09:09,820 First, we're going to look through a way that we can use this type of attack to defeat multi-factor authentication on a common web service. 67 00:09:10,000 --> 00:09:20,400 And then we're going to look at a more unique scenario where we can use the same methodology in order to defeat multi-factor authentication using a binary remote access client. 68 00:09:23,910 --> 00:09:33,720 So, for the first scenario where we are attempting to bypass multi-factor authentication on a web service, the stack that we're going to use is Flask as our micro framework for our web service. 69 00:09:33,720 --> 00:09:42,260 We are going to use Apache to independently host the copied or replicated content from the legitimate remote access server. 70 00:09:42,260 --> 00:09:54,700 And then we are going to be using Selenium in order to emulate and execute browser operations that will allow us to replay those credentials and execute a login in real time. 71 00:09:57,420 --> 00:10:10,200 For this demo, I have used a Flask one-time pass multi-factor authentication proof of concept that was published by Miguel Grinberg, who does some fantastic Python and Flask stuff. 72 00:10:10,200 --> 00:10:12,300 And so, definitely worth checking out. 73 00:10:12,320 --> 00:10:23,460 But fortunately for us, it makes a convenient option to test different attacks on multi-factor authentication without having to do a full deployment ourselves. 74 00:10:23,460 --> 00:10:33,560 So , you can essentially just git clone this repository, run it through Python, and you will have a one -time pass multi-factor authentication portal that you can perform testing on. 75 00:10:33,560 --> 00:10:38,060 Of course, we did some slight cosmetic changes in order to fit with the theme. 76 00:10:43,770 --> 00:10:56,050 So , first we're going to look at how we can use Python and Selenium in order to execute real-time logins to the actual multi-factor authentication remote access service. 77 00:10:58,440 --> 00:11:08,980 So, the first thing that we're going to need to do in order to achieve the multi-factor authentication bypass is we are going to need to be able to programmatically log in to the actual multi-factor authentication service. 78 00:11:08,980 --> 00:11:10,520 So, we're going to do that with Python. 79 00:11:10,680 --> 00:11:16,840 And we're going to start with from Selenium import WebDriver. 80 00:11:18,220 --> 00:11:20,940 Then we are going to create an instance of WebDriver. 81 00:11:20,940 --> 00:11:24,760 So, driver equals WebDriver.firefox. 82 00:11:25,980 --> 00:11:30,020 And you should see a browser open in the graphical interface. 83 00:11:32,510 --> 00:11:35,230 And next, we're going to browse to the actual site. 84 00:11:35,230 --> 00:11:39,190 So, we will do driver.get and then the URL. 85 00:11:47,220 --> 00:11:53,760 It's worth noting that for this demo, I did modify my Etsy hosts file, and I do not actually own this domain. 86 00:11:53,760 --> 00:12:01,540 So, then what we want to do is we want to enter text values in each of these different parameters, username, password, and token. 87 00:12:02,000 --> 00:12:04,660 So, in order to do this, we're going to inspect the element. 88 00:12:05,540 --> 00:12:14,120 And here, you'll see the Firefox inspector, which shows you the HTML element and also shows you the ID for the element is username. 89 00:12:14,160 --> 00:12:22,860 So, in order to interact with it, we will just do driver.findElementById and then pass it that value, username. 90 00:12:23,480 --> 00:12:28,860 And then we want to send it the keys of the username we want to send. 91 00:12:28,860 --> 00:12:30,020 So, I'll put Hutch. 92 00:12:30,320 --> 00:12:34,120 And as you can see, it programmatically populates that field. 93 00:12:34,120 --> 00:12:37,300 We can then do the same thing for password by inspecting it. 94 00:12:37,520 --> 00:12:42,180 And here, we can see that the ID value for password is also password. 95 00:12:42,180 --> 00:12:56,880 So, we'll do driver.findElementById password, then send keys, and my password. 96 00:13:00,260 --> 00:13:02,620 And as you can see, it then populates the password field. 97 00:13:02,620 --> 00:13:04,440 And finally, for token, we can do the same thing. 98 00:13:04,440 --> 00:13:05,580 Inspect element. 99 00:13:05,720 --> 00:13:07,520 The ID is token. 100 00:13:07,940 --> 00:13:16,740 So, we can do the same thing, but this time, we'll make our token value 1, 2, 3, 4, 5, 6, and the ID is token. 101 00:13:19,700 --> 00:13:22,680 So, now that all the fields are populated, we just need to click the login button. 102 00:13:22,680 --> 00:13:26,380 So, we just right-click this and inspect element. 103 00:13:26,380 --> 00:13:31,740 And here, we can see that the ID value for that login button is the word submit. 104 00:13:31,840 --> 00:13:40,260 So, all we have to do is driver.findElementById submit. 105 00:13:41,220 --> 00:13:45,180 And then this time, instead of send keys, we'll use dot click. 106 00:13:48,820 --> 00:13:57,540 And as you can see, by the error returned, invalid username, password, or token, we were able to programmatically make a login attempt to the actual site. 107 00:13:58,040 --> 00:14:06,560 So, now that we've created our Selenium login script, we also need to create a replica of the site that we are attempting to gain remote access to. 108 00:14:06,560 --> 00:14:10,280 So, in order to make the replica website, we'll start by opening up a web browser. 109 00:14:10,280 --> 00:14:14,060 And then we will browse to the page that we want to clone. 110 00:14:21,760 --> 00:14:29,220 And then to save a recursive copy of the page, all you have to do is go over to the menu bar over here and do save page as. 111 00:14:30,920 --> 00:14:34,960 And this will save a full recursive copy of the website. 112 00:14:34,960 --> 00:14:40,080 If you want to drop it directly into your web root, browse to var www.html. 113 00:14:40,140 --> 00:14:48,340 And then even better yet, if you want it to be the web root index file, then you can just name this file as index.html. 114 00:14:49,180 --> 00:14:50,680 Then save. 115 00:14:53,220 --> 00:14:56,060 And then next, you'll want to go to that directory. 116 00:14:56,060 --> 00:14:58,140 So, var www.html. 117 00:14:58,400 --> 00:15:01,960 You'll see that we've now saved the index.html. 118 00:15:01,960 --> 00:15:08,620 And then all the supporting files to include JavaScript and CSS files in this index files page. 119 00:15:08,620 --> 00:15:18,160 And actually, what's helpful about this is it actually rewrites all of the links in the HTML content to these different paths dynamically. 120 00:15:20,410 --> 00:15:25,290 So, essentially, the only thing that we need to do is chmod 755. 121 00:15:26,050 --> 00:15:27,510 Make this recursive. 122 00:15:27,510 --> 00:15:30,010 Dash r 755 everything. 123 00:15:31,310 --> 00:15:35,550 And then we can open up a web browser. 124 00:15:39,350 --> 00:15:42,210 And make sure that the Apache service is on. 125 00:15:42,210 --> 00:15:44,070 So, apache2 start. 126 00:15:44,070 --> 00:15:48,590 And then we access 127.0.0.1. 127 00:15:50,050 --> 00:15:50,750 And there you have it. 128 00:15:50,750 --> 00:15:54,410 You now are hosting a cloned website that looks identical to the original login. 129 00:15:54,410 --> 00:16:12,010 Now that we have the target website cloned, and we also have our Selenium login scripts, the next step is to create a Flask authentication handler that will receive any authentication attempts to our evil clone, and then connect those to the Selenium logins to replay those credentials in real time. 130 00:16:15,370 --> 00:16:19,770 So, I have a proof of concept that I have published on GitHub. 131 00:16:19,770 --> 00:16:21,450 I have the link available here. 132 00:16:22,390 --> 00:16:26,690 In order to test this out, what you're going to want to do is just git clone the repository. 133 00:16:26,970 --> 00:16:32,710 If you want to test this for yourself, extract the app from the OTP target app.zip file. 134 00:16:33,030 --> 00:16:35,610 And then you can run that via Python. 135 00:16:35,930 --> 00:16:44,530 You'll need to register a victim user with an OTP client, a one-time pass client, at the slash register URL. 136 00:16:44,870 --> 00:16:55,810 And then you can use this proof of concept to host the malicious clone that will then target and bypass the multi-factor authentication on that target app. 137 00:16:55,810 --> 00:16:58,110 So real quick, let's just step through the code here. 138 00:16:58,110 --> 00:17:01,670 First, we do our imports JSON for parsing. 139 00:17:01,670 --> 00:17:04,750 We've got a few Selenium functions that are imported. 140 00:17:04,790 --> 00:17:08,250 And then we also have our Flask functions that are imported. 141 00:17:10,790 --> 00:17:17,070 We you'll also need to update the login URL to wherever you're hosting the target app that you extracted. 142 00:17:17,110 --> 00:17:24,270 In this case, I'm using a modified version of Etsy hosts to have it hosted at dundermifflin.com slash login. 143 00:17:24,610 --> 00:17:26,210 And we have a couple different functions. 144 00:17:26,210 --> 00:17:27,570 The first is create browser. 145 00:17:27,570 --> 00:17:31,130 So this is very similar to what we did previously in step one. 146 00:17:31,130 --> 00:17:38,470 However, the one difference is that we've changed it to headless so that it isn't visibly apparent within the GUI. 147 00:17:39,590 --> 00:17:43,830 Then we have a login function that receives the username, password, and token. 148 00:17:43,830 --> 00:17:48,860 And it performs the same Selenium login that we coded in step one of the process. 149 00:17:49,230 --> 00:17:53,670 And then finally, the last part is the Flask component. 150 00:17:53,670 --> 00:17:56,390 And this is actually the Flask receiver. 151 00:17:56,390 --> 00:18:01,750 We have a intent mapped to the slash harvester location. 152 00:18:01,750 --> 00:18:07,990 And this will receive from a post method request the username, password, and token. 153 00:18:07,990 --> 00:18:09,410 That is sent. 154 00:18:09,410 --> 00:18:12,710 It will print those to the user. 155 00:18:12,710 --> 00:18:16,210 And then it will also use that login function. 156 00:18:17,730 --> 00:18:21,930 Pass it those credentials in real time and execute the real time relay. 157 00:18:21,930 --> 00:18:24,190 It will then print the cookies back to us. 158 00:18:24,190 --> 00:18:30,290 And we can then use these cookies as the attacker to hijack the compromised session. 159 00:18:30,430 --> 00:18:38,290 So finally, we also have the malicious Flask handler hosted on a separate port. 160 00:18:38,290 --> 00:18:40,310 In this case, I have it hosted on 8080. 161 00:18:40,310 --> 00:18:46,930 But a separate port than the content that we're hosting for the cloned Evil Twin replica site. 162 00:18:46,930 --> 00:18:54,890 And the reason that we have these hosted on two different ports is because of the fact that we are running the clone content through Apache. 163 00:18:54,890 --> 00:19:01,790 And then we are hosting the handler through a separate service, which is used by Flask. 164 00:19:01,790 --> 00:19:13,990 Now, you could actually do this all within the Flask app by just having the HTML, CSS, and JavaScript content that you cloned within static files in the Flask app. 165 00:19:13,990 --> 00:19:19,710 Unfortunately, this becomes a lot more complicated than just using the relative links that exist whenever you save it. 166 00:19:19,710 --> 00:19:25,590 Because you then have to update all of the different links and map them to the static directories within Flask. 167 00:19:25,590 --> 00:19:28,990 So it's actually much easier to just host it on two separate ports. 168 00:19:30,910 --> 00:19:38,230 So now let's go ahead and look at how we can git clone this bypass script and execute it within our own environment. 169 00:19:38,730 --> 00:19:42,610 Okay, so now we are going to clone the repository and launch the bypass script. 170 00:19:42,610 --> 00:19:45,830 So we'll go to the GitHub repository. 171 00:19:46,730 --> 00:19:58,890 And then we will copy the clone path and do git clone paste selection. 172 00:20:00,450 --> 00:20:01,610 All right. 173 00:20:01,610 --> 00:20:04,500 And then we now have our cloned directory. 174 00:20:05,070 --> 00:20:09,350 And we can do python3 bypass. 175 00:20:10,170 --> 00:20:12,950 And it is now running on port 8080. 176 00:20:14,450 --> 00:20:19,250 Okay, so now we have our cloned web app content for the target web app. 177 00:20:19,270 --> 00:20:21,190 And we are hosting that on Apache. 178 00:20:21,190 --> 00:20:24,630 We also have our harvester that will gather credentials. 179 00:20:24,630 --> 00:20:27,110 And that's running on Flask on a separate port. 180 00:20:27,110 --> 00:20:39,490 What we need to do is now update the cloned content to where whenever a post is submitted with that form, it then submits it to our Flask harvester on port 8080. 181 00:20:42,570 --> 00:20:48,490 Okay, so we are going to go into our web root directory where we previously cloned the web content. 182 00:20:48,490 --> 00:20:53,210 So var www.html, then list the contents. 183 00:20:53,430 --> 00:20:58,470 And again, we had written the HTML file to index.html. 184 00:20:58,470 --> 00:21:06,850 All of the recursive supporting files, such as CSS, JavaScript, are all here within the index files directory. 185 00:21:07,330 --> 00:21:09,530 And all the content is automatically mapped. 186 00:21:10,170 --> 00:21:16,350 So the only thing that we need to change in this Apache directory is where the form will post to. 187 00:21:16,350 --> 00:21:17,850 So we can use any text editor. 188 00:21:17,850 --> 00:21:20,810 I'll use nano, then index.html. 189 00:21:20,810 --> 00:21:25,610 And then I'll use ctrl w for the find function and search for the word action. 190 00:21:26,250 --> 00:21:35,130 Now action in an HTML form tag defines where the post will or where the form will be submitted to. 191 00:21:35,130 --> 00:21:43,570 And here, when it's empty, it actually just submits it to the same directory or the same URL path as it's currently on. 192 00:21:43,570 --> 00:21:49,890 So what we need to do is update this to say http colon backslash backslash dunder mif. 193 00:21:49,890 --> 00:21:54,750 And I used a one for the L for the malicious app. 194 00:21:54,750 --> 00:22:00,370 So dunder miflin looks almost identical dot com slash harvester. 195 00:22:00,830 --> 00:22:03,410 And you also need to specify the port. 196 00:22:03,410 --> 00:22:05,550 So we're hosting it on port 8080. 197 00:22:05,650 --> 00:22:11,350 So it would be HTTP, your malicious app that you're hosting through Flask. 198 00:22:11,590 --> 00:22:14,690 And then the port that you're hosting it on. 199 00:22:14,690 --> 00:22:16,730 By default, I have it set to 8080. 200 00:22:16,770 --> 00:22:22,350 And then the path of the Flask receiver, which is slash harvester. 201 00:22:22,690 --> 00:22:24,650 Otherwise, there's nothing else that we need to change. 202 00:22:24,650 --> 00:22:28,590 We can do control X and then Y to save the content. 203 00:22:31,590 --> 00:22:45,230 So now whenever an authentication form is submitted to your evil twin copied content, it will submit the authentication request to your Flask handler in order to be able to be replayed in real time. 204 00:22:45,970 --> 00:22:47,470 Okay, so we now have everything set up. 205 00:22:47,470 --> 00:22:57,970 The only thing left to do is use our social engineering techniques, most likely phishing, to entice our victim to access the fake site and convince them that it's the real site. 206 00:22:57,970 --> 00:23:03,250 And then when they enter their credentials, all should work as planned and we should be able to hijack their session. 207 00:23:04,950 --> 00:23:07,150 So now it's time to exploit our target. 208 00:23:07,150 --> 00:23:08,770 You will look at two different screens here. 209 00:23:08,770 --> 00:23:18,050 First, we have the victim that has accessed or been enticed to access our fake dundermifflin.com login site. 210 00:23:18,050 --> 00:23:22,710 And then, as you recall, we have our Apache service hosting the clone content. 211 00:23:22,710 --> 00:23:30,590 And we also have the Flask bypass that is running our harvester on port 8080. 212 00:23:30,590 --> 00:23:38,490 And as you can see, the attacker currently is not logged in, if we refresh this page, to the actual dundermifflin site. 213 00:24:03,400 --> 00:24:13,860 Okay, so once the hijack is completed, it will actually send the initial user back to the actual dundermifflin.com login page so they don't suspect that anything has gone wrong. 214 00:24:13,860 --> 00:24:18,880 However, on our attacking side, we can now see that we have received the username. 215 00:24:18,900 --> 00:24:20,740 We have received the password. 216 00:24:20,740 --> 00:24:22,640 We have intercepted the token. 217 00:24:22,640 --> 00:24:25,640 We also replayed the token in real time. 218 00:24:25,640 --> 00:24:30,020 And we're able to acquire the session token for that user. 219 00:24:30,020 --> 00:24:34,220 So once again, we are not currently logged into the actual site as the attacker. 220 00:24:34,220 --> 00:24:37,000 And we are going to attempt to hijack that user session. 221 00:24:37,000 --> 00:24:40,640 So what we're going to do is we're going to open a cookie manager. 222 00:24:40,640 --> 00:24:43,160 And there's a bunch of different plugins that you can use for this. 223 00:24:43,160 --> 00:24:47,100 And we're going to manage cookies for dundermifflin. 224 00:24:47,420 --> 00:24:55,740 And then the only thing that we have to do is we have to modify this session token so that it has the value of the compromised session. 225 00:24:55,740 --> 00:24:57,800 So we will copy this. 226 00:25:01,450 --> 00:25:07,790 And then drop this into the value of the previous session token. 227 00:25:07,890 --> 00:25:10,570 And then we save the contents of the cookie. 228 00:25:10,570 --> 00:25:13,390 And we go back to dundermifflin.com, the actual site. 229 00:25:13,390 --> 00:25:14,010 Click enter. 230 00:25:14,010 --> 00:25:21,830 And we have now successfully bypassed the multi-factor authentication without having to enter the username, password, or token. 231 00:25:23,030 --> 00:25:27,450 Undoubtedly, at this point, somebody is thinking that this sounds an awful lot like EvilGinX. 232 00:25:27,450 --> 00:25:33,550 And admittedly, the first proof of concept that we discussed is something that you could accomplish with EvilGinX. 233 00:25:33,670 --> 00:25:43,990 For any that aren't familiar, EvilGinX is a multi-factor authentication bypass toolkit that allows you to perform very similar attacks to the ones that we just discussed. 234 00:25:44,010 --> 00:26:05,470 With the only difference being that instead of operating at the application layer, it operates as an HTTP proxy relaying communication for the authentication sequence and then also thereby intercepting the session tokens and providing those back to the attacker in order to allow them to assume the compromised sessions. 235 00:26:06,390 --> 00:26:11,330 But there's an inherent problem with relying on ScriptKitty toolkits and red teaming. 236 00:26:11,330 --> 00:26:18,950 For one, if the developers of the toolkit decides to stop supporting it and it no longer works, then you're dead in the water. 237 00:26:18,950 --> 00:26:27,910 Similarly, if something breaks in that toolkit and you don't understand the underlying components well enough to be able to troubleshoot it, there's also nothing else that you can do. 238 00:26:28,150 --> 00:26:40,230 But most importantly, anytime that you're relying on a cookie cutter toolkit, it will likely address the bulk number of instances where you need to use that exploit. 239 00:26:40,230 --> 00:26:53,390 But the reality is because you don't have control over all of the integral components of that exploit, it makes it very difficult for you to be able to tailor those attacks to unique and unconventional environments. 240 00:26:55,670 --> 00:27:07,650 So EvilGenX is an HTTP proxy, which means it can only operate within the context of relaying multi-factor authentication sequences over a web service. 241 00:27:07,650 --> 00:27:15,370 However, a lot of times these days where we're entering our multi-factor authentication credentials are not web services at all. 242 00:27:15,370 --> 00:27:26,490 But instead, they are login clients that exist on your local operating system, such as operating system level multi-factor authentication, VPN clients, or remote access clients. 243 00:27:28,830 --> 00:27:45,070 So for our second proof of concept, we're going to look at just such a scenario where we would want to take the credentials that were compromised via our phishing web service and be able to relay those to a local login client that requires multi-factor authentication. 244 00:27:45,610 --> 00:27:49,350 So our attack stack in this scenario is slightly different. 245 00:27:49,350 --> 00:27:57,570 We are still using Flask to handle the authentication requests and map those to the replay functions. 246 00:27:57,570 --> 00:28:02,730 And we're still using Apache in order to host the login content. 247 00:28:02,730 --> 00:28:17,170 The only real difference is that instead of using Selenium to hook into the browser, we're instead using something called Auto-IT to hook into the operating system in order to control operating system functions, such as mouse movement or entering text or clicking, 248 00:28:17,570 --> 00:28:23,990 and thereby performing authentication sequences within local authentication clients. 249 00:28:24,750 --> 00:28:36,230 So first, let's look at how we can use Auto-IT in order to perform operating system login functions that we could potentially use in a real-time replay attack. 250 00:28:36,530 --> 00:28:53,370 So in order to perform a real-time replay attack that includes the one-time pass-based multi-factor authentication with a login client as opposed to a login web service, we need to be able to programmatically interact with the underlying operating system instead of just the web browser. 251 00:28:53,410 --> 00:28:57,190 So in order to do that, we're going to use a different Python library this time. 252 00:28:57,190 --> 00:28:59,690 So instead of Selenium, we're going to use Auto-IT. 253 00:28:59,730 --> 00:29:04,750 And in order to import that, we're going to do import and then pyAutoGUI. 254 00:29:07,310 --> 00:29:09,710 And a few different functions that we need to be aware of. 255 00:29:09,710 --> 00:29:17,290 There is the moveTo function, which allows us to specify where it will move the mouse on the screen. 256 00:29:17,410 --> 00:29:21,190 There is the typeWrite function, which allows us to type text. 257 00:29:21,190 --> 00:29:24,670 And then there's a click function that allows us to click the mouse button. 258 00:29:24,870 --> 00:29:33,770 And one other function that's worth mentioning is the position function, which tells you what the coordinates are for the mouse at any given time. 259 00:29:33,910 --> 00:29:36,630 So we will demonstrate that real quick. 260 00:29:37,810 --> 00:29:39,490 pyAutoGUI.position. 261 00:29:40,150 --> 00:29:46,630 And we can see that the X and Y coordinates for the current cursor location is 674,334. 262 00:29:46,630 --> 00:29:59,130 Now, what we want to do is determine what the coordinates are of the cursor whenever it's hovering over the username field so that we can tell the mouse to go over to the username field to click and then to enter text. 263 00:29:59,430 --> 00:30:08,750 So what we'll do is we will use the same command, the same function, but this time before executing it, we will hover the mouse over the username field and press enter. 264 00:30:09,150 --> 00:30:15,510 And now we know that the coordinates of the mouse hovering over the username field is 227,369. 265 00:30:15,510 --> 00:30:23,890 Now, with that knowledge, we can easily create a quick function that allows us to enter text into that particular location. 266 00:30:23,890 --> 00:30:33,450 So to do that, we'll create a function with def and we'll call this supply username and then we'll pass it a username value. 267 00:30:34,270 --> 00:30:45,110 So first we'll do py autogui.moveTo and then we'll supply it those X and Y values of 227,369. 268 00:30:46,190 --> 00:30:53,890 Then the next thing that we want to do after we hover it over the usernames, we want it to click in that field to activate that particular input field. 269 00:30:53,890 --> 00:30:59,350 So for that, we'll do py autogui.click. 270 00:30:59,910 --> 00:31:05,790 And finally, now that the field is clicked and activated, we want to supply it the text that we provided. 271 00:31:05,790 --> 00:31:13,490 So we'll do py autogui.typeWrite and then the username that's supplied. 272 00:31:15,050 --> 00:31:20,590 Okay, so now that we've created that very basic function, we can execute that function with a given username. 273 00:31:20,590 --> 00:31:28,610 We'll do supply username and then we'll try the username Hutch and press enter. 274 00:31:28,910 --> 00:31:34,530 And as you can see, it automatically moves the mouse over, activates that field and types in the word Hutch. 275 00:31:34,530 --> 00:31:57,510 Now we can use all of these different functions of moving the mouse around, clicking and entering text in order to fully populate the different parameters within the multi-factor authentication sequence in order to relay communications from a malicious web app that's phishing credentials into a client in order to gain access via the client login. 276 00:31:58,070 --> 00:32:13,730 So now that we know that we can interact with and authenticate to a local login client using AutoIT, we want to use a similar process to what we did before where we create a phishing web service. 277 00:32:13,730 --> 00:32:34,470 But this time we modify the authentication replay sequence so that rather than replaying it to a separate web service, we simply use AutoIT in order to replay those credentials real-time to include the multi-factor authentication token to the login client in order to gain remote access. 278 00:32:35,910 --> 00:32:43,310 Now we have slightly modified the previous proof of concept in order to accomplish precisely this. 279 00:32:43,730 --> 00:32:48,370 So let's quickly look at what we've done here that is different. 280 00:32:48,990 --> 00:32:53,670 This time, instead of importing Selenium, we're using PyAuto GUI. 281 00:32:54,330 --> 00:32:56,870 We do have a few additional parameters. 282 00:32:56,870 --> 00:33:07,770 We are doing a two-step multi-factor authentication sequence process where in the first step, the username and the multi-factor authentication token is supplied. 283 00:33:07,770 --> 00:33:10,670 And then in the second step, the password is supplied. 284 00:33:10,670 --> 00:33:13,330 So because of this, we have two different redirects. 285 00:33:13,330 --> 00:33:18,830 The first redirect from the initial authentication sequence step. 286 00:33:18,830 --> 00:33:31,610 And then the other redirect URL is where the victim will be redirected after they complete the entire malicious multi-factor authentication sequence. 287 00:33:33,450 --> 00:33:45,970 Now, in order for this to be successful, we do have to define the specific coordinates of where different login components and buttons are within the GUI login interface. 288 00:33:45,970 --> 00:33:49,890 And again, we accomplish this the same way as what we just looked at. 289 00:33:50,810 --> 00:33:58,210 We have a function for validating the token using the username and token value. 290 00:33:58,210 --> 00:34:10,820 And this is accomplished, once again, by using Auto-IT and just moving the mouse to the location of specific fields, clicking on those fields to activate them, supplying the text, and then moving on. 291 00:34:11,630 --> 00:34:17,750 The second step in the process is essentially doing the same thing, but this time supplying the user password. 292 00:34:19,630 --> 00:34:28,850 And then we have a Flask app that has two different URL receivers or routes at this point. 293 00:34:28,850 --> 00:34:36,730 The first is the initial harvester one that represents that first step in the process of the username and the multi-factor authentication token. 294 00:34:38,690 --> 00:34:44,830 And as you can see, that then invokes the validate token function from above. 295 00:34:44,830 --> 00:34:54,670 And then the second is the second step in that multi-factor authentication sequence, which is validating the password for that particular user. 296 00:34:54,670 --> 00:34:59,810 And again, you see that we then invoke the validate password function that we created above. 297 00:34:59,810 --> 00:35:11,190 And similar to before, we are executing this or hosting this on a port that is different from the one that we are hosting the malicious replica web service on. 298 00:35:13,010 --> 00:35:16,430 So a few different things to take note of here on this proof concept. 299 00:35:16,430 --> 00:35:25,010 On the right side here, we have the malicious app that we are using to phish the victim's credentials to include their multi-factor authentication token. 300 00:35:25,010 --> 00:35:34,350 On the left side, we have the actual client that we would use in order to enter the intercepted username, passcode, and password. 301 00:35:34,550 --> 00:35:41,310 And here at the bottom left corner, you can see the bypass code running with client bypass.py. 302 00:35:41,310 --> 00:35:44,050 So we're going to run that once again running on port 8080. 303 00:35:44,050 --> 00:35:47,150 And then we're going to simulate the victim entering their credentials. 304 00:36:00,410 --> 00:36:08,170 And here you can see that using auto-IT, it was able to successfully migrate the client on our desktop. 305 00:36:08,170 --> 00:36:16,310 And then we're going to simulate the user entering the second component of the authentication, which is the password. 306 00:36:17,810 --> 00:36:19,910 And then they click login. 307 00:36:20,010 --> 00:36:23,510 And similarly, credentials are intercepted, replayed. 308 00:36:23,870 --> 00:36:28,290 The victim is redirected to a VMware Horizon client website. 309 00:36:29,370 --> 00:36:32,190 The password is disclosed at the bottom left corner. 310 00:36:32,190 --> 00:36:37,970 And it successfully loads our desktop and allows us to bypass multi-factor authentication. 311 00:36:39,270 --> 00:36:46,710 So we've now established that it is possible to bypass one-time pass-based multi-factor authentication for both web and non-web clients. 312 00:36:46,710 --> 00:36:49,790 But what about push-based multi-factor authentication? 313 00:36:49,790 --> 00:36:58,010 For anybody that's not familiar, push-based multi-factor authentication is a second factor that's initiated by the server side instead of the client side. 314 00:36:58,010 --> 00:37:04,430 So you enter your username and password, just as you would with any type of authentication sequence. 315 00:37:04,430 --> 00:37:15,870 And then for the second factor, instead of sending a temporary password that you retrieve from your mobile device to the server, the server instead sends a request to an app that you have loaded onto your device. 316 00:37:15,870 --> 00:37:25,510 And that request indicates that an authentication attempt was performed and then asks whether or not you approve or authorize that authentication attempt. 317 00:37:25,510 --> 00:37:32,470 Now, on the surface, this may seem like the more secure solution because there is no temporary password to be replayed. 318 00:37:32,610 --> 00:37:40,830 The problem is because of the out-of-band nature of this authentication sequence, it makes it just as easy to bypass. 319 00:37:40,870 --> 00:37:46,890 So let's look at our attack sequence and look at it within the context of push-based multi-factor authentication. 320 00:37:46,890 --> 00:37:52,810 So similar to before, we have a cybercriminal that sends a phishing email to our victim. 321 00:37:52,810 --> 00:37:58,390 The phishing email has a link that leads that victim to an evil twin clone server. 322 00:37:58,390 --> 00:38:04,890 The victim supplies their credentials and attempts to log in to what they think is the legitimate remote access service. 323 00:38:04,970 --> 00:38:12,990 That evil twin clone site replays in real time those credentials to the legitimate remote access service. 324 00:38:13,190 --> 00:38:22,790 The remote access service sees that it has an authentication attempt, so now it sends a push authorization to our victim. 325 00:38:22,810 --> 00:38:29,490 Now again, keep in mind that our victim believes that they've just logged into the legitimate remote access service. 326 00:38:29,490 --> 00:38:34,090 So they're fully expecting that they're going to get an authentication authorization request. 327 00:38:34,530 --> 00:38:41,470 So not thinking twice about it, the victim is going to click yes, it's me and approve this authentication attempt. 328 00:38:41,470 --> 00:38:49,290 So our victim is now informed of the server that the authentication request is good and the session is established. 329 00:38:49,290 --> 00:38:51,810 Just like before, the rest is history. 330 00:38:51,810 --> 00:39:01,810 We are able to use the evil twin server in the established session to harvest those session tokens and thereby hijack the session of the victim user. 331 00:39:04,010 --> 00:39:11,910 So we've now demonstrated that it's possible to bypass multi-factor authentication for one-time pass and push-based multi-factor authentication. 332 00:39:12,390 --> 00:39:15,950 So I'll close out here with just a few final parting thoughts. 333 00:39:16,810 --> 00:39:25,930 If you're on the blue team, we've probably established that your multi-factor authentication solution sucks and that you're likely not getting out of it what you want to get out of it. 334 00:39:25,930 --> 00:39:40,290 And don't feel bad because almost everybody in the industry is in that boat because everybody is using either push-based or one-time pass-based multi-factor authentication with only a very small percentage of organizations that have now moved to U2F. 335 00:39:40,290 --> 00:39:46,850 The obvious best thing that you can do is to use U2F for prevention. 336 00:39:46,850 --> 00:39:55,410 But if you can't do that at this time, then you probably need to focus more on your detection skills and identifying these types of attacks. 337 00:39:55,410 --> 00:40:10,950 And if you're looking for any other recommendations, because I don't want to spend too much time solutioning in red team village, you can look to FidoAlliance.org, which has a lot of good recommendations around how to effectively do multi-factor authentication. 338 00:40:10,970 --> 00:40:18,770 Or you can also check out some blogs that we did and that I authored at SetSolutions called Doing MFA the Right Way. 339 00:40:21,730 --> 00:40:27,890 If you're on the red team, I encourage you to remain flexible anytime you're doing penetration testing or red team engagements. 340 00:40:27,890 --> 00:40:31,970 Remember that penetration testing is more art than audit. 341 00:40:32,050 --> 00:40:43,230 And what I mean by that is that you're never going to get anywhere by going into an organization and attempting to perform such an assessment by checking off a series of tests in a list. 342 00:40:43,230 --> 00:40:44,950 You need to be flexible. 343 00:40:44,950 --> 00:40:59,350 You need to familiarize yourself with the environment that you're in and then be ready to adapt and create your own tools and solutions in order to exploit the ways that different components are communicating within that organization. 344 00:41:00,030 --> 00:41:13,530 One of the great things that I love about InfoSec is the fact that there is so much knowledge within this field and so much potential information in so many different areas that you can potentially specialize in. 345 00:41:13,530 --> 00:41:17,210 No matter how good you get, there's always more that you can learn. 346 00:41:17,890 --> 00:41:22,370 So obviously, continue to improve yourself in any way that you can. 347 00:41:22,370 --> 00:41:24,490 Be creative and think outside the box. 348 00:41:24,670 --> 00:41:32,590 I know that it's somewhat controversial to say this because some people will tell you that you can be just as effective of a penetration tester without being able to code. 349 00:41:32,590 --> 00:41:47,430 Learning to code is like a superpower whenever you're doing red teaming and penetration testing because of scenarios just like this where you can adaptively and quickly prototype exploits that are unique to the environment that you are attempting to assess. 350 00:41:47,730 --> 00:41:54,270 And always kind of approach things with that mentality of picking it apart, understanding how it works, and then putting it back together. 351 00:41:55,750 --> 00:42:01,090 To the staff at DEF CON and also for the Red Team Village staff, I really appreciate the opportunity. 352 00:42:01,090 --> 00:42:02,130 This has been awesome. 353 00:42:02,970 --> 00:42:06,590 Thank you for making this happen despite everything that's going on. 354 00:42:08,430 --> 00:42:12,810 And to everyone else, stay happy, stay healthy, stay hacking. 355 00:42:13,610 --> 00:42:16,130 This definitely has sucked, I think, for everybody. 356 00:42:17,110 --> 00:42:26,130 I admittedly was somewhat disappointed to not be going to Vegas this year for DEF CON and definitely will miss seeing all you guys out there. 357 00:42:26,850 --> 00:42:29,810 But I think this is something that we can all get through together. 358 00:42:29,810 --> 00:42:33,210 We'll put behind us and hopefully we'll see you guys next year. 359 00:42:34,610 --> 00:42:35,910 Quick shameless plug. 360 00:42:35,910 --> 00:42:39,170 Check out our podcast, Ready, Set, Secure, from Set Solutions. 361 00:42:39,470 --> 00:42:45,090 Also, feel free to follow my research at sociosploit.com or follow me on Twitter at Hackett Hutch. 362 00:42:46,550 --> 00:42:47,870 And that's a wrap. 363 00:42:48,310 --> 00:42:48,970 CREASE CLOSED!