HackTheBox - Sorcery

IppSec · Beginner ·☁️ DevOps & Cloud ·2mo ago

Key Takeaways

HackTheBox's Sorcery challenge using nmap, Neo4j, and XSS

Full Transcript

What's going on, YouTube? This is Ipsac, and today we'll be doing Sorcery from Hack the Box. And there's too much to summarize here in this intro. I feel like it has at least 20 steps, but my favorite by far is the beginning of this box. It starts off with cipher injection, which I just find fun to do. That eventually leads us to a place where we have cross-sight scripting on the admin user and can take control of their browser. The web app is pretty minimal, so there isn't a lot of places to attack with cross-sight request forgery. However, it does support pass key login, which is a newer way websites authenticate. We'll do a lot of what feels like magic here to forge a Pasi, add it to their account, and then log in as that user. After that, there's still a lot to do on this box and hopefully I do it justice, but I'm running out of time to record this video. So, with all that being said, let's just jump in. As always, we're going to start off with an end map. So, SC for default scripts, SV enumerate versions, VVV for double verbose. This gives us things like the TTL OA output all formats put in the end map directory and call it sorcery. And then the IP address of 10129237.242. 242. This can take some time to run, so I've already ran it. Looking at the results, we have just two ports open. The first one is SSH on port 22, and the banner tells us it's an Abuntu server. We also have HTTPS on 443. Its banner tells us it's engine X and also um has the host name of sorcery.htb because it is redirecting us there. We also have it in the SSL certificates common name. So, I'm going to go ahead and add that to my host file. So pseudo v Etsy host then 10129 237242 sorcery.htb save that and there is one other thing I find interesting in this end map and that is going to be the ttl of the web server is 62 whereas the ssh server is 63 so this just means it's one extra hop away from this SSH server. So these are probably different boxes. There's some type of virtualization going on or something like that. It's not really groundbreaking to know, but like in Hack the Box where machines are just one image, uh this could mean something as simple as this is a Docker image. And that becomes useful when getting like a um remote code execution because a lot of times Dockers may not have a lot of binaries a typical um host has like curl ping, wget, things like that. So it's just important to understand um you may be in Docker or something like that. That just kind of screws things up, right? So, let's go ahead and take a look at the page. So, let's go to um https sorcery.htb accept this SSL certificate and we'll see what we get. So, um we have a login. Uh they love open source and it says check out our repo. It is going to the git subdomain. So, I'm going to go ahead and add this to my host file. So, let's do a pseudo vt host. Uh if I can type that would be awesome. And then let's go to get. Save that. And let's see. Can we hit this again? It's going to give us a SSL start, of course. Let's accept it. Uh, we also get a potential username, Nicole Sullivan, but we have a lot of source. So, I'm just going to go ahead and clone this. So, let's do a get clone. Um, oh, shoot. I forget exactly how um to ignore SSL in Git. Uh, we could probably just download the tar or something like that, but I really want to just do the um gateway because um I want to get like the commit history and everything like that. Like if I just go and download the zip, I think it like loses the.get folder that has potentially a lot of good information. So, we have the source here. I'm just going to make a directory source and move it there. And realistically, um, we could go over this source code probably for the next 30 minutes. Um, the front end I want to say is a Nex.js application and then all these other things are going to be I think Rust and it's using like the rocket uh web framework for Rust. But um there's a lot going on here and if I spent 30 minutes going over it, finding everything interesting, then I'd spend the next like hour exploiting this, pointing back to things I did like an hour ago, and it it just didn't make sense. So, I'm going to have this source code here. Um when doing the box, definitely take a look at the source code. Um, I'd probably start with like opening up in Visual Studio, passing it to Sneak, getting some static code analysis done, but um, I'll refer back to the source code whenever it's relevant, right? Um, because you don't exactly need to know everything that's going on to begin poking at the application. And honestly, like poking at the application, I think that is what most people would do. You don't always have the source code, right? So, let's go ahead and uh, go to register. And I'm going to register the name IPSC. The password of password registration key. It says it's only for sellers. We don't know what this is yet. So I'm just going to click register. And now we can log in. So let's do IPS password. And we get access to a page. It looks like it's an online store. We can have various items. If I go to profile, um we can have our username, type, client, login type as a password. We could enroll pass key and we'll talk about this more later. Um, but if we go over to the store, click view, there's really not too much going on here, but whenever I see something I think could be like database related or something like that, I always like playing with the data I have. If I do a single quote, uh, we just get a not found. If I do a double quote, we get an error message. So, at this point, I'm thinking, oh, sweet. I have some type of SQL injection. So, let's make sure I'm going over to Burp Suite and let's start playing with this. So, I'm going to turn intercept on, refresh this. Let's go here, send it to the repeater tab, and we get the error. Let's just make sure we don't get that error. Awesome. So, what I'm going to do is the d-space dash. Um, and we still get the error. Now, normally, if it's typical SQL injection, this would kind of solve it, right? Uh, we could try other ways to do a common. So, let's see. That would be like adding a I think a pound sign in some languages. So, let's just URL encode that. We still get errors if I start playing with this a lot. Um, I can't ever seem to get this query to work. So, my next step is um to poke at the source code. And also I'm going to um set up Clawude to run in the background because I always like having some type of recon going on. And um sometimes I like passing things over to AI when um it's possible. Right. So I'm going to set Claude in dangerously skip permissions and we'll just see how it does. Right. So what I'm going to do is give it some context. So we will say um let's see. I believe there is some type of injection here. If I do a this it gives me not found but quote triggers an error. Uh use the cookie and let's see exactly how we authenticate here because if we didn't do this um it probably just would get a authentication error and can't um hit this page, right? So, we could either tell Claude how to log in or just give it the cookie. Um, name of token. Let's see. I'll put this in quotes because that's a bit odd. Um, with a value of to hit the page. And let's see what else can we tell it to do. um you are in a directory with the source code to the page for guidance. So now that that's running, let's go and see what we have here. So um we will mainly go into the source and try to find exactly where this is. So I'm going to grap for maybe slashstore and we'll see where this hits. Um let's see. This is going to be the front end. I probably want something like background related or back end, not background. Um, let's see. This is URL format. Maybe it's looking at like product ID will help. Let's see. Internal. Not exactly. Let's see what happens if I say query. Um and let's go everything with backend. So we have a few things. Um query we have Neo4 RS. So this is probably going to be a Neo4j. And then in this backend macros we have a lot of queries. So I'm going to start taking a look at this. Um so we can go in here and let's see model derive. Let's see. So it looks like the queries are all going to be um near 4J like cipher queries. Um searching query didn't result to in any like SQL or Postgress or things like that. If I knew Rust better, I would be like gpping for libraries to see if SQL is ever used. But at this point, I just see um cipher queries. So let's see. This looks like it could be it. So, I'm going to GP this file for match so we can see all of the queries. Um, and there's only two. So, let's just play with this real quick. Um, let's see. Grab this v. Let's open a new pane. So this is going to be the query and I'm expecting let's see result is probably going to be like product and then maybe we'll give it the ID and this will be the value right these are all the parameters I think we're passing in and um whenever you have these double squiggly brackets normally this just means one right that's how like you may be thinking the way to escape a bracket is like backslash bracket but in a lot of these templating languages you just do double bracket and then that becomes single bracket. So this is what I think the query is. Let me just paste here what it was before. So let's see um if we're injecting into value here we put a single quote and now it has all of this data. So what we have to do is return result. But before we do that we want to escape this. So now our query um actually we'll put this in. I'm going to do a double slash. This is a comment and Neo4j. So everything after this is commented out. All we want to do is reconstruct the query to verify this is a Neo4j injection. So I'm going to type in the um squiggly bracket parenthesy and then return result. So if this query works um we should be golden. Um let's see I should just say single quote um like that return result and then we will comment. So now one of the issues that I have a gripe with burp there's probably a better way to do this but if I go ahead and paste this in let's go over here. Let's see this should work. Yep. I'm going to put that query and then we're going to URL encode this. And we get a redirect. Um I think this goes to like a not found. It's a little bit of a pain to see this, but since we're in the URL, a plus is not a good way to encode this. Um also, um these slashes are also not a good way to encode because slashes are used as delimiters. So, I'm going to do the quick lazy thing is um let's just do a said and I'm going to match um every space. I'm going to replace it with percent 20, which is the better way to encode a space in a URL. And then we want to match every slash. Um and we're going to replace that with I think percent 2F is the hex for slash. So, this is the query I want. So I'm going to now copy this. We will go here, paste it in, and there we go. We get a 200. Okay. So what I want to do is intercept a request. So let's just grab this. I'm going to paste my query in and hopefully we get mystic elix. Awesome. So we have kind of validated this is going to be a near 4J injection. Also like we see the quote is at the end of this. So like it's kind of funny that it's like reflecting this input here, but we now we know we have cipher injection. So what we want to do is try to find what data is important here. And it's part of an but we can actually dump all the users and password hashes. I'm going to show that a bit later because I want to like put the lets us skip a few steps after we do the initial exploitation. Right? So um just keep in mind normally when you do injections of course we go for um the user hashes and all that so we can crack but for the sake of the video I think this is the easier thing. So there is a registration key right here. We want to exactly figure out what this is and we could go back to claude to see um if it found it. And um well, I said I wanted to ignore how to dump users, but we can see uh Claude did the claw thing and just magically did it for us. Um injection confirm. Now it crafts a cipher union payload and uses that to dump password hashes. Um so again, um AI did solve it. uh this piece it doesn't like if we try to crack this it doesn't work but um it is showing you kind of like the power when you got it on what to do. Um so hopefully um you enjoy seeing this type of stuff. If you don't want to see this type of AI in my videos, let me know in the comments and I will avoid it. But I mean I like showing people how I approach problems, right? Um I'm not actually going to use this union. I can do another cipher injection a bit manually. Um, and I also think like it's important to know how AI approaches a problem versus how a human may because um, detections are cool, right? So, let's see. Uh, what we want to get is registration key. We'll focus on this user stuff in a little bit. But, um, let's go back over to the source code. I'm over here. Uh, let's see. Let's grab, um, registration. That sounds like a cool thing to do uh, for everything here. Let's see what do we have. Um, registration key. This is where it's set and connections are S. How is this looking? Registration key. That's removing. Okay. So, there is a config structure. We got is initialize and registration key. So, I'm going to assume maybe there's a user strct as well. And that's where the users are. So there's a product. So I want to dump things out of config. So I'm going to go back into my notes and let's see. So what we want to do here is we will um let's think about this. I want to get out of this match. So we will say a um squiggly this. So now we're outside of the product and we'll do a new match of config. So I'm going to say the variable C is now equal to config and then we want to return something. We want to return the actual config and we have to have it match the structure of the product. Um, and we return as result. Um, and by structure I mean like we have this product and it has various things. Um, maybe we only have to do ID. Let's try this real quick. Um, let's do ID and then that would be like C.istration key because that was the name of the variable. And this may be all we have to do. Um, let's see if this works. I actually do not know. Um, let's see. Return that as Yep, that should be fine. Let's do the said again. Now we have this. Copy. Let's go over. Let's see. This was our incognito window so we could show registration key. Let's go back here. Um, where is my burp? Intercept. Refresh. And then we will paste this in. I'm just eyeballing it to make sure it looks fine. Forward it. Uh, we have an application error. I wonder if I did not match enough things. Let's see. C. Registration key. As a result, I didn't make any typos. I think yeah, that seems fine. So, let's go ahead and um look at what the product looks like. So let's see. We can go back to the source code grap- r product and see where this exists. Um we have some seed data. So this is going to be easy to kind of look at. We don't even have to find where the strruct is. Um let's see how is this saved. So we have an ID, we have a name, a description is authorized and created by. So, I'm going to assume um when we returned, we just returned the ID and then maybe the Rust code was trying to pull the name, description, things like that to um put on the front end and that's kind of where it failed. So, let's return all that type of stuff. So, let's do the ID. I'm going to give the ID of we'll just say config. It really doesn't matter. Um I'm going to put the name as the registration key, right? And then what else do we have to give? It was a description. We'll just give it a blank description. Um, is authorized was another one. We can do true. And then what is the created by? Is it created by ID? By ID. And I'll just give it a blank. Um, nulls are probably bad, so that's why I'm doing that. Uh, let's see if this query works. If it doesn't, I'm probably screwed up somewhere. and I'm going to uh regret life. But we'll see. We'll see if I get it right the first try. Uh what do you think? I think I'm not going to get it right. Uh it rarely works the first time. And that's why doing these boxes helps greatly because you start improving your trouble uh shooting and seeing why things fail because in real life things don't always go your way the first time. It's important to have some type of endurance. Um but that's a 200. Okay, maybe I'm actually getting good at this type of stuff. Uh, let's go ahead and paste this in forward intercept off and boom, we have a registration key. It looks like at least a gooid. Um, so let's go ahead and unregister or not unregister log out. And uh we will log in with the username if and I'll do a password and put this registration key. If we click register, successful. Um, let's try logging in. Boom. We're in. And now we have this new field of a new product. So if I say uh the product name, let's do test and the description. I always like putting some HTML in here. Um so we can see if it actually like renders this. If I create, we scroll down. Boom. We see it. But oh man, we have HTML here. Uh if we click it, we see now this test is bolded. So, it's actually processing HTML, which normally means we have some type of um cross-ite scripting we can do, right? Uh, oddly enough, if we go to the source, something that I like always doing, uh, let's go grab uh go into the source directory, um, if you just g for the word dangerous, a lot of times you maybe think you only get comments, but there are also functions in a lot of languages that say like this is dangerous, you shouldn't do it. Um here we can see the um I want to say this is Typescript TSX. I really should know but I don't know TypeScript. Um but we can see it's dangerously setting the HTML which is going to bypass any type of um like protection put in place. It's just saying you know what put this raw data here. It's fine. In this case, spoiler alert not fine. But this is why we have the cross-ite scripting thing. Um that's always a fun thing to look at. But let's see. Uh, where am I? Oh, we want to know how to weaponize this, right? So, I'm just going to let's go back into the notes. So, V notes. Um, let's see. Let's start creating this XSS payload, at least testing it, right? So, I'm going to do image source of X and then I'm going to say on error, and this is going to allow us to um run JavaScript, right? So let's say um vars s is equal to document create I don't know if it's casing um let's do create element. So we're going to create the element script on the page and then we're going to set that to be us. So http 10 10 10 15 8 um let's do port 8000. I'm going to call pone.js. And then we want to append this. I'm just making sure I'm using the right quotes. I am. Uh document head append child. So we append this to the document and close it out. I think that is good, right? Let's hope it is. Let's cat notes. Grab this. And then I'm going to make a directory. Dubdubdub. We will go in here. Python 3 HTTP server. Um, the reason why I'm making the directory is I don't want to run HTTP server in this and leak all my information in case anyone hit my web server, right? Um, but yeah, let's now go back. New product, uh, test two. Let's see. I probably should paste this in somewhere and test it, but you know what? You only get one shot to live. So, let's go ahead and create that view. That may be looking good. And boom. We do have a hit. Um, oddly enough, we have a hit from uh the actual bot, but not ourself. Like, this is not 127001 or 10158. This is the IP of the box, which means something hit this page and then reached back to our web server. But why did we not hit it? Um, let's go over into the console and we see we have a blocked loading mix content. It's blocking it because we're on an HTTPS page, but we linked an HTTP thing and Firefox by default is blocking that behavior. But apparently, um, whatever is running on the server is kind of ignoring that mixed content thing. So, what I'm going to do real quick, let's just do a listen with netcat on port um 8000. I'm going to run this again so we get another hit. Do we even have to hit the page? I think we do. Let's click view. Okay. And we can see it is running in headless Chrome. So, we know what the page is or the bot is running. Um, do I actually have to create a new product every time? I think I may because I'm refreshing and not getting that hit again. But what I wanted to do is get that hit on um my Firefox. So, I'm going to go into about config. And this is always a dangerous thing to do because I'm going to weaken my browser security. But we're going to disable this um mix content black block active. And now I should no longer get this warning. It is trying. Um it gave us the warning. And we can see um a hit. So now if I go back to my HTTP server refresh, I should be getting yeah 10158 is now reaching it. So um that is going to be my browser. What we want to do now is create pone.js. And we have to find a way to kind of weaponize this. Um if we look at our cookie like the first thing to think is how is our cookie set? It is set http only. So we can't just xfill this. So we have to think about what things we could do through cross- sight scripting. Right? We could have the bot send us the page back like just send us the whole HTML it sees and see what functionality it has. Um that would be cool. But if we go to this profile we do have pass key. So we could try to man in the middle essentially the bot enrolling in a pass key and that would let us log in as the bot itself. So let's go ahead and try that. Um I guess we don't know what username that bot is running, right? So maybe the first step is let's go and look at a page and see what that exactly looks like. So let's go ahead and start our payload.js JS and let's see I'm just going to use async because this makes it easy to use fetch and things like that. Um let's see can I do I think that should be fine. So that's just going to put everything in like this asynchronous loop where we can make this easy. So let's do const lh host and we want to send it over to uh 10 10 15 8 and we'll send it to port what 8,000. That sounds cool to me. Okay. So the first thing we want to do is um get the profile. So we're going to do a profile response. Let's camelc case that like that. And that will be await fetch. And then let's do dashboard profile. Like that. So we're going to fetch the profile. And then we want to um grab the text of it. And that should be all we need there. And then we want to send the page back to us. So we'll do send page await. And all this await is doing is saying wait for this command to finish before going to the next line. Um we'll do fetch. And then we want to fetch from what is it? Lhost. Um that should be fine for now. Let's just grab L-Host. Eventually, we'll have to give like special pages and things like that, but this thing super simple. Um, we're going to tell it to go into a post mode so we can put the body of the text in body, right? We'll say profile. And then I'm going to tell it to disable cores on this. If I don't do it, then it's going to make a options request to us to see what verbs are supported, things like that. And that just screws up netcat. So, I'm going to tell it um let's ignore course for now. Save that. And I think this hopefully works. Um, let's cat payload and we'll test it against ourselves first. So, let's just do nvnp 8000. Go in Firefox. Refresh here. Paste. Uh, syntax error. Missing variable name. Uh oh, hold on. We have to put that in brackets. We're not talking about bash. This is JavaScript. There's that. And this is why I like running things locally, right? Um if we did this and try to get the bot first, uh there's a very high chance we just spend a lot of time waiting around, not getting the result, not knowing um where we screwed up. Like we just screwed up again. Where are you screwing up? Let's see. Method P. This highlighting looks off to me, but that that looks fine by eyeball. Oh, I don't know. There we go. I think I found it. I probably should be using an IDE or something, but I think that's it. cat payload. Let's go ahead and third time is the term. It would help if I listen on 8,000 first. That looks good. And we get a page back. Awesome. Um, I'm just going to Gret for my name. I can see yes, my name is definitely in this page, right? So, um, that should be fine. Let's see. Payload. I'm actually going to send this to Whoops. 801 because I'm lazy and don't want to stand up like configure my web server just yet, right? I want to be able to listen on 8,000. So, um, the bot can get payload.js and then 80001 is going to be where I get the response. We could code our own web server where like hitting uh /pal.js gives it that and then hitting slashsave page will write the file for us and we don't need to use netcat. But, um, that's more work than we want to do at this point, I think. So we can save this. Let's go ahead. New product. Give it a name. Create it. Uh and own.js. I caught a payload. There we go. And I have to probably create a new product. Um, please work. Paste this. There we go. We get a connection. That looks all good. Let's do Firefox page.html. And let's see. Um, it kind of broke because we have the headers in. Uh, we can clean that up real quick. Let's just delete that and see what this looks like. But you did see the username and stuff. It's It's still ugly, but there we go. We have username admin, user type admin, login type, pass key. So, we know we're targeting the admin with this um exploit. So, let's go ahead now and we're going to have to change up well um we have to figure out how pass keys work. So, I don't think there's a way to do this piece in Firefox. I want to go over to Chrome because Chrome has something cool. I mean, I guess we could if I like had my UB key hooked up to my VM. We could do that, but um this makes it much easier. So, let's do ipsack password login and let's see. Uh yeah, profile. If I enroll pass key, if I did this in Firefox, let's go this enroll. It wants us to touch our security key. I don't have one connected, so it's not working. Um but if we're in Chrome, oh, there we go. It's asking to touch my security key again. Don't have one. Not working. I'm going to press F12 to get in develop mode. Control shiftP. Uh that am I in Firefox when I did that? Yeah. F12 develop mode. Control shiftp web off. And then you get into this web off console. We enable virtual authenticator environment. Let me just remove what's active. And now we have a virtual pass key within Chrome. So we can uh support everything. Let's click add. And now if I click enroll pass key. Hold on one second. I want to turn intercept on when I do this. We can see the request that it's making. So let's just forward it all. Um we'll look at the request in a second. We see we have a pass key. Now if I go back into Burp Suite, uh let's see the first request we're sending something blank. this request, we're sending a bunch of data. This is the challenge response kind of thing, right? So, here we go. We send something. This next action is critical in um next.js things. This is going to be like how the application knows to hit what endpoint it does. So, we'll need to make sure we set that. But we just say, hey, this essentially says we're starting a pass key authentication, right? And we give it no data. The server responds back to us. Um, maybe this is ID. I don't know exactly what this field is. Gives us a weird blob, but here we have a bunch of JSON. It's giving us a challenge, right? It's saying, okay, here is a public key. Sign it with your security key and get back to me. So, that's what this request does. And we have a different next action. So, this next action is probably like finish pass key registration or something like that. And it's going to go, okay, here's the response. Here's it all signed. Um, this is a JSON blob. can see like challenge whatnot and then it goes okay um here's this again again don't know exactly what that is but result null you passed um we go here do we see that anywhere I don't um so I don't know exactly what that is but that is how the pass key thing is working so we kind of just have to mimic this in JavaScript and we're not going to purely use JavaScript I'm going to mix it up with a Python server. So, we're going to create a JavaScript thing. It's going to hit this request. Um, it's going to say this is the next action. It's going to grab this data. It's going to send this data to our Python server that has a virtual key authenticated. It's going to succeed this challenge, feed it back to the JavaScript. The JavaScript's then going to send the response back to the server. It's going to enroll the pass key. But really we calculated the pass key on our device. So now we have it and we can log in as that user right. Um if you want to see pass keys working. Um let's see my username is IPS. So I can do log out. We can go pass key. Log in with IPSAC. Click log in. Um I think I have to enable pass key on this page. It's really painful. Uh web off. And there we go. Enable it. We have the pass key here. Log in. Uh maybe because I closed it, it lost my pass key. Um ipsack password. I don't know why it did not save. That is added. I wonder if I just click enroll. There we go. We have it. Log out. IPS. There we go. And now we can log in. It just magically used this pass key. And if you want to look at the things, we make this request to off pass key. We say, "Hey, we're IPS." It says, "Okay, great. Here's a challenge." We take that challenge. We sign the challenge with our uh pass key and it gives us a JSON token back that we can use to log in. Um, and the JSON token says we came from a pass key. So that is this in a nutshell. So, um, yeah, let's go ahead and, uh, do this. There's one thing I want to look at real quick. Okay, I was just looking at this only for paths. I noticed this with the pass key. Um, they're both set to null, so that's not really important. So, let's go ahead and start this JavaScript. Um, I'm going to copy pone. Let's just back that up to till day, right? And let's see, cone.js. And we will need to create some um variables, right? So, we're going to do a con start pass key regge and we will do one for stop as well. Stop pass key regge. And let's get those two numbers. Let's see. It is probably here we sent blank. There's the first one. This is going to be start. And then the second one is here. This is stop. There we go. So, let's see the profile response. Um, we hit the profile. Then what we want to do, let's close this out. Like that. I think I think that's good. Um, let's see. We have to do a method of that's not in quotes. Post. Then we want to send a blank body, right? uh we want to set the headers. So the header was next action and this is going to be start pass key regge. And then I'm also going to set the content type and grab whatever content type we have. Whenever using fetch, it is important to set the content type variable at least with a uh post request, right? content type is not part of gets but it is on fetches. So, we send this and I think that's all we do, right? We grab it. We have the profile. We have the body of it here. Um, let's call this um I call it challenge response. Challenge. We'll do challenge body. Yeah, that seems okay. And I'm just going to um filter it out because I want to grab the bottom line and I just want to send this JSON data. I don't want to send one colon. I just want to send raw JSON. So let's go ahead and um get this. So const challenge JSON is equal to JSON parse the challenge body. We will split it on new lines. Grab the bottom one or the second. And then I'm going to skip two characters. So, that should grab this line, skip these two, and just give me the JSON. And let's see. Um, I'm just going to console.log that challenge JSON. We will delete send page because we don't need it. That all looks good. Let's cat this. going to grab it. We will go to a browser. Refresh console. Paste. And it's pending. We probably screwed something up because I expected it to log. Let's see. Cons const we fetch this method post. Why is it still pending? Do I forget a semicolon somewhere? I don't think I did. Let's see. console.log challenge JSON. Wonder if we need to like say JSON. Stringify maybe. console log showing JSON and let's say console log response getting and the final one will be starting Okay, so this should hopefully help us figure out where we screwed up. So we can copy this, refresh the page, paste it. Getting showing it just magically worked that time. So now we have the JSON blob. So let's go ahead and go in pone.js. We can remove some of our nasty logging. Maybe it was this JSON stringify that fixed it, right? Um, normally JavaScript's really good about um automatically doing all that for us. But now we have this um response. Let's see. All we want is the challenge. So we'll call this const challenge is equal to challenge JSON. Oh wait, challenge JSON dot result and challenge. There we go. So that should just grab me like this public key because that's what I need to send to the server. Okay. So let's see. This is going to be one start registration. And here we're going to start having to create our flask server, right? Because we want to send this to our box. So we uh to get response and let's go ahead and Google. I'm going to turn burp suite off. um web o and github python. Uh what else do I want to add? I'm going to add soft and virtual and see if I get a library real quick. Here we go. We have one. And there is a painful thing here. Um it doesn't support arguments. So we can't add some features that we need. If we go into the PRs, we have this custom flag support. I'm just going to use this. This is actually I want to say the box creator. Um I'm not 100% positive by that, but let's go ahead and use his repo because it gives us the ability to set custom flags with um this library that creates a virtual um authentication token or authentication device or pasky device, whatever you want to call it. Um and those flags are important. So let's see. I'm going to do a get clone branch of custom flags. There we go. Let's do Python 3-m VM to start this environment. And then source.vm bin activate. Let's go into soft web and we can say pip 3 install dot. So we're going to install this library. And then we'll probably need the um request library. And I'm also going to install Flask. I think we'll definitely need these three. Maybe we'll need more, but this is the start. Um let's call this um pass key accesss. Sure. Um there we go. I'm going to touch I'll do server.py. And then let's go ahead and open up Visual Studio. So let's start off with a from flask import flask. Uh we probably need to import request jsonify and I'm going to do send file as well. And let's go ahead and create our app. So we'll do flask. And um we need do like app.run debug true host everything. Uh, we can do port. I'm going to change it to 8,000. Okay. So, we have the flask. I want to get the um JavaScript into this directory. So, I'm going to do cp doubdubdub pone over here. And the first action we can say is app.oute. And I'm going to call this um /pone.js. And that looks good. There we go. Um, do we do anything? No, that's fine. So, all this is going to do is when we hit pone.js, it sends us the JavaScript. Um, so let's go ahead and just validate that. Uh, Python 3 um, server curl localhost 8000.js. Boom. So, that makes it easy. The next thing we need is to um start the pass key registration. So we'll do app route start and that is going to be a post. And let's see. I know the co-pilot suggestions are ignore uh annoying. Um I wish I disabled them before recording, but I forget exactly how to do all that. So we'll just live with it. Um let's do print. I'm just going to say start request so we know we hit this. Um, and then we can say challenge is equal to um, what was it? Request.json. Is that how we do it? Let's just see if this works. Um, I thought it was just request.json and then brackets, but it's telling me to use the getter. So, let's just try this real quick. Um, we did get port 8000. Let's go over to pone and let's see. Oh, we have all these logs still. I thought I deleted them. Delete. Delete. Delete. Delete. Did I not save something? I think I forgot to save something. But at this point, I think it's quicker just to do this. I know I'm going to end up regretting this, but oh well. Uh, we have that. And let's go. Um, where am I? Dubdubdub. Let's just grab the backup because this is how I sent it to me, right? So, we can copy this. Go here. We don't need to log that. Let's just send it to this 8000. And we're sending it to slash start, right? That's what we just called this. Um server.py slash start. Yes, we did. Method post profile that is no longer profile. That is going to be what like JSON dot actually we can say JSON, right? challenge JSON. I think this is going to work. U no course. Yeah, that should be fine. Um we don't need anything else. I hope this works. Um let's just try this. Go into this. We will be pasting Do I have the server running? Okay. Uh, post. We errored somewhere. We got start request 4:15. What are you? Unsupported media type. So, we definitely got here. Um, but it did not like something challen Oh. Um, you know what I always said when we do this, we have to give body JSON. Yeah, this is right. I think in Python we could just do JSON in the actual request, but in this I don't think we can. Again, this is why it's important to do your testing locally, right? We still got a 415. What's my request look like? So, we send this response header request um content type. Let's just not be fancy and we'll do the um stringify, right? Oh, we did. We did that. Actually, let's leave that content type. How did I do this before? Um, I have a feeling we're still going to hit this error. See unsupported. Yes. Request body headers. We send that still fails. Let's just try setting the content type to application JSON because that's where I know it should be. That makes the most sense to me. Make sure we are running the server. Send it. It failed. I wonder we go to the server. Do we need to have the JSON library? That can't be it. I'm going to laugh if this is it. Let's just hit up enter. Still that error. So, I'm going to do how I normally do it. Um I don't like that. Get Let's just go like this, right? I think this is it. request JSON challenge. Yeah. Um, save, rerun, resend. Still fails. Let's see. Let's see exactly what it sent me. Start user agent content type. It's sending text plane. We got JSON. Um, did I not copy after I did this? that is bizarre. Um, wants that How do I get to send me JSON request content type text plane and that's definitely not what I want. Um, let's see. Fetch send JSON body There's JSON data. So that looks fine. Content type. Wait. So they set that. That's fine. JSON stringify. I am confused. Is this what they did? Let's try this text plane still. I'm honestly not sure why that header is screwing up, but we should be able to even just fix this in our server, right? So, we're going to just say challenge is equal to request.get data. Um, and we'll just get it as data and then convert it to JSON later. It's fine. It's not elegant. I don't like it. But that is where I'm at right now. So that should be fine. Let's just see what happens here. Let's go. Server run. Why' you error? Oh, wait. Start did not return a valid respon. That's a better error. That's not the 415 we've been seeing. That is a 500 because we need to return something. That that's fine. I can work with this error. You're going to work now. I'm going to command you. No error message. And we have data. Awesome. Uh I don't want this challenge JSON result. I just want to get to public key. So that is as simple as going to pone. And what we can we do? Um challenge result. Do I need these brackets? That is weird how I have it. Go here. Paste challenge JSON challenges undefined. Um, do you just have result? I think you just had result. That's fine. Ah, result challenge. Let's fix that. challenge. Save or not save, but paste. Go here. There we go. Now I just have everything I need. This is good. We are back in business. We'll save that as pone.js. And we have our challenge. So, what we have to do now is um let's see. There's going to be a few things. Um, this is B 64 URL encoded. Um, let's see. Is there easy way to show this? I bet if I just echo B 64-d, uh, invalid input, of course. Um, let's go to cyber chef because that makes it easy. There's probably a way in command line with B 64 to do URL, right? Uh grab URL URL face uh cyersh it is input um base 64 from alphabet URL safe and I we're going to have to deal with um padding. I just remember that being a pain. Um but I guess we'll just deal with that when we get there. So let's see. We have this dart. We need to grab the challenge. Before we do that, we have to actually import that virtual device. So let's just do this from soft web offn import the device and then we need to import the flags. If you don't have this authenticator data flags I think that is um that special branch we pulled but we have this. So now we can create the device and I'm going to uh create the device here so it's more global and then here we can start playing with it. So um a test station is equal to uh device dot I think we create and then we can paste in the challenge then the URL that this is good for so sorcery.htb HTB and then we have to give it the flags. And let's see. I know I'm going to handwave this part. It's a huge pain to um debug. You can see AI's kind of um doing it for me. There is a different flag here. A test station. Um, credential data included. I think I have a type or something. Authenticator data flags. I could swear this was a flag. Oh, it's attested, I think. Attested credential data included. There we go. That should be fine. And I think here is where we actually will um get an error. So if we just return. What happens if I do this? I think we're going to hit many errors with this, but let's just see what happens. Run it. We have an error. What's this say? It's having a problem decoding this base 64 in the public key challenge. So that's what we have to fix here. So we did challenge public key is equal to URL safe B64 decode. So what what we're trying to do here is put this challenge in raw bytes. So, if we do this from base 64 import, yep, it's going to probably error here that um some weird padding issue is my guess. Um, you're all safe. Incorrect padding right at the bottom. And I wish there was a flag um for this. I know like in 2017 there was a PR issued to create a flag in this library to ignore padding and just like four or five days ago there was finally movement on it almost a decade later. Um I wish I knew how to pull up that PR immediately but I can't remember exactly what I googled to find it. So we'll just fix it. Um fix padding. Is it going to do it for me? Uh padding l challenge maybe we'll have AI just magically solve that paste. We still error uh bite is not JSON serializable. So the issue now we just ran into um we decoded this. So we have the challenge as um raw bytes and raw bytes can't be put into JSON. So we have to have a convert function now to go into um our object and convert everything into uh B 64 essentially. uh def convert and we'll do dictionary. I have no idea. Um, please say this works cuz I don't want to do that code. Send. We errored. What are you going to tell me? Bite is not JSON serializable. Um, did that not just change my object in place? I think that's what happened. We I think it worked. Um, we have something we had some type of progress. Uh, we don't know exactly what that is because we don't have logging, but um, let's see what this response looks like. So, we have the send page. So, I'm just going to copy this and we'll say um send uh I I guess I I'm bad at these names, but let's see what happens. Uh please please please work. That's there. Empty string. That is not what I wanted. That is not what I wanted at all. Is this going in Burp? Uh, I don't think so. Can it go into burp? It can. So, we sent this and we got null. Well, we didn't error because we got that. Um, okay, that's annoying. Um, what if we just get rid of this JSONify Let's do print. Why are you not even printing? Oh, um, sure. XSS. Sure. I don't care about vulnerabilities at this point. empty string JSON serializable. Okay, so we have to fix our code and I bet it's going to be where we let AI just take the reign. So let's look at this. We're going in if it's is instance. Wait, what? We want it to encode. Um, let's do URL save B64 encode. And we're going to kind of rewrite this. So we can say key value in dictionary items. Um, maybe I'm going to regret this, but let's try it. Uh, Python 3 serve. Let's go here. Run. Still empty. But we didn't error. Why are you empty? Let's see. That was better. Um, it just did not work like we thought it would. Oh, I don't even have to keep restarting because we're in debug mode. So, it automatically does that. Return to empty string. So, that's in bytes. That's in bas. This is definitely 100 times better, right? Because this is before the convert. We see it's in bytes. And this is where like um it's not JSON serializable. We've now B 64 encoded it and it's still just not returning exactly as we expect. But this is the request we made and we have it. So our issue now is in the JavaScript itself which is much much better. Um let's see. Uh we have send page.ext. That's this. We go to start. That's awaited. That's awaited. Why is it empty? Um, we have something weird here run top 544. So when I paste this that send page body should be this and that should be send page that's this. It should not be an empty string. So this is it. We grab that. Then we go to start. We sent this to our server. Our server responded with this. It tells us it's JSON. Um, let's see. JSON stringify. I wonder if we have to parse it. I bet this is now just an encoding error. Let's see. Await. Send page.ext. Does it like ajson uncaught? JSON batch post. It almost feels like this is running before this finishes and the send page is blank because even that's text no matter what we should be able to get a length zero. So our issue is now completely on the JavaScript side. I want to say do that because in burp we're definitely sending it. I hope that line break there is not the problem. Let's see. Do we have a post from a long time ago? Or actually we have sure this. Yeah, we do send a line break at the end. This is actually a little bit baffling. Okay, I did some research and this mode node cores is actually what's breaking it. Um, when this is set, we don't have access to the body. Um, so let's get rid of that. I also noticed we have a small typo here. It doesn't look like it broke anything, but we had content type twice there. Um, so let's see. Now that that is out, what we have to do is add cores in our Python application. So let's do a pip 3 install flash cores has been installed and we can say from flash corores import uh cross origin and corores and we can say corors app to enable it and then this dart we can say put cross origin headers in and suddenly I think this is going to work. Um, wonder this JSONifi is going to break anything. I don't think we want JSONify there. Let's just try this. So, run the server. Let's go here. Paste zero still. Let's see. Go and start. Let's see what we have. Control origin null vary. We have data. Um, oh wait, I didn't grab a new payload from this. So, let's just copy this. I just hit up, right? Please say that was the issue. 650. There we go. That is much better. Um, so now we have size. We can start beginning here. Um, we can go back here. We don't want that grab. This is making me feel much more sane. Uh, browser security sometimes creates issues. But here we go. We have the JSON back. Um, so we can now send this to the next stage, right? So right here, um, this is get the challenge from the server. This will be send the challenge. Um we'll say to the MITM to calculate the response. Sure. And we will do this. And let's see. We sent a dashboard profile. The method is post. We're sending the body back. Stop it. Um we have to send this is application JSON right um we can set this although I don't think it actually matters um is that all we do in the JavaScript uh I think it is um we can do a um yeah I think that's all we have to Let's just see if this works. Uh let's see. Go to the server. Let's go to a different account because we have Oh, we don't have a pass key here. This is just login type password. Um I think we enrolled on IPSSE and this is IP. So that may be fine, right? Debugger is active. Let's go ahead and grab pone. We will paste this in. And looking at this, we have hit start. I don't see any errors. So, if I refresh, if we have a pass key, it should tell us we have a pass key enrolled. And we don't. Um, hold on one second. Let me validate my thought process here. So we do this IPSAC password profile. Yeah, because we have a pass key ID here. We don't have pass key ID here, which leads me to believe we have not successfully completed it. So let's see. We have stopped pass key registration. We did do that ID correctly. Um, I wonder if we have to like say we'll just call this const finish is equal to this. We could also have looked at burp and saw what it was, but we'll just try this. Finish text is equal to that. Log it. So now we'll have the error message inside of Firefox error unprosible entity. So there's something it sent it did not like. So this is what we sent ID. The content type is application JSON and we get unprosible entity here. I wish I knew the first one. That is us logging in with the pass key. Okay, so we're sending I wonder if that's it. I wonder if it's a content type. So, I think this is us finishing. We sent this data. I'm just going to send that to repeater. And it actually was this even though it was JSON data, which is a little bit funky. My server started still unprocessable entity. But now we should have a successful and a error inside of Burp. So we can see why it errored. So that's 609. We have next action 609. So we give ID, row ID, response type. So, it's giving it in a list and we just gave it in straight JSON. So, I wonder if that's the issue. Um, let's see. Where's the body? Let's just go that and that. So, we're now going to send this in a list. paste result null. That is much better than what we were seeing. So now if I go back here, we now have a pass key. So that is all fine and dandy. We now have um this working. So we can enroll pass keys on accounts. Now what we have to do is complete the authentication process. Um because right now we just have this pass key. I don't have a good way to export this pass key, throw it into Chrome and log in manually, right? So what we could do is after we register the pass key, use the pass key and grab the um JWT. And thankfully we don't have to do much in JavaScript here. Um all we're going to do is tell our application um the pash key registration is finished. So let's do a fetch on LHost. And we're just going to say um pass key. And that should be all we have to do, right? Yeah. All we have to do is hit the pass key endpoint. And this is going to tell our um script the pass key authentication was a success. So now we can use the pass key and log in. So let's say slashpass key and all we have to do is log in. Um let's see response. We have two responses. The first is going to be the um login. Let's go over to proxy pane. Where did we log in? Let's see. O pass key. Is this it? Profile. Looking for where we go. Yeah, this looks like it. So, the first thing we do, we send it a list of just IPS. That's going to be our user. And we have to get the um next action. So, this is going to be pass key start login. So, we'll go back up here. Where' we do it? Um I'll just call it um pass key start login is equal to that and then pass key finish login is equal to I'm going to be impressed if it actually knows the finish because that'll be mean. It has been trained on this and already knows the answer. Let's see. The next action is 5 a. I have no idea what it was thinking this d whatever. So, it was just making up a random MD5. But now we have the start and finish. Um we have to um send a request to essentially just um have a blank body. So let's do request post and then we send the post to um sorcery.htb was it pass key? Yep. O pass key. Then we want to do uh JSON is equal to or user um where am I logged in as we're going to leave it as it um for now. And then let's see we need to put the headers and that is the next action. And do we do anything else? Um, we'll have to tell it to ignore SSL errors. And I want to say that's it. Uh, we also have to import request. There we go. Is it a capital B? No. Post. There we go. Where are you erroring? Uh, was not closed. You are closed. I'm going to ignore that for now. Oh, um, forgot the comma. There we go. And let's see. Reload it. We're splitting because what it gives us is this. We want to grab that. And then we skip two because we only want to grab that. That all makes sense to me. So after the challenge um we want to grab the result challenge component and I guess we can print this. Let's see what happens. So we're going to restart this. Let's go ahead and go into pone grab. Go here. Paste. Um, cross origin is missing. That is odd. Let's see. Pass key. I wonder if I have to go in my script server like that. Same origin policy pass key cores. I don't know if that's actually a bad thing because we are kicking this off. We're just getting an error inside of this because we're not returning anything. Sure. There we go. That's better. And I think that is what we printed, right? Um, let's go in here. Print starting pass key login. Let's see. Starting pass key login challenge. There we go. So, we're getting very, very close to having this done. Thank god. Um, so there we go. We have the challenge. Um, now we're going to have to do pretty much the um same thing and uh fix that padding issue. So, we'll do challenge public key is equal to challenge public key. And how do we fix padding? I guess we could just do it this way. Um, that should be fine. Okay, that is done then. Okay, now we've B 64 decoded it. Let's now do credential is equal to device get. And this is going to be where we open up our um virtual pass key authentication device. I guess that's a good way to say it. Um and we want to give it the flags. of user present and user verified and then let's go call convert on credential and that's going to B 64 encode all of the binary data right and now we can say um this convert actually goes outside of that And let's say response two and let's see I think this is all we need right we give it the credential we give it pass key finish login and verify false I think we may be missing one thing because I think I have to put like the username in here um but let's see what this does. And this is happening on our box. So let's do a proxies is equal to 8080. There we go. So now I'm going to have this request in Burp Suite. Go here up. Something's not serializable. That is bad news. Let's go to the very top of this. We only have one off to pass key unprosible entity type because I think we need to include the name or do we have to put it in a list? Let's see. I'm going to say this has to be in a list just like the last one. And we also have to include the name of that. It's already refreshed. Run it. Look, something's not serializable. We go here. This post We did get a token. So, our script is wrong. Um, I don't know where this is erroring. Uh, I think it's erroring at trying to send the data back. Um, sure, that's fine. And let's just do print f um JWT. We'll say off response that I think this is going to work. So, start it, run it, go to our script, and there we go. We have author response, and we have a JWT. So, now the fun part. Um, let's just go ahead and change the name over to admin. Uh, we have to go up here to admin. There we go. And we have to find where we have that payload. I think that would be this window that grabs pone.js. This is going to be the magical part if it works. Right. So, we have the server. We go. We create a new product. Um, we call it give me your creds. Put in the p um the XSS payload. Create the product. We click give me your creds because right now it all Oh, no. Wait, did it do it? It may have already just done it. Um, maybe it happens when we create it. Let's see. Token this echo-n B64D. Yeah. So, it already did. I guess when we create the um user or create the product, it's when it does the uh bot to make sure we log in. But now we have this. We can just go into Firefox, F12, go into storage, and paste this in. And then when we refresh, we are magically admin. And we can go and see all these new endpoints. But now that we have that, um, I'm going to log out and we're going to skip all of that. I don't know how long I just spent on um, this segment. It's probably close to an hour if I feel like I'm doing this correctly. But um there was a pretty big bypass if you know Cipher. Uh Claude kind of pointed it out. If we went all the way back here, we had the Argon 2 hashes of both admin and IPSC. Um I guess we will show doing this manually. Um but all we're going to do is take our hash and then save it over top of admin and then we can just log in with admin. It's a pretty fun bypass if you know cipher queries. So that is all the way back over here. Um let's go. Uh we I guess can get rid of this pass key. Um I probably should save that JWT just in case we want it. So save this. The notes uh notes is open here. admin JWT. There we go. That's saved. I don't know if I copied that correctly. Oh well. Um, we can just redo the login in a second. So, what do we want to do? I want to clean up some of my PES. Um, I want that. So, this will be malicious server. I'm saving that one because I'm in the virtual environment. Uh don't need this. Okay. So, let's go back into notes and essentially what we'll want to do is return users. And the tricky thing is going to be is this product is meant to return one object because we go to um this page give it the ID of one product and it returns just one thing. We want to return multiple users. So we'll have to kind of do like the cipher equivalent of group concat and my SQL, right? But let's start off by doing a match user to match the user object. And then we're going to do a width reduce. And this is going to be like that group concat thing I just said. So we'll say um the separator is equal to nothing and then x in the collection of username. And then we're going to give it a colon and the password. Then we uh say s plus x. And I'm going to give it a line break so we can easily um separate the users. And then call this users. And then we just need to do the same return we did before. So we'll say return ID. Um doesn't really matter what we return the ID as. Uh the name I'm going to say all users and then the description we're going to put users. That's going to be um that. What else do we have? We had is authorized true and created by ID nothing. Then we close that and say as result, right? I think that's all we do. We already got the return. Um, do we have the set in our history here? We do. We'll do tail one. And I think this is, if we did this query correctly, we'll dump all the usernames. It'll be the same output Claude kind of gave us. Um, I want to just paste this. Uh, we have an error. That may be error because uh, Firefox is doing some encoding I don't want it to. So, let's just safety net check this by doing it in Burp because we know how Burp encodes things. No internal server error. So, we have some type of mistake which sucks. Um, do we have to close that out? Let's look at our query votes. Let's see. This config worked. So, we do the match. That's fine. This piece is new. Collect you, username, you password. I think that is good syntax. That is closing out the reduce. We say as users return let's see we return we got ID we got name we got description is authorized and created by ID. We have all of that. It's not something as silly as that has to be capital. I know that. Do I though? Did we get lucky? I don't think we did. It's got to be with the reduced piece, but we will double check. Does not hurt. Go here. Paste. Still internal server error. So, let's go. That looks fine. We close that out. Close that quote. Oh, I forgot to close this quote. Um, or at least open the quote. Come on, Vim. Let me type a quote. There we go. And this, I think, will work. Copy. paste. There we go. That's looking better. So, if we go here, uh, paste that forward. The response is horribly ugly. Um, but we got it. I I don't know what we did wrong here. Um, but let's see. Can we uh v out paste. There we go. So now what we want to do is take one of these queries that have our password set as password and save it on administrator. So let's cap this so I can copy the hash B notes. We will paste the hash here just so we have it. And now we can begin creating the payload. So first things first, we want to close it out and we do the match user again. So we do match user and the difference here is after we match the user we want to specify which user we want to match right. So we're going to open a object. We're going to say match where username is equal to admin. And then we can do a set and say the password then becomes that argon 2 hash of um one of our other users. And then we just have to do the simple return. So we can say return ID. It doesn't really matter what we put here. Um name um admin. We'll say the description is password reset. And what other things did we have? We have is authorized true and then created by ID doesn't matter. Um, I can say as result and then comment. If this query is done correctly, it will reset that password. Um, if the video is gone to plan, um, it probably won't work the first time. I'm going to spend like five minutes debugging it, but we'll see. Let's go ahead kill that. Go to burpuite intercept on. Hit enter. Let's paste this in. That looks good to me. Forward says admin password reset. So if I log out, I should be able to log in with admin and then password. Um there's a weird bug in the application. Sometimes you have to log in twice. I'm not exactly sure why, but there we go. We have now logged in um as admin. Uh, these pages say require pass key to log in, which is a little bit annoying. Um, what I'm actually going to do because I think I copied that JWT wrong, we're just going to use a virtual pass key to quickly log in. Um, let's go do this twice. We already have the web auth. This fishing stuff becomes important in a little bit. We're not going to worry about this second. Um, we have this host. Um, I'm going to give it 10 10 158. Uh, we'll give it port 80. We'll give it 8,000. And then let's do nvnp 8,000. Send it. We can see the server makes a request to us. We can say add data field data it wants as hex. I'm going to give it a. We send or I gave it b's, not a's. That's fine. Uh, we could also expect a response and we'll give it C's. Um, maybe I close it. There we go. We have 43s, which is C's, and then my line break of me closing it. Um, I think that's end of string, not line break, but whatever. Um, we can see we can open things and uh like this debug lets us send hex or send bytes to a packet or to a socket. If we wanted to, we could find exactly where that is in the source code. Um, let's see. Infrastructure grap. Uh, where are we going to do? Um, debug. Sure. Um, let's see. Do we have it back end? Is it DNS sort main? Let's see. Is this it? Function vector DNS entries. I don't think this is it. Um, but something interesting came up from looking at this um thing. Uh, we have something listening. Um, and I the thing that stuck out to me is we have this process match and then bash- c argument spawn. Um, so if we can get into this argument, we have command injection. We just have to find exactly what it's doing. We have this consumer poll and it's Kafka. So just think now we have Kafka to um run commands. And what I would have done if to find that, what I planned on showing is just grepping for um like command and it shows us where we import like OS, right? And then we see that. But I think we're getting ahead of ourselves right here. Um we want to know what this debug is. So we could also just look for files that have debug in the name. And we see the backend API has that. If we open this up, let's see. Request port data. This looks like it. Um, let's see. Port data function. And here we go. We can see TCP stream. We're connecting to the host uh the port and then we're just sending data if it exists and then getting data back. So, this is essentially just a way to craft packets. And now the main issue is going to be um we can only send packets once. we can't perform any handshakes or things like that. So any like service that is running on SSL we can't do. Um but we could uh let's see probably test this out to hit various endpoints, right? Um from just before we talked about something with Kafka and command injection. Keep that in the back of the mind. We're going to go there in a second. But to really show this, um, I think it's easier to wrap our heads around TCP, right? So, we have a box called get T. It looks like it's listening on port 3000. So, let's play with this real quick. So, we can do get T. If we left it on um this, we get not found. It's on port 8,000. If it was 30,000, we get a response of null. Uh, we say expect response. It's going to take a little bit longer. We sent this. This is an HTTP. Um, we want to send an actual HTTP request. So, we need to do um get let's let's just formulate it in a command prompt real quick. Uh, v http sure. We can say get/http11 host get t. And I think we do two line breaks, right? So, we have that. Let's convert it to hex. So, we send this So we can go here. Send this data. We expect a response back. It's taking its time. Hopefully we get a page. It looks like we will copy this v out. Uh we already have out. Uh v page. I should have done like page.hex, but oh well. We can cat page and then xxdr-p. And here's the get t page, right? So we can craft a packet. If this was SSL, we couldn't again because SSL will require a handshake and we can't do handshakes. Um, but there was CFKA. So, let's see where was I. So, we had get T. We do have Kafka that's listing on 9092. So if we went here and said Kafka 9092, we can send it bad data, but we get response back. Um, if we send a port that doesn't exist, I was hoping it would give us a weird error. There we go. Not found. So 9092 doesn't error. Um, we just have to send a Kafka packet. And let's exit this real quick. Um, let's see. We wanted to get back to command this DNS source. Yep, this is going to be what we want. And I am going to use AI heavily for this next part. And we're going to hope it works for me this time. Um, always a danger of using AI in the videos because you don't always get the same response you did when you ran it a day or two ago, right? But this is it. Um, let's see. We have this Kafka consumer. It's with topic with group. We want to see what those are. Um the topic is equal to update. The group is equal to update. And this is just like a messaging protocol. So what this is doing is connecting to the Kafka broker subscribing to the update topic and anytime someone writes to this update topic, it can see that um update and then it runs code. Right? That's what this polling is doing. It's waiting for someone to write an update message. And then when they write the update message, it's going to iterate over that. We go in, we see, oh, that update message has a value. Okay, let's keep going. That value, oh, that's a command. And now it's going to match that command and run it in bash. Not exactly realistic. It's kind of just showing you um Kafka, right? So what we're going to do is hope we get lucky with claude and create a um oneshot. So let's say I can send raw bytes to a TCP socket. Kafka is listening on port 9092. I want a single packet that contains if I can type contains whatever I put as arg one to Kafka's update topic group. Look at the proto call here and create a Python script to construct the packet encode it in hex and then I just have to um give it the um documentation where Kafka's protocol is and maybe that's this. I don't think that's it top link. We'll give it this. This looks like it. Paste. And we're going to see what this comes up with. Um I hope it works well. I know I'm kind of cheating on this part, but it if I wasn't, it would probably take me like three to four hours to like reverse this protocol and figure out exactly what to send. Um, magic of AI is really good at doing these types of things. Okay, it looks like it has come up with something. So, we're looking through, it's kind of explaining the packet. It's using um API key zero. I think it should use API version zero. Um, maybe it'll work. Um, we'll see. It does give us the hex there. So, let's go ahead and try this out. I'm going to open a new pane. I don't like closing this because we have some things in our context if I want to make updates to it. Um, it's always helpful to make the update in the same exact chat pane that it was in, right? But we should have a Python 3 Kafka produce uh find.gra Kafka. Where is it? Um, I guess it's in source infrastructure. That should be fine. Uh, Kafka produce. And we got to give it a value. So, we'll do bash- dev tcp 10 10 148 uh 15 8 91 0 and one. And I'm not giving it like the bash c because remember in the um rust code, do I have that up somewhere? Here's a vim. Uh that is not it. Um and this it's already doing bash c. So we just had to do the bash- i portion. Um so we have this. Let's do nc. Let's get into a better directory and let's see if we just magically oneshot this with AI. Let's go ahead and put the data in. Um, we don't really expect a response, do we? We can just send this and we get a shell. Awesome. So, that was all we had to do. We have to send a Kafka message to that broker, encode it in hex, and we magically get on this container. So, let's do Python 3- C import pty pty spawn bin bash. That looks fine. stgy raw minus echo foreground enter export term is equal to xterm so we can clear the screen good we can close the AI window and start this process the host name of this box of this makes it somewhat obvious we're in a docker we could also just do lsla slash and see we're in docker environment right um we exploited the DNS box so most likely we're on the DNS entry point I bet if we look at docker entry point this will be a bit more obvious. Um maybe not. Um but if we we could just enumerate the network that like 172 uh is it 16 or 19? Whatever network Docker uses. Um do we have if config? Oh man, we'd have to like cat. Um what is it? SIS then is there a network where is it like proc net and what is it TCP will probably give us an IP address um let's see if I copy this Python 3 Uh, what was it? 0x07. Let's see. 0x AC. That's 172 19. Then zero and 7. So, the Docker network's 17219. Um, but we don't really have to do that much enumeration there. I bet if we're on the DNS, maybe like cat Etsy host will have DNS entries. There's probably some way um we could do this. Maybe dig AX FR on this to do a zone transfer um 127001 AXFR. Maybe that way. I forget exactly how to do this. We probably also have to specify um some type of TLD or something. It's been a while since I've done a zone transfer via dig, but what I'm trying to get at is we could just look at the Docker file, right? We're in Docker. We have access to the Docker Compose. And that will tell us like what's on this network, right? We got the back end. Uh we got the front end. We got Neo4j. We got Kafka. We got DNS. That's where we are. We have mail. That is MailHog. That's interesting. We have FTP. Um, and looking at FTP, they give FTP public. And it looks like we're mounting some type of certificate inside of public. So, um, we can probably get to this via anonymous, which is interesting. Uh, get t a mailbot. This is talking to something called the MailHog server. Um, fishing username, fishing password. I'm guessing we're going have to do some type of fishing here, right? We got that. That is odd. Uh, let's see. engine X and that's it. Um, if we went and looked at the actual blog, we'll see some p uh post fishing training, fishing awareness. Um, looks like Tom Summers falls for fishing quite often. This is telling us we need um to use HTTPS and have it signed. Um, right, the subdomain uses a root CA. The private key is stored on the FTP server. So, right now it's somewhat obvious to me we have to go towards that FTP server and um get the key out, right? So, I'm going to upload Chisel and that's going to let me quickly um just talk to everything on this Docker network, right? So, let's do a cdubdub. We will cp chisel chisel over here. Python 3 HTTP server um do we have which curl which w get none of those so let's not do http server we'll do nclvnp 90001 give it chisel and then I can cat dev tcp 1010 158 90001 and we'll send this to devshm chisel Um, let's see. Cat like this. 10 10 15 8 91 dev shm chisel. Does that work? There we go. That works. Um, I don't know if this is going to automatically end once it sends chisel. I'm sure there was a way I could have done this so it automatically sends that line break. Um, I think it's a netcat flag. I know if I go to IPSC.rocks, I probably have it in one of my videos already, but I'm just talking to kill time, so I can just kill this and hope we have chisel on the box. Um, let's do dev shm MD5 someum chisel. MD5ome chisel. That looks the same. That is good. Sage plus x chisel. Let's start the server. So, we'll start it here. Let's just do /chisel server reverse sock 5 port 801. That started. Let's do chisel client 10 10 158 9 uh 801 our sucks. I think it's lowerase like that. Um permission denied. move chisel to slashtemp. It's also possible that dev shm has like no exec on it. There we go. Um, so now we have connected. So I should just be able to do like proxy chains FTP. Um, shoot. We don't have the IP of this, do we? Uh, less infrastructure, less source infrastructure. Docker compose FTP. Maybe it's just FTP. I wonder if we have this. So, proxy chains FTP FTP. Uh, it helps if I type that correctly. Don't think that is connecting. Pseudovv Etsy proxychains.com. Is there like a DNS option? We are proxying DNS. Let's see. Let's just send us another shell real quick. Send it. Send. Why am I not getting a shell? Refresh. Kafka 9092. Add data field. I hope we can get two shells. That's going to be painful if we can't. Um, let's see. We actually had the script in this kafka produce b- dev tcp 1010 158 91 0 and1 get a magical hex paste the magical hex in send it and we don't have Oh that is that is annoying um let's see b- i devtcp P 10 10 15 8 91 0 and one. Let's background that like that. That did not work. do this. BC C did not work. There we go. We have two shells now. That is awesome. So, let's get back to our chisel. And we did that. So, we can just like ping FTP. Uh, ping's not here. Dig FTP. There we go. That is 1721908. So, we should be able to proxy change this now. So we can say proxy chains FTP and then let's log in with anonymous and I don't know why this is hanging. It's probably some weird like FTP thing because there's like a control and active protocol or something but we're in passive mode. Um FTP is always weird when you go through proxies. Let's go in cdpub directory changed. And I'm just hitting control C when I think it's hung and not doing anything. Um, it seems to be fine. So, we can just do a get rootca.sur. It looks like that's downloaded. It says 100%. Let's control C to kill that. And then we can get rootca.key. Says it's 100%. I'm assuming that's done. So, let's exit. And we do have two files. So, let's just make I'm going to call it fish. And I'm going to move root into fish. Go in here. And then key. Oh, it's encrypted. Cert is not. Um, is there a PM2 John key? Let's just do um root CA key crack, I guess. And I'm going to do something I normally don't. Uh, let's see. Do I even have hashcat on this hashcat? Because my Kraken is not powered online right now. Um, I'm going to see if this goes quickly. I'm going to run hashcat in my VM. First time I've done this in a video. I always say don't do this because it's going to go slow. Um, but we're gonna see what happens. It seems like it's somewhat happy. I'm I'm actually surprised it ran. I didn't have to do like dash force or anything. Um, I'm going to give it like five 10 more seconds to initialize this back time before I pause the video and we just power on the Kraken to try to crack it. Uh, but it looks like it's going. Um, what speed is this going at? Um, 63,000 hashes a second. What is the time? We're only 3.7. Like I think this would be within seconds if I was using GPUs. Um, but the progress seems to be going pretty fine. We're already at like 10%. So, by the time I power on my Kraken, I'm sure it will have cracked. Um, so I guess while that goes, we could take a look at something else, right? Um, let's go tell it to go through socks and let's see. Let's do a dig. Was there a mail hog? Did I remember reading that right on the docker compose? We dig mail hog um not available. So let's see less source infrastructure docker. It's just mail dig mail 1903. So let's see what is at this IP. So we do HTTP 1903 unable to connect because there's probably a special port. Um let's look at this again. Mail see it is not exposing what port mailbot maybe 8025. So let's try 8025. And here we go. We have MailHog. Um, doesn't look like we can send messages. We could enable Jim. I don't know who Jim is, but we have inbox. We have this that says connected. We can refresh. Um, there's not too much there. We do have SMTP, it looks like. So, if we look at SMTP, it's not telling us what the port is, but I'm guessing that just means it's going to be the default. And is there like an SMTP? It's probably just on mail, right? I'm guessing it's going to be the same host. Um, SMTP. We don't see anything here. Um, let's see. I want to say SMTP is probably like port 25. That's probably where they got 8025 from. So we can say proxy chains. This we want to use netcat- ZV 25. Connection refused. Let's see. SMTP port. Uh we want to turn socks off. Let's look at this. 25 587 465. Let's just try all these. So 25 587 and the last one was 465. So connection refused on all those. So let's see proxy chains end mapap s capital t port 25 let's say everything is online with capital PN 1721 19024 we're going to scan the whole docker subnet for SMTP. Um while that goes where is that crack? It is still trying to crack. Um, definitely probably should have spun up the Kraken. So, we'll still go in there, but we have end mapap going. Now, how slow is this? Um, 8 minutes. Let's see what else could we do. um class source infrastructure docker compose environment. Wait, I don't know what SMTP server is. I wonder if there's a separate server for SMTP. Let's see. Oh, there we go. It's cracked. Um, recovered zero out of one. I'm swear that should have cracked. I wonder if because I did it on a VM, uh, there was a bug and it didn't crack. Uh, I'm going to go down and power it on and we're going to send this over to the Kraken and see if it cracks that way. Okay, so moment of truth. Let's just go ahead and uh get the thing we want to crack. And that is a lot bigger than I thought it was. But we can copy this on our clipboard. Then sh to the kraken cd hashcat v hashes. I'll call it sorcery. Paste dot / hashcat up word listu.ext. text and let's see if this cracks. I'm going to pause the video and we'll resume when it's done. And it still did not crack, which is a little bit odd. Um, I'm going to uh try this again. Let's do pen to John. I ran that, right? And then we did um was it fish then rootca key. Just want to look at what this produces. So, we have that PEM one. Um, I wonder if my PM to John is old. GitHub pem to John. Let's go ahead. That is not John's um repo. Let's see. John the Ripper GitHub. And then we want to get PM to John. Let's just try making sure we're on the latest script. Let's w get this Python 3 to John then fish root CA key. And that's definitely a different hash. That is weird. Um so it looks like we just had a really old library. And that's one of the benefits of doing these ETFs. you kind of get these steps where you know they're vulnerable but um something breaks and you start testing your own like um system, right? So, we can remove that. Put this one in. That format is completely odd. The PBKDF um let's just try it. And you can see how much quicker this was, right? We were only in like the hashes per second. We're in the thousands of hashes per second when we used the GPUs. So, this definitely goes much quicker. um did not fit the format. Let's Google hashcat example hashes and see if we can get the format. So, dollar PM dollar. Let's see. So, this is definitely going in a slightly different format. Um it's probably this PEM 2. But before I go down this rabbit hole or this hole, I should Python 3 pm to John help. Do we have any helps? Huh? Let's just look at it. Hashcat. I was hoping there would be like a hashcat format so we could just change it in the script, but doesn't look like it. So, let's see. We do PEM24 ED. So, let's go hashes sorcery. So, PEM 2. Uh, we don't use PBK at all, right? I don't see that anywhere in here. So, I'm just going to erase that. Erase Shaw. Erase AE whatever. So, that's PM24. PM24. Then we get the hash iteration count. So hash maybe that's block size, not iteration count. That looks fine. 2 384. That's four characters. That looks fine. How does this end? Or is that the end? Save it. Does it recognize it? Do we just have to remove that small piece? Come on. Looks like we did. And it already cracked. So, it just looks like we had to remove that like PBKDF stuff, but we cracked it. And the end result is the password is password anticlimactic, but awesome. So now we can um root CA. I'm going to just do password. So we have it. Awesome. And now we can begin the hunt for that SMTP server. Uh we have finished the scan and we don't have any ports open for SMTP. I'm going to do GitHub mailog and we'll find this repo and see if it even has a SMTP server. It says SMTP testing. um SMTP server starts on port 1025. So let's try this. So in 1025 and it's open. Awesome. So let's go back over to mail hog. I'm going to set this on my proxy and we want to mail. So, I'm going to do SWAT server 1721903 port 1025. Let's make sure we do proxy chains. We'll do from admin at sourcy.htb 2 and the user we want is Tom Summers, right? Because they call them out. So, to Tom Summers at sorcery.htb HTTP um header subject verify get t account um add header let's give it content type text html so we can give them a link and then body um we're going to have to probably do a DNS name and SSL signing But for now, let's just see if they do HTTP 10 uh 158. Click here. Slash A. Okay. Pseudo NC LVMP 443. So if we email this, looks like that email went through. Let's go into here. We can see an email. It hit Tom Summers. We're asking them to verify get T. Um hopefully we see a bunch of junk down here of them trying like SSL. There we go. So, we have verified. There is a bot that is going to click that link. And do I still have my SWATs? Awesome. I want to make sure I save that because we're going to have to um do the man in the middle now. So I'm going to go into fish and now we just want to um generate a SSL script. So we'll do open SSL RSA in the root CA key and we want to decrypt it. So we'll do rootca- decrypted key passphrase is password. So now we have a decrypted key. Um the next step we want to do is create a server certificate. So let's do um open SL gen RSA out server.key 2048 is the standard size. We have to create a certificate signing request. So we can do open SSL request new key. We give it the key we just gave and we want to do subject and the common name is going to be ipsseack.cory.htb. We're going to point that over to server.csr. CSR and that is if you just want to see the file a certificate signing request. Um so finally we can use OpenSSL X509 um request in give it the CSR file. Um then we want to give it the certificate um authority. This is the public. Then we want to give it the certificate key. We'll do rootca- decrypted. Um, then we got to give like a serial. We'll give it a file of server.crt. And let's do days 14 or 15. Uh, 14's fine. So there we go. We now have a um signed certificate. So I'm going to do cats server.crt and then server.key. key and that is going to be server.pm. Okay. And then the next step is using a man in the middle proxy. We're just going to proxy everything to get t. Um if you don't have it, you can do like a uv tool install mit proxy, but I'm just going to do mitm dump. Let's just make sure I can do it in pseudo. Um looks like I can. Uh we'll do mode reverse then https get sorcery.htb the port 443 we give it a cell insecure is equal to server.pm and we'll say outfile fish.mmit. Okay. So now we're listening on 443. Anything that connects to us will be going to get sourcy.htb and we should be writing everything to fish.mmit. So the next step is we actually have to um create the ipsack what is it sorcery.htb domain. So let's go all the way back to our DNS server where we have the shell. Um let's do psf. We have DNS mask running. It's going to this DNS host users file. Um, let's see. L I don't have the TTY on this. So, Python 3- C import pty pty spawn bin bash stty raw minus echo foreground enter export term is equal to xterm. Okay. Um, PSF we want to uh sty- a rows 26 columns 121. STI rows 26 columns 121. DSCF. There we go. We want to look at this file. We don't have the cat DNS. Um, huh. Maybe this file doesn't exist yet. We can just create it. Um, so I'll do echo 101058 IPS.cory.htb host user because we're in the DNS. If I dig ipsack.sorcy.htb do not get anything yet. So let's now do that ps command again. I'm going to pkill DNS mask. So we're going to kill the DNS service. I'm going to run this command again. And we missed oh man missed some of the output. Um do I have it up here? I do it is host then DNS host. Okay. Um psf gns. You can see that is killed. Let's go ahead and copy this and it was host DNS host. I'm going to put this in the background. Failed to load name. No. Okay, I did host user, I think. Ekill DNS mask. Run it. There we go. It looks like it read one name. Um, let's do a dig. If I just do dig ipsack, let's see. D IPSAC.sorcy htb. Is it going to come back to us? It didn't. Cathost user 101058 IPSC sorcery htb dig at 127001 IPSC sorcery htb. Okay. Um I guess I had to specify the DNS server. I don't know where this is default configured, but there we go. We can see this does exist. So now we have to play the magical game of where we're swaps. Uh looks like Swax is here and we can say https ipsack sorcery htb. And before I do this, I just want to look here. Doesn't look like that message got deleted. I'm going to delete all messages. And then we're going to run this again. Let's see. We can see that mail did land. And I wonder if this will give me any output. Maybe not. So, let's go in fish tail-f. Don't see anything here yet. Oh, there we go. We have something. Server connect disconnect. Sending a bunch of data. Uh, we may just want to grab like post request or something. Um, do we have anything get? Let's see. What does this look like? So, we have data. Let's see. Tom summer. Wonder if I could give him the login URL. Let's go back to Swox. Uh, before we do that, what was it? Get. Sorceryh. kind of surprised it's not in my history. Sign in. Let's give him this link. There we go. I'm going to hit enter a few times to clear that. We'll probably see a second message. and mailhog. Make sure we're on socks. That was a minute ago. So, what is this message? So, we see, okay, I will try to sign in. So, he is mailing us back when he clicks something, which is interesting. So, we see when he actually gets that message. Uh, we have some things. So, we did a get user login web component disconnect. Oh, there is a post. So, we have a post right here. So, let's go back into our man the middle. And we do have a password. So, we can see here um username Tom Summers and this password. I don't know if it's that is his P like the question comma is part of his password or not. Um, let's see. Is this anywhere else? No, I don't want to go in that. There we go. So, input ID value. Is that actually the password? Now I'm starting to question. Um, label ID Tom Summers. Yeah, this must be the password right here. So, if we try to log into git t with this, let's do Tom summers. This password, it is invalid, but the message did tell us um pass the test. Tom entered their credentials and they changed the password. We can try sshing. So we can do Tom summers at um sorcery.htb put in the password we have and we get logged in. So we finally have a shell on a box. Well I guess we had a shell on the DNS server but let's do vgs.ext just so we have this password to summers. Paste that in. We can kill our m man the middle dump. And let's see, we do lsla on. Oh, we have root uh user.ext. Um, no docker. We have an xorg directory, which is odd. We do IPA, bunch of virtual networks, but we can see ETH0 is the IP of the box. So, it looks like we're finally no longer in a um like Docker virtual environment. Um, interesting enough there was that exorg directory. So if we go in here there XVFB and there is a screen um if we do PSF- forest uh let's see we can see that is part of like X11 we have screen here um but essentially uh we can see Tom Summers does have a text editor open I think mouse pad or something but this file is a um dump of the current screen or a process window right so let's convert this or get this back to our box so I'm just going to do nc 90001 we'll direct it here I can cat this file and send it to devt tcp 1010 158 90001 and you could just Google like this file name and it will tell tell you exactly what it is, but we can just run convert xw the file name and then I'll do out.png. And if we open it, we can get Tom Summers admin and another password. And if that doesn't work for you, um, make sure you do like an apt install image magic because chances are if that doesn't work, you just don't have image magic installed. So let's do shm summers_admin at sorcery.htb htb and the password of let's see D capital W p U K 7 CES and then BJT dash and there we go we get logged in as Tom Summers admin um if I do pseudo-l we can see we can do um Docker login as Rebecca Smith and also Srace which is a bit odd. The first thing I'm going to do is just try the Docker login. So we'll do pseudo-U Rebecca Smith and then user bin docker login. And we get some type of error. It says to login with a different account run docker log out. Um this account might be protected by two factor. I think this is all like um custom part of the box like um it's not an actual like two-factor authentication that's realistic. Um, but it seems like the challenge is there's going to be some type of script that is running docker login and we have to srace that script in order to get the password or something like that, right? Um, so my first thought is I'm just going to upload peace by on this box and then we'll use peace by to see what is exactly happening on a cron. So let's go um on our box. Do we still have something listening on 8,000? We don't. So let's just go dubdubdub and copy opt py py 64 here python 3 m http server wget get http 1010 1058 8000 then py uh we need 64. There we go. It's downloading chmod plus x pace by 64 and then we can do dot slash to run it. And then I'm just going to let this run for a while. Um, probably like 10 to 15 minutes or something. And look at what processes just magically appear. And as this is running, we realize it's got a lot of processes starting and stopping constantly. Um, we probably should have some type of filter in piece by or something like that. Um, I'm just going to see if it ever ran. So, we can search for Rebecca. We don't have any hits yet, but that is a lot more data than I ever expected to see. Um, let's see. We'll let that keep going a little bit. And I want to see if we can quickly find any argument or something. Uh, chod plus x pace spy. Is there a filter? Uh, let's see. FS events, help, interval, proc events, watch a directory. I don't see any like filter. There's a scan every millisecond for process. It's doing 100 milliseconds. Um, let's see. Do we have any Rebecca? There we go. We have something. Um, we have provision cron rebecca.p password. Then it calls this OTP script and nothing else. Um, let's see. Wonder if we can easily direct this to a file peace by.out. And if we go here, I think I'm in devsh, right? Yep. tailpiece by.out. Awesome. I'm actually going to change the interval to be let's do 10 seconds. Wonder if this changes anything if we do it every 10 seconds of scanning for new processes. So there we go. Peace is running again. It's a lot of data. Um there was something interesting I just saw. If I can find it again. It was something like with a mouse or something. uh pgret mouse pad. So this is running Tom Summers in the text editor. Let's see. That's doing something with Docker. We have constant checks on probably SMTP or something. That's 21 maybe FTP. I don't know exactly what's going on um with this. Let's see. Do we have a good way to filter this? So, let's do a cat vpy. Let's see. Grep-v. We'll do 127001. We can do actually let's do fast dash C and cat. That seems fine. Um, and then I'm also going to do run C. Let's see. What if we also add Can we do like backsplash to this to do that? That did not work. I was hoping to like ignore everything that ended with just a pipe, but that doesn't work. Let's see. VSFTPD. We don't need um she bang. We have something here. We have root OTP.sh. Let's see. Cats peace spy. I think our script as we're trying to filter this out is going into um this. So what if we just gpped Rebecca for ppy.out. Maybe this is the better way to do it. Doesn't look like it exists yet. Um OTP did exist. What if we did dash after 10 dash before 10? So, this gives us everything running around that OTP script. Does this help us filter anything? Let's see. Instead of 10, that's 20 lines every time it appears. Let's do two before, two after. And we still don't have much. Um, and I realize every time I do this, I'm just polluting the data. So, what I'm actually going to do, let's remove ppy.out. Do this again. And I'm just going to come back in um let's do 300 seconds. So 5 minutes. It hasn't been 5 minutes yet, but I am impatient. So let's see. Do we have anything yet? Um looking at this, it looks like we do. We have htpass WD. It's doing this uh registry password Rebecca Smith. And then we have this um this is going to be Rebecca Smith's password. And this is the actual OTP that I think is valid for like 5 minutes, maybe 10 minutes. But we do have the registry password. And this is an unintended way to get it. I don't think I'm going to show the intended way because um I think this video goes live in less than 12 hours. So I'm kind of running low on time here. Um the intended way we'll have to do a little bit of net reversing. I'm sure if you go to OXDF's blog, um it will show that. But for now, let's just do the quick way. So, we'll do curl-ashu Rebecca Smith. Put in this. And then if we do localhost 5000 v2, this is going to be the default Docker registry. Um, unauthorized. I wonder if it's already reset, which will be a pain. Um, let's do 7 E A Z. Uh, 280. Is it how mine ended? It is. R E A B CCA Smith. I'm hoping. Um, let's try this one more time. We'll see if we capture it. Maybe have to be a little bit quicker. Um, can I like GP for HT pass? Can I do this with Peace Spy? Well, I think so because my GP actually came back. Um, so it looks like maybe this will actually work doing it this way. So, we kind of filter through a lot of that junk. Um, because all we really care about is when HT pass comes up in a process. Um, let's see. Echo HT pass WD. Echo's not showing it. I wonder if it's because that's within 10 milliseconds or something. Like it starts and stops so quickly. Uh, VHD pass WD. This should definitely show up, right? Maybe not. D- C sleep five echo. This may not be a good way to do it, but uh, maybe it works. That definitely pulled up and showed it. So, let's see. We got the password one time. There it is again. So, let's just try this really quick. We have just literally captured it. Let's go ahead paste that. There we go. We get logged in. So, we have some time to do this. So, this is the just um general docker catalog. If we do underscore catalog, we can see there's a test domain workstation. So, what we want to do is download this um docker image. Uh there is a docker registry grabber tool um that is on GitHub. I know I've used it once before in a video, but let's just go here. I got to try working quickly. Get clone. Okay. Can I do like a UV run dg.py? Is that going to just magically work? UV tool install like this. Uh, that does not seem to work. Um, Python 3-m VM.VM source VM bin activate pip 3 install. I think this is how we install it, right? It should be uh this is what it's telling me to do. We'll do it this way. Okay, it worked. Um, so we should now be able to run dg.py, right? Python 3. Awesome. Uh, do we have the password still? Please be working. It still works. Grab this. Okay. So what we want to do is this HTTP um localhost 5000 boxy chains um shoot. We should stand up a shell. Let's kill kill and dashd 1080. There we go. So now we should be able to do proxy chains doog.py pi http localhost 5000 list and it doesn't want the password this way. See dashp put this in quotes username is dash yu um it doesn't want port 5000. Okay so that worked. So we now successfully listed. So, we want to do a d-dump and grab this image. There we go. It is now downloading the image. And then once we have this image, we should be able to um analyze this docker. And watch the password may have just died. Oh, nope. It's just taking a while to download. Okay. So, I'm going to pause the video and we'll resume once all of this image is downloaded. We're at three. There are 10 blobs, so it'll probably take a couple minutes. Okay, now we have everything downloaded. If I do lsla, uh, we have to go into the test workstation, but we have a few files. I'm guessing the ones that it downloaded that we don't have here would probably just blank. Um, I'm going to make a directory one. go in here um cd one and we can tar xvf on the first one. I always like making a directory when I do this just because um these will clobber directories and how Docker layers work sometimes it like writes the file twice if you don't extract in the right order. Maybe you miss a file or something like that. But the very first thing I get is a docker entry point. So if we look at this to see exactly what this Docker is, it looks like it's installing the IPA client. We got Donna Adam and a password. So, this looks like it's trying to join to a Linux kerros domain. This is like um identity policy. I don't know what IPA stands for, actually. Um let's see. Linux IPA. Hopefully, it doesn't get me like the Android package. Um it's part of free IPA um identity policy audit. That's what it is. So it's like the kurros for Linux um or active directory ver like the Linux version of active directory I guess I should say. Um so we have a password here. So if I sh Donna Adams at sorceryhb paste in the password we get logged in. Awesome. Now, one of the odd things if you like Grep um Donna on Etsy Pass WD um let me just spell it correctly doesn't exist. We can look at Etsy Pass WD if you don't believe me. Search back for Don doesn't and that's because this is using um the SSSD. This is going to be like the Kerros login. If I do K list, we can see I do have a Kerros ticket. If I do get ent pass wd donna Adam um Adams there we go we can see I do have an entry in like the kerros realm right this is just not using the um local ways to log in like passd shadow things like that it's using LDAP so um we could do a IPA user show Donna Adams and this command is going to hang for around a minute until it gets back I don't know exactly what it's doing. It's since it's like consistently a minute, I think it's trying to do some type of reverse DNS lookup failing and then just um magically working. I wonder do we have a way? Let's see. IPA do not DNS lookup. I don't know. Um wonder if this is going to be an option, right? Uh remove DNS server server setup DNS. It just feels like some type of DNS issue going on. And I don't know. There we go. It's already finished. So, um, we can see I have a roll. We can change user password to ash winter ldap. So, if we just did like an IPA user mod ash_winter. Actually, before we do this, we can say like um get ant pass wd ash winter. Confirm that user exists. Right. Um so now we can say what was it IPA then user mod ash winter pass wd uh no such option user mod let's see there should be a way to set password right prompt to set so it's password This will probably take again a minute to come back. I guess while it's doing that, let's just go with um putting a password on my clipboard. We will copy this and we will just wait for this to come back to see if it actually works. Um if this doesn't then we can probably do like set attribute and just manually do the password. But it looks like this came back. We'll paste. Paste again. And it looks like Ash Winter has been set. So I'm just going to open a new thing. Sh ash_winter at um sorcery.htb. Paste in the password. Um current password. Set it new. It can't be the same one. I'm going to add an exclamation point. Too similar. Oh man. Um, IPSC. Can that work? It It's Oh. Oh, this one's my current password. Put this uh What is going on? Your password expired. Current is this. New password. IPSAC shorter than Please subscribe. Please subscribe. Oh my god. Please subscribe. There we go. We get logged into Ashwinter. Awesome. So, now that we're this user, uh, we can do another IPA user show ash_winter IPA. There we go. And again, we got to wait that minute. This really feels like a DNS issue. It's probably like got the static IP address coded. So, it's probably going to like 10 10 11 whatever for like the DC and that's why it's hanging is my best guess. Um, really have no idea, but this definitely feels like DNS when it's like consistently the um same time it slows down. Okay, so we see that Ash Winter has the ability or has the role addis admin. So we can do a IPA then group show. Um we could do like group find as well and that'll list all the groups, but this would be just very specific. I'm not running a bunch of these IPA commands because you can see they do take a little while to run. Um and I'm kind of short on time. It's a little bit annoying waiting for all these IPA commands to come back. But essentially, we're going to look at um the CIS admin group and see what CIS admin can do on this box. And then from there, hopefully we find another path, right? Um, so we're still waiting on this. Uh, that did not work. Um, let's see. Group find d-all. I probably had a typo or something. Um, but this one definitely should. Uh, let's see. Do we have Donna? We do. Here. Can I send the pain to seven? There we go. So now I can kind of play with IPA as we go, right? Um, let's see. IPA- a IPA help. Um, topics command. I was hoping to find a quick way to list all the commands we have in IPA. Um, man IPA. Nope. Um, it's a little bit of a pain, I guess. Uh so we have CIS admins and we can manage the pseudo rules from CIS admin. So what we want to do now is IPA. Let's do a group ad um member. I think that's it. CIS admins. That is the issue. I did a group show CIS admin and it was CIS admins. That had to be it. Um and we'll add Ash Winter to this group. We can really add any group um user. We could have added let's say Donna to this group as well, but I'm going to do it all from Ash. So once this gets added um we should be able to play with something better. Let's see. IPA help pseudo. So we got pseudo rule. Um let's see. pseudo. So sudo rule- add. Okay. So now we have added this. So Ash winter is now a member of CIS admins. So we have an indirect role of manage pseudo rules. Um let's do an IPA pseudo rule find. And this should list all the pseudo rules on the box. Um, we may have to create a pseudo rule or maybe there's already a pseudo rule we can just add ourselves to. Um, let's see. Was there a find here? Yeah. So, we can see all the things we can do um with it here. So, we have add, allow command, run as group, delete, remove, whatever. Um but there is a rule called allow pseudo and it's going with all host all commands all run as whatever right so we want to do IPA pseudo rule then add user allow pseudo and we're going to specify ash winter can be added to this group and if we did a help on this add user that's kind of how you get to this flag. It's a real pain navigating all those help menus, but this should give us pseudo access soon as this finishes. Um, I'm waiting on it. It's taking its time. There we go. We do pseudo-l. We don't see that rule, but we can restart the um SSD service. So, let's do a pseudo this. That's going to restart it, which means it did nothing. I was hoping that meant it would work. Um, let's see. Let's add I wonder if like a cleanup script happened. So, we added the user allow pseudo. Is it just user No, that it definitely said it added. I wonder if it's going to error that we did user. Let's see. Help. Pseudo rule. Add user. Let's see. It definitely wants users. None. Ash Winter is already a member. Okay. I guess it just took some time once we restarted it. Maybe it doesn't um work instantly, but now we have a pseudo su. Um, please subscribe. This is going to be the painful thing. Did I remember the password? Can I type it correctly? Um, shoot. I wonder if we need to restart Ashwinter's password. Uh, reset it. User mod. Ash Winter. Password. Please subscribe is in my clipboard. Awesome. So, we're going to try resetting the password and hopefully this works. Pseudo SU still wants the password. Wonder if we could modify this to not require a password. Let's see. Allow. Don't see it. Put that. Put that. There we go. Sudu. Paste the password. Current password. Let's just redo that. There we go. Awesome. So that is the box. Um hopefully you enjoyed it.

