[00:00.000 --> 00:05.180] Welcome to my talk, broadcasting from my quarantine bunker to yours. [00:05.260 --> 00:09.940] My talk is Yippee-ki-yay MFA-er, bypassing multi-factor authentication with real-time [00:09.940 --> 00:12.520] replay session instantiation attacks. [00:13.600 --> 00:15.960] So before we get started, I'll go ahead and introduce myself. [00:15.960 --> 00:17.220] My name is Hutch. [00:17.220 --> 00:20.460] I am the consulting services practice lead at a company called Set Solutions. [00:20.460 --> 00:26.940] I lead our government's risk and compliance, cyber incident response, threat hunting, [00:26.940 --> 00:29.520] penetration testing, and red teaming operations at the company. [00:29.520 --> 00:33.400] I have a master's in information systems and multiple different certifications on both [00:33.400 --> 00:35.480] the blue and red team side. [00:35.620 --> 00:41.100] I got into information security in the United States Air Force, did many years of consulting, [00:41.100 --> 00:45.400] went in industry for about three years into the financial sector, and now back at the [00:45.400 --> 00:46.580] consulting game. [00:47.160 --> 00:51.900] Aside from my hacktivity interests, I do enjoy spending time with my family. [00:51.900 --> 00:56.280] I enjoy country life and getting away from the big city, algorithmic trading, and last [00:56.280 --> 00:58.300] but not least, street tacos. [01:02.060 --> 01:05.560] So when I got started in penetration testing and red teaming, I remember that it was fairly [01:05.560 --> 01:10.380] easy to breach the perimeter and gain unauthorized access to an organization's network using [01:10.380 --> 01:12.740] usually one of two different attacks. [01:12.740 --> 01:18.160] One was password spray attacks, where we would take a large number of different potential [01:18.160 --> 01:25.840] usernames and we would iterate through those usernames and attempt a common guessable password. [01:25.840 --> 01:32.040] And we would usually do this multiple times every few hours in order to prevent any user [01:32.040 --> 01:36.480] accounts from being locked out, but still attempting thousands of different password [01:36.480 --> 01:38.140] attempts every hour. [01:38.880 --> 01:42.900] The other attack that we would commonly use would be credential harvesting attacks, where [01:42.900 --> 01:48.840] we would send a phishing email to our different targets, and we would set up a fake evil twin [01:48.840 --> 01:51.640] replica site of their login portal. [01:51.640 --> 01:57.200] We would use that site to harvest their usernames and passwords, and then we would subsequently [01:57.200 --> 02:02.580] use those compromised credentials to log into the actual site and gain remote access to [02:02.580 --> 02:03.920] those networks. [02:05.540 --> 02:12.080] So the security industry's response to these types of attacks was multi-factor authentication. [02:12.400 --> 02:16.580] And by multi-factor authentication, the common understanding is that that's any combination [02:16.580 --> 02:20.220] of at least two of the following authentication factors. [02:20.220 --> 02:22.600] Something you know, such as a password. [02:22.600 --> 02:26.480] Something you have, such as a mobile device or a hardware token. [02:26.480 --> 02:29.100] Or something you are, such as biometrics. [02:29.380 --> 02:35.160] So the thinking was that even if an attacker was to gain usernames and passwords through [02:35.160 --> 02:40.240] credential harvesting attacks, or even if they were to perform password sprays against [02:40.240 --> 02:46.560] an external service, they would not be able to gain access to the network because there [02:46.560 --> 02:49.520] would still be that second factor standing in their way. [02:52.020 --> 02:58.880] So now things become a lot harder for red teamers these days because they can no longer [02:58.880 --> 03:03.680] easily perform those basic password sprays or those credential harvesting attacks in [03:03.680 --> 03:07.120] order to immediately gain access to a corporate network. [03:07.120 --> 03:13.980] Multi-factor authentication stands in the way and is better securing remote access services. [03:15.780 --> 03:19.260] Fortunately, not all hope is lost for the red team. [03:19.260 --> 03:23.520] When it comes to multi-factor authentication implementation, the right way is also the [03:23.520 --> 03:24.260] hard way. [03:24.260 --> 03:31.680] And what I mean by the right way is FIDO compliant UTF or universal second factor authentication. [03:32.180 --> 03:40.000] And this is a legitimate form of something you have because the hardware device that [03:40.000 --> 03:46.180] you use for UTF authentication does an actual cryptographic authentication sequence within [03:46.180 --> 03:52.540] the same authentication sequence that you are performing to access the remote service. [03:53.520 --> 04:00.300] Unfortunately, the overhead involved in supporting a UTF program at an enterprise level is extremely [04:00.300 --> 04:02.880] difficult for most organizations to achieve. [04:02.880 --> 04:08.400] So the reality is most organizations are doing multi-factor authentication the wrong way. [04:08.400 --> 04:12.620] And the two most common types of multi-factor authentication that we're seeing for remote [04:12.620 --> 04:18.080] access services are either one-time pass or push-based notifications. [04:18.220 --> 04:22.480] And the reason that I say that this is the wrong way is because in reality, if you look [04:22.480 --> 04:28.020] at the way that these two different authentication sequences are performed, it's not actual multi-factor [04:28.020 --> 04:28.900] authentication. [04:28.920 --> 04:32.720] You have the factor of something that you know, which is the username and password. [04:33.340 --> 04:38.000] And the assumption is with these two types of authentication that the second factor is [04:38.000 --> 04:38.900] something that you have. [04:38.900 --> 04:40.360] But in reality, it's not. [04:40.360 --> 04:45.600] Because with the one-time pass, while you do have possession of the phone, the phone [04:45.600 --> 04:48.600] is not the thing that's actually authenticating to the service. [04:48.680 --> 04:53.780] Instead, you look at that one-time password that's good for, say, 30 seconds. [04:53.780 --> 04:58.900] And within that 30 seconds, that becomes something you know, and then you supply to the access [04:58.900 --> 05:01.900] portal in the same way that you would supply your username and password. [05:02.080 --> 05:07.420] And so it becomes subject to the same types of attacks that those username and passwords [05:07.420 --> 05:07.720] are. [05:07.720 --> 05:09.920] Specifically, they are replayable. [05:10.380 --> 05:16.420] And the other type of multi-factor authentication, which is push notifications, actually doesn't [05:16.420 --> 05:22.400] even operate within the same sequence as the original authentication sequence to access [05:22.400 --> 05:23.520] that service. [05:23.520 --> 05:27.300] Instead, it's done completely out of band, and your phone just receives a notification. [05:27.300 --> 05:30.060] So it makes it very easy to bypass as well. [05:30.740 --> 05:36.460] So because most organizations are using one-time pass or push-based notifications, this makes [05:36.460 --> 05:43.300] it very easy for the red team to still bypass these remote access services and gain access. [05:43.580 --> 05:49.560] So for an example to demonstrate the types of attacks that we're discussing, let's consider [05:49.860 --> 05:53.500] a scenario of a life and a guy of a typical white-collar office worker. [05:53.500 --> 05:57.340] For the sake of conversation, we will call this office worker Dwight Schrute. [05:57.560 --> 06:02.500] And Dwight, like most white-collar workers, occasionally does work from home, and when [06:02.500 --> 06:08.240] he does, he has to access his corporate remote access service by using his username and password [06:08.240 --> 06:12.480] to access corporate resources like email, file shares, or VPN. [06:13.920 --> 06:19.820] Unfortunately, for Dwight, there are bad people in this world that seek to do harm to business [06:19.820 --> 06:21.020] organizations. [06:21.280 --> 06:26.680] So let's suppose, hypothetically, that we have a hardened cyber criminal that has sent [06:26.680 --> 06:31.680] an email to our office worker, and this email indicates that Dunder Mifflin is making an [06:31.680 --> 06:36.660] update on its servers and that it needs the employee to log in in order to ensure that [06:36.660 --> 06:38.760] their account is migrated properly. [06:38.960 --> 06:44.140] Because this email looks like legitimate email, Dwight Schrute then clicks the link and browses [06:44.140 --> 06:51.120] to what is actually an evil twin clone server hosted by the hardened cyber criminal. [06:52.180 --> 06:56.840] And because this server looks identical to the original server that Dwight is accustomed [06:56.840 --> 07:00.100] to log into, he then supplies its credentials. [07:01.100 --> 07:07.360] So those credentials are then forwarded to the attacker, and the attacker is able to [07:07.360 --> 07:11.280] use those to log in to the legitimate remote access service. [07:13.700 --> 07:17.720] And so, unfortunately, without multi-factor authentication, this leaves our employees [07:17.720 --> 07:18.700] vulnerable. [07:19.580 --> 07:26.020] This gets the attention of upper-level management, who then decides to call in the IT department [07:26.020 --> 07:29.100] to implement multi-factor authentication. [07:29.100 --> 07:33.400] And now, when Dwight logs in to his remote access portal, he doesn't just have to supply [07:33.580 --> 07:36.640] a username and password, but he also has to supply a token passcode. [07:38.560 --> 07:43.000] So multi-factor authentication saves the day, the fortress is impenetrable, and all of the [07:43.000 --> 07:44.440] hackers have given up, right? [07:44.740 --> 07:45.780] Not exactly. [07:46.360 --> 07:50.480] So now let's talk about how this attack can be performed in order to bypass that multi-factor [07:50.480 --> 07:51.300] authentication. [07:51.600 --> 07:55.700] So, similarly, we have our white-collar worker, the attacker that sends the email. [07:56.110 --> 08:03.000] The email looks exactly the same, and the victim then clicks the update account link, [08:06.130 --> 08:15.490] accesses the site, and, similar to before, enters his credentials, and this time to include [08:15.490 --> 08:17.990] his multi-factor authentication token. [08:21.400 --> 08:26.280] So then, upon logging in to the malicious evil twin site, instead of sending those credentials [08:26.280 --> 08:33.700] to the cyber criminal, the site replays them in real time while that one-time pass multi-factor [08:33.700 --> 08:36.700] authentication token is still valid to the actual server. [08:36.700 --> 08:43.180] This establishes a legitimate login session with the actual server, and the session token [08:43.180 --> 08:45.960] is then forwarded back to the attacker. [08:46.140 --> 08:52.060] The attacker is then able to use that session token in his browser in order to assume the [08:52.060 --> 08:58.520] compromised session, and thereby gain remote access to the legitimate remote access server. [09:00.980 --> 09:03.140] So, now let's look through a couple demonstrations. [09:03.140 --> 09:07.760] First, we're going to look through a way that we can use this type of attack to defeat multi-factor [09:07.760 --> 09:09.820] authentication on a common web service. [09:10.000 --> 09:14.600] And then we're going to look at a more unique scenario where we can use the same methodology in [09:14.600 --> 09:20.400] order to defeat multi-factor authentication using a binary remote access client. [09:23.910 --> 09:27.840] So, for the first scenario where we are attempting to bypass multi-factor authentication on a web [09:27.840 --> 09:33.720] service, the stack that we're going to use is Flask as our micro framework for our web service. [09:33.720 --> 09:40.200] We are going to use Apache to independently host the copied or replicated content from the [09:40.200 --> 09:42.260] legitimate remote access server. [09:42.260 --> 09:49.420] And then we are going to be using Selenium in order to emulate and execute browser operations [09:49.420 --> 09:54.700] that will allow us to replay those credentials and execute a login in real time. [09:57.420 --> 10:04.680] For this demo, I have used a Flask one-time pass multi-factor authentication proof of concept that [10:04.680 --> 10:10.200] was published by Miguel Grinberg, who does some fantastic Python and Flask stuff. [10:10.200 --> 10:12.300] And so, definitely worth checking out. [10:12.320 --> 10:19.460] But fortunately for us, it makes a convenient option to test different attacks on multi-factor [10:19.460 --> 10:23.460] authentication without having to do a full deployment ourselves. [10:23.460 --> 10:29.340] So, you can essentially just git clone this repository, run it through Python, and you will have a [10:29.340 --> 10:33.860] one-time pass multi-factor authentication portal that you can perform testing on. [10:33.860 --> 10:44.110] Of course, we did some slight cosmetic changes in order to fit with the theme. [10:44.110 --> 10:51.870] So, first we're going to look at how we can use Python and Selenium in order to execute real-time [10:51.870 --> 10:56.050] logins to the actual multi-factor authentication remote access service. [10:58.440 --> 11:01.520] So, the first thing that we're going to need to do in order to achieve the multi-factor [11:01.520 --> 11:05.560] authentication bypass is we are going to need to be able to programmatically [11:05.560 --> 11:08.980] log in to the actual multi-factor authentication service. [11:08.980 --> 11:10.520] So, we're going to do that with Python. [11:10.680 --> 11:16.840] And we're going to start with from Selenium import WebDriver. [11:18.220 --> 11:20.940] Then we are going to create an instance of WebDriver. [11:20.940 --> 11:24.760] So, driver equals WebDriver.firefox. [11:25.980 --> 11:30.020] And you should see a browser open in the graphical interface. [11:32.510 --> 11:35.230] And next, we're going to browse to the actual site. [11:35.230 --> 11:39.190] So, we will do driver.get and then the URL. [11:47.220 --> 11:51.820] It's worth noting that for this demo, I did modify my Etsy hosts file, [11:51.820 --> 11:53.760] and I do not actually own this domain. [11:53.760 --> 11:58.980] So, then what we want to do is we want to enter text values in each of these [11:58.980 --> 12:01.540] different parameters, username, password, and token. [12:02.000 --> 12:04.660] So, in order to do this, we're going to inspect the element. [12:05.540 --> 12:11.160] And here, you'll see the Firefox inspector, which shows you the HTML element and also [12:11.160 --> 12:14.120] shows you the ID for the element is username. [12:14.160 --> 12:20.440] So, in order to interact with it, we will just do driver.findElementById [12:20.440 --> 12:22.860] and then pass it that value, username. [12:23.480 --> 12:28.860] And then we want to send it the keys of the username we want to send. [12:28.860 --> 12:30.020] So, I'll put Hutch. [12:30.320 --> 12:34.120] And as you can see, it programmatically populates that field. [12:34.120 --> 12:37.300] We can then do the same thing for password by inspecting it. [12:37.520 --> 12:42.180] And here, we can see that the ID value for password is also password. [12:42.180 --> 12:56.880] So, we'll do driver.findElementById password, then send keys, and my password. [13:00.260 --> 13:02.620] And as you can see, it then populates the password field. [13:02.620 --> 13:04.440] And finally, for token, we can do the same thing. [13:04.440 --> 13:05.580] Inspect element. [13:05.720 --> 13:07.520] The ID is token. [13:07.940 --> 13:12.320] So, we can do the same thing, but this time, we'll make our token value [13:12.320 --> 13:16.740] 1, 2, 3, 4, 5, 6, and the ID is token. [13:19.700 --> 13:22.680] So, now that all the fields are populated, we just need to click the login button. [13:22.680 --> 13:26.380] So, we just right-click this and inspect element. [13:26.380 --> 13:31.740] And here, we can see that the ID value for that login button is the word submit. [13:31.840 --> 13:40.260] So, all we have to do is driver.findElementById submit. [13:41.220 --> 13:45.180] And then this time, instead of send keys, we'll use dot click. [13:48.820 --> 13:52.780] And as you can see, by the error returned, invalid username, password, or token, [13:52.780 --> 13:57.540] we were able to programmatically make a login attempt to the actual site. [13:58.040 --> 14:02.540] So, now that we've created our Selenium login script, we also need to create a replica [14:02.540 --> 14:06.560] of the site that we are attempting to gain remote access to. [14:06.560 --> 14:10.280] So, in order to make the replica website, we'll start by opening up a web browser. [14:10.280 --> 14:14.060] And then we will browse to the page that we want to clone. [14:21.760 --> 14:24.180] And then to save a recursive copy of the page, [14:24.180 --> 14:29.220] all you have to do is go over to the menu bar over here and do save page as. [14:30.920 --> 14:34.960] And this will save a full recursive copy of the website. [14:34.960 --> 14:40.080] If you want to drop it directly into your web root, browse to var www.html. [14:40.140 --> 14:44.920] And then even better yet, if you want it to be the web root index file, [14:44.920 --> 14:48.340] then you can just name this file as index.html. [14:49.180 --> 14:50.680] Then save. [14:53.220 --> 14:56.060] And then next, you'll want to go to that directory. [14:56.060 --> 14:58.140] So, var www.html. [14:58.400 --> 15:01.960] You'll see that we've now saved the index.html. [15:01.960 --> 15:08.620] And then all the supporting files to include JavaScript and CSS files in this index files page. [15:08.620 --> 15:13.420] And actually, what's helpful about this is it actually rewrites all of the links in the [15:13.420 --> 15:18.160] HTML content to these different paths dynamically. [15:20.410 --> 15:25.290] So, essentially, the only thing that we need to do is chmod 755. [15:26.050 --> 15:27.510] Make this recursive. [15:27.510 --> 15:30.010] Dash r 755 everything. [15:31.310 --> 15:35.550] And then we can open up a web browser. [15:39.350 --> 15:42.210] And make sure that the Apache service is on. [15:42.210 --> 15:44.070] So, apache2 start. [15:44.070 --> 15:48.590] And then we access 127.0.0.1. [15:50.050 --> 15:50.750] And there you have it. [15:50.750 --> 15:54.410] You now are hosting a cloned website that looks identical to the original login. [15:54.410 --> 15:58.890] Now that we have the target website cloned, and we also have our Selenium login scripts, [15:58.890 --> 16:03.230] the next step is to create a Flask authentication handler that will receive [16:03.230 --> 16:06.590] any authentication attempts to our evil clone, [16:06.590 --> 16:12.010] and then connect those to the Selenium logins to replay those credentials in real time. [16:15.370 --> 16:19.770] So, I have a proof of concept that I have published on GitHub. [16:19.770 --> 16:21.450] I have the link available here. [16:22.390 --> 16:26.690] In order to test this out, what you're going to want to do is just git clone the repository. [16:26.970 --> 16:32.710] If you want to test this for yourself, extract the app from the OTP target app.zip file. [16:33.030 --> 16:35.610] And then you can run that via Python. [16:35.930 --> 16:40.290] You'll need to register a victim user with an OTP client, [16:40.450 --> 16:44.530] a one-time pass client, at the slash register URL. [16:44.870 --> 16:49.770] And then you can use this proof of concept to host the malicious clone [16:49.770 --> 16:55.810] that will then target and bypass the multi-factor authentication on that target app. [16:55.810 --> 16:58.110] So real quick, let's just step through the code here. [16:58.110 --> 17:01.670] First, we do our imports JSON for parsing. [17:01.670 --> 17:04.750] We've got a few Selenium functions that are imported. [17:04.790 --> 17:08.250] And then we also have our Flask functions that are imported. [17:10.790 --> 17:15.630] We you'll also need to update the login URL to wherever you're hosting the target app [17:15.630 --> 17:17.070] that you extracted. [17:17.110 --> 17:20.410] In this case, I'm using a modified version of Etsy hosts [17:20.410 --> 17:24.270] to have it hosted at dundermifflin.com slash login. [17:24.610 --> 17:26.210] And we have a couple different functions. [17:26.210 --> 17:27.570] The first is create browser. [17:27.570 --> 17:31.130] So this is very similar to what we did previously in step one. [17:31.130 --> 17:33.850] However, the one difference is that we've changed it to headless [17:33.850 --> 17:38.470] so that it isn't visibly apparent within the GUI. [17:39.590 --> 17:43.830] Then we have a login function that receives the username, password, and token. [17:43.830 --> 17:48.860] And it performs the same Selenium login that we coded in step one of the process. [17:49.230 --> 17:53.670] And then finally, the last part is the Flask component. [17:53.670 --> 17:56.390] And this is actually the Flask receiver. [17:56.390 --> 18:01.750] We have a intent mapped to the slash harvester location. [18:01.750 --> 18:07.990] And this will receive from a post method request the username, password, and token. [18:07.990 --> 18:09.410] That is sent. [18:09.410 --> 18:12.710] It will print those to the user. [18:12.710 --> 18:16.210] And then it will also use that login function. [18:17.730 --> 18:21.930] Pass it those credentials in real time and execute the real time relay. [18:21.930 --> 18:24.190] It will then print the cookies back to us. [18:24.190 --> 18:30.290] And we can then use these cookies as the attacker to hijack the compromised session. [18:30.430 --> 18:36.530] So finally, we also have the malicious Flask handler [18:36.530 --> 18:38.290] hosted on a separate port. [18:38.290 --> 18:40.310] In this case, I have it hosted on 8080. [18:40.310 --> 18:46.930] But a separate port than the content that we're hosting for the cloned Evil Twin replica site. [18:46.930 --> 18:49.250] And the reason that we have these hosted on two different ports [18:49.250 --> 18:54.890] is because of the fact that we are running the clone content through Apache. [18:54.890 --> 19:01.790] And then we are hosting the handler through a separate service, which is used by Flask. [19:01.790 --> 19:05.690] Now, you could actually do this all within the Flask app by just [19:05.690 --> 19:13.990] having the HTML, CSS, and JavaScript content that you cloned within static files in the Flask app. [19:13.990 --> 19:16.550] Unfortunately, this becomes a lot more complicated [19:16.550 --> 19:19.710] than just using the relative links that exist whenever you save it. [19:19.710 --> 19:22.670] Because you then have to update all of the different links [19:22.670 --> 19:25.590] and map them to the static directories within Flask. [19:25.590 --> 19:28.990] So it's actually much easier to just host it on two separate ports. [19:30.910 --> 19:35.810] So now let's go ahead and look at how we can git clone this bypass script [19:35.810 --> 19:38.230] and execute it within our own environment. [19:38.730 --> 19:42.610] Okay, so now we are going to clone the repository and launch the bypass script. [19:42.610 --> 19:45.830] So we'll go to the GitHub repository. [19:46.730 --> 19:58.890] And then we will copy the clone path and do git clone paste selection. [20:00.450 --> 20:01.610] All right. [20:01.610 --> 20:04.500] And then we now have our cloned directory. [20:05.070 --> 20:09.350] And we can do python3 bypass. [20:10.170 --> 20:12.950] And it is now running on port 8080. [20:14.450 --> 20:19.250] Okay, so now we have our cloned web app content for the target web app. [20:19.270 --> 20:21.190] And we are hosting that on Apache. [20:21.190 --> 20:24.630] We also have our harvester that will gather credentials. [20:24.630 --> 20:27.110] And that's running on Flask on a separate port. [20:27.110 --> 20:31.490] What we need to do is now update the cloned content [20:31.490 --> 20:35.270] to where whenever a post is submitted with that form, [20:35.270 --> 20:39.490] it then submits it to our Flask harvester on port 8080. [20:42.570 --> 20:45.870] Okay, so we are going to go into our web root directory [20:45.870 --> 20:48.490] where we previously cloned the web content. [20:48.490 --> 20:53.210] So var www.html, then list the contents. [20:53.430 --> 20:58.470] And again, we had written the HTML file to index.html. [20:58.470 --> 21:02.790] All of the recursive supporting files, such as CSS, JavaScript, [21:03.410 --> 21:06.850] are all here within the index files directory. [21:07.330 --> 21:09.530] And all the content is automatically mapped. [21:10.170 --> 21:13.050] So the only thing that we need to change in this Apache directory [21:13.050 --> 21:16.350] is where the form will post to. [21:16.350 --> 21:17.850] So we can use any text editor. [21:17.850 --> 21:20.810] I'll use nano, then index.html. [21:20.810 --> 21:23.710] And then I'll use ctrl w for the find function [21:23.710 --> 21:25.610] and search for the word action. [21:26.250 --> 21:32.250] Now action in an HTML form tag defines where the post will [21:32.970 --> 21:35.130] or where the form will be submitted to. [21:35.130 --> 21:36.850] And here, when it's empty, [21:36.850 --> 21:40.110] it actually just submits it to the same directory [21:40.110 --> 21:43.570] or the same URL path as it's currently on. [21:43.570 --> 21:45.430] So what we need to do is update this to say [21:45.430 --> 21:49.890] http colon backslash backslash dunder mif. [21:49.890 --> 21:54.750] And I used a one for the L for the malicious app. [21:54.750 --> 22:00.370] So dunder miflin looks almost identical dot com slash harvester. [22:00.830 --> 22:03.410] And you also need to specify the port. [22:03.410 --> 22:05.550] So we're hosting it on port 8080. [22:05.650 --> 22:11.350] So it would be HTTP, your malicious app that you're hosting through Flask. [22:11.590 --> 22:14.690] And then the port that you're hosting it on. [22:14.690 --> 22:16.730] By default, I have it set to 8080. [22:16.770 --> 22:22.350] And then the path of the Flask receiver, which is slash harvester. [22:22.690 --> 22:24.650] Otherwise, there's nothing else that we need to change. [22:24.650 --> 22:28.590] We can do control X and then Y to save the content. [22:31.590 --> 22:33.850] So now whenever an authentication form is submitted [22:33.850 --> 22:38.330] to your evil twin copied content, [22:38.330 --> 22:42.010] it will submit the authentication request to your Flask handler [22:42.010 --> 22:45.230] in order to be able to be replayed in real time. [22:45.970 --> 22:47.470] Okay, so we now have everything set up. [22:47.470 --> 22:51.010] The only thing left to do is use our social engineering techniques, [22:51.010 --> 22:56.050] most likely phishing, to entice our victim to access the fake site [22:56.050 --> 22:57.970] and convince them that it's the real site. [22:57.970 --> 23:00.290] And then when they enter their credentials, [23:00.290 --> 23:03.250] all should work as planned and we should be able to hijack their session. [23:04.950 --> 23:07.150] So now it's time to exploit our target. [23:07.150 --> 23:08.770] You will look at two different screens here. [23:08.770 --> 23:12.490] First, we have the victim that has accessed [23:12.490 --> 23:18.050] or been enticed to access our fake dundermifflin.com login site. [23:18.050 --> 23:22.710] And then, as you recall, we have our Apache service hosting the clone content. [23:22.710 --> 23:30.590] And we also have the Flask bypass that is running our harvester on port 8080. [23:30.590 --> 23:34.390] And as you can see, the attacker currently is not logged in, [23:34.390 --> 23:38.490] if we refresh this page, to the actual dundermifflin site. [24:03.400 --> 24:05.140] Okay, so once the hijack is completed, [24:05.140 --> 24:10.540] it will actually send the initial user back to the actual dundermifflin.com login page [24:10.540 --> 24:13.860] so they don't suspect that anything has gone wrong. [24:13.860 --> 24:18.880] However, on our attacking side, we can now see that we have received the username. [24:18.900 --> 24:20.740] We have received the password. [24:20.740 --> 24:22.640] We have intercepted the token. [24:22.640 --> 24:25.640] We also replayed the token in real time. [24:25.640 --> 24:30.020] And we're able to acquire the session token for that user. [24:30.020 --> 24:34.220] So once again, we are not currently logged into the actual site as the attacker. [24:34.220 --> 24:37.000] And we are going to attempt to hijack that user session. [24:37.000 --> 24:40.640] So what we're going to do is we're going to open a cookie manager. [24:40.640 --> 24:43.160] And there's a bunch of different plugins that you can use for this. [24:43.160 --> 24:47.100] And we're going to manage cookies for dundermifflin. [24:47.420 --> 24:51.540] And then the only thing that we have to do is we have to modify this session token [24:51.540 --> 24:55.740] so that it has the value of the compromised session. [24:55.740 --> 24:57.800] So we will copy this. [25:01.450 --> 25:07.790] And then drop this into the value of the previous session token. [25:07.890 --> 25:10.570] And then we save the contents of the cookie. [25:10.570 --> 25:13.390] And we go back to dundermifflin.com, the actual site. [25:13.390 --> 25:14.010] Click enter. [25:14.010 --> 25:17.890] And we have now successfully bypassed the multi-factor authentication [25:17.890 --> 25:21.830] without having to enter the username, password, or token. [25:23.030 --> 25:27.450] Undoubtedly, at this point, somebody is thinking that this sounds an awful lot like EvilGinX. [25:27.450 --> 25:30.490] And admittedly, the first proof of concept that we discussed [25:30.490 --> 25:33.550] is something that you could accomplish with EvilGinX. [25:33.670 --> 25:38.590] For any that aren't familiar, EvilGinX is a multi-factor authentication bypass toolkit [25:39.210 --> 25:43.990] that allows you to perform very similar attacks to the ones that we just discussed. [25:44.010 --> 25:48.550] With the only difference being that instead of operating at the application layer, [25:48.550 --> 25:54.370] it operates as an HTTP proxy relaying communication for the authentication sequence [25:54.370 --> 25:58.350] and then also thereby intercepting the session tokens [25:58.350 --> 26:05.470] and providing those back to the attacker in order to allow them to assume the compromised sessions. [26:06.390 --> 26:11.330] But there's an inherent problem with relying on ScriptKitty toolkits and red teaming. [26:11.330 --> 26:17.430] For one, if the developers of the toolkit decides to stop supporting it and it no longer works, [26:17.430 --> 26:18.950] then you're dead in the water. [26:18.950 --> 26:22.630] Similarly, if something breaks in that toolkit and you don't understand [26:22.630 --> 26:25.370] the underlying components well enough to be able to troubleshoot it, [26:25.370 --> 26:27.910] there's also nothing else that you can do. [26:28.150 --> 26:33.710] But most importantly, anytime that you're relying on a cookie cutter toolkit, [26:33.710 --> 26:40.230] it will likely address the bulk number of instances where you need to use that exploit. [26:40.230 --> 26:45.090] But the reality is because you don't have control over all of the integral components [26:45.090 --> 26:49.210] of that exploit, it makes it very difficult for you to be able to [26:49.210 --> 26:53.390] tailor those attacks to unique and unconventional environments. [26:55.670 --> 27:02.210] So EvilGenX is an HTTP proxy, which means it can only operate within the context of [27:03.470 --> 27:07.650] relaying multi-factor authentication sequences over a web service. [27:07.650 --> 27:13.370] However, a lot of times these days where we're entering our multi-factor authentication credentials [27:13.370 --> 27:15.370] are not web services at all. [27:15.370 --> 27:20.550] But instead, they are login clients that exist on your local operating system, [27:20.550 --> 27:23.570] such as operating system level multi-factor authentication, [27:23.570 --> 27:26.490] VPN clients, or remote access clients. [27:28.830 --> 27:33.670] So for our second proof of concept, we're going to look at just such a scenario [27:33.670 --> 27:36.650] where we would want to take the credentials that were compromised [27:36.650 --> 27:40.550] via our phishing web service and be able to relay those [27:40.550 --> 27:45.070] to a local login client that requires multi-factor authentication. [27:45.610 --> 27:49.350] So our attack stack in this scenario is slightly different. [27:49.350 --> 27:53.370] We are still using Flask to handle the authentication requests [27:53.370 --> 27:57.570] and map those to the replay functions. [27:57.570 --> 28:02.730] And we're still using Apache in order to host the login content. [28:02.730 --> 28:06.930] The only real difference is that instead of using Selenium to hook into the browser, [28:06.930 --> 28:11.030] we're instead using something called Auto-IT to hook into the operating system [28:11.030 --> 28:13.890] in order to control operating system functions, [28:13.890 --> 28:17.170] such as mouse movement or entering text or clicking, [28:17.570 --> 28:23.990] and thereby performing authentication sequences within local authentication clients. [28:24.750 --> 28:29.730] So first, let's look at how we can use Auto-IT in order to perform [28:29.730 --> 28:36.230] operating system login functions that we could potentially use in a real-time replay attack. [28:36.530 --> 28:39.270] So in order to perform a real-time replay attack [28:39.270 --> 28:43.030] that includes the one-time pass-based multi-factor authentication [28:43.570 --> 28:47.230] with a login client as opposed to a login web service, [28:47.230 --> 28:51.510] we need to be able to programmatically interact with the underlying operating system [28:51.510 --> 28:53.370] instead of just the web browser. [28:53.410 --> 28:57.190] So in order to do that, we're going to use a different Python library this time. [28:57.190 --> 28:59.690] So instead of Selenium, we're going to use Auto-IT. [28:59.730 --> 29:04.750] And in order to import that, we're going to do import and then pyAutoGUI. [29:07.310 --> 29:09.710] And a few different functions that we need to be aware of. [29:09.710 --> 29:12.550] There is the moveTo function, which allows us to specify [29:13.110 --> 29:17.290] where it will move the mouse on the screen. [29:17.410 --> 29:21.190] There is the typeWrite function, which allows us to type text. [29:21.190 --> 29:24.670] And then there's a click function that allows us to click the mouse button. [29:24.870 --> 29:27.970] And one other function that's worth mentioning is the position function, [29:27.970 --> 29:33.770] which tells you what the coordinates are for the mouse at any given time. [29:33.910 --> 29:36.630] So we will demonstrate that real quick. [29:37.810 --> 29:39.490] pyAutoGUI.position. [29:40.150 --> 29:46.630] And we can see that the X and Y coordinates for the current cursor location is 674,334. [29:46.630 --> 29:51.530] Now, what we want to do is determine what the coordinates are of the cursor [29:51.530 --> 29:54.030] whenever it's hovering over the username field [29:54.030 --> 29:57.510] so that we can tell the mouse to go over to the username field to click [29:57.510 --> 29:59.130] and then to enter text. [29:59.430 --> 30:03.930] So what we'll do is we will use the same command, the same function, [30:03.930 --> 30:05.790] but this time before executing it, [30:05.790 --> 30:08.750] we will hover the mouse over the username field and press enter. [30:09.150 --> 30:11.690] And now we know that the coordinates of the mouse [30:11.690 --> 30:15.510] hovering over the username field is 227,369. [30:15.510 --> 30:18.570] Now, with that knowledge, we can easily create a quick function [30:18.570 --> 30:23.890] that allows us to enter text into that particular location. [30:23.890 --> 30:26.270] So to do that, we'll create a function with def [30:26.270 --> 30:29.250] and we'll call this supply username [30:30.330 --> 30:33.450] and then we'll pass it a username value. [30:34.270 --> 30:38.330] So first we'll do py autogui.moveTo [30:38.890 --> 30:45.110] and then we'll supply it those X and Y values of 227,369. [30:46.190 --> 30:49.450] Then the next thing that we want to do after we hover it over the usernames, [30:49.450 --> 30:53.890] we want it to click in that field to activate that particular input field. [30:53.890 --> 30:59.350] So for that, we'll do py autogui.click. [30:59.910 --> 31:03.450] And finally, now that the field is clicked and activated, [31:03.450 --> 31:05.790] we want to supply it the text that we provided. [31:05.790 --> 31:09.530] So we'll do py autogui.typeWrite [31:10.210 --> 31:13.490] and then the username that's supplied. [31:15.050 --> 31:17.970] Okay, so now that we've created that very basic function, [31:17.970 --> 31:20.590] we can execute that function with a given username. [31:20.590 --> 31:28.610] We'll do supply username and then we'll try the username Hutch and press enter. [31:28.910 --> 31:31.850] And as you can see, it automatically moves the mouse over, [31:31.850 --> 31:34.530] activates that field and types in the word Hutch. [31:34.530 --> 31:38.390] Now we can use all of these different functions of moving the mouse around, [31:38.390 --> 31:42.010] clicking and entering text in order to fully populate [31:42.010 --> 31:46.670] the different parameters within the multi-factor authentication sequence [31:46.670 --> 31:51.030] in order to relay communications from a malicious web app [31:51.030 --> 31:57.510] that's phishing credentials into a client in order to gain access via the client login. [31:58.070 --> 32:05.150] So now that we know that we can interact with and authenticate to a local login client [32:05.150 --> 32:10.470] using AutoIT, we want to use a similar process to what we did before [32:10.470 --> 32:13.730] where we create a phishing web service. [32:13.730 --> 32:19.650] But this time we modify the authentication replay sequence [32:19.650 --> 32:23.650] so that rather than replaying it to a separate web service, [32:23.650 --> 32:28.670] we simply use AutoIT in order to replay those credentials real-time [32:28.670 --> 32:32.170] to include the multi-factor authentication token to the login client [32:32.170 --> 32:34.470] in order to gain remote access. [32:35.910 --> 32:40.210] Now we have slightly modified the previous proof of concept [32:40.210 --> 32:43.310] in order to accomplish precisely this. [32:43.730 --> 32:48.370] So let's quickly look at what we've done here that is different. [32:48.990 --> 32:53.670] This time, instead of importing Selenium, we're using PyAuto GUI. [32:54.330 --> 32:56.870] We do have a few additional parameters. [32:56.870 --> 33:02.550] We are doing a two-step multi-factor authentication sequence process [33:02.550 --> 33:07.770] where in the first step, the username and the multi-factor authentication token is supplied. [33:07.770 --> 33:10.670] And then in the second step, the password is supplied. [33:10.670 --> 33:13.330] So because of this, we have two different redirects. [33:13.330 --> 33:18.830] The first redirect from the initial authentication sequence step. [33:18.830 --> 33:24.450] And then the other redirect URL is where the victim will be redirected [33:24.450 --> 33:31.610] after they complete the entire malicious multi-factor authentication sequence. [33:33.450 --> 33:37.610] Now, in order for this to be successful, we do have to define the specific coordinates [33:37.610 --> 33:45.970] of where different login components and buttons are within the GUI login interface. [33:45.970 --> 33:49.890] And again, we accomplish this the same way as what we just looked at. [33:50.810 --> 33:58.210] We have a function for validating the token using the username and token value. [33:58.210 --> 34:01.710] And this is accomplished, once again, by using Auto-IT [34:01.710 --> 34:06.550] and just moving the mouse to the location of specific fields, [34:06.550 --> 34:10.820] clicking on those fields to activate them, supplying the text, and then moving on. [34:11.630 --> 34:14.830] The second step in the process is essentially doing the same thing, [34:14.830 --> 34:17.750] but this time supplying the user password. [34:19.630 --> 34:28.850] And then we have a Flask app that has two different URL receivers or routes at this point. [34:28.850 --> 34:32.750] The first is the initial harvester one that represents that first step in the process [34:32.750 --> 34:36.730] of the username and the multi-factor authentication token. [34:38.690 --> 34:44.830] And as you can see, that then invokes the validate token function from above. [34:44.830 --> 34:51.690] And then the second is the second step in that multi-factor authentication sequence, [34:51.690 --> 34:54.670] which is validating the password for that particular user. [34:54.670 --> 34:59.810] And again, you see that we then invoke the validate password function that we created above. [34:59.810 --> 35:05.770] And similar to before, we are executing this or hosting this on a port that is different [35:05.770 --> 35:11.190] from the one that we are hosting the malicious replica web service on. [35:13.010 --> 35:16.430] So a few different things to take note of here on this proof concept. [35:16.430 --> 35:20.870] On the right side here, we have the malicious app that we are using to [35:20.870 --> 35:25.010] phish the victim's credentials to include their multi-factor authentication token. [35:25.010 --> 35:30.550] On the left side, we have the actual client that we would use in order to [35:30.550 --> 35:34.350] enter the intercepted username, passcode, and password. [35:34.550 --> 35:41.310] And here at the bottom left corner, you can see the bypass code running with client bypass.py. [35:41.310 --> 35:44.050] So we're going to run that once again running on port 8080. [35:44.050 --> 35:47.150] And then we're going to simulate the victim entering their credentials. [36:00.410 --> 36:07.210] And here you can see that using auto-IT, it was able to successfully migrate the client on our [36:07.210 --> 36:14.110] desktop. And then we're going to simulate the user entering the second component of [36:14.110 --> 36:16.310] the authentication, which is the password. [36:17.810 --> 36:19.910] And then they click login. [36:20.010 --> 36:23.510] And similarly, credentials are intercepted, replayed. [36:23.870 --> 36:28.290] The victim is redirected to a VMware Horizon client website. [36:29.370 --> 36:32.190] The password is disclosed at the bottom left corner. [36:32.190 --> 36:37.970] And it successfully loads our desktop and allows us to bypass multi-factor authentication. [36:39.270 --> 36:43.750] So we've now established that it is possible to bypass one-time pass-based multi-factor [36:43.750 --> 36:46.710] authentication for both web and non-web clients. [36:46.710 --> 36:49.790] But what about push-based multi-factor authentication? [36:49.790 --> 36:52.910] For anybody that's not familiar, push-based multi-factor authentication [36:53.430 --> 36:58.010] is a second factor that's initiated by the server side instead of the client side. [36:58.010 --> 37:03.290] So you enter your username and password, just as you would with any type of authentication [37:03.290 --> 37:07.910] sequence. And then for the second factor, instead of sending a temporary password that [37:07.910 --> 37:12.550] you retrieve from your mobile device to the server, the server instead sends a request [37:12.550 --> 37:15.870] to an app that you have loaded onto your device. [37:15.870 --> 37:21.430] And that request indicates that an authentication attempt was performed and then asks whether [37:21.430 --> 37:25.510] or not you approve or authorize that authentication attempt. [37:25.510 --> 37:30.450] Now, on the surface, this may seem like the more secure solution because there is no temporary [37:30.450 --> 37:32.470] password to be replayed. [37:32.610 --> 37:38.930] The problem is because of the out-of-band nature of this authentication sequence, it [37:38.930 --> 37:40.830] makes it just as easy to bypass. [37:40.870 --> 37:45.370] So let's look at our attack sequence and look at it within the context of push-based [37:45.370 --> 37:46.890] multi-factor authentication. [37:46.890 --> 37:52.810] So similar to before, we have a cybercriminal that sends a phishing email to our victim. [37:52.810 --> 37:58.390] The phishing email has a link that leads that victim to an evil twin clone server. [37:58.390 --> 38:02.470] The victim supplies their credentials and attempts to log in to what they think is the [38:02.470 --> 38:04.890] legitimate remote access service. [38:04.970 --> 38:11.490] That evil twin clone site replays in real time those credentials to the legitimate remote [38:11.490 --> 38:12.990] access service. [38:13.190 --> 38:19.170] The remote access service sees that it has an authentication attempt, so now it sends [38:19.590 --> 38:22.790] a push authorization to our victim. [38:22.810 --> 38:28.010] Now again, keep in mind that our victim believes that they've just logged into the legitimate [38:28.010 --> 38:29.490] remote access service. [38:29.490 --> 38:33.170] So they're fully expecting that they're going to get an authentication authorization [38:33.170 --> 38:34.090] request. [38:34.530 --> 38:39.370] So not thinking twice about it, the victim is going to click yes, it's me and approve [38:39.370 --> 38:41.470] this authentication attempt. [38:41.470 --> 38:46.650] So our victim is now informed of the server that the authentication request is good and [38:46.650 --> 38:49.290] the session is established. [38:49.290 --> 38:51.810] Just like before, the rest is history. [38:51.810 --> 38:57.450] We are able to use the evil twin server in the established session to harvest those session [38:57.450 --> 39:01.810] tokens and thereby hijack the session of the victim user. [39:04.010 --> 39:07.930] So we've now demonstrated that it's possible to bypass multi-factor authentication for [39:07.930 --> 39:11.910] one-time pass and push-based multi-factor authentication. [39:12.390 --> 39:15.950] So I'll close out here with just a few final parting thoughts. [39:16.810 --> 39:21.170] If you're on the blue team, we've probably established that your multi-factor authentication [39:21.170 --> 39:25.330] solution sucks and that you're likely not getting out of it what you want to get out [39:25.330 --> 39:25.930] of it. [39:25.930 --> 39:30.110] And don't feel bad because almost everybody in the industry is in that boat because everybody [39:30.110 --> 39:35.490] is using either push-based or one-time pass-based multi-factor authentication with only a very [39:35.490 --> 39:40.290] small percentage of organizations that have now moved to U2F. [39:40.290 --> 39:46.850] The obvious best thing that you can do is to use U2F for prevention. [39:46.850 --> 39:52.390] But if you can't do that at this time, then you probably need to focus more on your detection [39:52.390 --> 39:55.410] skills and identifying these types of attacks. [39:55.410 --> 39:59.150] And if you're looking for any other recommendations, because I don't want to spend too much time [39:59.150 --> 40:06.430] solutioning in red team village, you can look to FidoAlliance.org, which has a lot of good [40:06.430 --> 40:10.950] recommendations around how to effectively do multi-factor authentication. [40:10.970 --> 40:15.510] Or you can also check out some blogs that we did and that I authored at SetSolutions [40:16.110 --> 40:18.770] called Doing MFA the Right Way. [40:21.730 --> 40:25.390] If you're on the red team, I encourage you to remain flexible anytime you're doing [40:25.390 --> 40:27.890] penetration testing or red team engagements. [40:27.890 --> 40:31.970] Remember that penetration testing is more art than audit. [40:32.050 --> 40:36.230] And what I mean by that is that you're never going to get anywhere by going into an organization [40:36.230 --> 40:43.230] and attempting to perform such an assessment by checking off a series of tests in a list. [40:43.230 --> 40:44.950] You need to be flexible. [40:44.950 --> 40:49.770] You need to familiarize yourself with the environment that you're in and then be ready [40:49.770 --> 40:56.690] to adapt and create your own tools and solutions in order to exploit the ways that different [40:56.690 --> 40:59.350] components are communicating within that organization. [41:00.030 --> 41:03.530] One of the great things that I love about InfoSec is the fact that [41:04.370 --> 41:10.910] there is so much knowledge within this field and so much potential information in so many [41:10.910 --> 41:13.530] different areas that you can potentially specialize in. [41:13.530 --> 41:17.210] No matter how good you get, there's always more that you can learn. [41:17.890 --> 41:22.370] So obviously, continue to improve yourself in any way that you can. [41:22.370 --> 41:24.490] Be creative and think outside the box. [41:24.670 --> 41:28.570] I know that it's somewhat controversial to say this because some people will tell you [41:28.570 --> 41:32.590] that you can be just as effective of a penetration tester without being able to code. [41:32.590 --> 41:37.270] Learning to code is like a superpower whenever you're doing red teaming and penetration testing [41:37.270 --> 41:42.370] because of scenarios just like this where you can adaptively and quickly prototype [41:43.030 --> 41:47.430] exploits that are unique to the environment that you are attempting to assess. [41:47.730 --> 41:51.330] And always kind of approach things with that mentality of picking it apart, [41:51.330 --> 41:54.270] understanding how it works, and then putting it back together. [41:55.750 --> 41:59.690] To the staff at DEF CON and also for the Red Team Village staff, [41:59.690 --> 42:01.090] I really appreciate the opportunity. [42:01.090 --> 42:02.130] This has been awesome. [42:02.970 --> 42:06.590] Thank you for making this happen despite everything that's going on. [42:08.430 --> 42:12.810] And to everyone else, stay happy, stay healthy, stay hacking. [42:13.610 --> 42:16.130] This definitely has sucked, I think, for everybody. [42:17.110 --> 42:22.690] I admittedly was somewhat disappointed to not be going to Vegas this year for DEF CON [42:23.230 --> 42:26.130] and definitely will miss seeing all you guys out there. [42:26.850 --> 42:29.810] But I think this is something that we can all get through together. [42:29.810 --> 42:33.210] We'll put behind us and hopefully we'll see you guys next year. [42:34.610 --> 42:35.910] Quick shameless plug. [42:35.910 --> 42:39.170] Check out our podcast, Ready, Set, Secure, from Set Solutions. [42:39.470 --> 42:42.670] Also, feel free to follow my research at sociosploit.com [42:42.670 --> 42:45.090] or follow me on Twitter at Hackett Hutch. [42:46.550 --> 42:47.870] And that's a wrap. [42:48.310 --> 42:48.970] CREASE CLOSED!