
So, uh, I hope your brain's feeling full. Um, I know mine is. I'm a little tired, but we got one more great talk here. Uh, it's, uh, my introduce, it's my pleasure to introduce Wade King, the maestro of encryption, mischief, and true legend in finding vulnerabilities that keep security folks up at night. Uh WDE's research into CBC padding oracles is so cutting edge, rumor has it his slides have their own NDAs. But uh today he's lifting the curtain on with CBC padding oracles in 2025, promising new insights, a few scary stories, and hopefully no existential crisis for anybody running legacy systems. So let's wake up stretch a bit, welcome Wade King to wrap up our cyber security venture.
Let's give him a big chair as much as talk as for surviving the conference to the very end because I am barely doing so. Wait, King.
>> All righty. Hello, Bides. Thanks for sticking around. I know there's beer waiting and this isn't a talk about AI, so I really appreciate the turnout. All righty. Um, yeah, I'm with Packet Labs. I started this journey about a year ago on a bug bounty project I was doing when I was testing a online gambling platform. And I had kind of, you know, poked around the platform. I hadn't found too much that was super interesting, but there was one thing that really bugged me was the password reset token. And I noticed when I sent the password reset token at the same time, it would start with the same characters. So I knew there was some
sort of structured data underneath it that was based on time. And so I started playing around with it a little bit. I was like, "Okay, what encryption scheme are they using? There's no way they're using CBC and there's no way it's like a padding oracle. Those are like dead. No one uses that anymore." Um, and eventually I just decided to try it on win whim. And sure enough, it was vulnerable to CBC padding oracle. And then over the next couple months, I developed the exploit a little bit more and got a full account takeover on this gambling platform. Um, but I'll run you through those slides later. Uh, so that's a little story about me.
And uh what we're going to be going through in the talk, first we're going to talk about CBC. So what is CBC? Um cipher blockchaining, it's just like a mode of uh symmetric encryption. We'll get into all that later. It's going to be a little bit technical, but we're going to get through it together. It's okay. There's going to be math and stuff. I know it's scary, but um yeah. Anyway, so we'll get into a couple a bit of my research on some of the new techniques on getting a padding oracle without a padding error and recovering the first block and as well as the initialization vector. And then we'll talk about why CBC is still in use today
and why it's all over the place. Go through a real world world example for the gambling platform and talk a little bit about some of the other places I've seen it. And then talk about defense and QA and maybe do a live demo if we have time. So to talk about CBC and symmetric like uh the modes of encryption we have to first talk about AES which is sort of the building block that a lot of these modes are based on. It provides symmetric encryption and it's great but it only encrypts fixed blocks at a time. So uh normally 16 bytes 16 characters. So if I want to encrypt anything over 16 characters, I need to find some other
way of doing it. And that's where all these modes come in. Um, and a quick note about padding. Um, in the scenario where you have less than a block of text that you want to encrypt, you just need to add content, but you need to add it in such a way that the server that's decrypting it knows what the padding is and so they can remove it, separate the padding from the plain text and etc. So key thing to note is that the last bite of the last block will always say the number of padding characters in this particular padding scheme. So CBC is one of many encryption modes. There's lots of other ones. CBC seems to
be pretty popular for some reason and we'll get into that later. Um, and it's built on top of AES or you could even use dees as well, which is more of an outdated cipher system that's worse than AES and may have back doors or something like that. Um, but essentially what happens is that after each cipher text block gets decrypted by as I'm going to use my laser pointer here so I can point things and hopefully this works. Okay, so this block cipher decryption here that's where AES is. So the cipher text gets decrypted and you get an intermediate block here that gets combined with the initialization vector. Oh, or in later te times it gets combined
with the previous cipher text. So if you don't have previous cipher text, the intermediate block here gets combined with the initialization vector to make the plain text. And that's sort of all it is. The reason why we're going with this whole CBC approach is because if you try and just you know get rid of all these links here you just do cipher text down through as plain text blah blah blah that's called uh electronic codebook mode and that has some issues because you know the other way the same plain text always maps to the same cipher text. So if you get something like an image that has a lot of repeating plain text and you try and
encrypt it using electronic code book then you'll still be able to see what the image is because you'll have all these you know you can see when there's all zeros like when it's white it'll always look the same like the image will look a little different the text the colors will be off but you can still see the edges or any text or or anything like that. So that's why we want the same plain text to map to different cipher text. Adds a bit of extra privacy. The key observation here with this link here is that modifying the cipher text creates a predictable change in the next plain text block. So if I modify this
bite of the cipher text, it will modify this bite of the plain text in a pred in a predictable way. But the trade-off is that it will scramble the plain text that it maps to because if you make a small change in the cipher text, AES is such a way that it'll kind of propagate that change and scramble everything up. And so you won't be able to modify this in a meaningful way, but you can modify this in a meaningful way. So let's walk through a practical example here. So we have uh we've kind of simplified it down to eight bytes. So this is kind of more dees than AES. Um which I've seen in a while a lot
surprisingly even though it's less secure. But um we have the three cipher text blocks here which maps to let's count two three and then we have this padding here because we have three bytes remaining that need to be filled. We have 3 three three and that's the standard uh padding procedure. So if I modify this cipher text then it totally scramles this here but it changes this from a three into a two. Now when your decryptor is stripping that padding it does the validation on the padding. So if it say sees one two here instead of two two it's going to throw an error and say that's that's invalid padding. So this is where a classic padding
oracle comes in. It comes down to figuring out the difference between a data error and a padding error. Once you know you have a data error instead of a padding error, you can use that to figure out exactly what bite is at this position. And then you can use that to slowly unravel the plain text and decrypt it and forge new plain text which we'll be seeing later. Um okay. And so you know modifying that gives the two which gives invalid padding and we get a different padding error here. So on the previous slide because it was just random bytes here we'd get a data error. Modifying this 91 to a 90 changes the padding makes it a padding error.
Now the trick here is that we keep modifying this last bite until we get one here and then we get a data error instead of a padding error because this is valid padding. So when we're looking at our server responses, we take the last bite of the second last block that we're looking at and we just keep keep on modifying it. Keep getting padding errors and then we get a data error and that lets us know that there's a one at this position right here. So we've figured out that 93 maps to a one in the next plain text block. And so what we can do with that is because we know 93 corresponds to one we
can take 93 x or with one and that will give us zero. Now why do we want zero? We want zero because it gives us a nice way of writing any bite to that. So because we know 92 is zero there. Say we want to write a five. We just take 92 exor with five and then we get a five in that plain text block. And this is what allows us to build our attack and make it go further. And the other reason why we want zero is because if we go back to this intermediate state here, this is 92 here and this is zero here. So that means that this must also be 92 because
any number xord with itself is zero. So we're slowly uncovering this intermediate state as we go through the padding oracle attack. And then at the end once we've figured out what this intermediate state is, we can combine it with the cipher text in order to get the plain text.
So this is just going through that approach. We've discovered that the intermediate block is 92. And so what we do is we take 92 with the original cipher text which is 91 and that will give us the plain text bite which is 03 which is what we set it to. So 91 x92 is 03 and that all checks out. So we've just uncovered the first bite of plain text in this block. Now on to the next bite. This is where it was important to know what zero was. So because we know 92 is zero, we want to make sure that we can get a valid padding. So 92 xor 2 is 90. That will put the two there. And then we can start
to work on this bite and keep guessing until we get 22. And that will be our valid padding. So we get an a data error instead of a padding error. And that's our key that we've just written a two into this second to last position here. And we just rinse and repeat the whole procedure again. We get you know uh 11 x2 is 13 and uh so we know the plain text is 13 combined with original cipher text which is 10. 10 xord 13 is three, which is again the other padding bite here. So that's the second bite of plain text that we're eventually encoding. And so this is the whole procedure kind of in a nice little gif. When it's green
there, that's our padding error that signifies to us that we've got uh a data error instead of instead of a padding error. So we're able to use that to find the intermediate bite there which is from that uh intermediate block. And we just slowly repeat the process, uncover it bite by bite, uncover the plain text, and then we're good to go. We can decrypt whatever we want. Um and so this has a a small problem though because we rely on knowing the difference a data error and a padding error to know when we have a particular bite at a particular position. We're totally reliant on that fact right now. If we can't tell the difference between
a data error and a padding error, then we don't know when the the plain text has a known value and we can't determine the intermediate block and ultimately the padding oracle won't work. But this is where a new technique comes in that I've been working on. I I think it's new. I haven't seen it documented anywhere else. Um, but essentially what happens here is that if the server stops reading the plain text at some point or doesn't validate it, then the server is vulnerable. It's uh, you know, if they're if you're using something like JSON where you know a JSON parsing library takes the whole plain text and sort of validates it, you know, make sure it's valid for JSON,
then it probably won't work. But most of the time a lot of these CPC uh servers that are using CBC are just using uh little horizontal slashes in order to terminate the data they want. So it's not very rigorously validated and it'll kind of just stop reading after it gets all the values it wants. So here we have no difference between a padding error here. So what we've done here is we've taken uh the last bite of the second last block and modified it to scramble the padding. So this will have an invalid padding and it'll say you know could not decrypt token blah blah blah and now we're causing a data error just by chopping off most of the cipher
text and that it will also give us the exact same error. So in a traditional padding oracle we'd be hooped at this point. you know, we wouldn't have any way of pro proceeding with the attack. But this little server that I've made, you know, it only thinks about the first part of it. So, it's only going to, you know, count all the parts up to four and grab those and it's just kind of going to kind of ignore everything else. So, what we can do is we can just put two cipher texts back to back and it will still be valid. There will be maybe a bit of padding in the middle, but it should be fine. And
so here we have cippher text one and we have cippher text two and we've modified this bite here which would normally give us a data error but it instead gives us a success because none of the data over here is being validated. So knowing that however we can still cause a padding error because the padding is still validated. So what we we've essentially done is we've reduced the problem to instead of having a data error and a padding error now we have a success and a generic error. And that generic error is our signifier that we've caused a issue with the padding. So this is sort of a bit more of a like what it looks like under the hood. So we
have the data that's validated by the server here in green and then we have the padding of that and then we have the start of our second cipher text and all of this is unvalidated. And at the very end we have the padding that's validated by the encryption library. So what we essentially do is we just you know put that uh when we put that second cipher text there, our padding oracle causes the uh block to be scrambled in that unvalidated section, but it causes a predictable change in that validated uh padding at the end there. And so this actually works really well with existing tools. So here I'm using Padre and the regular padding oracle attack fails. You
know, there's no there's no way for it to detect a uh fingerprint. It can't do the padding oracle. But if I just take any valid token and I put it at the beginning like as a prefix for this um uh dollar sign there, then what will happen is the padding oracle will succeed no problem. And I this is the tool out of the box. I haven't had to modify it whatsoever. So we kind of get into an interesting situation here. The whole CDC security depends how the plain text is read. Like by the time the uh like the developer can do all their due diligence like they can handle the error messages correctly. they can, you know, make sure it's it's
valid data and everything like that, but if they're just not looking at the rest of the plain text, then they're vulnerable, which is kind of weird because the CBC decryption process has already done its job. It's just how the developer process the processes the data afterwards that makes it vulnerable.
Now, that was the first of one new technique. Another new technique I'm going to be talking about is the first block attack. So, typically in padding oracles, you can get any intermediate block you want. But in order to get the plain text, you have to combine that with the previous cipher text or the initialization vector. And normally the initialization vector is just hardcoded on the server somewhere. You can't you don't have access to it. So you can get the first intermediate block, but you can't get the first plain text block. But if the plain text is random enough and you know the range of that randomness, so say it's random hex characters, we know we can get enough of those
intermediate blocks and we know that the it's going to be between zero and one and a and f. And so what we can do is even though we don't know the initial initialization vector or the plain text, we can take a guess at what the initialization vector bite will be. We'll just take a random guess, any any bite, and then we'll collect all of these intermediate block bytes. And if it's rounded enough, we should be able to get enough in order to to eliminate any wrong guesses for the initialization vector. So I'm going to start with like one and then I'm going to run through all of my intermediate block bytes and it will eventually make that wrong guess
fall outside of this range. And so I'm using that constraint to eliminate all my wrong guesses.
Once you only have one, that's obviously the valid initialization vector bite. So what we do to get the first block is what we can do is, you know, if you're doing this by hand, you'll be able to figure out what the the intermediate block is just through your calculations. But if you're using a tool, it helps just to put all these zeros in front because when it calculates the intermediate block, it will combine it with the previous cipher text block. And so if you have a bunch of zeros and combine it with the intermediate block, you're still left with the intermediate block. So it'll just output it right to the terminal there. And it's nice and
convenient. So that's how you get the intermediate block, which is right there. And I like to use a SMT solver for this just because, you know, it's they're pretty nice. They're if you don't know what an SMT solver is, it's kind of like black magic. Like it if you remember trying to do quadratic equations in high school, you know, this is what it's made for. It can, you know, it's essentially an equation solver. So what we do, I have my list of intermediate blocks up here, and then we add the constraints in here. We know it's a lowercase hex character. So we know that it has to be between 0 and 9 and a and f. And so we
just run through all of these and we find all the solutions that make it match these constraints for all of these intermediate blocks. And you only get if you get enough intermediate blocks. I had about maybe 25 or 30 here. You'll need more depending on how large your range is. Like if you have asy characters, you'll probably need the order of like 100, 150, something like that. And you find the solution just using the SMT solver, which is great. And that matches what we have for the initialization vector there. So we're good to go. We we know what the initialization vector is. We know what the first block is, and we can just plop that in front of our cipher text, and we
can use that to decrypt what the plain text is. Now, one caveat with this is that the server will still see random bytes in front of this because it'll be taking the actual initialization vector on the server and combining it with this one we've put here. But that's okay for this. We're just trying to read it right now. Now, where this might come in handy is if the initialization vector is used on multiple like session cookies or anything like that and you have one that's random and one that stays the same because one caveat with this is that you won't be able to perform this attack if you don't know what the underlying plain text is or if it's not
changing enough because then you can't build out all of these intermediate blocks and you can't plug them in SMT solver. and you won't be able to solve it like that. But if you solve it somewhere in the application, maybe you can use it somewhere else and use that same initialization vector to grab the first first block. And sometimes developers might use the AES key as the initialization vector and then it's kind of game over because you can write the whole the whole string and and you're set. So why is CBC still in use today? It's um well AI suggestions maybe this is uh just on Google Gemini um I was just asking you know create a server that
validates a token with AES and you know suggest CBC seems to be popular but with AI agents uh lately chat GPT has been correctly suggesting AES GCM which is a better alternative because it provides uh validation on cipher text which prevents from tampering and and everything like that. Also, it's just alphabetically first. So, when you get code suggestions, you pick the first option, and that's CBC. So, silly reasons to get pawned. Yeah. Now, I promised uh to talk a little bit about this gambling platform. So, this is the password reset token, and hopefully I haven't disclosed what this gambling platform is because I could get in trouble. Um, but yeah, here is the password reset token. This is what
wouldn't change if you sent two password reset tokens at the same time. And so it was actually at first vulnerable to a classic padding oracle attack and then after I reported it, they fixed it and now it's just vulnerable to my double cipher text attack. So this server is still vulnerable by the way. you can still take over people's accounts on this gambling platform. Um, >> yeah, exactly. So, here we are decrypting it using a double cipher text attack with Padre and we get this user ID. So, all the other parameters were timebased. So, if I sent two tokens at the same time, all these parameters would be the same even if they were sent to different users. It
was just this user ID that was different. And what's more is that this user ID seemed to be like increasing incrementally. So if you know roughly when the user signed up, you can just guess this and forge the token, reset the password like that. But we're going to find a different way of doing that, which is also pretty cool. So the key is to send the victim and the attacker the token at the same time so that you can get rid of all of those other variables. you don't have to worry about them because you'll have the exact same copy as them. The only thing that you need to figure out is the user ID.
And so we can do that using Turbo Intruder. So, uh, it was a little finicky to do, but you try it, you know, a few times and eventually you get two that were sent to, you know, my email address and the vulnerability reporting email address at the same time. And so now we're ready to start exploiting it. So, we need to find the victim's user ID. And when I was looking through my Burp history, I was like I was trying to find this ID anywhere on the web application. But fortunately, I had gone into my Gmail when I was, you know, in my Burp browser and I got a hit for the same user ID, but it was in the reply to
email address in the password reset token. So it had a bunch of other timebased variables that were all the same between the victim and the attacker because the tokens were sent at the same time, the email was sent at the same time, etc. It was just that the user ID was also in here. So we need a fast way of brute forcing the user ID. So what we can do is I noticed if I send an email with a link to this reply to uh email address, I get a DNS call back only if it's a valid reply to email address. So, I just uh you know set up my own SMTP mail server and I just started
spamming them and trying to get through as many user possible user IDs as I could about I I was able to get about 300 emails per second which is a lot and uh I had to set up my own because obviously Google wouldn't let me send 300 emails a second on their servers and they'd you know ban me and everything like that. Um, and I did get banned from or like kind of blocked, I guess, on the receiving side, but that didn't really matter because they were scanning the emails anyway. And so, they'd still do that DNS hit even though I was getting blocked, which was pretty neat because I could just, you know, keep on hammering
them and I was getting what I wanted. So eventually after a couple hours of hammering their inbox, I get a call back for the vulnerability reporting address there. And so I just take that, which is the user ID of their account and I plug it in and I forge the token and then I'm able to get their password reset page. And I can't actually show the screenshots of the account because that then you'd know what the gambling platform was and I'd get in trouble. And yeah, now talking a little bit about defense, the key takeaway here is not to rely on hiding padding errors. You need to have more defense than that because, you know, it's there's quite a few ways to
get around it. Um, so you want to switch an algorithm with tamper protection. So something that signs the cipher text, so you can't fiddle around with the bytes and cause an a change in the plain text. So something like ASGCM is a great option. It's faster for one, it's more secure. I don't know why people aren't using it in, but uh if you really want to stay with CPC, you can switch to uh validating your cipher text. So adding an HMAC which essentially you know adds uh an authentication. So if anyone tries to fiddle with your cipher text it won't be valid. The HMAC will fail and you'll know someone's trying to attack you. Now
I'm actually going to do a live demo before we do Q&A. So, I have this server set up here, which is kind of the same server from the slides, and we're going to run through the example. So, I'm going to show you what a a data error looks like first on this server to let you know that they're the same. So here I've chopped off the first block which will you know give invalid data and will cause a dating error data error and you'll get this you know could not decrypt token blah blah blah and now the padding error will be the exact same although I might have gotten these the other way around. Oh yeah this will this will be a padding
error because the last block is missing so the whole padding section is missing. So it will the server will complain that oh I got a different thing there. Okay. Uh oh I didn't copy it correctly that's why. Okay.
No. Okay. This was working just a second ago.
So from there
and I'm going to grab the first one as well. Hopefully that should work. No. Okay. Well, just pretend this says the same thing. But um so basically what we have here is if I try and attack this using the typical Oracle padding attack, it won't be able to detect the difference in the padding oracle. Uh but if I add this just one more time at the beginning
the injection point here is is at the dollar sign. So if I just add that there then it works fine and I can decrypt it because it can tell the difference between a success and a general error. And so it thinks it has its padding error which it kind of does. Um, and now what I can do is I can forge this. So if I go back into my command history here. Perfect. So yeah, I'm just going to write my role to be an admin and I should be good to go. Grab that and cat txt. Grab that. Put that there token. And I'm an admin. There you go.