Original Description

00:00 - Introduciton 00:40 - Start of nmap 05:00 - Logging into the website, discovering some type of injection. Sending it to Claude in the background, while we look at it. 09:40 - Playing with a Neo4j/Cypher injection, found the query in the source code 20:30 - When we register as a seller, we can create products there is XSS here 27:20 - Using CSRF to have the user send us the page they are on, which is how we know the username 33:40 - Using Chrome to enroll in passkey (WebAuthn) login via a virtual passkey, looking at the HTTP Requests in BurpSuite 39:50 - Modifying our CSRF Script to start the enrollment of a PassKey and then send the challenge to our box, which lets us forge a response 46:25 - Start of creating our Flask App that uses the Soft-WebAuthN library to act as a virtual passkey, this lets us complete the challenge/response of passkeys 1:23:02 - Finally got the passkey registered via JS, now we need actually authenticate via the passkey so we can grab the cookie 1:39:00 - Showing the unintended that lets us skip the XSS and PassKey Step. Using a Cypher Query to save update the admin password then logging in as them 1:48:00 - Admin has the ability to send TCP Packets and specify data, showing this by making a request to a webserver 1:53:50 - Looking at the Rust Code, seeing it executes anything sent to the update topic of Kafka. Using Claude to build us the TCP Packet we can send to kafka and trigger RCE 2:02:30 - Uploading Chisel so we can easily pivot around, then downloading a certificate off the FTP Server 2:09:40 - Using pem2john to try and crack the RSA Certificate, finding out i needed to update pem2john to get it to work. 2:22:30 - Using OpenSSL so sign a key with the CA, 2:24:30 - Using MITMDump to forward all requests to gitea, and dump the traffic so we can phish the user, then update the DNS Container to include our hostname and swaks to email the user 2:33:00 - Got tom_summers credentials which gets us SSH Access, finding a xvfb dump, conv
Watch on YouTube ↗ (saves to browser)
Sign in to unlock AI tutor explanation · ⚡30

