 time to get started. So how's everybody doing? Yeah, good morning. All right, awesome. So I am here to talk to you guys about the time I use Ruby to crack my Reddit password, kind of. So my name is Haseeb Koreshi. I'm a software engineer at earn.com and I am going to tell you guys a story. So I used to be addicted to useless websites. I still am, but I used to be too. And, you know, I'm sure you guys know what this is like. Here's just an artist depiction of what a useless site might look like. We all have our own poisons, you know, you probably have something like this. Also I should note that I have no self-control to speak of. So that's pretty bad. So this is actually a, it's kind of a nightmare, but this is actually, if you guys remember your English class from like high school or something when you read the Odyssey, there's a famous story about when Odysseus tried to maintain some self-control by tying himself to his own mast. My problems are not quite this severe, but they're kind of similar in their own way. And so, you know, keying off the story of the Odyssey, I decided to use that ancient psychological technique that Odysseus used, which is locking himself out of his online accounts. What he did was a little bit different, but basically, I feel like it's kind of the same, right? Basically analogous. So let me tell you exactly what I did to try to spend the last time wasted on, on Useless websites. So on most of these websites, you have some kind of password system, and obviously you can change your password. So what I did is I typed in just some random gibberish. So I just, I just grabbed my keyboard and started smacking away, came up with something sufficiently weird, and entered that in as my new password. Okay. And of course I don't remember what this string is, just a bunch of random characters. I hold on to it, though, and I also changed my account recovery email. So now I have a new password that I have no idea what it is. Also, I've changed my account recovery email to a throwaway email that I just created that I also threw away the password to. So there's no way to get my account back, save for this password. Okay. This password is the key to my kingdom. All right. So what I do is now my plan is to prevent myself from having access to this password until some later date in the future, right? So, you know, for now I want to go into crunch mode, I want to study, I want to practice, I want to do whatever it is I got to do that, you know, these time wasting websites are keeping me away from. So I need some way to receive this password at a later date. Now, unfortunately, normally the way you do this is through this mechanism known as friends. Not, not, not, I figure there's probably some way to automate this, right? You don't need friends. So I go and, you know, use, use the Google to try to figure this out. And I come across this great, wonderful website called Letter Me Later. And this kind of sounds nice. Like, okay, it allows you to send emails at a future date and time you choose, you know, no friends are acquired. This is pretty perfect for me. So, you know, it's a little bit 1995 looking, but that's okay, you know, maybe they're just really focused on what they do. So, so what I do is I go ahead and compose a new email to myself, I create an account. And what I do is I fill in a subject line, I'm going to call it password because it's my password, set a date in the future when I'm going to email it to myself, put in my password in there. And I set it on to hide mode. And with hide mode, what that allows me to do is not actually click on it when I log in to Letter Me Later. And so when it's hidden, I have actually no way to see it until it gets sent to me, right? So I got it, you know, full proof, I know that I'm just an awful human being. And so the only way that I will not access my password if there's literally no way I can get to it, okay? So for a while, I actually used this system to keep myself from wasting time on highly addictive useless websites. So my talk is actually not about, you know, productivity techniques, this is a programming conference after all. So why am I telling you all this? So actually, the story is a little bit more involved. So I use this for a while, it was pretty effective. But later on, it ended up coming back to bite me. So cut to two years later. So two years later, I was working at Airbnb. And I had a job, I was gainfully employed. Surprise, surprise. I know I don't really believe it either, but that's fine. And so you know what that means, as I'm working at Airbnb, they have a huge Rails app, a lot of tests. And of course, that means a lot of waiting. And waiting means that it's time to start wasting company time. So what can I do to, you know, obviously, I can't do work while the test suite is running, that would be silly. So what I do instead is I want to go and, you know, get back into my time wasting activities. So I want to go log back in to this website. So I go back to let him later. I remember that I, you know, I locked my password away, go back to let him later, go retrieve my password. It's been a while, it's been like a couple years, actually. So I guess I kick the habit for a good bit. But now it's time to, you know, go back and get another fix. So when I log back in, I realize that it's still grayed out. That's kind of weird. Because, you know, I think, you know, usually I'd send it to myself like a month later at a time, you know, like give myself a period of time to just focus and then a little bit of time to, you know, get back to the candy. And I realize, oh, shit. I scheduled it for 2018. The last time that I put this there. I guess I didn't remember, but I'd gotten like so annoyed at myself that I actually set like some dates super far in the future, just be like, screw you, Steve, like you need to get a grip on yourself. And I was like, I can't wait that long. This test suite isn't that slow. So I got something, okay, maybe there's maybe there's some way around this. So right here is letter me later. Here's the actual real website. And so, you know, I can log into my account, I can see here, this thing is hidden. There's no way for me to see it. It's scheduled for June. Other than that, I'm screwed. And so as I was clicking around on this, I realized I was about ready to give up. But of course, you got to try a few things first. I realized that there's this, there's a search bar here. Okay, well, what can I search for? So maybe I can search for my name and see if my name isn't there. It's not. So it's not indexing my name. All right, fine. Now, what if I search for a? Okay, so that popped up. Right? So maybe, maybe I might be looking for subject lines, right? And so you can ascertain that pretty quickly by typing password, I can see, okay, yeah, it's definitely it's indexing the subject line, that makes sense. But remember the body of my email was just the actual password, right? So if I search for a letter that's not in password, let's look for E. E is not in there. Okay, what about one? One is not in there. What about two? Two is in there. Okay. So what's going on here? What's going on here? All right, hold on, hold on. Let's, let's, let's think for a second. So I think what's going on here, correct me guys if I'm wrong, but I have a way to do substring queries into my password. I have an oracle. And my oracle will basically give me this query, right? It will tell me if the body plus the subject, the subject was password, the body was the actual password itself, if any of that concatenated together includes any string that I ask it. Okay. So when I realized this, I ran home, I was, I was off work at this once I didn't just run home from work. But I ran home, busted out a piece of paper and a pen. And it was like, okay, let me see if I can figure this out. How can I retrieve my password? So here's the algorithm. All right, let's think about this like, think about a wheel of fortune style. Okay, so I have this, this big one thing and this subject at the top and the body down here. And I don't know any of the characters in the body, but I do know the characters in the subject, right? You can imagine that I have like a word bank. And the word bank is all the letters, except I've already, you can imagine I've already tried the letters password. Right, because if I do a subject inquiry for P, and I find that it returns to I don't actually know if it's true because it was in the body or because it was in the subject, right, the subject would automatically give a hit. But if you think about it, if I try all the letters that are not in password, then I know for certain I've hit a letter that is only in the body and not in the password. Okay, so if I just keep trying letters that are not in the string password, eventually I will make a hit. Once I make a hit, I know I'm in. I have one of the characters somewhere in my password. Okay, on average this will take, actually it won't take over two guesses, it will take so what less, but you can imagine it sort of being somewhere in the middle of the alphabet, I'll try letters until I get a hit. Then what I do is I try to append another letter and do a longer search because I know that, you know, that that letter plus the letter after it will be a valid substring. And so I keep just iterating through every single letter, including the letters in password, because now any letter can be appended to this to create a substring. And I just keep going down one by one until I find the next character. And on average I'll find the character somewhere around the middle of the alphabet. And so then I just keep repeating that. And every single time it's going to take A over two guesses, where A is the size of the alphabet, until I finally have a letter where I can, no other letter works. And if no other letter works, then I know I've fallen off the end. That's the end of this part of the string. Okay, so what that means I have is not the entire string, it means I have a suffix to the string. But I don't know where in the string I started with. So what I can do after that is I can just repeat the process going backwards. Instead of appending to the end of the string, I prepend to the beginning of the string. And I just keep going until again I fall off, going in the other direction, and then I know, okay, cool, that should be my entire string. So if I do this, now if you think like, sort of this illustration is not exactly correct, because on the end there would take A guesses rather than A over two guesses because I have to know I have to exhaust the entire search space and know, okay, there's no other letter that fulfills this string going longer. So two A guesses for the ends, A over two times N minus two for everything in the beginning because they're N minus two characters if you don't include the ends, where A is the alphabet and is the length. So if you assume that A is, if the alphabet is A through Z and zero through nine, all lowercase because that's how you slam on keyboards. Also the password length is 22. Then basically that means to do this entire thing, we'll take about 432 queries. That's actually doable. That's like a reasonable number of things that you can just, you know, you can just do entirely serially through API calls. So let's do it. How does it sound? Yeah. Okay. All right, let's do this. Okay, so here's what I'm going to do. So I'm going to create a letter me now dot rb. And let's go ahead and open letter me now. That is the wrong folder. Oh, it's because I'm in the desktop. Live coding. And let's touch letter me now dot rb. There we go. Okay, so first things first, I got to figure out how I'm going to actually do this, do this querying, right? I need some kind of access to this Oracle. So I'm going to go ahead and build that first. So you can see here that the way this is set up, it's like letter me later dot com slash account dot php and then a query string and it's like qe equals the query, right? So if I change this query to a b, then I can see qe becomes a b. Okay, so obviously this thing has no apis. I'm going to scrape this thing directly. That's cool. I can do that. So let's go ahead and start this. So I'm going to create an api class. And here I'm going to have a URL that I use for the api class. And I'm going to remove this part so I can put the query string in programmatically. So what I'm going to use is I'm going to use the Faraday gem, which is just a nice simple gem for making HTTP requests. And I'm going to have a def self.get method and it's going to take in a query. And what I'm going to do is I'm going to say faraday.get, the URL, and the second argument to Faraday is going to be the query string. And the query string here is just going to be qe should be the query. Okay, so what I'm going to do right now is I'm just going to run this and kind of see if I can make this work. So let's open up pry. I'm going to paste this code in. And I'm going to say api.get. And let's say I'm going to search for the string password. Okay, so that didn't work. And the reason why it didn't work, of course, is because it's giving me a 302 redirect and it's saying you must be signed in to see this page. So obviously I don't have any of my cookies. So in order for this to work I'm going to need to make sure I pass those in in the headers. So that should be easy enough. Just go to inspect. Go to network. Grab any, okay, let's go ahead and refresh here. And we can go ahead and look, whoops, we can go ahead and look and see, okay, we've got this cookie. I can just go ahead and grab it. I will make sure to sign out when this talk is done. That way this cookie is invalidated. But let's go ahead and do this. All right, so cookie equals this. All right, great. It's always good when they're storing the user ID client side. But oh, well, you know, any port in a storm. All right, so first argument is the query string. Second argument is the headers. So I need to provide cookie as cookie. And now if I am not mistaken, this should do the trick. So if I do api.get, hello, boom. All right, this looks like the actual webpage. So this is a 200. And you can see, yeah, looks good. I don't see my name anywhere in there, but I'm sure it's somewhere in there. Okay, great. So that's fine. Now, of course, what I'm getting is I'm getting all the HTML of this webpage. That's kind of not really what I want. I want to know, I want to somehow know, like, did this query return true or false? So the way that I can figure it out is the easiest way is to just look at, okay, in the HTML that comes back, is there some unique string that I can find that will uniquely identify that, yes, in fact, this returned true. So you can see here there are a few things that show up when, like, they're scheduled. I don't think it shows up anywhere else on the page. There's also password that doesn't show up anywhere else on the page. Let's just use password for simplicity. And we'll say, instead of having the self.get, we'll have self.include query, and this will just return get query.include, because I'll make sure this returns a dot body. And if the body of this HTTP request includes the string password, then I'm good to go. And so let's go ahead and paste this in one more time. api.include now, a turns true, and ab returns false. Cool. So I've got my oracle. All right, so now, as I'm testing this, I'm not going to want to use the real oracle because that's going to be really slow. I'll make a bunch of HTTP requests. I don't want to do that to myself or to them. So I'm going to go ahead and create just a stubbed api that I can use while I'm testing, and then I can sub that out later with a real api. So this one is going to have a fake password, which is just going to be some random characters. Okay, great. And then we're going to have a def self.include, same interface that's just going to be fake password.include the query. Okay, and so now I can use the stubbed api. So it's going to be much faster, right? I don't want to make any HTTP requests while I'm testing. Okay, so I need to build that algorithm. So let's go ahead and do that. So we'll have a password cracker, and the password cracker is going to be stateful. It's going to take in the api. So we'll inject that dependency just to make things a little bit easier. So we'll have the api equal that. We'll also set the password to start off as an empty string and we'll successfully find the password through different stages of this algorithm. And I also want to set the number of iterations so I can keep count of that and initialize that to zero. Okay, so what is this actually going to do? So we're going to have, let's call it a crack method. And in the crack method, we're going to sort of go through each of the steps of that algorithm. So if you remember, the first thing we talked about was getting the first letter, right? And I need to like do some logic to figure out what the first letter is. So we'll just do that. We'll say find first letter. Okay, great. Actually let's put a bang on it. So find first letter. Okay, so once I found the first letter, then I can keep building forward appending different characters and seeing if it works. So we'll just call that build forward. Okay, once I've built all the way forward, I've fallen off the end of the string, then it's time to go backwards. Right? So then we'll do build backward. And then at the very end, I'll just return the password. So that's roughly the code that I want. Obviously it doesn't do anything yet. But let's fix that. Whoops. Okay, so find first letter. So in order to find the first letter, I need to know the alphabet I'm working with. I also need to know the subject line, right? Because remember, I'm not going to try any of the characters that are in the subject line, because they don't necessarily tell me the character is in the password. So let's just make those constants. So we'll say subject line equals password.chars. Those are all the characters in the subject line, right, if you guys remember. And the the alphabet, for the alphabet, I'm just going to use the letters. I'm going to use a through z, all lowercase, because I'm going to assume that everything was lowercase. If there was uppercase, this will fail, and I'll try uppercase as well. And I'm going to also do zero through nine. Zero through nine. And then just flat that. And actually what I'm going to do as well is I'm going to shuffle all this, just so that in case, just to kind of make the numbers more round in the distribution in case, you know, the letters are all on one side or something. Okay, cool. So that's my alphabet. I've got my subject line. Now to find the first letter, I want to iterate through each of the characters that are not in the subject line, but still in my alphabet. Okay, so that should be easy enough. So I'll say alphabet minus subject line.eachdocharacter. And for each character, I'm going to look it up in the oracle. So I'll say atapi.includechar. So that's to check if this is the first character. I'll say if that's true, then what do I want to do? If it's true, then I want to say at password equals that character. So I have my first character and then I can return. I can go ahead and jump out of here. And if that completely fails and I don't find anything, I probably want to raise and say I could not find a first letter. Okay, so this should be able to find a first letter. Now all right, cool. Now one thing I want to do is I want to keep count of how many iterations I'm doing throughout this whole algorithm. So to make that a little bit easier for myself, I'm also just going to have an include method that both does all the api calls, but also logs the iterations. I'll say at iterations plus equals one. Great. And now instead of include, api.include I'll do just include there. So that should return the same thing, but just also handle all the logging for me. Cool. So all right, so that should handle getting the first letter. Now what if I want to build forward? So to build forward, it's pretty similar logic, right? So now I'm not worried about the subject line anymore. I just iterate through the entire alphabet. So I'll say alphabet.eachDoCharacter. And for each character in the alphabet, what I want to do is I want to see, you know, try out the current password plus one more character on the end, right? So I'll say query equals at password plus char, just depending on to the end of the current, currently known good password. So once I have that query, I can say if include the query, then I know, okay, great. The password should be the query now. The query is correct. So I'll say at password equals query, take that char on. And the easiest way that I'd like to just do this is do this recursively. And it's actually quite elegant when you do it recursively. So what you do is you just build forward again. And once you're done with all the building forwards and eventually build forward finally terminates, then you just jump out of all the stack frames and just return all the way back up. So because we don't expect the password to be very long, this is totally fine. And this is actually just really nice. This is pretty much all you need to implement building forward. Now to build backward, because remember we have to, whoops, build backward. To build backward, we basically do the same thing except, so we can just copy this code right here. But instead of the password plus the char, we prepend the char to the password. So we just flip this around. We say char plus at password. And instead of build forward, we build backward. Okay. And I think this should basically do the trick. So it's provided that we didn't mess anything up, which is quite likely. But let's go ahead and see. So we can put password cracker.new, pass in the stubbed API so we can see what's going on, and call .crack. Okay. Let's cross some fingers. All right. Let me now see what happens. Oh, was that it? Yeah, that's it. Okay, cool. So that seemed to work with the stubbed API. That was a little bit a little bit underwhelming. Okay. So hold on. Hold your applause. Hold your applause. So that's pretty good. But we like to have some more logging. Right? There was a little bit anti-climactic. Just have that thing just plop out on the screen. So let's let's get a little bit more context going. So we'll put here a cracking password. Beep boop. Okay. Cool. So I like my programs to talk to me and sound like computers. All right. So find first letter. So we'll put found first letter. And we'll put the first letter right there. Okay. So then we'll say puts building forward. Okay. So here it's building forward. Then we'll put building backward builds backwards. And then at the end, it'll say congratulations. Your password was found in at iterations iterations. Okay. And then basically as I built forward and built backward, I want to always mention once I once I say, okay, great, your your password is elongated. Let's just print out the password. So we'll just put at password every time that happens. So cool. Now one thing before we before we actually run this, you know, we're all engineers here. We noticed there's some code duplication. Let's let's refactor this thing. Let's dry it up. So all right, I think there's a nice way to do that. So we can instead of doing build forward and build backward, we can just have one method called build and build bang goes on this side. Build will just take an argument that's like forward. Okay. We'll have a keyword argument and we'll just say if forward, then we'll append the char to the end of the password. Otherwise, we'll say it's char plus at password. We'll prepend if it's not forward, if it's going backwards, right? So pretty straight forward. And then basically instead of doing this build forward, we just build and we pass forward whatever forward was originally. And now we can just delete the two of these. Yeah, we can just delete the old one. And now instead of build forward, this just becomes this just becomes build build forward true. And this becomes build forward false. So now we should be able to try this out with the real API. So let's see what happens. All right, fingers crossed. I hope the internet here is good because otherwise it'll be in trouble. All right. Here goes nothing. Packing password beep boop. All right. It's talking to us. Here we go. Found first letter. First letter is J. Here we go. Coming in J2. All right. This is exciting. Exciting. Yeah. All right. This is completely out of my control at this point. It's just all it's all up to the internet connection. Okay. All right. It's going. Look at that. We've got a 2492. There's a little bit of symmetry there. That's kind of cool. Okay. We've got a G. Things are moving. I don't actually remember what my password is. So I'm just as cool as you guys are. All right. Cool. It's still moving forward. It's still going. 29. Okay. I think it's like 20-something characters. So we're not going to be here all day. I promise that we're going to get out in time. Okay. Okay. Good. I got worried when it sticks here for too long because I was like, shit. I wanted the API call scale. All right. J. Nice. It's going. Wow. Right. Still going. Yeah. How are you guys doing, by the way? You doing all right? Yeah. We've got a minute here to just kind of chat and check in with one another. All right. Oh, nice. It's going off the end. RPO. Okay. Oh, no. No. Please don't. Oh, okay. Okay. So it fell off the end. Now it's going to build backwards. It's figured out that's the complete suffix. And it's building backwards now. Okay. It's got the I. Things are moving. This is good. It's a good sign. There's hundreds of API calls made seriously. None is paralyzed. So this is poor guys who are running their servers. I kind of suspect nobody's actually at the wheel right now, but that's fine. Maybe in a few days when they check the logs, they'll see what the hell is going on. Okay. ASD. Okay. Oh, and that's it. 403 iterations. Oh, my God. We did it. Yeah. Yeah. Yeah. Oh, that's so good. Oh, my goodness. Oh, my goodness. Yeah. Okay. So that was me cracking my password. Now note, I just want to quickly go back to the math. So we decided that it would take 432 expected queries, just doing the math straightforwardly. And our actual number was 403. So we were within 10% of the actual answer. So, holy shit, math actually works. That's pretty cool. Was, you know, sometimes it doesn't. So that's the thing about math. All right. Takeaways. It's mostly just a funny story, but I felt like there was something here. So the cool thing, at least to me, this was actually the first problem that I really actually had other than unemployment that I solved through programming. And like it's actually kind of a magic moment in a programmer's life when there's like something that is like just broken or lost or just like you can't do. That's not, you know, your job related or, you know, launching a web app or whatever. I think we kind of get acclimated to that sort of thing. But the idea that like, you know, I felt like I had lost my keys in a desert or something like that in a way that was totally unretrievable. And with the power of programming, I could fix it. I could solve it. I could do this amazing thing that before I was able to program, I was never able to do. And that's just, it's just kind of awesome to have that power. And I think as programmers, it's easy to kind of get complacent or get pissed off at our tools or our abilities. But there's something really awesome and magical about that that I think, you know, it takes an experience like that to really drill that in and drive it home. And I think it's just pretty crazy that we can do shit like that. So that's, that's it for me. So I want to share that story with you all. I'm Hasib Qureshi. I work at earned.com, which is a blockchain company. You should check it out if you haven't heard of it. You can find me on Twitter at adhasib. You can read my blog where I write about this and other stuff at hasibq.com. And thank you so much for listening, guys.