 What's going on everybody? My name is John Hammond. Welcome back from the YouTube video, still looking at the all-army cyber stakes or ACI CTF that went on this past week. I want to be showcasing some challenges from here. I'm running out of time until the game ends. It's Saturday morning and the game should end Sunday night, so I want to get a few more of these out. This challenge is called Speak Plainly. It's for 150 points in the cryptography section. Currently at the time of recording only 96 people have solved it, but a few more might come in before the end of the game. It says there's something suspicious about how account logins happen on this server. We're giving a link here, so I will fire that up in another browser, another tab here, and it says Speak Plainly, a new kind of secure website. Create an account and log in with an existing user. It says, secured with AES. Speak Plainly is built with military-grade AES encryption. Your account is unhackable and your data is secure. Create an account to experience this revolutionary technology for yourself. Looks like there is a lot of boilerplate stuff in here. Military-grade encryption, 128-bit AES ECB. Ooh, that's a good telltale, because now we kind of know what encryption scheme that's really working. We encrypt your login cookies so hackers can abuse them even if they're lost or stolen. We append a secret token before encrypting. Even if our AES keys are stolen, hackers cannot forge credentials. These social media links are fake. They don't go anywhere, so let's try and create an account. I'll just say, please sub, please sub, and there we go. Okay, I don't need to add that to last pass. Thank you. Soon, this page will be full of lots of interesting features. If you find a way to hack our strong token, submit it via the form below to get a flag. Okay, and we're still talking about AES. So let's go take a look at our cookies, because it did suggest that our login cookies are what is actually being secured here. So I don't see anything particularly set just yet. Maybe that's just managed our cookie being weird. That's fine. We'll go ahead and work with it through Python, because that's the best way to do things, in my opinion, anyway. So let's go make a script to do this. I'll make a directory for speak plainly. And let's get started with a simple AES script. I will drag this down so you guys can see it, and we'll start to code. So I'm going to have my shebang line start us off. I will import requests, because we are going to be interacting with this web server. I'll go ahead and create a session. So I'll do a little s request session, and I'll close that out at the very end of my script. So let's go ahead and get this URL. That's this guy here in my case. Make that a variable so I can use it later on. So let's get our URL. And there was a login page. So if I clear all of my cookies, I want to make sure I can still work with this. Let's see and kind of monitor, okay, what really happens in the network tab when I create an account. So username, password, create an account looks like we post to register, post request to slash register with the parameters username and password. So that's pretty easy. Okay, let's do that. So let's create a data dictionary. And let's say the username can equal our username. And let's make password also be a string of just simply password password. There we go. Okay, now once that's returned, let's save that actually as a variable so we can make sure that we've got it. Let's print out that our text, make sure we can see it. And I'll have this go to a second tab. So I'm going to use shift Alt and to and sublime text. And I'll make that page noted as HTML. So we can read it nice and easily. Okay, now it says did not log in for me. Oh, duh, because I didn't actually log in. I just requested the page. Let's go ahead and post with our data. Sorry, guys. Still early in the morning. It's not really I just kind of woke up late because I stayed up late, like you do on the weekend data and data. Now let's post that guy and okay, it looks like great, we have a response of okay. So can I now get that original page s dot get URL following that. Great. Doesn't seem to have me be logged in. It doesn't say welcome username. That brought me to profile. So maybe I need to be in profile for that to make sense. Let's check that link profile. redirected and see if that logs me and find there we go. Welcome username faces in the way. No, we're good. Okay. So now that we're logged in, let's go ahead and take a look at those cookies because it did mention hey, perhaps we have some available to us. There we go. We have an auth token. Whoa. Open another tab there. Off token that looks like hex. So let's get just that off token. Print that out specifically. There we go. That's our long string. And how long is that? So that's 64 characters long right now. But if I were to change some field or some entry, if I were to change, let's try the password first of all, I could be like anything and I'm assuming I can create multiple accounts over and over and over again. And it shouldn't matter. Okay, so that has the exact same string. If I were to change my username, please subscribe. Let's send that guy along. Oh, and that is still 64 characters, but it's a different string. Okay, so I'm curious if it's AS ECB encrypting the username, well, then we could maybe find where we're actually getting different block sizes. So the way that AS ECB works, it's because it's block cipher mode of operation. Talk about electronic codebook ECB the simplest method of encryption with this block mode is electronic codebook that will use the same key to decrypt every single block. The problem with that is that you could essentially still be able to recover some information, especially in this case, because we can control what it is encrypting, maybe we could determine what the actual key or the rest of that actual strong token that might be included in the auth token for us, what that could really be. If you check out the hints for this challenge, I described that a little bit, your username, the secret strong token are the only components of the encrypted cookie. How long, how does the length of your username determine the length of the cookie? It's possible to guess the strong token one by the time because of how AS ECB works, the strong token itself does not contain any semicolon characters. Okay, so with that in mind, we know maybe if we were to have a longer block, like let's say please subscribe, please, does that make it a little bit longer for us? It does. So now that length is 96 characters. So we should determine, okay, where is the threshold? Where is the sweet spot that we're going to get another block that would allow us to actually know, okay, where are we going to end up filling what the AS ECB auth token encryption there is with other padding, or with some of the potentially useful information that we might want to leak out, because we can use a technique to determine what else could be in this. So let's determine how long it takes for our data to grow. So if I send a username of just the letter a, that's 64 characters long. So let's try username can be a or or that should work just fine for now. And let's get username length to equal a. Let's also get off token length. So let's grab that as an actual variable. And now let's determine the length of the auto token. So off length can equal that. Let's start off with off length being just 64. So we could start kind of a loop and be able to add things on to this. So let's say while off length is not equal or is equal to 64. Let's keep adding things on to our username. So in that case, we could use username username length that was still a bad value. And that's my bad off length or username. And let's do a for I in range of how far do we want to go? We can just keep adding to it. Well, let's let's use this iterator. So that way if we actually have a good number, so let's do like 20 or so. Or, yeah, that might that might increase just fine. Our username can actually equal a times the length of our iterator. So I guess we don't need to find it up there for us. But now we're getting the username length, supplying that as our actual username, the variable that we're going to be controlling and determining the length of it. So we'll post to the page, the register, get the profile, which I don't think we need to do because it'll create the cookie just fine for us. So we can say print username, length, blah, blah, blah. Let's let's do some format stuff. I should switch Python. I should switch sublime text actually use Python three so I can use some good display in sublime text not have to switch back and forth. It's using Python two right now. And I know everyone hates me for that. I'll do that after this video, I promise username length yields off length. And let's format those to include our username length and off length. Let's try that. So once this gets past 64, we could determine, okay, that will break out of our while loop and it should stop running for us. Ideally, let's try that might be still because of its four length. So we can just add an actual if statement here if law off length does not equal 64 break out of that loop. Let's try that guy username length zero yields length 64 1516. Okay, so username length 16 yields off length 96. So that means that the beginning of a new block will start at the 16th character. So let's actually determine what those blocks are and what they look like. Let's make a function, let's just kind of encapsulate this determine block length. That's not the right name for it. But you know what, that's totally fine. We essentially know, okay, now that 16 is the sweet spot, we can kind of put that idea away. But let's also determine what the blocks are. So let's get blocks with an auth token passed in. And we'll do a four I in range of, let's make a list of this new, or doesn't matter, zero length of auth token. And how big were those token how big were those pieces? Well, if it's moving 96 and 64. So 64, the difference between 96 and 64 is 32. So 32 characters or 16 bytes, right? So we want to actually be increasing that by 32. Because we know that our block size, let's say that's it's it's 16 bytes, but it's 32 characters, right? So 32. Let's do block size. And then we can carve out the pieces of the auth token. So it's new dot append off token index at zero and or I in that case, because it's iterating through it and adding on the block size. So we get those chunks. Let's return new. So with that, we could potentially view if we run our determine auth length, let's have it display out the blocks as well. So let's do print of get blocks. That's going to return a list for us. So let's make that joined with new lines. So get blocks of auth token. And let's also print like a little banner to know that we're on different output there. Now let's try and determine the block length and see if it will show me the blocks with it. Variable length off length is not yet assigned. What? Is it because it's a global and you're worried about it? Just slap it into that function, then that's fine. String object has no attribute get blocks. Oh, I forgot to actually run join my bad. Okay, now that should run. No, off length is not defined off length, it should be off token. I'm sorry, I'm falling apart guys. Okay, so now we're able to see all of the username blocks that are going through here. If I supply just one block, it always seems to be adding a second one here. It's not just one block. So there are two seemingly that need to be in place. Because when I use username length 16, that creates another block, you can see I have three here, rather than just two. So what we could do is we could check if we supplied A's here. Or how should that work, right? I always get confused with this logic. If we were to supply 15 A's, then we know that this block is all the way filled with A's until potentially the last character or the first character in what would come following that. So we need to grab this at length 15, because we know that the very, very first character of what's following our username is actually being included here. And we have our 15 A's. So if we could send something else that with that length of 15 A's, and then a potential possible character, if something matches that, then we know, okay, we have found the correct character, the correct first character of the auth token. And we could slowly shift our A length back, so we can leak out more information. So let's try to do that. Let's grab the auth token with a username of, well, we need we need those 15 A's, right? So data, let's make that one more time. username can equal a times 15, or let's make a variable for that. So start block, gap, not that gap can be 15, right? So then we'll leave one position available for the first character to actually be able to be filled in. So if I were now to get the password in there, a times start block, oh, no, that should just be literally anything the password doesn't matter. What we could do is we could send that we could do our simple, how do we use that syntax over in the function here, I'll just grab that again, just to reuse some of the code that we already have written. And I don't need to worry about that function at the moment. Let's grab the blocks of our auth token. Excuse me, get blocks of our auth token. And let's just call that blocks. So knowing that the second block in our case, or index one, because Python is zero based, then that's going to be where okay, at the very, very last character, we should have the character of the encrypted. And all of these are going to be encrypted different ways. So it's not it's not just going to be hex a five, it's going to be something that would be filled in with however else the ECB block is encrypting it. So with that in mind, we can use this first one with nothing in it as sort of a test to say, okay, that is what the first that is what that block should look like. So let's try that. Let's go ahead and determine what that block blocks one is. So let's print him out. Just to see that. So this is what block should be equals that. And with that, we can now start to loop and determine, okay, what potential character after our a is is going to be the character that matches this block. Let's try to import string or let's do from string import printable printable. There we go. So now let's try and see how we could get a hit. Let's do a for C in the printable. Let's also modify our data here. And let's include our C in the username. And that way. So 16 is going to fill that block, we're still using the start gap in there. So that 15 will fill and then we'll test what is that last character until it matches the block that we expected. If block one is equal to what the block should be, then we can print. We got a hit. And let's display out what that block is. So let's do block one. And let's also say character C. How about that? So if I run this? Why is that wanting Oh, prints not going to return anything blocks that. So let's print out our blocks one block should be that guy block is not defined, it should be blocks struggling today. So now it's looping through things. We could be printing that out to see if it actually is working. Let's try and do that. Print trying character. And let's use C. Let's also print out our blocks with that. And that didn't get any hit whatsoever. Print new line join of our blocks to run that guy now. So now we're trying all these characters. And that is giving me three blocks. Because I have 16 being filled. So why is that the case? I should not be seeing three blocks. Oh, no, that's right, because there are three blocks when I enter 16. But the block that we care about is not going to end up being that one. It's going to be the first one. So zero when we use a block size of we're just using the first block. So let's try that. So one a three five five is what ours should be matching. And we want to find a character that matches that. So going through uppercase letters now. How do we look? Oh, we got a hit. Okay, so the semicolon should be the first character block is not defined blocks. God is gracious. That's fine. At least it at least it broke. That's what we wanted. So let's break in that. So with that, we can start to build out what we know. So known data can be kind of defined up here as the list of our string. So we now need to shrink our start block gap. And subtract out the length of what we know, and then add in what we know, join known data. And then we need to use that as the baseline to make sure, okay, we're getting the next character out of that. And still display that with it. So rather than the original start block gap, we will subtract out what we know. And we'll add that in to what we know known data dot append, see, we'll break and we'll keep looping this over and over again. In our known data baseline. So we'll make one request first to see how long or what should that block look like to begin with. And then we will go ahead and determine the next character that it should be. Let's see if we get any output with that. Let's try a character or trying string now with our known data and the string that we're adding in. So let's start that in a loop. Let's do a little while one. And we'll break whenever we find a new character. So that should look now getting with the start with the semicolon. And it's still the first block that we care about, right? Because we haven't moved through another block yet we haven't gone past 16 characters just yet. But we might we might soon. So let's see if that gets another hit at all. Oh, I got a hit. I got another semicolon. That's weird to me. Why did why did it get another semicolon? Is it another semicolon after that? See if it gets just another one. It got another semicolon. Okay, so something is clearly wrong. Maybe, unless it's just a bunch of semicolons that are being used. Should I be getting a different block to test or my start block gap minus so 15 minus the length of everything that we already know. It's just getting semicolons. Let me stop that. Be killed by the adding in all that we know. So 15 minus one plus one. Oh, I need to subtract one, because that needs to still be that still needs to leave a gap. So don't add in the joint data. Because even if we had two missing spaces, it's still going to be the first two that are working. So it'll add in the known data with what we have after it. Will that work better? Now let's see if we get just another semicolon. Nope, nope. Now we get an I. Okay. So now it should be working. Right? Let's see if it gets another character. Got a W. All right, I'm going to pause the video and let this work fill out as much as it can. Okay, we're still cruising along. I had to reset it for just a second because it had a little connection error. And I just made this a kind of multi line one with three single quotes and then added it in. With that, I was able to add in everything that we know thus far. Okay, so now it's having an issue because we're getting some longer strings. With that, it thinks that it has leaked everything out and it's just getting everything properly, but it's not. So let's p kill that. That's because we're entering another block. So we should probably maneuver to the second block rather than the first for what we're actually looking for. I think we had up to the where was when where was did we start? Did we actually get the hit on the less than symbol? Yes, we did. And then the zero started to roll through. So we got a lot more once after that. How long is that string? If I were to paste that in and look at it, that is 16 characters long. So we know that's filling a full block. What we should do is actually go right ahead then and change our start block up to 31, because we know that 32 would end up being the start of the next block. So 31 is actually going to leave us that gap and will let us remove more of our a's to leak out more information. With that, we need to switch to the block should be the one following it. So let's change that blocks there and switch that. So now we should be able to leak out more there and some strings are being funky. Got a hit though, I guess a blind text wanted to change that in a strange way. Can I set a syntax to anything? Maybe that's a little bit of a better display. Okay, so we got a less than and lowercase r. Now what else can we retrieve? Wow, it syntax hiding is killing things. Can I turn off syntax hiding in sublime text? Is there a none option? Not really? Diff? That's fine. Okay, so now we're getting stars over and over again. It seemingly there are multiple stars. Let's see if we get another that might just be padding, which means that we've leaked out the entire strong token as we needed it to. Yep. Okay, another star. I'm going to stop that script then because I feel like we've leaked everything out that we need. So this should be our strong token. Let's just kind of take note of that here. Strong token, uncovered as that. And it says it doesn't need the semicolon for according to the hint, right? That just might be stopping it. So let's try and submit that and see if it will give us the flag. Did we get it properly? Yes, we did. Okay. So that was that technique. The whole point is to determine the block length. And see where you have the sweet spot of where you're going to start another block that's going to be used with the AS ECB encryption. If you leave a gap or if you leave some spot open for the proper next character to fill in that spot, you could use that as a baseline to test against your own permutations and your own iterations, allowing you to go determine what is the next character going to be. If it matches that baseline block that you captured, then you know, okay, that's the right character from the original actual data that's appended in there. So we could shift our A's and kind of deep decreasing the value until we determine all of the potential characters we might want leading up to that. So that's the technique with AS ECB having something appended on or added to the data that you can control. And with that, you are able to potentially leak out some more information. So okay, enough talking for this video. I hope that was okay. I hope that was good. I hope you learned something. If anything, we just got some good dirty Python scripting in. But I hope you guys enjoyed this video. If you did, please do hit that like button. If you didn't, don't maybe click the dislike button like twice. So I know that you didn't like it that much. And then maybe hit the like button afterwards. It's up to you. Leave a comment, subscribe, do the whole YouTube algorithm things. I would be super duper grateful. There's a Discord server, if you want to hang out with some other CTF players, programmers, hackers, tons of smart people in there, much smarter than me. Love to see you guys on Patreon, PayPal, if you're willing to support LinkedIn, Facebook, Twitter, Instagram, all the other social media things. But okay, see you in the next video. Love you.