Related AI Lessons

Chapters (18)

Introduciton
0:40 Start of nmap
5:00 Logging into the website, discovering some type of injection. Sending it to Cl
9:40 Playing with a Neo4j/Cypher injection, found the query in the source code
20:30 When we register as a seller, we can create products there is XSS here
27:20 Using CSRF to have the user send us the page they are on, which is how we know
33:40 Using Chrome to enroll in passkey (WebAuthn) login via a virtual passkey, look
39:50 Modifying our CSRF Script to start the enrollment of a PassKey and then send t
46:25 Start of creating our Flask App that uses the Soft-WebAuthN library to act as
1:23:02 Finally got the passkey registered via JS, now we need actually authenticate v
1:39:00 Showing the unintended that lets us skip the XSS and PassKey Step. Using a Cyp
1:48:00 Admin has the ability to send TCP Packets and specify data, showing this by ma
1:53:50 Looking at the Rust Code, seeing it executes anything sent to the update topic
2:02:30 Uploading Chisel so we can easily pivot around, then downloading a certificate
2:09:40 Using pem2john to try and crack the RSA Certificate, finding out i needed to u
2:22:30 Using OpenSSL so sign a key with the CA,
2:24:30 Using MITMDump to forward all requests to gitea, and dump the traffic so we ca
2:33:00 Got tom_summers credentials which gets us SSH Access, finding a xvfb dump, con
Up next
How to Open KRP Files (Krita Document)
File Extension Geeks
Watch →