That time I used Ruby to crack my Reddit password
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Qureshi, Haseeb
I lost my password. So I used Ruby it crack it, kinda. I will re-enact the story live in front of a group of strangers. I'm going to be honest, this is a weird and fairly embarrassing story. You probably shouldn't come see it. You know what, forget I even said anything.
 I think it's probably about 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, and how I use it. 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 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 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 nightmare, but this is actually a... If you guys remember your English class from 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 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 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 into some random gibberish. So I just grabbed my keyboard and started smacking away. I came up with something sufficiently weird and entered that in as my new password. And of course, I don't remember what this string is. There's just a bunch of random characters. I hold onto 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. So 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. So 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 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. I figure there's probably some way to automate this, right? You don't need friends. So I go and 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. No friends are required. This is pretty perfect for me. So it's a little bit 1995 looking, but that's okay. Maybe they're just really focused on what they do. So what I do is I go ahead and compose a new email to myself. I create an account. 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 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. 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 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 used 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 didn'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 get back into some time wasting activities. So I want to go log back in to this website. So I go back to Letting Me Later. I remember that I locked my password away. Go back to Letting Me Later. Go retrieve my password. It's been a while. It's been like a couple years, actually. So I guess I kicked the habit for a good bit. But now it's time to 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 usually I'd send it to myself like a month later at a time, give myself a period of time to just focus and then a little bit of time to 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 so annoyed at myself that I actually set some dates super far in the future. Just be like, screw you, Steve, 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. OK, maybe there's some way around this. So right here is LetterMeLater. Here's the actual real website. And so 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, and I was about ready to give up, but of course you got to try a few things first, I realized that there's a search bar here. OK, well, what can I search for? So maybe I can search for my name and see if my name is in there. It's not. So it's not indexing my name. All right, fine. Now, what if I search for A? OK, so that popped up. So maybe you might be looking for subject lines. And so you can ascertain that pretty quickly. If I type in password, I can see, OK, yeah, it's definitely indexing the subject line. That makes sense. But remember, the body of my email was just the actual password. So if I search for a letter that's not in password, let's look for E, E is not in there. OK, what about 1? 1 is not in there. What about 2? 2 is in there. OK, so what's going on here? What's going on here? All right, hold on, hold on. 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. 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. OK, so when I realized this, I ran home. I was off work at this point, so 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, OK, 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. OK, so I have this big one thing and I have 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 substring query 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. OK, 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. OK. So the averages 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, it will 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 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. 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. OK. So what that means I have is not the entire string, it means I have a suffix to the string. Right? 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. Right? 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, OK, cool, that should be my entire string. So if I do this, now if you think like on, sort of, this illustration is not exactly correct, because on the end, it 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, OK, 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 do entirely serially through API calls. So let's do it. How does it sound? Yeah. OK. All right, let's do this. OK, 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, that's 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. OK, so first things first, I got to figure out how I'm going to actually do this, do this querying. 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. So if I change this query to AB, then I can see QE becomes AB. So obviously this thing has no API, so I'm going to have 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 dot 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 dot 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. 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 dot get, and let's say I'm going to search for the string password. 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'll make sure to sign out when this talk is done. That way this cookie is getting validated. 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 the 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 web page. So this is a 200. And you can see, yeah, it 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 web page. 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 that 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 there are a few things that show up when it's 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. So 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 the.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, which turns 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 just 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. It's going to be much faster, right? I don't want to be making 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. Oops. 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 dot char's. Those are all the characters in the subject line, right, if you guys remember. And 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 dot each do character. And for each character, I'm going to look it up in the oracle. So I'll say at API dot include char. 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 dot 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 I'm not worried about the subject line anymore. I just iterate through the entire alphabet. So I'll say alphabet dot each do character. 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. So I'll say query equals at password plus char, just depending it 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. 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, you know, 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. 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 passwordcracker.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, Angie Flamac just had that thing just plop out on the screen. So 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 at the end it'll say congratulations, your password was found in at iterations, iterations. Okay, and then basically as I build forward and build backward, I want to always mention once I say, okay, great, your password is elongated. Let's just print out the password. So it just puts at password every time that happens. So cool. Now, one thing before we actually run this, you know, we're all engineers here. We noticed there's some code duplication. Let's refactor this thing. Let's try it out. So all right, I think there's a nice way to do that. So 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 it's going 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. Oops. Yeah, we can just delete the old one. And now instead of build forward, this just becomes build. Oops. 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. Practical password beep boop. All right, it's talking to us. Found first letter. First letter is J. Here we go. It's coming in J2. All right, this is exciting. Exciting. Yeah. All right. This is completely out of my control at this point. It's all up to the internet connection. All right, it's going. Look at that. We got a 2492. There's a little bit of symmetry there. That's kind of cool. Okay, we 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 going forward. It's still going. It's going to be 229. Okay. I think it's like 20-something characters. We're not going to be here all day, I promise. We're going to get out in time. Okay, okay, good. I get worried when it sticks here for too long because I'm like, shit, did one of the API calls fail? All right, J, nice. It's going, wow. All right. It's still going. Yeah. All right. How are you guys doing, by the way? You all right? Yeah, we've got a minute here. Just kind of chat and check in with one another. All right, 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. Okay. This is a good sign. This is hundreds of API calls made seriously. None of this is paralyzed. So this is, poor guys 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. 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 to do this. 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. You know, sometimes it doesn't. So that's the thing about math. All right. So 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 had. The first problem that I really actually had other than unemployment that I solved through programming. And it's actually kind of a magic moment in a programmer's life when there's something that is just broken or lost or that you can't do. That's not your job related or launching a web app or whatever. I think we kind of get acclimated to that sort of thing. But the idea that 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 it's just kind of awesome to have that power. And I think as a programmer, it's just 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 it for me. So I want to share that story with you all. I'm Ashie. I work at earn.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 adhaseeb. You can read my blog where I write about this and other stuff at haseebq.com. And thank you so much for listening, guys. Thank you.
 I think it's probably about 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,  and how I use it.  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 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 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 nightmare, but this is actually a...  If you guys remember your English class from 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 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 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 into some random gibberish.  So I just grabbed my keyboard and started smacking away.  I came up with something sufficiently weird and entered that in as my new password.  And of course, I don't remember what this string is.  There's just a bunch of random characters.  I hold onto 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.  So 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.  So 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 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.  I figure there's probably some way to automate this, right?  You don't need friends.  So I go and 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.  No friends are required.  This is pretty perfect for me.  So it's a little bit 1995 looking, but that's okay.  Maybe they're just really focused on what they do.  So what I do is I go ahead and compose a new email to myself.  I create an account.  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 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.  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 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 used 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 didn'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 get back into some time wasting activities.  So I want to go log back in to this website.  So I go back to Letting Me Later.  I remember that I locked my password away.  Go back to Letting Me Later.  Go retrieve my password.  It's been a while.  It's been like a couple years, actually.  So I guess I kicked the habit for a good bit.  But now it's time to 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 usually I'd send it to myself like a month later at a time, give myself a period  of time to just focus and then a little bit of time to 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 so annoyed at myself that I actually set some  dates super far in the future.  Just be like, screw you, Steve, 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.  OK, maybe there's some way around this.  So right here is LetterMeLater.  Here's the actual real website.  And so 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, and I was about ready to give up,  but of course you got to try a few things first, I realized that there's a search bar  here.  OK, well, what can I search for?  So maybe I can search for my name and see if my name is in there.  It's not.  So it's not indexing my name.  All right, fine.  Now, what if I search for A?  OK, so that popped up.  So maybe you might be looking for subject lines.  And so you can ascertain that pretty quickly.  If I type in password, I can see, OK, yeah, it's definitely indexing the subject line.  That makes sense.  But remember, the body of my email was just the actual password.  So if I search for a letter that's not in password, let's look for E, E is not in there.  OK, what about 1?  1 is not in there.  What about 2?  2 is in there.  OK, so what's going on here?  What's going on here?  All right, hold on, hold on.  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.  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.  OK, so when I realized this, I ran home.  I was off work at this point, so 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, OK, 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.  OK, so I have this big one thing and I have 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 substring query 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.  OK, 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.  OK.  So the averages 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, it  will 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 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.  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.  OK.  So what that means I have is not the entire string, it means I have a suffix to the string.  Right?  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.  Right?  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, OK, cool, that should be my entire string.  So if I do this, now if you think like on, sort of, this illustration is not exactly  correct, because on the end, it 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, OK, 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 do entirely serially through  API calls.  So let's do it.  How does it sound?  Yeah.  OK.  All right, let's do this.  OK, 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, that's 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.  OK, so first things first, I got to figure out how I'm going to actually do this, do  this querying.  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.  So if I change this query to AB, then I can see QE becomes AB.  So obviously this thing has no API, so I'm going to have 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 dot 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 dot 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.  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 dot get,  and let's say I'm going to search for the string password.  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'll make sure to sign out when this talk is done.  That way this cookie is getting validated.  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 the 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 web page.  So this is a 200.  And you can see, yeah, it 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 web page.  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 that 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 there are a few things that show up when it's 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.  So 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 the.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, which turns 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 just 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.  It's going to be much faster, right?  I don't want to be making 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.  Oops.  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 dot char's.  Those are all the characters in the subject line, right, if you guys remember.  And 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 dot each do character.  And for each character, I'm going to look it up in the oracle.  So I'll say at API dot include char.  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 dot 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 I'm not worried about the subject line anymore.  I just iterate through the entire alphabet.  So I'll say alphabet dot each do character.  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.  So I'll say query equals at password plus char, just depending it 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.  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, you know, 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.  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 passwordcracker.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, Angie Flamac just had that thing just plop out on the screen.  So 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 at the end it'll say congratulations, your password was found in at iterations, iterations.  Okay, and then basically as I build forward and build backward, I want to always mention  once I say, okay, great, your password is elongated.  Let's just print out the password.  So it just puts at password every time that happens.  So cool.  Now, one thing before we actually run this, you know, we're all engineers here.  We noticed there's some code duplication.  Let's refactor this thing.  Let's try it out.  So all right, I think there's a nice way to do that.  So 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 it's going 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.  Oops.  Yeah, we can just delete the old one.  And now instead of build forward, this just becomes build.  Oops.  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.  Practical password beep boop.  All right, it's talking to us.  Found first letter.  First letter is J. Here we go.  It's coming in J2.  All right, this is exciting.  Exciting.  Yeah.  All right.  This is completely out of my control at this point.  It's all up to the internet connection.  All right, it's going.  Look at that.  We got a 2492.  There's a little bit of symmetry there.  That's kind of cool.  Okay, we 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 going forward.  It's still going.  It's going to be 229.  Okay.  I think it's like 20-something characters.  We're not going to be here all day, I promise.  We're going to get out in time.  Okay, okay, good.  I get worried when it sticks here for too long because I'm like, shit, did one of the  API calls fail?  All right, J, nice.  It's going, wow.  All right.  It's still going.  Yeah.  All right.  How are you guys doing, by the way?  You all right?  Yeah, we've got a minute here.  Just kind of chat and check in with one another.  All right, 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.  Okay.  This is a good sign.  This is hundreds of API calls made seriously.  None of this is paralyzed.  So this is, poor guys 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.  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 to do this.  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.  You know, sometimes it doesn't.  So that's the thing about math.  All right.  So 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 had.  The first problem that I really actually had other than unemployment that I solved through  programming.  And it's actually kind of a magic moment in a programmer's life when there's something  that is just broken or lost or that you can't do.  That's not your job related or launching a web app or whatever.  I think we kind of get acclimated to that sort of thing.  But the idea that 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 it's just kind of awesome to have that power.  And I think as a programmer, it's just 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 it for me.  So I want to share that story with you all.  I'm Ashie.  I work at earn.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 adhaseeb.  You can read my blog where I write about this and other stuff at haseebq.com.  And thank you so much for listening, guys.  Thank you.
