Imposter-syndrome Mac OS

broken image
Imposter Syndrome Definition
Imposter-syndrome Mac Os Catalina
Types Of Imposter Syndrome
Mac Os Versions
Imposter Syndrome Test
Writeups many times make the hackers seem like god-like creatures that just cut through the challenges like a hot knife through butter. It could go something like this Login as jobert
Register a user on https://h1-415.h1ctf.com/register , intercept the request and change the email to jobert@mydocz.cosmic3e . Log out and re-login using the recovery-QR-code.
What is Impostor Syndrome? Impostor Syndrome is a pervasive feeling of self-doubt, insecurity, or fraudulence despite often overwhelming evidence to the contrary. It strikes smart, successful. The JoyPoll was. Have you ever experienced Imposter Syndrome? Yes, a few times. Yes, much of the time! There's a part of me that says no, but another part that knows the answer is yes. Dec 09, 2020 Background: Imposter Syndrome (IS), also called Imposter Phenomenon (IP), has been studied in a variety of paradigms over the past few decades. However, IP is not a well-researched concept in the field of aviation, and no studies that we know of have examined this phenomenon with student pilots. Method: Two hundred and forty-one student pilots were interviewed from two southeastern universities with flight schools. Imposter syndrome can stifle the potential for growth and meaning, by preventing people from pursuing new opportunities for growth at work, in relationships, or around their hobbies. It's too bad: I have lots of imposter syndrome about understanding my OS and using Linux seems like a great cure. But after literally decades of trying, what I've noticed is that hardware (or at least, my acclimating to new hardware) always advances enough past Linux's capabilities that the switch is difficult.
You are now logged in as jobert .
This user has a regular account, not a trial account. This means he can chat with the support. BXSS the support crew
Bypass the CSP with a directory-traversal and send a BXSS through the support-chat with a XSS-payload that sends back document.location to your burp-collaborator.
Then enter the command quit
When the modal for feedback comes up, select one star to have a support-crew review your conversation, this will trigger the BXSS. IDOR the user-editing on the support-page to include HTML/XSS in other users name
The location sent back contains the (unprotected) URL of the page the support-crew uses to manage chats. http://localhost:3000/support/review/57e8ef9b1e36a3cb397d7b1a9942d2fa1e435ed3a54d94224a55210b054c3907
Visit that page and change the username, but intercept the request in burp and replace the user_id with another user_id that you have created. You can now change the name of your other user without any filter for script-tags. SSRF into Chrome Devtools Protocol Viewer
Change the username for your other user to
iframe src='http://localhost:9222/json/list' height=1000 width=800/iframe Render document containing SSRF and extract secret document location
Convert any image using the second user and view the generated pdf containing the rendered iframe revealing the location:
http://localhost:3000/login?secret_document=0d0a2d2a3b87c44ed13e0cbfc863ad4322c7913735218310e3d9ebe37e6a84ab.pdf IDOR secret document
Swap out the id of the generated document with the one found: https://h1-415.h1ctf.com/documents/0d0a2d2a3b87c44ed13e0cbfc863ad4322c7913735218310e3d9ebe37e6a84ab
This reveals the key:
h1ctfy3s_1m_c0sm1c_n0w
Easy!
What not many of you that did not try the CTF or gave up do not realize is that the way to finishing the CTF was waaaaay more complicated than the picture-perfect super-hacker-way illustrated above.
I argue that there is more to learn for new hackers by looking at what I tried and that did not work than looking at the pure solution.
So, I would like to take this opportunity to detail my entire way from starting the CTF to finishing it. When starting the CTF this was my plan all along, keep track of the time, document as many thoughts and ideas as i can. I wanted to take lots and lots of screenshots and save all payloads so that you can get a feeling for how many dead-ends and bad ideas i had during the course of the CTF (spoiler: LOTS!).
This is more an account of all the things i tried that did not work to solve the CTF.
So, with that said, this is a detailed account for my journey. Complete with mistakes, bugs, spelling-errors and a timeline for what happened when. Warning : Pretty long blog-post head! Imposter Syndrome Definition Friday 020-01-17 11:30 (5d 21h 30m remaining)
Had read about the CTF the evening before and thought i would give it a shot.Before even starting looking at the first website i got the hint posted by @Hacker0x01 on twitter.
Started looking at the site trying to make out what that hint meant
After looking at the source-code i saw something that was strange in the HTML with joberts testimonial:
Ok, so we have the email-address of the user we are looking for. Good!Lets try to log in as jobert@mydocz.cosmic with some random password
Well that obviously was not working but it got us a new endpoint for uploading a recovery QR-code. Things are getting interesting!
Lets try to upload a random png-file to see what happens. Error-message returned Something went wrong, please try again.. Friday 2020-01-17 11:36 (5d 21h 24m remaining)
The host started giving first HTTP 504 Gateway timeout, then HTTP 502 Bad Gateway.As the host was said to reset everything at 35 minutes past the hour every hour this was expected.
But the host is not coming back in the amount of time i would expect Friday 2020-01-17 11:41 (5d 21h 18m remaining)
Figure that this probably has something to do with the QR-code upload and start researching python packages for creating QR-codes. That way i can write something that will allow me to run SQL-map and fuzzing with my normal tools. My first thought is a burp-plugin so lets dive down this rabbit hole Friday 2020-01-17 11:46 (5d 21h 14m remaining)
Realize that a burp-plugin is probably not the easiest route, a MITM-proxy or a simple script will probably be easier. Got a first python version that creates a png-file from a input.
The host is still down Friday 2020-01-17 12:16 (5d 20h 44m remaining)
The host is back up. Finally!Lets register a user with the name p4fg .
Ok, after registering the user we are given the recovery QR-code for the account.
Lets save that for later and see what functionality we have to play with.
There is a converter page where you can upload a png/jpg-file, lets try that!
We get redirected to a page listing all our documents and I can click on the image I just uploaded. It turns out to be a pdf-version of my image.
I immediately notice that we have user-input in the rendered page. The username is reflected, this is probably a attack-vector that is likely given that @nahamsec and @daeken presented how to exploit PDF-generators at defcon 2019.(Awesome talk by the way, you can find it here)
Lets go to the user-settings-page and change our name to psfg as this will immediately show if it is rendered.
The page returns us the username with the tag-characters filtered out lets try to convert a file anyway to see if the missing tag-characters in the page is just a display issue
Nopethe characters are filtered out
Lets try double-encoding the name using burp-repeater p253Cs3E4fg and then converting a file
Nope. gets rendered as p3Cs4fg . Friday 2020-01-17 12:32 (5d 20h 28m remaining)
Lets fuzz a bit more with the name, there must be something here, it cannot be a coincidence!
Change the name to javascript-unicode p4fgu003csu003exxxxx . Convert a document.Hmmm now im getting a broken page when fetching the document strange Maybe we got in the middle of a reset?
Host goes down with HTTP 502
Lets view the contents of the QR-code while the server resets
Using cyberchef (https://gchq.github.io/CyberChef/) it is easy to play around with stuff like this.
The image decodes to:
It took me a while of failed hex-decoding until i observed the colon in the middle of the hex-string. This is actually two hex strings:
and
So we have our email there, nice. And then some 128-byte hash or other secret.
The host comes back up, as it is reset i need to create a new account. And get another QR-code.Now that we have two, created with the exact same parameters, we can compare the data inside the code.
The new code is:
So its the same length, and the start is the same (which was expected). But everything after the colon (the hash) is totally different. And it does not decode to anything useful. Friday 2020-01-17 12:52 (5d 20h 7m remaining)
Now lets try the js-unicode again: Set name to p4fgu003csu003exxxxx and convert a file.Still gets the broken document. so it did not have anything to do with the reset. Maybe this is how we get further in?
Lets try with name p4fgu0078 same thing, broken
Lets try with a forward slash: p4fg/x same thing. broken document. It does not seem to convert with those characters maybe the name is used in some path during creation and we can path-traverse?
Changing name to p4fg/./p4fg . if this works then we have something to investigate!
Nope changing back to p4fg and try to convert the QR-recovery-code to a pdf. maybe there is some QR-decoding going on in the conversion-process as well? Like some metadata appended to the bottom of the page. then maybe we could inject there.
The conversion fails and i get a broken document again very strange. Something seems broken in the system. Switching focus to look at the recovery-QR-code again.
I log out of the system and send in the QR-code from the first session (before server-reset) to see if that file still is valid after the account was removed. All details of the account are the same (email, name, username and password), so it might work
Nope invalid code.
Try using the current QR-code and immediately gets logged in.
If we can replace my email with jobert@mydocz.cosmic then maybe that could work? Something along the lines of an IDOR in which the backend only validates that the second part is valid, but not that it belongs to the email in the first part.
Using cyberchef i converted joberts email to 6a6f62657274406d79646f637a2e636f736d6963 , appended my second part and converted that to a QR-code.
I try using this new fake QR-code in the recovery-page
Does not work. invalid code
Just to make sure that the cyberchef QR-code generation are not messing it up, i create a QR-code using my data through cyberchef. Just to check that any QR-code will be decoded by the backend, not just the ones created by the system itself.
That works. As do creating a QR-code with the hex-characters in uppercase. So it seems that we have proper QR-code-decoding going on in the backend. Thinking about testing SQLi against the email-address in the QR-code, that would be quite fun, and certainly hard enough worthy of a CTF. Friday 2020-01-17 13:15 (5d 19h 44m remaining)
502 Bad Gateway
Doing some other bughunting while waiting for the host to come back up. Friday 2020-01-17 13:25 (5d 19h 34m remaining)
502 Bad Gateway
Did a quick cyberchef recipie where i can enter email and get it automatically converted to a QR-code with my valid second part. This will allow me to do basic SQLi-testing, but if that is successful then i need to write a proxy, no way I want to do a blind timing-based SQLi exfiltration through a QR-code manually Friday 2020-01-17 13:40 (5d 19h 19m remaining)
After a quick lunch the host is now up and converts files normally.
Trying names again p4fg/./p4fg and p4fgu003c but it only prints out the name normally.
Lets fiddle a bit more with this conversion, what if we send up something else than an image or with a modified filename?
First tweak the filename by using repeater in burp on the upload-request:
filename='png-file.png2'h1test' gives error
filename='png-file.png2' gives error
Seems like the file needs to end with png or jpg
filename='png-'file.png' works but nothing interesting happens.
Looking at the first bytes of the generated PDF seems to give some clues as to how the pdf is created Friday 2020-01-17 13:57 (5d 19h 2m remaining)
Try to convert with filename='png-22file.png' with Content-Type: text/html and some ascii-payload instead of the image.
This gives a broken image in the generated PDF. interesting that it still saves the image and then tries to load it. This must be a good lead!
So the workflow is something like:
Upload image
Save image somewhere
Create HTML with contents like this
Lets look up what this Producer Skia/PDF m79 is that was visible in the header of the PDF, never heard of that before
Reading a bit about that until i find
And realise that Chrome is using Skia to create the PDF. so the conversion process seems to be chrome-based anyways lets get out of this rabbit-hole
and try the SQLi i thought about earlier
Create a QR-code with email
.only to get invalid code back
Realised I forgot to try HTML-encoding my name-payload earlier when testing the name-change and immediately switch focus to trying a new name: p4fglt;sgt;kaka . converting. only to find out that (as one might imagine) it is displayed as p4fgskaka in the generated PDF.
Lets try some HTML-unicode, because why not? p4fg26231281693B .Using this as the name gives me an error when saving so the backend does not seem to accept unicode. Friday 2020-01-17 14:35 (5d 18h 24m remaining)
Time for that server-reset i am starting to dislikeStarting to realise that i am running out of ideas Friday 2020-01-17 14:57 (5d 18h 2m remaining)
Creating two users to try to test for IDOR between the users.This seems to give at least some insights:
A document created by user1 is readable by user2 if the (long) URL/path/document-id is known.
Soif we can find the hash/document-id for the jobert-document we should be able to read it without being logged in as jobert.
Was this entire QR-code-thing a wild goose-chase?Somehow i want to find directory-listings or similar so that I can see other users files Friday 2020-01-17 15:08 (5d 17h 51m remaining)
Starting a directory-search through burp-intruder. there MUST be some hidden pages here somewhere!
I realize quite quickly that if the directory brute-force is too fast the server starts blocking me with HTTP 503 responses. Setting the delay between tries quite high and then let the bruteforce run in the background.
Switching focus (yes, again!) to looking at the QR-code. The first part is the email, but what is the second part? Is it just a hash? Is it encrypted data? XOR perhaps?
Generating three new accounts to compare the data in the QR-code. All are 256 characters hex-encoded, so 128 bytes Could it be encrypted with a static password, can we see some common data between the accounts? A nonce perhaps?
Not finding any common data in the last 128 bytes of data Friday 2020-01-17 17:00 (5d 15h 59m remaining)
Getting nowhere fast Time to put this away for the weekend, guests are staying over and the family needs attention. Monday 2020-01-20 10:56 (2d 22h 3m remaining)
Starting all over again. Got lots of rest and time to thing during the weekend.Lets compare if different user-parameters affect the QR-code.
Gives QR-code
and
Gives QR-code
Looking at this for a while trying different options in cyberchef to juggle the data around but dont really get anywhere
The thing with a CTF is that there IS a way in. you just need to find what that is.Compared to bug-bounties there might not even exist a way in
What are they thinking of?
How have they designed this to be a tough challenge?
How the hell have anyone solved this already?
One of the bigger things research-wise presented during 2019 was request-smuggling by @albinowax. surely that must be in here somewhere?
AAAAhh!! Yes!
What if: The character-filter that removes on the username is done in another machine/application and the request is then forwarded to the backend. that would be kind of unlikely in a real-world situation but could be in a CTF? Time to read up on request-smuggling
My idea is to not do normal request-smuggling but to send two requests in one, and have the filter on the first box only look at the first request, but then on the backend have it interpreted as two requests, where the second one is not filtered Monday 2020-01-20 11:58 (2d 21h 1m remaining)
YEEEEEEEEEEEEEEEEEES! Request-smuggling works! at least i can send two requests in one (TE.CL) and receive the response from the first request, but when refreshing the page the account is updated with the second one (or really updated twice, but with the second one last).
So sending this request will give a response with the name p4fgaaaaaaaaaaaa3 but when reloading the page the user is named p4fg .
Time to test my filter-theory and send p4sfg as the name in the smuggled request
EPIC FAIL followed by disappointment
The filter still removed the characters Monday 2020-01-20 13:10 (2d 19h 50m remaining)
Checking in with my friend @nilssonanders on Telegram during lunch, telling him:
-Im not getting anywhere. Imposter-syndrome delux .
Imposter-syndrome is a tough thing, but you need to continue working even though you feel worthless. (Which i at this point felt.)
Testing sending in some really long usernames. read an article on regex-filters that started failing when given texts over 1000000 charactersIf we just append our HTML-tags after that, they might slip through
Not working too well that either, the server does not accept any usernames over 100-200 characters
(The server started blocking me so it was hard to know the exact limit). Monday 2020-01-20 16:02 (2d 16h 57m remaining)
Starting working on a script that will create a QR-code for me and try to do a recovery-based login. Monday 2020-01-20 17:00 (2d 15h 59m remaining)
Time to call it a day. Kids to feed and stuff to do Im not really getting anywhere
What the hell was that hint about regex:es about??? Tuesday 2020-01-21 09:20 (1d 23h 39m remaining)
Finally some progress!! I have found the first piece of the puzzle.
It all started with me continuing to mess with the QR-codes.
If i put in strange characters in the email before the @ the recovery-page would say something like Invalid email-address . Imposter-syndrome Mac Os Catalina
But if the characters was in the end of the email I would get wrong code .
This and the fact that the settings-page filtered out got me to try something.
If i tried to register a user as jobert@mydocz.cosmic it would not work as that account already exists.
If i tried to register a user as
the javascript-logic on the page would prevent me from submitting the page.
So I entered username p4fg , email jobert@mydocz.cosmic , intercepted the request in burp, and changed the email to
Saved the QR-code, as i always do. Got quite a few of them now
And got logged in as p4fg . But at least the system accepted the strange email, that must be some progress.
Now i looked at the QR-code. AND look at that
It starts with 6a6f62657274406d79646f637a2e636f736d6963:
There is no 3e as the last character of the email in the QR-code. The QR-code is for jobert@mydocz.cosmic !!!
And when using that through the recovery-page I get logged in as jobert!!
Finally some progress!
When logged in as jobert you are not a trial customer anymore. Maybe there is different options in the settings? Maybe no HTML-filter?
Iimmediately tried to change name to something with HTML in it, but that did not work. The jobert-user is not allowed to change name at all Tuesday 2020-01-21 09:28 (1d 23h 32m remaining)
Then I tried to convert a document. That did not work either, giving a error 'license has expired' .
Looking at the documents-page, this was also (ofcourse) empty so the only thing that this new account brought to the table was the ability to contact the support-staff through a chat that was not enabled for the trial users.
This looks promising. You entered quit to exit the chat and was then prompted to give feedback on how the support was.
The chat was vulnerable to HTML-injection and probably XSS. but this would only be self-XSS.
unless you had someone on the other end looking at the conversation, which they probably would do if you gave the conversation a one-star rating Then we would have blind-XSS
Looking at the CSP, (and assuming it is the same for the other party) it is pretty strict
So any image-src would work to leak data. I enter a message containing
entered quit and then gave the conversation a 1-star rating.
A text appeared: We're sorry about that. Our team will review this conversation shortly. .
Shortly after, this happened:
Someone (or something probably in this case as it is a CTF) is on the other end, inspecting conversations with a bad rating.
Pretty realistic actually! I am definitively on the right track now!
Lets see if the simulated person on the other end will press any buttons in our payload, that could happen Because if we can do this, then we can make POST-requests. And what if the support-staff can change names on users without any filter? In that case we can get a HTML-payload into our name! So first we test if the system will press buttons
Nope. nothing happens.
Nope, this didt work either (and would actually not work with the CSP anyways).
Lets try
Nope. nothing here either
But CSP says script-src 'self' . so we can have scripts included from the same site (if i understand the black-magic of CSP correctly) Block kicker 3d mac os .
So if we can load scripts from the same site. how would we upload a script? Hmmm. We did get that broken image when uploading non-images in our converter lets try that!
Here we send up a file containing a simple alert() to the converter
Looking at the generated HTML in the documents-page:
Ok. so the thumbnail is not working since it is hard to do a thumbnail of a non-image And the pdf only shows a broken image-symbol But if we have a XXXXX-thumb.png then maybe we have a XXXXX-full.png as well?
Nope. not XXXXX-full.png but XXXXX.png works!Our uploaded js is saved as /documents/877935c0bcd2ff1d5609f6a352bb02604cd8e14f7effe0ee094d189c543ac210.png
fetching it shows some badness though:
The content-type is image/png and that will not work as a script src as i quickly realized. (Pretty sure this used to work back in the days)
Tried some combinations of entering different mime-types when posting the file, but no matter what I did the content type was set to the same as the file-extension (only .png and .jpg was allowed). Tuesday 2020-01-21 10:42 (1d 22h 18m remaining)
Going through my main strategy:
I want to do some kind of SSRF/CSRF from the internal support-person to the /settings endpoint and change the name to a HTML-payload in order to do things in the PDF-conversion.
Probably doing an iframe to another internal system.But to get any further i want to run javascript. im not entirely sure i need to, but i want to make it happen anyways. It is the most likely way forward. Tuesday 2020-01-21 10:51 (1d 22h 9m remaining)
Starting to bruteforce (using burp intruder) different extensions on my uploaded files to see if maybe there is metadata stored (a log perhaps) of the conversion, that somehow can be used as a script-source.
https://h1-415.h1ctf.com/documents/2fc15fd88e06b1b1c522505d4537952e13d275e3463f2f70542f1744d24f818c.png Tuesday 2020-01-21 10:59 (1d 22h 0m remaining)
I started asking myself: What is wrong in this puzzle? What part does not really fit in?
Went back to the CSP and analyzed it with https://csp-evaluator.withgoogle.com/
I started asking myself Why would they include https://raw.githack.com/mattboldt/typed.js/master/lib/typed.min.js from that location? Why not stick that under /js/ like everything else??
It turns out that githack is a service that lets you serve js directly from github with the correct content-type (you cannot use the raw-links directly from github as a script src, just because they dont want people to use github as a CDN).
This got me even more interested. but we could only access files from /mattboldt/typed.js/master/lib/ so i went to the github-repository https://github.com/mattboldt/typed.js/tree/master/lib thinking that there MIGHT just be some js-file there that i could use in some kind of chain
Answer: No, nothing there
Then it dawned on me. maybe we can do a directory-traversal on the githack-site? And serve stuff from one of my own repositories to run arbitrary blind XSS Tuesday 2020-01-21 11:31 (1d 21h 29m remaining)
Seems like the directory traversal approach might be successful I created a repository with a file containing a simple alert:
Using burp (to avoid issues with the browser messing the path up) i requested
the response is very promising:
Correct content-type, correct path on correct server. this might be the way!
So i enter the following message in the chat:
As script-tags that are added by DOM-manipulation are not executed i could not see anything happening
But if I reload the support-page (in my browser) all conversation messages are included and the script is run or rather. it is NOT run due to the CSP.
As this is rendered in the page it becomes:
this is normalized by the browser to:
and that is outside the allowed CSP Tuesday 2020-01-21 11:55 (1d 21h 4m remaining)
OK. but this worked in burp! (Famous last words) We want the slashes rendered as 2f so we need to double-url-encode it
New support message:
And reload the page:
After many dead-ends and wild goose-chases we finally are getting somewhere, but ofcourse popping an alert on a headless browser on the other end will get us nowhere, but it sure helps with the imposter-syndrome
Now lets move on with our plan, do a CSRF (or is it SSRF? The lines blur a bit here as it is both.) from the support-account to the settings-page.
First I want to make sure that I can do POST-requests from the other side so i enter the message:
where the submit.js file contains Tuesday 2020-01-21 12:14 (1d 20h 45m remaining)
Unable to try this last part as the server starts responding HTTP 502 to every request GAaaaah!!! Tuesday 2020-01-21 12:29 (1d 20h 30m remaining)
Server is back up. Feeling a bit stressed out as the server will reset in 6 minutes. Tuesday 2020-01-21 12:32 (1d 20h 28m remaining)
YES!
The script is loaded and executes, it submits the form to burp collaborator. Tuesday 2020-01-21 13:32 (1d 19h 28m remaining)
After a lunch-break, it is time to continue our explorations
Looking at the cookies sent from the server, the _csrf_token is not set as http-only. This means we kan leak it. Lets try!
Sending:
where the submit.js file contains
Our burp collaborator receives the request:
So we can get the _csrf_token from the remote service-agent but can we use it without the http-only session-cookie?
But it sure does look promising that the agent has a _csrf_token
Lets try to see what documents this account have access to, maybe the service-people can see all documents in the system? Sounds resonable!
We want to see what the other browser sees when looking at /documents
Creating a new JS-file: Tuesday 2020-01-21 13:50 (1d 19h 9m remaining)
Waiting for this to be cached in githack. this takes forever
After waiting a bit everything seems in place. Try the payload in the chat. and.nothingHmmmm It would be easier if we could avoid updating github all the time. what if we could put our payload in the form and just have the script execute it using eval() ? That would be nifty
Lets update our submit.js with the following code:
and have the following message:
So the script would take the payload in evaldata decode that using base64 and doing eval() on that. No more update-cache-cycle via github and much faster turnaround-time
But it turns out there was a bug in that script, we need to access the .value of the input element of course
Well. it could have worked. under a different CSP:
Ok. back to basicsWe will continue updating github and take that approach. lets try to fetch the documents again! (Using the same form as before)
This works in my browser when reloading but not in the blind context.
(-Works on my machine! wont help me here)
Are we using the wrong URL? Maybe h1-415.h1ctf.com does not resolve on the machine?
New try using a relative path
Nope. no POST-request this time either. why??
Time to investigate a bit more on what is happening remote lets write more javascript. because we have to
So we have a function leak() to append images dynamically to leak information, just like a logger.
This actually returns some interesting stuff as onload_200 :
Done, got 5921 bytes
So the remote is loading the contents of /settings and its getting back 5921 bytes of data. promising indeed
Another try, and this time we add some code to leak the contents of the response and also fetch /documents :
This failed in a strange way The script successfully leaks the document.cookie and document.domain but nothing else why? Do we get some kind of javascript CORS exception when we access XHR.response ?? Cant be because we previously got XHR.response.length . StrangeIt works fine locally.
For some reason I got to thinking that maybe it was the leaking via image-tags that was the problem so i updated the leak-function to do XMLHttpRequests instead
Of course this was a bad idea. after thinking of it the XHR post is a CORS-request and not allowed by the CSP. but realizing this after the fact does not really help
Reverting to the leak-function with images, and making the script request urls via a function.
Still, nothing is returned. Debugging javascript remotely and blind is really sub-optimal.but lets try Updating the reqListener-function to
This actually sends the pre-response but then its silent. what is going on here? It should work Tuesday 2020-01-21 15:36 (1d 17h 23m remaining)
Server-reset time Setup everything from scratch again, re-register all users, etc, etc.until we are in a position to send chat-messages again.
Time to do some more debugging by adding a try-catch and leaking the exception:
Burp collaborator receives the exception! Maybe now we can find out what is going on The data posted is:
which decoded is:
Oh. eehso this was no CORS-issue, it was something that the btoa-function did not like ok Stackoverflow to the rescue: https://stackoverflow.com/questions/23223718/failed-to-execute-btoa-on-window-the-string-to-be-encoded-contains-characte
So all we need to do is unescape(encodeURIComponent()) of the data first because reasons Tuesday 2020-01-21 15:52 (1d 17h 7m remaining)
SUCCESS!!! We are getting the HTML-back from the support-agent requesting the pages on their end.
What a beautiful sight!!
https://lzcaplaycomparefree-betfreeblackjack.peatix.com . I will not post the entire decoded HTML, but just a snippet of it to illustrate my complete devestation:
All leaked HTML-pages were identical. it is the login-page the support-agent is not logged in to the system Or are we simply not sending the cookies in our request
I seem to remember an option withCredentials that can be used with XMLHTTPRequests Lets read the documentation https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
That saysdoh.
So no that should not be the reason. but just to make sure lets try it:
and while we are updating the script, lets leak the location as well:
Lets get the bad news out of the way first:
withCredentials did not make any difference, the user running the simulated support is not logged in (or so it seems).
The good news is that document.location revealed some interesting stuff:
As the support-agent is not logged in, we might be able to access this URL ourselves?
Yes we could!! And here we can edit the user as well! Seems our original strategy could be correct, just not the way I thought!
Editing the jobert-user returns a page saying:
But looking at the request it takes three parameters: name, user_id, _csrf_token Lets just change user_id to one of our users and try for an IDOR. (the user_id for a user is in the change-name request that the user can do for him/her-self):
returning
'result':'success' Tuesday 2020-01-21 16:17 (1d 16h 43m remaining)
Switch to a private-session with the second user logged in, and converting a png-image
YES!!!!! We can now inject HTML into the page converted to a PDF! A (now) classic scenario that I have pulled off as a critical bounty before, where i extracted AWS-credentials via an iframe
Lets first see what documents this user can see if they browse to /documents and (for some reason) localhost:443 by setting the name to
and converting a document.
So this user/browser does not seem logged in either. lets use the same script as before to leak the document.location .
I set the name to
and convert a file.
The leaked location returns the path that the converter uses:
Interesting. But when trying this path as a user we get a Forbidden-page.
I lookup the ip-address of that sends the requests to burp-collaborator: ec2-18-218-90-126.us-east-2.compute.amazonaws.com
HAHAHA, this will be simple, enough of investigation. Lets just steal the AWS-keys and probably go to the next level.
Im thinking S3-buckets and what-not. Wonder how many levels deep this goes?
Lets re-use the last code i used to steal AWS-credentials, because why not.
The last time i did this the script needed to mark that everything was done/rendered by setting window.status = 'READY' and adding a class to the body.
This isnt needed here, but i would rather not cause another bug by editing it however this script requires an element with id=log that it uses to append log-data, so we need to add that element to our name.
So. lets run this payload Tuesday 2020-01-21 17:13 (1d 15h 46m remaining)
502 Bad gateway when converting
Wellshit
Other pages load fine. but I cannot save user settings on any endpoint.
After 5-10 minutes the endpoints work fine again.But now i get Internal server error when setting the name
After a bit of experimenting i realize there is some kind of length restriction on the name, so changing it to
That name-change works.
Now lets convert a file.
FAIL . The document does not get created at all. Maybe my script was bad?Lets try a iframe directly in the name just to eliminate the potential script error:
Same thing. fails to create a documentSoo. maybe they dont like the IP-address.
Lets try a hostname with a 302 redirect to the metadata-URL!
FAIL. same thing happens. Now i really need to go cook some food for the kids Tuesday 2020-01-21 18:43 (1d 14h 16m remaining)
Lets try with brand new javascript:
Convert a file. Nope. Page not found. Something is broken or the outbound connections are filtered
I try a quick iframe to https://www.nasa.gov , that works. kind of Only the first basic parts of the page is rendered, so it seems like the pdf is generated before the iframe is completely loaded.
However. it seems like we cannot access the AWS-metadata at all. Thinking about if they were evil enough to host this on another cloud-provider and tunnel the traffic through AWS as a decoy. but that would be very far-fetched.
Lets see if there is anything interesting in localstorage in the browserthat might actually be it!
Yes, there is probably some cool data in the localstorage that shows us to the next level in this dungeon.
Upload, change name, convert checking burp-collaborator drumroll
.
OK. i really had my hopes up for that one
. my bad
Somehow i just realise that im not using the path-traversal for the js-file on githack
But. then. i dont need to update it on github and githack anymore doh!
I tried a js-file hosted elsewhere than on raw.githack.com and it works perfectly well.
No CSP on this page (or not any CSP that affects us yet anyways) Tuesday 2020-01-21 20:50 (1d 12h 10m remaining)
After the server-reset i no longer get callbacks from the blind XSS on the support-chat. im using the same payloads as before. something is broke. Poor hackers that are reaching this level in this reboot-cycle, they will never realise the blind XSS Tuesday 2020-01-21 21:00 (1d 12h 0m remaining)
Still no response from the blind XSS. calling it a night Monday 2020-01-22 08:10 (1d 0h 49m remaining)
Went swimming for exercise. That gives me some time to think things throughLast day of the CTF. time to get my shit together. im sooooo close. but how close?Im expecting another level of insanity after this one, AWS with s3-buckets. or maybe using that request-smuggling i think i found earlier Monday 2020-01-22 09:43 (0d 23h 16m remaining)
While swimming i made a mental list of things i wanted to try this last day.
One thing was doing a brute-force for pages returning 403 forbidden just like the converter-page.
Maybe the converter can access them too?.
Another thing was trying other ports on the same machine.
maybe i will find a open Jenkins-instance, Tomcat-admininstration page or something like that.
One last thing i remembered from somewhere, the Chrome DevTools Protocol that sometimes is open on port 9222.
This enables remote debugging on headless instances.
I made a new JS-file that writes out the URL and also measures the time from the start of script-execution to pdf-generation, to get an idea of how long time we have for iframes to load. https://vegas-inkfree-bet-real-rvfk-online-slots.peatix.com . This will also help us if i can find out a way to keep the browser busy in order for the pages to load.
First try: Chrome DevTools on port 9222.
Well hello there!! Seems like dev-tools are enabled. now what can we do with this?
The pdf does contain the links in the page as well:
Lets first try fetching this ourselfes without websockets (via iframe as usual):
Nope. this got returned empty (It might not be empty but the pdf is generated after 500ms)
Lets try the entire thing, even though i really doubt it will render in time:
This gives a broken document, file not found
Time to study: https://chromedevtools.github.io/devtools-protocol/
ok. so there is an endpoint for the debug-tools /devtools/inspector.html
but same thing here. it will not render completely
Reading a bit more under the section HTTP Endpoints in the document
Why not. at the very least i will learn something Monday 2020-01-22 10:11 (0d 22h 48m remaining)
Setting up the name of the user to be a iframe to http://localhost:9222/json/list , and converting a file
Well this looks interesting.
http://localhost:3000/login?secret_document=0d0a2d2a3b87c44ed13e0cbfc863ad4322c7913735218310e3d9ebe37e6a84ab.pdf
Now lets use the IDOR (might just be intended functionality for sharing??) from early on and just swap out the document-id with any other document and fetch it using our browser:
https://h1-415.h1ctf.com/documents/0d0a2d2a3b87c44ed13e0cbfc863ad4322c7913735218310e3d9ebe37e6a84ab Monday 2020-01-22 10:13 (0d 22h 47m remaining)
No more levels, this was the end! A very nice CTF indeed!
If you made it all the way here, thank you for reading!
My message with this very long writeup is that there are ALOT of failures during the way. Not many people plow through this easily, and now you have a first hand account on how it looks like working through this
I hope you learned a bit from my mistakes, i know i did!
Imposter syndrome is a bitch, but just keep on throwing stuff at the target and look for things that are a bit off,in the CTF-world they are either clues or things that need to be there in order for a vuln to work.
/p4fg
Writeups many times make the hackers seem like god-like creatures that just cut through the challenges like a hot knife through butter. It could go something like this Login as jobert
Register a user on https://h1-415.h1ctf.com/register , intercept the request and change the email to jobert@mydocz.cosmic3e . Log out and re-login using the recovery-QR-code.
You are now logged in as jobert .
This user has a regular account, not a trial account. This means he can chat with the support. BXSS the support crew
Bypass the CSP with a directory-traversal and send a BXSS through the support-chat with a XSS-payload that sends back document.location to your burp-collaborator.
Then enter the command quit
When the modal for feedback comes up, select one star to have a support-crew review your conversation, this will trigger the BXSS. IDOR the user-editing on the support-page to include HTML/XSS in other users name
The location sent back contains the (unprotected) URL of the page the support-crew uses to manage chats. http://localhost:3000/support/review/57e8ef9b1e36a3cb397d7b1a9942d2fa1e435ed3a54d94224a55210b054c3907
Visit that page and change the username, but intercept the request in burp and replace the user_id with another user_id that you have created. You can now change the name of your other user without any filter for script-tags. SSRF into Chrome Devtools Protocol Viewer
Change the username for your other user to
iframe src='http://localhost:9222/json/list' height=1000 width=800/iframe Render document containing SSRF and extract secret document location
Convert any image using the second user and view the generated pdf containing the rendered iframe revealing the location:
http://localhost:3000/login?secret_document=0d0a2d2a3b87c44ed13e0cbfc863ad4322c7913735218310e3d9ebe37e6a84ab.pdf IDOR secret document
Swap out the id of the generated document with the one found: https://h1-415.h1ctf.com/documents/0d0a2d2a3b87c44ed13e0cbfc863ad4322c7913735218310e3d9ebe37e6a84ab
This reveals the key:
h1ctfy3s_1m_c0sm1c_n0w
Easy!
What not many of you that did not try the CTF or gave up do not realize is that the way to finishing the CTF was waaaaay more complicated than the picture-perfect super-hacker-way illustrated above.
I argue that there is more to learn for new hackers by looking at what I tried and that did not work than looking at the pure solution.
So, I would like to take this opportunity to detail my entire way from starting the CTF to finishing it. When starting the CTF this was my plan all along, keep track of the time, document as many thoughts and ideas as i can. I wanted to take lots and lots of screenshots and save all payloads so that you can get a feeling for how many dead-ends and bad ideas i had during the course of the CTF (spoiler: LOTS!).
This is more an account of all the things i tried that did not work to solve the CTF.
So, with that said, this is a detailed account for my journey. Complete with mistakes, bugs, spelling-errors and a timeline for what happened when. Warning : Pretty long blog-post head! Friday 020-01-17 11:30 (5d 21h 30m remaining)
Had read about the CTF the evening before and thought i would give it a shot.Before even starting looking at the first website i got the hint posted by @Hacker0x01 on twitter.
Started looking at the site trying to make out what that hint meant
After looking at the source-code i saw something that was strange in the HTML with joberts testimonial:
Ok, so we have the email-address of the user we are looking for. Good!Lets try to log in as jobert@mydocz.cosmic with some random password
Well that obviously was not working but it got us a new endpoint for uploading a recovery QR-code. Things are getting interesting!
Lets try to upload a random png-file to see what happens. Error-message returned Something went wrong, please try again.. Friday 2020-01-17 11:36 (5d 21h 24m remaining)
The host started giving first HTTP 504 Gateway timeout, then HTTP 502 Bad Gateway.As the host was said to reset everything at 35 minutes past the hour every hour this was expected.
But the host is not coming back in the amount of time i would expect Friday 2020-01-17 11:41 (5d 21h 18m remaining)
Figure that this probably has something to do with the QR-code upload and start researching python packages for creating QR-codes. That way i can write something that will allow me to run SQL-map and fuzzing with my normal tools. My first thought is a burp-plugin so lets dive down this rabbit hole Friday 2020-01-17 11:46 (5d 21h 14m remaining)
Realize that a burp-plugin is probably not the easiest route, a MITM-proxy or a simple script will probably be easier. Got a first python version that creates a png-file from a input.
The host is still down Friday 2020-01-17 12:16 (5d 20h 44m remaining)
The host is back up. Finally!Lets register a user with the name p4fg .
Ok, after registering the user we are given the recovery QR-code for the account.
Lets save that for later and see what functionality we have to play with.
There is a converter page where you can upload a png/jpg-file, lets try that!
We get redirected to a page listing all our documents and I can click on the image I just uploaded. It turns out to be a pdf-version of my image.
I immediately notice that we have user-input in the rendered page. The username is reflected, this is probably a attack-vector that is likely given that @nahamsec and @daeken presented how to exploit PDF-generators at defcon 2019.(Awesome talk by the way, you can find it here)
Lets go to the user-settings-page and change our name to psfg as this will immediately show if it is rendered.
The page returns us the username with the tag-characters filtered out lets try to convert a file anyway to see if the missing tag-characters in the page is just a display issue
Nopethe characters are filtered out
Lets try double-encoding the name using burp-repeater p253Cs3E4fg and then converting a file
Nope. gets rendered as p3Cs4fg . Friday 2020-01-17 12:32 (5d 20h 28m remaining)
Lets fuzz a bit more with the name, there must be something here, it cannot be a coincidence!
Change the name to javascript-unicode p4fgu003csu003exxxxx . Convert a document.Hmmm now im getting a broken page when fetching the document strange Maybe we got in the middle of a reset?
Host goes down with HTTP 502
Lets view the contents of the QR-code while the server resets
Using cyberchef (https://gchq.github.io/CyberChef/) it is easy to play around with stuff like this.
The image decodes to:
It took me a while of failed hex-decoding until i observed the colon in the middle of the hex-string. This is actually two hex strings:
and
So we have our email there, nice. And then some 128-byte hash or other secret.
The host comes back up, as it is reset i need to create a new account. And get another QR-code.Now that we have two, created with the exact same parameters, we can compare the data inside the code.
The new code is:
So its the same length, and the start is the same (which was expected). But everything after the colon (the hash) is totally different. And it does not decode to anything useful. Friday 2020-01-17 12:52 (5d 20h 7m remaining)
Now lets try the js-unicode again: Set name to p4fgu003csu003exxxxx and convert a file.Still gets the broken document. so it did not have anything to do with the reset. Maybe this is how we get further in?
Lets try with name p4fgu0078 same thing, broken
Lets try with a forward slash: p4fg/x same thing. broken document. It does not seem to convert with those characters maybe the name is used in some path during creation and we can path-traverse?
Changing name to p4fg/./p4fg . if this works then we have something to investigate!
Nope changing back to p4fg and try to convert the QR-recovery-code to a pdf. maybe there is some QR-decoding going on in the conversion-process as well? Like some metadata appended to the bottom of the page. then maybe we could inject there.
The conversion fails and i get a broken document again very strange. Something seems broken in the system. Switching focus to look at the recovery-QR-code again.
I log out of the system and send in the QR-code from the first session (before server-reset) to see if that file still is valid after the account was removed. All details of the account are the same (email, name, username and password), so it might work
Nope invalid code.
Try using the current QR-code and immediately gets logged in.
If we can replace my email with jobert@mydocz.cosmic then maybe that could work? Something along the lines of an IDOR in which the backend only validates that the second part is valid, but not that it belongs to the email in the first part.
Using cyberchef i converted joberts email to 6a6f62657274406d79646f637a2e636f736d6963 , appended my second part and converted that to a QR-code.
I try using this new fake QR-code in the recovery-page
Does not work. invalid code
Just to make sure that the cyberchef QR-code generation are not messing it up, i create a QR-code using my data through cyberchef. Just to check that any QR-code will be decoded by the backend, not just the ones created by the system itself.
That works. As do creating a QR-code with the hex-characters in uppercase. So it seems that we have proper QR-code-decoding going on in the backend. Thinking about testing SQLi against the email-address in the QR-code, that would be quite fun, and certainly hard enough worthy of a CTF. Friday 2020-01-17 13:15 (5d 19h 44m remaining)
502 Bad Gateway
Doing some other bughunting while waiting for the host to come back up. Friday 2020-01-17 13:25 (5d 19h 34m remaining)
502 Bad Gateway
Did a quick cyberchef recipie where i can enter email and get it automatically converted to a QR-code with my valid second part. This will allow me to do basic SQLi-testing, but if that is successful then i need to write a proxy, no way I want to do a blind timing-based SQLi exfiltration through a QR-code manually Friday 2020-01-17 13:40 (5d 19h 19m remaining)
After a quick lunch the host is now up and converts files normally.
Trying names again p4fg/./p4fg and p4fgu003c but it only prints out the name normally.
Lets fiddle a bit more with this conversion, what if we send up something else than an image or with a modified filename?
First tweak the filename by using repeater in burp on the upload-request: Project z (slin) mac os .
filename='png-file.png2'h1test' gives error
filename='png-file.png2' gives error
Seems like the file needs to end with png or jpg
filename='png-'file.png' works but nothing interesting happens.
Looking at the first bytes of the generated PDF seems to give some clues as to how the pdf is created Friday 2020-01-17 13:57 (5d 19h 2m remaining)
Try to convert with filename='png-22file.png' with Content-Type: text/html and some ascii-payload instead of the image.
This gives a broken image in the generated PDF. interesting that it still saves the image and then tries to load it. This must be a good lead!
So the workflow is something like:
Upload image
Save image somewhere
Create HTML with contents like this
Lets look up what this Producer Skia/PDF m79 is that was visible in the header of the PDF, never heard of that before
Reading a bit about that until i find
And realise that Chrome is using Skia to create the PDF. so the conversion process seems to be chrome-based anyways lets get out of this rabbit-hole
and try the SQLi i thought about earlier
Create a QR-code with email
.only to get invalid code back
Realised I forgot to try HTML-encoding my name-payload earlier when testing the name-change and immediately switch focus to trying a new name: p4fglt;sgt;kaka . converting. only to find out that (as one might imagine) it is displayed as p4fgskaka in the generated PDF.
Lets try some HTML-unicode, because why not? p4fg26231281693B .Using this as the name gives me an error when saving so the backend does not seem to accept unicode. Friday 2020-01-17 14:35 (5d 18h 24m remaining)
Time for that server-reset i am starting to dislikeStarting to realise that i am running out of ideas Friday 2020-01-17 14:57 (5d 18h 2m remaining)
Creating two users to try to test for IDOR between the users.This seems to give at least some insights:
A document created by user1 is readable by user2 if the (long) URL/path/document-id is known.
Soif we can find the hash/document-id for the jobert-document we should be able to read it without being logged in as jobert.
Was this entire QR-code-thing a wild goose-chase?Somehow i want to find directory-listings or similar so that I can see other users files Friday 2020-01-17 15:08 (5d 17h 51m remaining)
Starting a directory-search through burp-intruder. there MUST be some hidden pages here somewhere!
I realize quite quickly that if the directory brute-force is too fast the server starts blocking me with HTTP 503 responses. Setting the delay between tries quite high and then let the bruteforce run in the background.
Switching focus (yes, again!) to looking at the QR-code. The first part is the email, but what is the second part? Is it just a hash? Is it encrypted data? XOR perhaps?
Generating three new accounts to compare the data in the QR-code. All are 256 characters hex-encoded, so 128 bytes Could it be encrypted with a static password, can we see some common data between the accounts? A nonce perhaps?
Not finding any common data in the last 128 bytes of data Friday 2020-01-17 17:00 (5d 15h 59m remaining)
Getting nowhere fast Time to put this away for the weekend, guests are staying over and the family needs attention. Monday 2020-01-20 10:56 (2d 22h 3m remaining)
Starting all over again. Got lots of rest and time to thing during the weekend.Lets compare if different user-parameters affect the QR-code.
Gives QR-code
and
Gives QR-code
Looking at this for a while trying different options in cyberchef to juggle the data around but dont really get anywhere
The thing with a CTF is that there IS a way in. you just need to find what that is.Compared to bug-bounties there might not even exist a way in
What are they thinking of?
How have they designed this to be a tough challenge?
How the hell have anyone solved this already?
One of the bigger things research-wise presented during 2019 was request-smuggling by @albinowax. surely that must be in here somewhere?
AAAAhh!! Yes!
What if: The character-filter that removes on the username is done in another machine/application and the request is then forwarded to the backend. that would be kind of unlikely in a real-world situation but could be in a CTF? Time to read up on request-smuggling
My idea is to not do normal request-smuggling but to send two requests in one, and have the filter on the first box only look at the first request, but then on the backend have it interpreted as two requests, where the second one is not filtered Monday 2020-01-20 11:58 (2d 21h 1m remaining)
YEEEEEEEEEEEEEEEEEES! Request-smuggling works! at least i can send two requests in one (TE.CL) and receive the response from the first request, but when refreshing the page the account is updated with the second one (or really updated twice, but with the second one last).
So sending this request will give a response with the name p4fgaaaaaaaaaaaa3 but when reloading the page the user is named p4fg .
Time to test my filter-theory and send p4sfg as the name in the smuggled request
EPIC FAIL followed by disappointment
The filter still removed the characters Monday 2020-01-20 13:10 (2d 19h 50m remaining)
Checking in with my friend @nilssonanders on Telegram during lunch, telling him:
-Im not getting anywhere. Imposter-syndrome delux .
Imposter-syndrome is a tough thing, but you need to continue working even though you feel worthless. (Which i at this point felt.)
Testing sending in some really long usernames. read an article on regex-filters that started failing when given texts over 1000000 charactersIf we just append our HTML-tags after that, they might slip through
Not working too well that either, the server does not accept any usernames over 100-200 characters
(The server started blocking me so it was hard to know the exact limit). Monday 2020-01-20 16:02 (2d 16h 57m remaining)
Starting working on a script that will create a QR-code for me and try to do a recovery-based login. Monday 2020-01-20 17:00 (2d 15h 59m remaining)
Time to call it a day. Kids to feed and stuff to do Im not really getting anywhere
What the hell was that hint about regex:es about??? Tuesday 2020-01-21 09:20 (1d 23h 39m remaining)
Finally some progress!! I have found the first piece of the puzzle.
It all started with me continuing to mess with the QR-codes.
If i put in strange characters in the email before the @ the recovery-page would say something like Invalid email-address .
But if the characters was in the end of the email I would get wrong code .
This and the fact that the settings-page filtered out got me to try something.
If i tried to register a user as jobert@mydocz.cosmic it would not work as that account already exists.
If i tried to register a user as
the javascript-logic on the page would prevent me from submitting the page.
So I entered username p4fg , email jobert@mydocz.cosmic , intercepted the request in burp, and changed the email to
Saved the QR-code, as i always do. Got quite a few of them now
And got logged in as p4fg . But at least the system accepted the strange email, that must be some progress.
Now i looked at the QR-code. AND look at that
It starts with 6a6f62657274406d79646f637a2e636f736d6963:
There is no 3e as the last character of the email in the QR-code. The QR-code is for jobert@mydocz.cosmic !!!
And when using that through the recovery-page I get logged in as jobert!!
Finally some progress!
When logged in as jobert you are not a trial customer anymore. Maybe there is different options in the settings? Maybe no HTML-filter?
Iimmediately tried to change name to something with HTML in it, but that did not work. The jobert-user is not allowed to change name at all Tuesday 2020-01-21 09:28 (1d 23h 32m remaining)
Then I tried to convert a document. That did not work either, giving a error 'license has expired' .
Looking at the documents-page, this was also (ofcourse) empty so the only thing that this new account brought to the table was the ability to contact the support-staff through a chat that was not enabled for the trial users.
This looks promising. You entered quit to exit the chat and was then prompted to give feedback on how the support was.
The chat was vulnerable to HTML-injection and probably XSS. but this would only be self-XSS.
unless you had someone on the other end looking at the conversation, which they probably would do if you gave the conversation a one-star rating Then we would have blind-XSS
Looking at the CSP, (and assuming it is the same for the other party) it is pretty strict
So any image-src would work to leak data. I enter a message containing
entered quit and then gave the conversation a 1-star rating.
A text appeared: We're sorry about that. Our team will review this conversation shortly. .
Shortly after, this happened:
Someone (or something probably in this case as it is a CTF) is on the other end, inspecting conversations with a bad rating.
Pretty realistic actually! I am definitively on the right track now!
Lets see if the simulated person on the other end will press any buttons in our payload, that could happen Because if we can do this, then we can make POST-requests. And what if the support-staff can change names on users without any filter? In that case we can get a HTML-payload into our name! So first we test if the system will press buttons
Nope. nothing happens.
Nope, this didt work either (and would actually not work with the CSP anyways).
Lets try
Nope. nothing here either
But CSP says script-src 'self' . so we can have scripts included from the same site (if i understand the black-magic of CSP correctly)
So if we can load scripts from the same site. how would we upload a script? Hmmm. We did get that broken image when uploading non-images in our converter lets try that!
Here we send up a file containing a simple alert() to the converter
Looking at the generated HTML in the documents-page:
Ok. so the thumbnail is not working since it is hard to do a thumbnail of a non-image And the pdf only shows a broken image-symbol But if we have a XXXXX-thumb.png then maybe we have a XXXXX-full.png as well?
Nope. not XXXXX-full.png but XXXXX.png works!Our uploaded js is saved as /documents/877935c0bcd2ff1d5609f6a352bb02604cd8e14f7effe0ee094d189c543ac210.png
fetching it shows some badness though:
The content-type is image/png and that will not work as a script src as i quickly realized. (Pretty sure this used to work back in the days)
Tried some combinations of entering different mime-types when posting the file, but no matter what I did the content type was set to the same as the file-extension (only .png and .jpg was allowed). Tuesday 2020-01-21 10:42 (1d 22h 18m remaining)
Going through my main strategy:
I want to do some kind of SSRF/CSRF from the internal support-person to the /settings endpoint and change the name to a HTML-payload in order to do things in the PDF-conversion.
Probably doing an iframe to another internal system.But to get any further i want to run javascript. im not entirely sure i need to, but i want to make it happen anyways. It is the most likely way forward. Tuesday 2020-01-21 10:51 (1d 22h 9m remaining)
Starting to bruteforce (using burp intruder) different extensions on my uploaded files to see if maybe there is metadata stored (a log perhaps) of the conversion, that somehow can be used as a script-source.
https://h1-415.h1ctf.com/documents/2fc15fd88e06b1b1c522505d4537952e13d275e3463f2f70542f1744d24f818c.png Tuesday 2020-01-21 10:59 (1d 22h 0m remaining)
I started asking myself: What is wrong in this puzzle? What part does not really fit in?
Went back to the CSP and analyzed it with https://csp-evaluator.withgoogle.com/
I started asking myself Why would they include https://raw.githack.com/mattboldt/typed.js/master/lib/typed.min.js from that location? Why not stick that under /js/ like everything else??
It turns out that githack is a service that lets you serve js directly from github with the correct content-type (you cannot use the raw-links directly from github as a script src, just because they dont want people to use github as a CDN).
This got me even more interested. but we could only access files from /mattboldt/typed.js/master/lib/ so i went to the github-repository https://github.com/mattboldt/typed.js/tree/master/lib thinking that there MIGHT just be some js-file there that i could use in some kind of chain
Answer: No, nothing there
Then it dawned on me. maybe we can do a directory-traversal on the githack-site? And serve stuff from one of my own repositories to run arbitrary blind XSS Tuesday 2020-01-21 11:31 (1d 21h 29m remaining)
Seems like the directory traversal approach might be successful I created a repository with a file containing a simple alert:
Using burp (to avoid issues with the browser messing the path up) i requested
the response is very promising:
Correct content-type, correct path on correct server. this might be the way!
So i enter the following message in the chat:
As script-tags that are added by DOM-manipulation are not executed i could not see anything happening
But if I reload the support-page (in my browser) all conversation messages are included and the script is run or rather. it is NOT run due to the CSP.
As this is rendered in the page it becomes:
this is normalized by the browser to:
and that is outside the allowed CSP Tuesday 2020-01-21 11:55 (1d 21h 4m remaining)
OK. but this worked in burp! (Famous last words) We want the slashes rendered as 2f so we need to double-url-encode it
New support message:
And reload the page:
After many dead-ends and wild goose-chases we finally are getting somewhere, but ofcourse popping an alert on a headless browser on the other end will get us nowhere, but it sure helps with the imposter-syndrome
Now lets move on with our plan, do a CSRF (or is it SSRF? The lines blur a bit here as it is both.) from the support-account to the settings-page.
First I want to make sure that I can do POST-requests from the other side so i enter the message:
where the submit.js file contains Tuesday 2020-01-21 12:14 (1d 20h 45m remaining)
Unable to try this last part as the server starts responding HTTP 502 to every request GAaaaah!!! Tuesday 2020-01-21 12:29 (1d 20h 30m remaining)
Server is back up. Feeling a bit stressed out as the server will reset in 6 minutes. Tuesday 2020-01-21 12:32 (1d 20h 28m remaining)
YES!
The script is loaded and executes, it submits the form to burp collaborator. Tuesday 2020-01-21 13:32 (1d 19h 28m remaining)
After a lunch-break, it is time to continue our explorations
Looking at the cookies sent from the server, the _csrf_token is not set as http-only. This means we kan leak it. Lets try!
Sending:
where the submit.js file contains
Our burp collaborator receives the request:
So we can get the _csrf_token from the remote service-agent but can we use it without the http-only session-cookie?
But it sure does look promising that the agent has a _csrf_token
Lets try to see what documents this account have access to, maybe the service-people can see all documents in the system? Sounds resonable!
We want to see what the other browser sees when looking at /documents
Creating a new JS-file: Tuesday 2020-01-21 13:50 (1d 19h 9m remaining)
Waiting for this to be cached in githack. this takes forever
After waiting a bit everything seems in place. Try the payload in the chat. and.nothingHmmmm It would be easier if we could avoid updating github all the time. what if we could put our payload in the form and just have the script execute it using eval() ? That would be nifty
Lets update our submit.js with the following code:
and have the following message:
So the script would take the payload in evaldata decode that using base64 and doing eval() on that. No more update-cache-cycle via github and much faster turnaround-time
But it turns out there was a bug in that script, we need to access the .value of the input element of course
Well. it could have worked. under a different CSP:
Ok. back to basicsWe will continue updating github and take that approach. lets try to fetch the documents again! (Using the same form as before)
This works in my browser when reloading but not in the blind context.
(-Works on my machine! wont help me here)
Are we using the wrong URL? Maybe h1-415.h1ctf.com does not resolve on the machine?
New try using a relative path
Nope. no POST-request this time either. why??
Time to investigate a bit more on what is happening remote lets write more javascript. because we have to
So we have a function leak() to append images dynamically to leak information, just like a logger.
This actually returns some interesting stuff as onload_200 :
Done, got 5921 bytes
So the remote is loading the contents of /settings and its getting back 5921 bytes of data. promising indeed
Another try, and this time we add some code to leak the contents of the response and also fetch /documents :
This failed in a strange way The script successfully leaks the document.cookie and document.domain but nothing else why? Do we get some kind of javascript CORS exception when we access XHR.response ?? Cant be because we previously got XHR.response.length . StrangeIt works fine locally.
For some reason I got to thinking that maybe it was the leaking via image-tags that was the problem so i updated the leak-function to do XMLHttpRequests instead
Of course this was a bad idea. after thinking of it the XHR post is a CORS-request and not allowed by the CSP. but realizing this after the fact does not really help
Reverting to the leak-function with images, and making the script request urls via a function.
Still, nothing is returned. Debugging javascript remotely and blind is really sub-optimal.but lets try Updating the reqListener-function to
This actually sends the pre-response but then its silent. what is going on here? It should work Tuesday 2020-01-21 15:36 (1d 17h 23m remaining)
Server-reset time Setup everything from scratch again, re-register all users, etc, etc.until we are in a position to send chat-messages again.
Time to do some more debugging by adding a try-catch and leaking the exception:
Burp collaborator receives the exception! Maybe now we can find out what is going on The data posted is:
which decoded is:
Oh. eehso this was no CORS-issue, it was something that the btoa-function did not like ok Stackoverflow to the rescue: https://stackoverflow.com/questions/23223718/failed-to-execute-btoa-on-window-the-string-to-be-encoded-contains-characte
So all we need to do is unescape(encodeURIComponent()) of the data first because reasons Tuesday 2020-01-21 15:52 (1d 17h 7m remaining) Types Of Imposter Syndrome
SUCCESS!!! We are getting the HTML-back from the support-agent requesting the pages on their end.
What a beautiful sight!!
I will not post the entire decoded HTML, but just a snippet of it to illustrate my complete devestation:
All leaked HTML-pages were identical. it is the login-page the support-agent is not logged in to the system Or are we simply not sending the cookies in our request
I seem to remember an option withCredentials that can be used with XMLHTTPRequests Lets read the documentation https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
That saysdoh.
So no that should not be the reason. but just to make sure lets try it:
and while we are updating the script, lets leak the location as well:
Lets get the bad news out of the way first:
withCredentials did not make any difference, the user running the simulated support is not logged in (or so it seems).
The good news is that document.location revealed some interesting stuff:
As the support-agent is not logged in, we might be able to access this URL ourselves?
Yes we could!! And here we can edit the user as well! Seems our original strategy could be correct, just not the way I thought!
Editing the jobert-user returns a page saying:
But looking at the request it takes three parameters: name, user_id, _csrf_token Lets just change user_id to one of our users and try for an IDOR. (the user_id for a user is in the change-name request that the user can do for him/her-self):
returning
'result':'success' Tuesday 2020-01-21 16:17 (1d 16h 43m remaining)
Switch to a private-session with the second user logged in, and converting a png-image
YES!!!!! We can now inject HTML into the page converted to a PDF! A (now) classic scenario that I have pulled off as a critical bounty before, where i extracted AWS-credentials via an iframe
Lets first see what documents this user can see if they browse to /documents and (for some reason) localhost:443 by setting the name to
and converting a document. Mac Os Versions
So this user/browser does not seem logged in either. lets use the same script as before to leak the document.location .
I set the name to
and convert a file.
The leaked location returns the path that the converter uses:
Interesting. But when trying this path as a user we get a Forbidden-page.
I lookup the ip-address of that sends the requests to burp-collaborator: ec2-18-218-90-126.us-east-2.compute.amazonaws.com
HAHAHA, this will be simple, enough of investigation. Lets just steal the AWS-keys and probably go to the next level.
Im thinking S3-buckets and what-not. Wonder how many levels deep this goes?
Lets re-use the last code i used to steal AWS-credentials, because why not.
The last time i did this the script needed to mark that everything was done/rendered by setting window.status = 'READY' and adding a class to the body.
This isnt needed here, but i would rather not cause another bug by editing it however this script requires an element with id=log that it uses to append log-data, so we need to add that element to our name.
So. lets run this payload Tuesday 2020-01-21 17:13 (1d 15h 46m remaining)
502 Bad gateway when converting
Wellshit
Other pages load fine. but I cannot save user settings on any endpoint.
After 5-10 minutes the endpoints work fine again.But now i get Internal server error when setting the name
After a bit of experimenting i realize there is some kind of length restriction on the name, so changing it to
That name-change works.
Now lets convert a file.
FAIL . The document does not get created at all. Maybe my script was bad?Lets try a iframe directly in the name just to eliminate the potential script error:
Same thing. fails to create a documentSoo. maybe they dont like the IP-address.
Lets try a hostname with a 302 redirect to the metadata-URL!
FAIL. same thing happens. Now i really need to go cook some food for the kids Tuesday 2020-01-21 18:43 (1d 14h 16m remaining)
Lets try with brand new javascript:
Convert a file. Nope. Page not found. Something is broken or the outbound connections are filtered
I try a quick iframe to https://www.nasa.gov , that works. kind of Only the first basic parts of the page is rendered, so it seems like the pdf is generated before the iframe is completely loaded.
However. it seems like we cannot access the AWS-metadata at all. Thinking about if they were evil enough to host this on another cloud-provider and tunnel the traffic through AWS as a decoy. but that would be very far-fetched.
Lets see if there is anything interesting in localstorage in the browserthat might actually be it!
Yes, there is probably some cool data in the localstorage that shows us to the next level in this dungeon.
Upload, change name, convert checking burp-collaborator drumroll
.
OK. i really had my hopes up for that one
. my bad
Somehow i just realise that im not using the path-traversal for the js-file on githack
But. then. i dont need to update it on github and githack anymore doh!
I tried a js-file hosted elsewhere than on raw.githack.com and it works perfectly well.
No CSP on this page (or not any CSP that affects us yet anyways) Tuesday 2020-01-21 20:50 (1d 12h 10m remaining)
After the server-reset i no longer get callbacks from the blind XSS on the support-chat. im using the same payloads as before. something is broke. Poor hackers that are reaching this level in this reboot-cycle, they will never realise the blind XSS Tuesday 2020-01-21 21:00 (1d 12h 0m remaining)
Still no response from the blind XSS. calling it a night Monday 2020-01-22 08:10 (1d 0h 49m remaining)
Went swimming for exercise. That gives me some time to think things throughLast day of the CTF. time to get my shit together. im sooooo close. but how close?Im expecting another level of insanity after this one, AWS with s3-buckets. or maybe using that request-smuggling i think i found earlier Monday 2020-01-22 09:43 (0d 23h 16m remaining)
While swimming i made a mental list of things i wanted to try this last day.
One thing was doing a brute-force for pages returning 403 forbidden just like the converter-page.
Maybe the converter can access them too?.
Another thing was trying other ports on the same machine.
maybe i will find a open Jenkins-instance, Tomcat-admininstration page or something like that.
One last thing i remembered from somewhere, the Chrome DevTools Protocol that sometimes is open on port 9222.
This enables remote debugging on headless instances.
I made a new JS-file that writes out the URL and also measures the time from the start of script-execution to pdf-generation, to get an idea of how long time we have for iframes to load. This will also help us if i can find out a way to keep the browser busy in order for the pages to load.
First try: Chrome DevTools on port 9222.
Well hello there!! Seems like dev-tools are enabled. now what can we do with this?
The pdf does contain the links in the page as well:
Lets first try fetching this ourselfes without websockets (via iframe as usual):
Nope. this got returned empty (It might not be empty but the pdf is generated after 500ms)
Lets try the entire thing, even though i really doubt it will render in time:
This gives a broken document, file not found
Time to study: https://chromedevtools.github.io/devtools-protocol/
ok. so there is an endpoint for the debug-tools /devtools/inspector.html
but same thing here. it will not render completely
Reading a bit more under the section HTTP Endpoints in the document
Why not. at the very least i will learn something Monday 2020-01-22 10:11 (0d 22h 48m remaining)
Setting up the name of the user to be a iframe to http://localhost:9222/json/list , and converting a file
Well this looks interesting.
http://localhost:3000/login?secret_document=0d0a2d2a3b87c44ed13e0cbfc863ad4322c7913735218310e3d9ebe37e6a84ab.pdf
Now lets use the IDOR (might just be intended functionality for sharing??) from early on and just swap out the document-id with any other document and fetch it using our browser:
https://h1-415.h1ctf.com/documents/0d0a2d2a3b87c44ed13e0cbfc863ad4322c7913735218310e3d9ebe37e6a84ab Monday 2020-01-22 10:13 (0d 22h 47m remaining)
No more levels, this was the end! A very nice CTF indeed!
If you made it all the way here, thank you for reading!
My message with this very long writeup is that there are ALOT of failures during the way. Not many people plow through this easily, and now you have a first hand account on how it looks like working through this
I hope you learned a bit from my mistakes, i know i did!
Imposter syndrome is a bitch, but just keep on throwing stuff at the target and look for things that are a bit off,in the CTF-world they are either clues or things that need to be there in order for a vuln to work. Imposter Syndrome Test
/p4fg
broken image