
Awesome. Thank you, TJ. [applause]
All right. So, yeah, today's talk is going to be about uh making proof of concepts that are unignorable. A little bit about me. I do application security uh practice lead for VDA Labs, a small cyber security firm based out of Grand Rapids, Michigan. uh one of the founders of the local Charleston infosc group and I've ever since besides uh 2016 maybe uh started hacking ATMs and uh I've done that a a couple of times since then um including spending a couple of weeks with an ATM manufacturer learning all we could about a specific ATM and then breaking it um so that they could make sure it was good before they put it out to the
>> [clears throat] >> So, what is this talk about? This isn't about shiny new novelty exploits or zero days. Uh, instead, this is about building proof of concept chains that make security unignorable. Um, and if you've ever had to fight to get a bug taken seriously, this talk is for you. Before I begin, I will start with this. Um, as a developer long long ago, uh, we had a mantra on our team. It actually came out of a a book writing secure code that was written in like 2002, but all user input is evil. If all developers took that to heart, I'd have a lot harder job to do um, as an attacker. On the flip side of that, as an
attacker, all user input is a potential code execution vector. So, I love input into an application because it gives me a chance to have your program do something with data that I control. Um, so keep those things in mind as we we go through this. And this talk is going to be kind of a a story-driven talk. Um, we're going to look at really grabbing someone's attention with a stored cross-ite scripting. And some of you might be out there thinking, "Wow, store cross-ite scripting still a thing?" Yes. Um, unfortunately there's a lot of it out there. Um, [snorts] and unfortunately also many places don't seem to take it seriously because what's the traditional evidence for the cross- site scripting
when you put it in a report? You've got an alert box with a cookie in it or maybe a message, right? And they look at that and the unimaginative people that look at that say, "Oh, well, it's just a message. I don't care that somebody can put a message on there. I'm not going to fix that. Um, so we'll make it so that they can't ignore it. We'll also tell about the day I found out that I am a highly sophisticated thread actor. Um, it's a plot twist on a classic DL injection. And then finally, we're going to look at how I abused verbose error messages and misconfiguration in a an application to implement my own feature in a client's
web application that gave me full account takeover capability for every account in their system, all 68,000 of them. So with that, let's jump into the first story. So this starts out with a cross-sight scripting vulnerability that was found in an application and in fact this particular one was a repeat like we had tested the client the year before found those stored cross-sight scripting gave them the traditional evidence tag they basically said okay that's nice and then the following year when we tested it they hadn't fixed it every in fact my old payload was still in that test environment. Um, I was like, "Wow, you guys really just didn't do anything with this." You know, they they fixed some other stuff. Um,
but this particular vulnerability, they were like, "Me, whatever." So, I decided, well, I'll just make it so they can't ignore it this time. Started out pretty boring. Um, I was uh getting the the first proof of concept. you know again want to make sure that the cross-ite scripting uh payload can actually fire. So of course you do it with a a quick message. Now one thing about this is they did have some protections and and this may be why they ignored it in the first place. Um you couldn't put in just a classic script, you know, alert whatever and then close script tag. Those payloads were scrubbed. So they were doing a good job with that.
They also did a good job with scrubbing JavaScript event handlers. So a lot of times if you can't get a cross-cripting payload to fire with, you know, your regular script tags, you'll end up using something like image equals this and then on error, you know, there's your payload, right? Well, they also blocked that. I said, okay, that that's good. You know, we're doing something. But what they didn't do was uh they still had um a feature in their application where you could put in an anchor tag. So you could put that to anything that you wanted it to. And at first I was like, "All right, you know, I could maybe put that to, you know,
something malicious uh and it gets ingested into their website." But then I started thinking, could I make the data in that be the payload itself? So if you're a bit of a HTML nerd, there's, you know, a data URL property for the href tags. Um, instead of putting, you know, the full link to a remote website, you can include your whole payload right there. The thing is you B 64 encode it. So I took my payload uh the proof of concept B 64 encoded it put it in a data URI and that's what fired over here. You can see I kind of blurred out the the full payload. Trying not to teach script kitties here but um just want to show
that it it does work. So I decided to make it interesting. [clears throat] I had just gotten off of a project previous to that where I was doing a lot of work in testing with websockets. And so I thought, well, maybe I can make websocket control through this cross-ite scripting payload. So I put together a little um attack server. Just threw up an EC2 instance in our Amazon cloud and put a little NodeJS uh server on there to handle some inter some basic interactions. and then made that payload um so that when the user visits the site and clicks that link it opens up a websocket back to my attacker controlled server and so I get
you know some limited control over their browser. So here we've got kind of the implementation of that. You got a little command shell up at the top showing the server running and then when somebody connects I get a notice that they've connected and then I can start interacting with it. So first thing I did was send a console message um you know hello from the attacker shell and it shows up at the bottom here where we've got hello from my attacker shell. All right, great. I've got some basic control now. But I also knew that with this particular client, if they didn't care about the potential for other stuff and they just saw the message, this was just
another message. But instead of being visible on the screen, it was now buried in the console. So let's send them to other websites. Now all of a sudden, I can just put like window open, push a a URL in there, and now the browser is going to um go to that website that I've directed them to. So in that case I can have a you know actual malicious website uh doing a whole lot more uh and you know game over for for the victim in this case. And in this case the client was like wait so you can affect all the users who visit the website now? Yes we could before but you didn't pay
attention but now all of a sudden they paid attention and they said they fixed it but I haven't gotten a chance to retest this yet. So we'll see how that that goes on the next round of testing.
So the next story is about the day I found out that I was a highly sophisticated thread actor. Um little backstory on this. I was testing a Windows application, a desktop application and uh this particular Windows application, the application was vulnerable to DLL injection. This particular client had a history of deprioritizing DLL injection. In fact, they they were telling us like, we really don't care about that. And I'm thinking to myself, that's an odd choice to make, but okay. Um, you know, and I started thinking, how can I make them care about it? And I was, uh, you know, they they weren't even really concerned about the initial thing with where you get remote code
execution out of that DL injection. So, um, when I first told them that I was experimenting with that, I said, you know, yeah, I can actually get remote code execution. and I can get a reverse shell back to the desktop. Um, and they're like, "Yeah, but you know, we don't think that's really going to impact us." Okay, I take that, put it back in my mind, and start letting it think on some different ways that I can make it uh make it useful. So I'm looking at some other DLS that they have and I noticed in one of them uh I was doing some little basic reversing and in this other DLL that they had they were storing JWTs and some
other interesting tokens and um stuff that I I like to call loot. when I'm an attacker and I'm looking for information on a system, you know, this DL started handling that. And I noticed that in the code for that, these tokens, these passwords, the JWTs were all being uh declared as string objects. So, who's got a development background in here? All right. So, what's the problem with storing sensitive information in a string? >> Anybody? >> Really easy to get. >> Easy to get. Why? >> Well, because you don't have to do anything to read it. >> Well, so there's that. But beyond that, strings are immutable. So once it's declared in memory, it's staying in memory in that state. It, you
know, they can't overwrite it. They can't change it. Um, it's just going to be in memory until garbage collection comes along and flushes that out, which developer may or may not have control over or may not may or may not care about it. Um there's a lot of modern development frameworks that do uh garbage collection automatically and so developers don't typically worry about that. So I started thinking okay if these objects are in memory and I've got remote code execution well now I can go rifle through memory. Awesome. So I wrote a couple of reaxes to look through the memory uh during my DL injection attack and look for things like uh JWTs um passwords. I was looking for
different hashes um you know if something was like MD5 hashed or uh you know I just wanted anything that looked interesting in memory and and pull it all out and put it in a little file for me to to go look at later and ended up getting all of those JWTs and then finding out that they're valid. So, for those of you that aren't familiar with like web app testing, if you have a JSON web token and you you look at it, you can see, you know, is it valid? You know, it's got an expiration date um plugged into it. Uh what resource it's for, you know, because it'll have that as part of the uh properties of it. And
so I start seeing that I've got tokens for other cloud services that this application interacts with. And to make it easy for the user of the application to export data or import data or move data around, they said, you know, yeah, you can connect your, you know, cloud account to this repository and we'll we'll handle that for you in the background. So now all of a sudden I have the DL injection that lets me rifle through memory and pull out interesting information in this case tokens and then I can go uh masquerade as that user uh to whatever cloud service that token was for and there was multiple tokens. So now all of a sudden they had
a couple of findings you know one don't store sensitive information in string objects. um go back to your dev team and have them rearchitect that, but then also maybe fix the DLL injection because the one finding isn't as impactful um without the remote code execution that the DLL injection. So if I would have just came to them and said you're declaring these sensitive objects in memory um as strings and they can be gotten by an attacker, they're going to like well who's getting access to the local machine? You know, just that user that's logged in. Well, now their DLL injection gives an attacker local access to that machine and they can pull the same information.
And when I was uh first looking at this particular attack path, I was concerned that this client was going to push back and say, "Oh, that's unrealistic. That's not a a realistic threat pattern that uh you know would happen." So, I reached out to a number of friends. Uh TJ was uh one of them. And I said, you know, have you seen this kind of activity or what would you say if you saw this kind of activity in the wilds, like you know, from a threat actor? And there was one other uh threat researcher that came back said, "I think I was dealing with a highly sophisticated thread actor." And I was like, "Awesome. I'm a highly sophisticated bad guy."
So, we turn the remote code execution into uh rifling through memory. and grabbing fun stuff. So yeah, in the end all the JSON web tokens end up coming to me. Um, and one thing that was interesting that came out of this as well, uh, this particular client now specifically asks us to verify that DL injection is no longer an issue in new stuff that they put out going forward because they were like, yeah, that that kind of was scary. So, um, thanks for opening our eyes to that. I think where they got burned in the past was before they'd come to us for application pen testing they were out on um there was a bug bounty platform I
don't know which one they were on but they got a lot of people that were writing in you know basically the same thing DLL injection but all they would do was their evidence was always just you know a quick message or it would launch calculator right that's another classic if you're pentesting and you get something like remote code execution what's a harmless thing I can do to prove that I can launch the calculator on the app. And so they were looking at it and they're like, who cares if you can launch the calculator? But that because that's how they were that's the only thing that they were thinking is that they weren't opening their mind to
the broader possibility. Well, if I can launch calculator, I can launch any other .exe on the system and including maybe one that I drop there. Um, so like I said, they they have their eyes opened now and and they're uh actively looking to prevent that in the future. So that was a fun one. Uh just for a quick thing uh for anybody that's brand new um you can aware how basic DL injection works to make that a a thing I'll give a quick overview um highly simplified but basically with modern Windows applications you have um the application isn't going to be written to contain everything in that main executable. there's some things that it it's going to do that um is
going to be called out in another piece of code. And so they'll have these libraries, the DLS, the dynamically linked libraries that may contain certain functions for the application to use. And so rather than it being written in the main application, it goes out to this DL and calls the code from there. Well, in Windows systems, it has a specified search order that it looks for those DLS. It starts in like the applications directory and then it kind of has a prescribed pattern that it follows where it looks for a specific DL when it needs to access that information or that function. And you can fire up a process monitor on your machine, launch an application, and
kind of see where it's trying to to read for that information. So I'll find the name for a DLL, just call it app.dll, DL and it's trying to load it and it can't find it. Um, so I'll say, okay, well, I know now I'm going to go put that DL in, you know, maybe the applications directory itself. Um, and then this way when it go when the next time the application looks for it, it finds my DL first um, and executes my code rather than the intended code.
This last story is about overly verbose error messages um that eventually led to a full account takeover. So, one thing that I often post as a anformational or low vulnerability finding is verbose error messages um that I see as an attacker. So, if I'm testing your web application um and I put something in and I cause the application to behave in a way that's going to generate an exception, there's a lot of times where I'll get a very verbose error message. You know, we expected a string piece of data at this line and this character um but instead we got an int and you know, it caused this stack trace to dump out and you
know, gives you a whole bunch of information. That's awesome if you're a developer because it helps you identify what went wrong in your application. Um, it's also really awesome as an attacker because it helps me identify what went wrong in my payload and how I might be able to fix it. So, in that simple case, I might think, okay, that was expecting uh int data and I passed it a a string data. Okay, now I know I need to maybe reformat my payload to pass in some integer data instead. Um, but again, there's a lot of times where developers or or the clients that get those, they kind of write it off. They're like, "Well, we need those
verbose error messages for our developers." I'm like, "Okay, you know, have it in your dev environment." Um, but don't have it in your your production system. Um, and like I said, there there's some cases where year after year with testing, we find the same thing and it's always a low severity and they don't worry about it and so we just continue to market and press on. Well, in this particular case, I was intrigued because this was testing something that I was new to uh from an attacker or a developer standpoint. It was um using GraphQL as the API backend for this web application and I had done some basic API stuff in the dark ages of web
development but I hadn't done anything with GraphQL. So, it was all new to me and I was like, you know, as an attacker looking at some of these verbose error messages, I was like, this is awesome because I'm learning how this app app works and how GraphQL works um as I'm attacking it. So, let's set the scene here. The client web application is using GraphQL. They had suggestions enabled. Not a good thing. Again, maybe great if you're in a development environment. You've got new devs. um it can help you work through any kind of issues that you may have, but don't have it on in production. Uh I notice there's a query that returns user object info, but based on my
exploration of the rest of the uh API in the in the model there, I knew it wasn't returning all of the information. I noticed specifically there's one property that was not returned and that was a system generated password reset URL. So the system would generate it on its own and then that became one of the user object properties. But for good reason they weren't returning that every time a user object was being moved or or manipulated within the application. So, I started filling around and put in some stuff that obviously didn't work. Um, and this is an example of, you know, GraphQL with suggestions turned on that it might say. So, I might have put in a query field for one
and it's like, well, we don't have one. Did you mean node? Oh, yeah. Yeah, that's exactly what I meant. you know, um, so then I reformat my attack to include the valid information and then that might get me past one error, but then I get another error. And so I go I keep going through this iteration and getting my path my attack path just right till I finally get the data that I'm after. And I was like, okay, that's awesome. I can now return the data that I was after. And so this is an example what you might see [clears throat] um with a query and it's getting like you know ID, name of a user um and some
other information. There might be uh some information that's not returned. So in the first one um it's not returning email for example, but in the second one they're asking for email and so it actually comes back. So what I did was basically got to the point where I could go through and I I modified the query on the fly so that I got back the password reset URL. Um and I thought, okay, that's awesome. I learned a lot of stuff um in the process, but I was like there they may not be quite as thrilled about that because I was also logged in initially as one of their admin users uh for the application. And so they're gonna be
like, "An admin's supposed to get back that if they want to." And I'm like, "Well, again, I I'd argue that maybe the admin doesn't need to get everybody's password reset URL." Uh because then they could you could have a rogue admin that, you know, starts going around and impersonating other users. So I went I kind of put that on the back burner and was looking at some other things. And that's when I had my but wait, there's more. this particular application was doing um a license check when it went when you went to create new users. So the application, if I went to go add a new user to the application, for whatever reason, they had their logic a little
bit backwards. They would check to they would create the user with all its properties and everything and then they would check to see if there was an available license for the user. If there was an available license, it would put them in the right group and then send them on their way. they can log into the application, do all the things as a regular user, and life was good. If there wasn't a valid license for that user, it would error out, but the user was already created. And so I was like, what can I do with that user? Um, you know, part of the the response that I got back was the initial um access information. And I found out I
could log in as that ghost user, but I couldn't do anything. I didn't get any kind of UI in the application and I just got a blank screen after I logged in. However, if I intercept requests and go through a proxy like Burp, I can still send GraphQL queries. Um, I can add mutations. I could do all kinds of stuff through the proxy that way. So now I started thinking, okay, I've got a ghost user that can't do anything, but it's a regular user in the application. can that user now return the password reset URL? And it turns out they could. So now all of a sudden my regular non-privileged user is able to uh make a query to get back information
on any user in the system including the admin users. Um and it includes that password reset URL. So then I tested that with the admin account that they had given me. It gives me back the URL. I go to that URL. It lets me set a password and then I can log in as that user. So now I have privilege escalation from the regular user to an admin user um through a feature that they didn't know that they had the capability. In fact, that was one of the things the their uh GraphQL developer was like, I didn't know my application could do that. Um, so ended up with full account takeover for any of the 68,000 accounts in the
system. But again, chaining that to to make it so that the client couldn't ignore it. In this particular case, I like that client. Um, they've been really great to work with over the years. um they they always get a little bit nervous when we start a test cuz um the first time I tested them, I think I had five or six criticals that included um you know, privilege escalation and and a whole bunch of other stuff. And they were just like, "All right, go easy on us the next time, but also don't because you know they they need that kind of stuff to be found so that it's not exploitable for for real." And so that's uh that I will uh open it
up to Q&A. If you're interested in connecting on LinkedIn, uh the QR code there, I promise it only goes to LinkedIn. It's not going to Rick roll you or or give you something else. Um I thought about it real hard. I was like, I can make that maybe do something. And I was like, no, no, no. Be nice. Um, so and then also my favorite high score screen, the uh ATM at Bsides when I got in and took over the screen. Um, one last thing I'll mention before uh questions. If anybody's brand new to infosc, um there is a friend of mine out in the lobby. He's doing resume reviews. He's a career coach. Um, I asked them to
come here as a favor, uh, because in the talks that I've had with new people to the industry, uh, they mentioned how brutal the job market is out there right now looking for new jobs as a brand new person that's maybe doesn't have a whole bunch of experience. Uh, they're saying it's just brutal out there trying to get, you know, get their resumes reviewed or seen. Um, so there's a guy out there, David. Um, he's doing resume reviews. He's a career coach. he's got some ideas that might be able to help you stand above the crowd a little bit and and maybe get that first job. Uh, you had a question. >> Yeah, I was just wondering about the
GraphQL. Does that circumvent the MFA? If the company's using MFA, >> they were not using MFA. They now do. [laughter] still need that in that. >> So yeah, if uh in the current So I'm not able to retrieve the URL anymore, but um if they would have had MFA, yeah, that would have stopped me from Oh, I could have still reset the password, but I wouldn't be able to log in. >> Yeah. >> So, but they've since implemented MFA, so good on them. >> Any other questions? >> Yes. on the bit injection bit. I was wondering whenever yall do like injection uh do y'all do you make like a craft deal that tries to mimic the
functionality of the original or do you just toss that one in there >> for the quick and dirty? Um just toss a bad one in there. Um they the the first time I did that the client did ask well like you know your DL that you put in was just a quick and dirty you know so it's going to break that particular functionality in our app and our users not going to use it anymore and I said yes but I also had only two weeks on this engagement if I was an actual attacker and there are big enough clients that they might they might get this kind of attacker on their on their heels. I said I would
actually build a DL that would uh proxy the the thing. So it would take the request, it would handle it, but it would also pass off uh to do my bidding as well. Um so that would take a little bit more time obviously, but for this case um they were like okay we we'll go with that. >> Other questions? >> Yes. >> Can you go back over in this method of injection? for the DL injection. Uh, basically just building a so I know that that application is looking for a specific DL. Okay, we'll say app 1.dll. Um, create app 1.dll that has my code in it that's going to give me the reverse shell or the memory
rifling. Put that in the folder where the um application lives. And then when the application goes to make that call, it finds mine first, it loads the information I put in there and it carries that out. >> Yes. >> When you were reading the JWT out of memory, what were you how were you reading that? >> I had reaxes that would look for it in memory and then >> did you create a handle to that process? So yeah the the um the I ended up using PowerShell for that. So PowerShell just yeah it started it connect to the memory pro for that process under the same context that the application is running. So in this case the application was
running just as a regular user not an admin but that's all it needed to be able to access the memory that that application had um had access to. >> Yes. >> Your company hire people. >> We're not going to hire you Michael. >> [laughter] >> Yeah, I was gonna say he already works with me. So, >> any other questions? >> Yes. real quick. Can you explain how the detction? >> So, what they started by doing is uh Microsoft recommends um to have a uh to do the search order in a different way or to have it hardcoded to like you want to go to this specific DLL in this specific location, not just look for the
first available. Um there there's a whole uh TechNet article um that goes into uh some different levels of protection against that. >> Other questions? Yes. >> Stuff seems super interesting. I've never worked directly with it. I wanted to start playing with it and kind of start at ground level and learn how to do application testing. What would you suggest? Where would I start? Have you got a development background or no? >> A little bit. >> So minimal. >> I always say it's it's a lot easier to teach a developer security than it is to teach a security person development concept. So if you've got a little bit of a development background, I would look at maybe starting a some web
applications. Um you know, and there's purposely vulnerable ones out there. So like in the web application space, you've got juice shop. Uh it's an OAS project that's uh open and available for anybody and it's made to clone like a modern e-commerce site, but there's tons of vulnerabilities in it. So you'll find cross- sight scripting, SQL injection, command injection, um all kinds of stuff and and there's a walkthrough guide, but there's also you can just load it and then start going after it. Um so look at the different vulnerability classes. Um most of them start with injection. So being able to put data in um and have the application look at that. Um there's also purposely vulnerable uh desktop
applications um APIs, almost anything you can think of. There's even purposely vulnerable IoT projects out there that you can load and then start to explore. Um if you're local to Charleston, uh certainly come out to the infosc meetups that we have the last Wednesdays of every month. There's people out there that will be glad to talk to you as well. [laughter] >> TJ's trying to tell me something. [laughter] >> They got to do like a
>> but yeah um there there's uh plenty of uh resources out there. I would recommend you know and then also looking at purposely vulnerable code. So when you find those applications that are doing um like for example the juice shop and you see an application error there for cross-ite scripting or SQL injection go look at the code and try to understand exactly what in that code the way it was written allowed you to have that vulnerability. >> Other questions? >> Yes. >> Yeah. I uh so Charleston infoset group established 2015. Um it's a local meetup of infosc professionals and people who are looking to get into the industry. Uh we meet at Frothybeard in West Ashley.
Um normally it's the last Wednesday of every month. For November and December it's the second to last Wednesday of every of those two months because it's close to the holidays for the last Wednesday of the month. But um yeah, so you can go to meetup.com and see more information. uh you can talk to me after the talk and get you plugged into the discord and so yeah >> yes >> the application you mentioned was just using a straight string check onlary hash check to make sure it wasn't >> it was not >> would that be a good mitigation step >> it could certainly be yeah you know so so it found this one does the hash of
that match what I'm expecting and if it doesn't then you know throw an error. Um, so yeah, >> other questions. >> Yes. >> Did the client have an onost? >> So this particular application is one that could be run by anybody on any Windows system. So they were more interested in the fact that so they they're actually their dev stations obviously have you know managed EDR um and many of their clients might but not everybody's going to be running something. So they were concerned with the possibility that somebody would download their software use it maybe not have that and then their software becomes the mechanism by which they get compromised. But yeah, certainly um you know, if
there was an EDR that had to be evaded, um it it's going to raise the bar. You're gonna like it wouldn't be able to just use a you know, skeleton DL with uh some basic information um to do that. But again, that's going to be like a a more realistic thread actor with unlimited time versus a twoe pentest engagement. Other questions? All right. Thank you so much, [applause]