 Hello everybody, my name is Jon Hammond and welcome back to the YouTube video still looking at the all army cyber stakes or ACI CTF that was going on this past week. So I want to take a look at one of the challenges down here. This one is called Blame it on the temp. It only has 54 solves currently it's Saturday the game in Sunday night. So we'll see if that gets a few more. So with 150 points in the web security challenge category, it says we had our intern create this awesome new secure file storage app. And they give us a link to go access here. So I will file this fire this up. And it looks like we have a super file uploader file upload utility insecurely store your secrets here are not so best practice encryption HTTP prevent snooping because the data is transferred so fast no one can see it. Also your files only last 60 seconds when uploaded. There's nowhere else you can store them. So that's that okay. So looks like we have an upload feature here, and a switcher to a different template. So it's on default. If I switch it to not default, page will load that did not switch. There we go. Okay, super file uploader now is a little bit of a different display. Same sort of page though. So let's take a look at the source code see what's actually going on here. Um, looks like it's loading static UI kit default CSS. Change log has a little comment as an HTML here added templating via Jinja to ooh, this is so much better than manually like before I should add more to app templates later. Okay. Looks like the post form takes a CSRF token. So we should grab that. Okay, same thing for the other forms that are two forms, right? One to actually change the template. And that's being listed out with default and not default. There's also this multi part form data. And that's actually going to take our file. Looks like we can supply the upload. Yeah, select file upload there. And we do need that CSRF if we're going to kind of mess with this. So it's using Jinja to which tells me it's Python. Maybe at least does there like a simple robots text? That's weird. Yeah. Okay, looks like there's something here. Okay, so app. This is like a tree structure. This is must be like a directory structure five directories four or three files. Huh. App DB config.py forms.py. Okay, so yeah, definitely Python. It's weird. They set up this robots dot text entry, though, I just kind of wanted to see if it was there and just in case, like we could run Nito or something to kind of scan it, take a look around uploads. That's all in static. And that's where flask and Jinja would typically reference things that it's looking for. So section background image noise. Okay, so these are all the files that are referenced because of static, the templates is where flask would typically end up creating or using templates that's going to render the pages with with Jinja looks like those have a base, an index dot HTML, a lot of files in there. Okay, so that at least gives us some idea of what the file names might be for those templates. And if we're going to try and upload our own, maybe we can try and get it into that template directory, will it allow us to let's start to script this let you know what let's start to build this. So let's get into ACI. Wow, make directory blame it on the temp. And now let's start to write a little script to get in on this. Okay. Let's close out of some other stuff that was present. Python. I'm going to import requests because we definitely need to be interacting with this web page. I have the URL here. So URL equals this guy. Let's do requests dot get on that URL. Let's set that as R and print out our text, see if we can see the page. There we go. Okay, it loaded took a little bit of time. Let's make that syntax HTML. And let's make that into a second pane with shift alt and two and sublime text. So now I have my code on the left and the output over on the right. So reading things. Let's go ahead and scrape out that CSRF token so we can kind of automate sending things. But just to verify, let's just make a stupid like test dot HTML, or I guess it should be like index HTML, right? If we can try and get into anything. Hello, just like, please subscribe. Classic sell out. Select a file to upload. I need to actually click that select a file to upload ACI blame it on the temp and let's upload that index HTML upload it. Okay, now I have an index.html file here. It says hello, please subscribe. So that put it in static uploads index.html. Is that going to be reflected in robots.txt? Will it show that? Yeah, 43 files now. So now there's that uploads. Does it not show it? Maybe it's not updating that doesn't seem to be. No, because it would show my index.html inside of uploads. And it's not. So can we get our Jinja template stuff in here like can we do some server side template injection if we could upload this need to actually select the file again. index.html. Let's upload it. Now I have two index.html. Supposedly. Okay, if I refresh that it's just reading config. So it's not actually going to use or a read through that Jinja template because it's not in the templates directory and it needs to be. That's how it would keep track of stuff in the actual application. That's how Jinja and Flask would work for us. So let's grab that CSRF token and try and write something that can do this for us automatically, programmatically. So let's grab that URL. And let's actually make a session do that. So request dot session just so we know that the CSRF token is probably going to stay constant, I think maybe, maybe we don't need to do that but whatever it's good practice. So let's do s dot get and then let's use regular expressions to go ahead and grab that CSRF token, which is down here in this input. So let's find that. Let's do re dot find all because that's a dirty quick win on all of that input. Let's use single quotes so that HTML stays just fine. And let's actually remove the value because that's what we want to carve out with regular expressions. So let's find that in re dot text. And let's actually say that as matches or something. So let's print out matches and see if we can find any results. It takes a little bit of time to load that page seemingly, or build output just isn't even over there that fail. R e has no output text because I keep doing that it should be R dot text R is the name of our response variable R e is the name of the regex module. Okay, now we have two entries for the CSRF tokens. Let's just grab the first one. Let's say CSRF token can equal that. And let's verify that we did extract it just fine looks like we did fantastic. Okay, so now we could start to try and upload things through our own program through our own little Python script. I'm going to use IO so I can have kind of a string as a file. I'll do that with a IO dot bytes IO. And then let's have our template, I guess so let's define a template file that can just be anything for now as an example. So let's say file handle equals that guy. And let's try and upload a file. So I always need to look up the syntax of how to do this with Python requests. You really just apply it as a keyword argument. So uploading files. Yeah, yeah, files equals a the name of the argument, the name of the HTTP variable that it needs for the page. And you can even specify in the name here. So and mime type and all that. So we don't need to specify the mime type. I think those are optional, but we can include the name. So let's grab just that syntax and kind of tinker with it. Let's do files equals what is the name of that HTTP variable that they need on the website. Look at the source again here. Looks like it needs to be called just file. Okay, perfect file. So we can keep that. And let's get a file name in place. So let's just call it again, or our like our test dot HTML. So that should be file name. And then we already have an open handle with our file handle. Great. So let's close out that tuple close out that dictionary. And we need to go ahead and upload this with the post method on what location does it have an action? Does it does it say submit should be submit? Let's just make that a data point as well. So submit that should be the value of upload by default. So let's just slap that in. Okay. So now that we have the page and our CSRF token, let's try and upload a file. So let's do that s dot post to URL. And we'll want to specify our files as what we've supplied and our data as what we're supplying. And let's say that as a variable and print it out onto the screen to see what we get. We don't need to worry about our CSRF token because we know that that is working. A bytes like object is required, not string. Okay, so Python three, we do need to make that bytes. So I'll just add that be prefix there. There we go. Okay, so now that page loaded and rendered out. Let's see if it actually uploaded anything. No files uploaded currently. Why did that happen? Oh, I forgot the CSRF token. Is it yelling at me for that? I totally forgot the CSRF token. So CSRF token can equal just that. Now let's spin that one more time. Let's see. I have a test dot HTML uploaded. Excellent. Okay, and that's in static uploads test dot HTML. So the question is, can I move out of that location and get me somewhere else? Because I need to get into that app templates folder. I know that I'm already going to be an app based off of the robots dot text output. So app, if I'm in static uploads, I need to climb the parent directory up one to get into static and up to to get into app. So let's try that. Can I just specify as my file name like up, up, up, up templates, save that run that script, see what it tells me static uploads up, up, up, up templates test that HTML that looks like it got there. Can I go access that? If I have 60 seconds to go see if that page exists. So let's go find out static uploads up up. Nope, it did not write it. seemingly I have that 404. Well, what the heck? There are some are in default, right? Or it needs to have like that index dot HTML. Can I put it something in that default directory? If I run that, does that work? It's not being displayed here. CTF warning, get your own folder. What the heck? What does that mean? Get your own folder? Can I just like use any folder that I want? Like, can I make one? Will it create something? Let me try that. If I I'll leave it in static, like in static uploads, let's just test to be like, any folder I want run that made it that made any folder theoretically, let's go see if that file exists. It does. Okay, so I can just make a folder then. Can I put something into templates? Does that work? Templates, anything, test that HTML, run that, put it in templates, anything, test that HTML. Can I access that page? No, why not? Weird. What the heck? What the heck? Anything is included up here. Anything is included as an option. What the heck? Is that like reading out? Let me refresh that page. Yeah, just access it in the browser, please. Load. Come on. Thank you. Anything is now an entry. Anything is now an option. Can I switch to that template? And it dies. What the heck? Does that robots dot text show that now? No, it's still not updating. But maybe it needs to have like one of the names from one of these files, like index.html. Does that work? Let me change that file name to index.html run that upload templates anything index.html we put it there. And anything is still an entry. That's just our test that we use. So if I refresh this page, I still have anything as an option of a template to use. And will it get an internal server error? No, it doesn't. It gets okay, okay, okay, so we can upload into templates. Now let's see if we can actually get some server side template injection. Let's use config. Let's run that. Looks like that uploaded test dot text. What is that? Is there someone else using this text.png? Index.html. They're doing weird stuff. However, that might be anything. Okay, that should now be a thing. And if I switch to that, does that refresh? Does that refresh? Oh, it's gone. It deleted itself. Okay, 60 seconds have passed. Can I just use like a different name? Let's just use a send it. See the page. A is listed in here update it. Oh, what the heck is that? What? Maybe we shouldn't use a am I stomping on someone else's progress in that challenge? Let's use like J. A. Whoa, or the Z A B a all that nonsense. He uploaded to J A. So now I see him there update. Switch to that template. Oh, and we're loading out the config file. Or the config object and data from ginger and flask. Okay, okay. So we could does it have anything like a secret key in there? Secret key? It does. But that's not the flag and seemingly nothing useful. So maybe we could start to get code execution with this or something like what could we do to leverage this now that we can get our server side template injection. Let me try to make this a little bit more interesting. Let's do import string. Let's just get like a dername equals random dot choice. This choice let me choose however many of them I want, or I guess I should just make a list of that. So let's get a random choice from string dot letters. Is that a thing in Python three Python import string string dot letters. No, ASCII letters. Yeah, yeah, yeah. Okay, ASCII letters. So I want to make a random directory name so that I could have my script to be able to create that folder and make that template. And then I want my script to be able to access the template because ginger will render it out. And we should still be able to see it through the webpage in my sublime text output, right? So let's get that for like range of 10 times or whatever, I don't care. And let's print that guy out. Now let's use that as my dername upload that. And good, now we have a randomly generated template. And I should be able to access that as an option in the templates. So if I select that it will render it for me, right? Yeah. Let's switch to that template. So that name is template and I need to post to this page again. So now that we've uploaded it, let's start another one where we can supply data as the template name that we're choosing, we just made dername as the option and select is going to let us do that. So that looks like all we need to supply as well as the CSRF token, we post it just to that current page. So let's try that template is what we're using CSRF token is what we've already scraped out. Do we need to grab that again, do we need to scrape it one more time because we've already posted the page, we might, we might. So let's just grab that code to find the CSRF token again. And now let's post to the current page with that data to switch to the template. And let's see if that will return our config information for me. Yep, it does. Okay, it's also HTML entity ising them, which is odd, but that's fine. So we don't need the output of that previous one. We just want to be able to see our new template that is getting us the response from that page. So let's see if we can do interesting things to like climb up the Python MRE or MRO or, I think it'll like module. What is that called Python MRO? MRO method resolution order is the order in which Python looks for methods in a hierarchy of classes. So we could crack down and try and determine what we should need to have that in Jinja template files here. That gives me an error. But if I just leave that as square braces, I could read the class of that, right? Yep, class list. And that is HTML entitizing them. Can I have that display? Please Python three HTML entities, HTML entities, Python, how do I decode those or encode them HTML, unescape and HTML escape in greater versions of Python. So let's try that let's print out the text that is HTML escaped. Right? Okay, unescape. There we go. Now they're read perfectly fine. Let's make that syntax Python or let's just make it regular fine. Thank you. Okay, so with the class, I could access the base class. I think that's the syntax. Nope. How do I climb up the tree in Python? Let's say I have an object. And I want to get the class of it. Is it base class first? Nope. What is in this? I'll use dir is in bars. Is that what it's in? Oh, yeah. There's a lot of stuff in that. That's kind of dirty. What? I've seen this technique in like Python jail escaping. Because you can climb up that whole tree of objects and be able to get like some of their actually accessible files. Yeah, so built ins dict. Does that work? Can I use that? Try that. Okay, that error, internal server error. Let's keep that HTML in there. That doesn't work. So let's use just the objects, as we were using before. I'm sure there's a resource that showcases this. There has to be. There we go. There we go. Common payloads, class bases, subclasses. Let's try that guy. He will be our payload. There we go. Now we get all of the potential classes that this thing could use. Let's just make that visible to us in a sane way. So I'm going to replace all of the commas with new lines. So I can read these nicely. And let's turn off our syntax highlighting. So what could we call or use what Python function or module could we use to actually do something interesting, like read a file, or execute a command or something? I don't see anything yet. I'd like to look for like the open function or file or something. OS. Okay, maybe we could run system commands. Other tools. Oh, pickles in here. Maybe we could do some like pickle deserialization. Oh, we also have sub process, sub process up hoping we might just be able to just straight run commands. That's at index 288. And that's why I do this on new line. So I can see by the line number what index in that listed is. So I guess it's 287 because this line is one and should be zero base. So let's try if I can access 287. Does that tell me what it is? Yes. Okay, it's sub process popin. So I could use that to run commands theoretically, right? Let's try to execute something like like some is not going to give me the actual output of this. But it Okay, it looks like it's starting it and running it then. So if I use like shell equals true, can I make sure that's actually running in the terminal? Yep, that loads just fine. So maybe I could get a reverse shell callback or do I have netcat? I don't know if that'll fail or die. Let's try. Let's let's get a connection to something out in the open internet. So SSH john himmin.org. That's just a droplet that I'm going to end up using. So that web page could actually access it and reach it. So let's netcat listen on quad eight. Good. And let's try and see if we can netcat to john himmin.org on quad eight runs. Got the connection. Okay, hell yeah. Okay, so that's code execution, right? So let's try and get a shell. We have netcat. Apparently, right? So pentest monkey reverse shell cheat sheet will netcat work for us. Let's try that. Start again on that listener. And that command should be netcat john himmin.org all on eight. Let's make that quad eight and let's netcat to john himmin.org. Please run. I hate control B. Maybe that didn't work. Are the pipes getting in the way? Let's try like bash. The bash one might behave. I'll make this actually visible for you. Sorry. So that needs to go to our IP address in our port where we're going to listen on to receive a shell. I'll run it. Nothing with that. Let me try bash taxi to run that. Does that work? Does that need to be in quotes? Try that. There we go. Nice. Okay, so that syntax that actually succeeded was bash taxi to invoke it. And then I use quotes to keep the entire arguments of all those bash things in there as part of it. And I made shell equals true so that would actually evaluate on the shell and be able to process those other characters. So now I have a shell. Do I have Python taxi print? Please sub. I do. Okay. So let me use quake. Just to stabilize this real quick stabilize shell. There we go. My prompt is a little weird. So let's export ps one to just that. Now where am I? Oh, I have a read me dot flag. Let's grab that guy. Nice. Okay. That's awesome. So that was that challenge. That was kind of getting in the middle and in the way of some upload that could allow you to do some directory traversal and climb up to where the templates were really stored for this app, not just in the static uploads file that would put you automatically. If you noticed once you had a directory name that wasn't default or not default, it would actually make that directory for you. And it would load it as a page that you could use within those options that that page was showcasing for you. If you had the file name index on HTML, which seems to be really what they were looking for in terms of all their other schemes for all their other templates, it would allow you to access it and ginger that template engine that Python flask will use will render that all out. Using server side template injection like this normally you could use the opening close curly braces two of them to get the config, which might give you the secret key so you could do dangerous things like forward session cookies or or because that's going to be evaluating just straight Python code, we could go ahead and try and see what objects are available to us by using climbing up one base object like an empty string or an empty tuple or an empty list. And then using that MRO and climbing up to see what else can I actually access in this current scope of Python, we were able to actually access that sub process open or that P open or the process open. And I know that's going to actually execute a command. So with that indexing it being able to reach that sub process P open function the constructor there, we could go ahead and call a command at the shell level and get a reverse shell connection back to us, or we could do other things like cat files, but we wouldn't get that output nice and easily because we don't have like a dot read function, or the popin object the popin constructor in Python, Python popin sub process. Maybe it will return the object for us, but I don't know if we'd be able to run like check output and we can't pass in the pipe, so that we'd be able to run like dot standard out dot read or anything. Maybe we could be able to get some command execution with other functions that we could call off of it, but I hadn't gotten it to work. So they don't end up showcasing their standard output, maybe communicate willed, we could try that. Just Okay, just for giggles, we have our reverse shell, we have the flag, let me just experiment. If you're totally cool with that, let's run ID. So we know that that will run and shift f2 again, so I can see that output another pane. We'll get back to my script. There we go. Now we have that object in scope. Let's try and run dot communicate to see the output of that command, none and none. Okay, so that wasn't getting anything for me. Do I need to actually supply input or timeout? No. Uh, in order to do that, you need to pass in standard and equals pipe, and send it out to use pipe. It seems that way, right? If it was pipe, the argument is a writable stream, but I didn't see pipe in something that we could actually access within our, like, objects and functions with Python that were accessible to me. So I couldn't carve out and grab that we can't just type in sub processed on pipe because it has no knowledge of what that is. Anyway, that's that getting the reverse shell is obviously kind of the better thing to do, but you might need to finagle the syntax a bit to actually get it to call back to you. That bash taxi worked well for me and I use quotes inside of that. So there is our script, there is our exploit, there is taking advantage of that vulnerability for server side template injection blaming it on the temp. The hints here, if you're ever checked them out, they do reference payload all the things to get some server side template injection in there, they showcase config, etc, etc. Oh, and they actually discuss, okay, how we can dump classes just like that. So they also discussed the work, zig utils, secure file name, which I'm assuming is how it's taking the uploads. I was kind of struck by this, because when they showcase an example with direct retroversal, it, it, it removes its directory traversal and also removes the directory that it makes with the etc and just uses an underscore. So I'm not entirely positive why this is referenced when it doesn't work. So I'm looking forward to you telling me why maybe they aren't using this file name, maybe they aren't using this this function secure file name. But anyway, that is that challenge. That is our flag. That is how we solve that challenge. And that would net us 150 points and be one of the few that have solved that challenge. So it just went up. Interesting, whatever. Thank you guys so much for watching. If you did like this video, please do press that like button. If you didn't, I don't know what to tell you, please do leave a comment, type a little something in the comment box hit enter. It helps the YouTube algorithm. And you know what else helps the YouTube algorithm subscribing. Thank you so much. I'm grateful for you. If you'd like to help support the channel in any way, there are patreon and paypal links in the description. I'm so so thankful for that. Love to see you guys on discord. Also link in the description. Love to see you guys on Instagram. For some reason, Facebook, Twitter, LinkedIn, all those other social media things on the internet. Appreciate your support. Thanks so much. See you in the next video. Take care.