That_time_I_used_Ruby_0_47.png That_time_I_used_Ruby_48_1175.png That_time_I_used_Ruby_1176_1355.png That_time_I_used_Ruby_1356_1979.png That_time_I_used_Ruby_1980_2351.png That_time_I_used_Ruby_2352_2507.png That_time_I_used_Ruby_2508_6323.png That_time_I_used_Ruby_6324_6431.png That_time_I_used_Ruby_6432_7091.png That_time_I_used_Ruby_7092_7607.png That_time_I_used_Ruby_7608_8579.png That_time_I_used_Ruby_8580_10475.png That_time_I_used_Ruby_10476_15527.png That_time_I_used_Ruby_15528_15887.png That_time_I_used_Ruby_15888_18599.png That_time_I_used_Ruby_18600_19127.png That_time_I_used_Ruby_19128_20207.png That_time_I_used_Ruby_20208_20903.png That_time_I_used_Ruby_20904_21815.png That_time_I_used_Ruby_21816_26963.png That_time_I_used_Ruby_26964_27863.png That_time_I_used_Ruby_27864_31211.png That_time_I_used_Ruby_31212_37895.png That_time_I_used_Ruby_37896_38399.png That_time_I_used_Ruby_38400_38603.png That_time_I_used_Ruby_38604_38711.png That_time_I_used_Ruby_38712_41063.png That_time_I_used_Ruby_41064_41086.png