
Okay. Are we good to go? Can everyone hear me? Okay. >> Oh. Oh, no. [laughter] >> Okay. Um, all right. If if the mic complains, then I will pause and let it sort itself out. But, um, thanks everybody for having me. Um, as you can tell by accent, I'm not from here, but, um, it's great city so far. Um, and thanks for coming along to learn about race conditions. So, I am Harriet. I'm a pentester at KPMG in the cyber defense team. Um, I've been working there for about three years now and pen testing for two of those. Um, I am a Czech team member, so that is a pentesting qualification. Oh [laughter] so um I am at least qualified to talk to
you about this today. Um, how did I get here? I did um my degree in computer science um and I joined the grad scheme at KPMG after that. Um I'm very passionate about getting more women and girls into cyber. And my main personality trait is that I have a dog. So I will let you look at how cute he is for two more seconds before we get into the presentation. So what are we going to be talking about for the next 15 or so minutes? Um, we're going to start with username enumeration. We're going to talk about how we can find a list of valid usernames for an application. We're going to drill deeper into timebased username
enumeration um, and then talk about race conditions and combine the two together um, to brute force a web application login page in practice. We're going to talk about what that means and the mitigations for it as well. So username enumeration then um what we do the aim of the game for this is to obtain a list of valid usernames for an application. So we just want to make a list of all the usernames or emails that are associated with accounts for an application. So to do this, we send requests um that contain valid usernames. So usernames that are associated with accounts and invalid usernames, so usernames that don't have accounts associated um and we look for discrepancies between the two.
There's a couple of ways that's shown on screen here um that you can find discrepancies. Um the main one that we're going to talk about today is discrepancies in time. So if you send um if you send a request with a valid username to log into an application um and a request with an invalid username um they might have different times to respond from the application. And similarly the response size. So a valid versus invalid username the response from the application um might be a different size. Error messages. So for example, if you're logging into an application um and you put in your email wrong um you might see that email is incorrect. But if you put the email right and your
password wrong, you might see that password is incorrect. So from that we can deduce that the username was correct but it was the pass password that was wrong. Registration and forgot password are kind of similar methods. So, if you're signing up to an application or you're going through a forgot password flow, then you put in your email to either sign up and it might tell you that email's already taken or that username's already taken or you've just worked out a valid username for the application. Or if there's a vulnerable for forgotten password method, um you might put in a username to get a password reset link sent to you and it might say great, we've sent it to you or it might say
that username doesn't exist in the application, then you can work out which ones are valid, which ones are not. And finally, lockout behavior. So if you try and log into an application um and you're sort of blocked after five incorrect tries um you might be able to work out that that's a valid username compared to if you're trying to log in with an invalid username and you just keep putting the wrong password in or I mean it's an invalid username so it hasn't got a password and it never tells you that you're locked out but your valid username told you you were locked out after five attempts. then you can work out there's a discrepancy there that tells you that
one is valid and one's not. So let's look at timebased more in depth. Um got some lovely PowerPoint um graphics to actually work out what on earth I'm going on about. So, we've got a login page here and this um purple table is the backend database which contains all of the usernames and passwords for the application. So, in this example um we have three users, users one, two, and three. In our um login page, we're trying to login as user five, which is an invalid username because it doesn't exist in the database. So we submit our credentials. The application looks through the database, doesn't find user 5, comes back, says invalid credentials. Now let's look at the valid username
case. We've got our login page again. We've got our backend database. This time we're trying to login as user three. So we submit our credentials. The application looks through the database. It finds user three. What it then has to do is check the password. Now, in real life, passwords will be hashed in the database. You'd hope. Um, but for ease here, they're just in plain text. Um, so it checks if the password we've entered, which invalid password, um, is the same as the password that's in the database, which it's not in this case. So, we return back invalid credentials. So this flow with the valid username compared to the previous flow with the invalid username. You can see that
there's a lot more steps that the application has to do and it has to check the password which then results in it taking more time to do that. So between both cases one of them will take longer than the other. So here's a real life example and I know this room is very long. So, if you can't read it, then I do apologize. Um, but on the top there, um, we have, um, there's two screenshots here from a tool called Burpuite, which we'll go into more in a second but um, the top is the screenshot of the first flow that we went through. So, the invalid username um, and the bottom in the blue is the valid username flow.
So on the right hand column we have the times it took the application to respond. Um this is in milliseconds. So a user wouldn't notice um if you were just sort of trying to log into an application. But using this tool to um get specifics of how long it took the application to respond, we can notice a difference. So you'll see in the top invalid username case, it only took about 14 milliseconds for the application to check through the database, say right, the username's not there, and then respond and say invalid credentials. In the bottom case, the valid username but invalid password, the application had to go check the database, check that the password was
right or not, and then come back. So it took a significant amount of time longer. So what we can do then is we can supplement this with a bit of OSENT. For example, if you were trying to hack into a specific company's website. Um you might look through LinkedIn, find all their company employees. Um and then make a list of what their usernames might be, even just their emails or potential um formats for usernames. Try them all. Get all the response times and then you can work out which ones are valid and which ones are not. So, what's the point of even getting a list of valid usernames? Why do we care? Um, we can try and brute force. So, we've
got our list of valid usernames. Um, we've got our login page. What we're now going to do is take one by one our valid usernames and try and guess their passwords. Um there's resources out there of um common password lists. So what you could do is you can just make a load of requests to the application cycling through the usernames and a load of password guesses to try and get the user's credentials and login as them. Not as easy as it sounds because most applications have some mitigating controls in place. for example, um account lockouts. So after five attempts um you might get locked out or a strong password policy which makes the password a lot harder to
guess but um using race conditions we can get around this. So race conditions occur when an application processes multiple requests at the same time. So we can send requests to the application at the same time and the application processes it at the same time. What does that even mean? Why is that bad? It sounds fine. Well, let's bring up some more PowerPoint graphics um to actually explain this. So we've got our credentials again, our little login field. And this time we got it three times. So imagine we've got um three browsers open or three tabs. Um, and we're going to try and guess user 3's password. In this example, because it's quite a small example, let's assume that the
lockout policy is one attempt. So, if you get it wrong once, you're done for. Um, so if we were to send these requests one by one, user 3's password is password three. So, if we started with request one, we'd get it wrong and then we'd be locked out and we wouldn't be able to try anymore. But using race conditions, we can actually send all of these at the same time and the application will process them all. So when you send one request, as we've seen, it checks if the username's correct. It checks if the password's correct. The application also checks if the user is locked out, which is what we're interested in here because we want
to bypass the lockout policy. So, as I said, if you just sent one, you'd get locked out straight away from the first one because the password's wrong. But if we send all three of these at the same time, the application will check at the same time if the user's locked out, which when you we haven't sent any requests yet, the user's not locked out. So if it asks this question at the same time for all three of these requests, the answer is going to be no. For example, there might be an account locked variable and it's false because the user is not locked out. So we can send all of these and the application won't consider the lockout policy
because there's a race condition vulnerability where it checks the requests at the same time. Now, sending three requests, we're not going to guess the password in three attempts, probably. So, let's scale it up then. We've got our login page. I've duplicated it as many times as I can be bothered. Um, so we're going to send all of these to the application at the same time. It's going to ask, is the user locked out for all of them at the same time? Um, the answer to that is no. So then all of our requests can be processed successfully. Now you'll get obviously different responses. So most of them are going to be invalid credentials, but you might
get lucky and one of your password guesses might be correct and you'll receive um a successful login message. Um but the application only treats all of these as one login attempt. So you're sending however many is on the screen, I don't know, 20 at the same time, but the application only treats it as one. So you won't be locked out even after sending all of these if the lockout policy was say five or something. So let's see how you actually do this. Um, is anyone here familiar with Burpswuite at all? Couple of pans, couple of nods. For those who aren't, it's a proxy tool. So, it basically sits um in the middle of um the application and our browser.
So, it intercepts any requests that we might send. So, for example, if you press a button on the website or you do an action, um that sends the request to the application. Buite sits in the middle. So you can see the requests and responses that are sent um when they come back and forwards from the application. So again, if you can't see, I do apologize, but in this example, we have um a login request um and we've got our credentials over here. We've got test.com and we've got test as the password. So, we're going to send this to what's called the repeater in Bertswuite, which lets us um send the request again and again, multiple times without having to
go back to the website and keep pressing login. And then we're going to add that tab to a new group. We'll create a group called our race condition group. And you can choose a nice color for it. I've gone with purple. Then you will see up here that our race condition group has now been created and we have our request test.com and test within our group. So we're going to now duplicate our request. Um and I've gone for 25 times. So, in order to proof of concept this attack, um what you want to do is send here we've gone for 25 um invalid requests to login to the application and then try and log in again one time
after that. Um and say the lockout policy was five. Um if it lets you log in after you've sent all of these 25 requests that are incorrect, then you know you've bypassed the account lockout policy. because you've just sent 25 incorrect um login attempts, then you've tried to login again and it's worked. So, you've sent 25 wrong ones. It hasn't locked you out. So, that's how you know it's vulnerable to race conditions, which is what we're proving here. So, I'm going to duplicate it 25 times. You can see there's a load of tabs up here. Now, um these all contain the same credentials. So, if you were doing an attack, you might want to check you'll
change this password um to all the guesses that you want to guess. Then, to send them all at the same time, you're going to go um to the little arrow next to send to click send group in parallel. So, this sends all of your requests at the same time in order to bypass the lockout policy. Obviously, don't do this unless you're allowed to um because it's against the law. So, unless you're permitted to test it or you're in a lab environment, um then don't actually send the requests. So, this is what we've just done in Buite. Then in real life, um we've sent we've duplicated a load of requests, sent them all at the same time. um the
application will check if the user is locked out at the same time um and let you through. So what does this all mean? Let's put everything together that we've gone through. So first of all, we identified timebased username enumeration vulnerability on an application. So we made a list of valid usernames um because when you send valid usernames to the application it takes a lot longer to respond than if you send invalid usernames. Um so we've made a list of valid usernames for the application. What we've then done is we've identified that the application is vulnerable to a race condition vulnerability. So we can send 25 invalid login requests and still be able to login after it. um which
proves that we've bypassed the five attempt lockout policy. Now that's enough to prove that the application is vulnerable to race conditions. So that can go into a pentest report. For example, if you want to go a step further and actually exploit this vulnerability, you can um obviously duplicate your requests hundreds of times, change the invalid password to a load of different guesses um and then send them all at the same time and see if any of those guesses worked. Obviously, um sending hundreds of requests to an application at once um is getting into DOS territory. So, um often when we're working with clients, we aren't allowed to do this because we don't want to break their applications um too much.
So, um that's why we sort of tend to stop at just proving that it exists, but in theory, you can go further. And then hopefully one of your hundreds of requests will be successful. And then you've gained credentials from nothing to gaining a username and a password. So you started from zero and you've worked out the username, you've worked out the password. So you can get from an unauthenticated standpoint to an unauthenticated user which then if the application is vulnerable to more things, you've now got credentials, you can exploit stuff, you can cause mayhem within the application. What can we actually do about this? Um, it's all great to talk about vulnerability, but we need to know how
to fix it. So to fix time based username enumeration, you need to make sure that the login function is consistent. So it takes the same amount of time to give a response for an invalid username versus the same amount of time to give a response from a valid username. Or you can add random time delays. Obviously we're working milliseconds here. So a user is not going to notice. It's not going to impact productivity. Um, but just adding a couple, I don't know, 100 or 50 millisecond time delays um, at random will just stop the possibility of being able to notice those discrepancies. To mitigate race conditions, um, you need to make sure that your application
is not processing all of those requests at the same time. So, you can lock resources. For example, we had our account locked variable um and all of the requests were being processed at the same time. They were all checking if the account was being locked at the same time. But if you lock that resource, only one request can check it at once. So it stops the vulnerability that we found. Uh you can use transactions. So that's um sort of encapsulating um different checks or processes um into one. So for example, checking the username, password, and the account lockout um do it all at once um to stop the um other requests also checking at the same time.
Um and that's similar to atomic and indivisible operations. So they're sort of one single operation at a time. You can use rate limiting. Um we're sending hundreds if not thousands of requests in order to try and find a password. So most users won't need to send that many requests in practice. Um so you can rate limit and just stop the possibility um of all of those requests being sent. Um you can discard any redundant or conflicting requests. So we're trying to log in with the same user but a load of different passwords. Obviously, they're all loads of them are redundant because there's never going to be that many different password options for one user. So, um just get rid of them.
And also avoid mixing data from different storage places. Um if you're trying to grab data from different places in order to evaluate an operation, um you're sort of increasing the length of time that's possible for um a race condition to occur. So that is everything. Um a really good resource is the uh Portswigger labs. So Portswiger make burpuite the tool we use today. Um so if you want to learn more about web application hacking or race conditions in particular, I'd really recommend checking out those resources. There's a lot of free labs on there that you can practice this in. And my LinkedIn's also there if you want to um have a have a look or send me a
message for anything. Any questions at all? >> Yeah, time for one. >> What's that? Sorry. >> That's a really good question. So we've actually seen it becoming more and more frequent when as developers are trying to streamline their code and make things as efficient as possible. If the username isn't there they'll return back that um they'll return back as soon as possible. Um with the race conditions again developers are trying to make their code um [clears throat] more streamlined and efficient. So often you're sort of not thinking about blocking things because then it means that other things can't access it. So it makes it kind of slower. Um but obviously there's going to be a
trade-off between security and productivity um at a certain point. So um yeah, we've we've seen it more and more often, but um if if you sort of add the protections in um then you'll you'll be fine.
Great. Thanks everyone. [applause]
>> Thank you Harriet. So that is we now have our last break of the day at 25 minutes and then it's a straight shot through lightning talks back on here regular talks in the main stage keynote after. Um couple of things to make you aware of. There is no coffee being served in the ballroom this break. Um, so we had coffee in the morning, we had coffee at lunch, but we don't have any now. Sorry, budget. Um, what we do have in the ballroom is the sticker stall if you haven't checked it out. There's a bunch of cool stickers from other security events that you can get for in exchange for a donation to Mar Cury. So,
take a look there if you haven't. Thanks everyone.
really I was I want to hear.