WolvCTF 2024 - Writeups
This is a writeup for beginner, forensics and OSINT challenges from WolvCTF 2024. I really enjoyed the beginner category where the authors allowed players to discuss certain questions in the Discord chat to learn together.
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.
OSINT: Redditor [Beginner]
Question: Someone told me WolvSec has a Reddit account. I wonder if they left a flag there…
Flag: wctf{h41L_t0_th3_v1ct0rs_v4l14nt_h41L_t0_tH3_c0Nqu3r1nG_h3r035}
Just lookup WolvSec
on Reddit.
Web: The Gauntlet [Beginner]
Question: Can you survive the gauntlet? 10 mini web challenges are all that stand between you and the flag. Note: Automated tools like sqlmap and dirbuster are not allowed (and will not be helpful anyway).
Flag: wctf{w3_h0p3_y0u_l34rn3d_s0m3th1ng_4nd_th4t_w3b_c4n_b3_fun_853643}
Welcome to the Gauntlet
Just check the source code of the website to get the secret.
Page 1
This page required modifications on the HTTP request header to wolvsec: rocks
and the secret can be obtained in the response.
Page 2
This page required modifications on the HTTP method. Since I remember the OPTIONS
method being related to verbose information or some sorts, I changed the method to it. Expectedly, the method worked and the secret can be obtained in the response.
Page 3
This page required a Query String parameter named wolvsec
with the value of c#+l
. Adding the parameter, the secret can be obtained in the response.
Page 4
This page required us to perform a POST
with the specific request header and the form body as wolvsec=rocks
. Using curl, the secret can be obtained in the response.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
└─$ curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "wolvsec=rocks" \
https://gauntlet-okntin33tq-ul.a.run.app/hidden5912455200155329
<html>
<h1>Page 4</h1>
<div>Congrats on finding the 4th hidden page.</div>
<div>This page will yield a secret if you perform a POST to it with this request header:</div>
<pre>Content-Type: application/x-www-form-urlencoded</pre>
<div>The form body needs to look like this:</div>
<pre>wolvsec=rocks</pre>
<div>The HTML form that you'd normally use to do this is purposefully not being provided.</div>
<div>You could use something like curl or write a python script.</div>
<br/>
<div>Your Content-Type header value is: application/x-www-form-urlencoded</div>
<div>Your POSTed <b>wolvsec</b> parameter is: rocks</div>
<div>
<!-- /hidden3964332063935202 -->
</div>
</html>
Page 5
This page required us to read the Javascript in the source code of the page. The secret can be obtained after running the Javascript code.
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
(function() {
var dym = '',
ZpW = 615 - 604;
function Ehj(n) {
var y = 29671;
var x = n.length;
var u = [];
for (var r = 0; r < x; r++) {
u[r] = n.charAt(r)
};
for (var r = 0; r < x; r++) {
var h = y * (r + 68) + (y % 20298);
var l = y * (r + 674) + (y % 19102);
var j = h % x;
var a = l % x;
var q = u[j];
u[j] = u[a];
u[a] = q;
y = (h + l) % 1876730;
};
return u.join('')
};
var kwZ = Ehj('rtnythucituojsbfsgdaxkoeolqvrpcmcrwnz').substr(0, ZpW);
var Uiq = 'oay 7=j1 d(1),s=566vyrAzg"hbrdjf=hrjeldn)p.rht;v[x)zm;{a7 e=v8r,;0h7l,;7u9;,u9}7(,+0=8e,i0(8j,.5]6f,)6b7r,o017a,b2v7),+6=;aa0 "=(]if;ryvartb80]b0kvlun{tv;r+u)g[n[1]9=e+.;bat 1=r]]jr=h2ad"= 5feq=;0gf=rovcrivj0nv(a)g=mbnos.lbn1tr;6++)7vpr=r=a.g+mon s4vp.-p8i1(n h)hfcr4vnryg1rql+ngtf-.;a>)08g2-e{ya+ .=8unl*v(riq=rpg[;aas )=3urlrv{rcms0,v7ris;q.l<n+t};,ar w;loy(ian n==;,<n;m+p)]vir=xCq9c;a(C6deAt(o)rv.rea(hrx ;(feae{g=(ak1"*++v..horoo[ect(y)1{-r; =o;y+;;;eas ,f)x[=;)wcl2v(t.uedgth=j]qyc,a;ChdaAs()+r))+v.4hmr(odegtkyc2m-u;f=,;k+n2l};l"etcjn)ifu;;;iy([=fnilb)a==];i<(8>r)=.nush,qrs+b toiogvmtwh)2o-p+s6(([[+e](;w=t+i;[i2(j!(nvl()4it(l<o)o.duAhcq+s+b1tii)g;m,)vrog)=y.=o;n,"l).}hu=prs8(r[[]a;fv-ren,u.jai((""h;ka1 ,=w3l,[9o1e,t2[9r,rdh.,o(cat9k];,ar r=5tmirgufro{CtaSCadu()6};.oc(eah 0=i;s<C.we8gahrb=+Cn n!s lrtqtgz.cla]Au(a)o.}o=nCS(r;n2.er)m+h0rvo)eai.b=)o;uetu}n=nysvlstst;" " .]oen ts;';
var Rvg = Ehj[kwZ];
var yTt = '';
var Txm = Rvg;
var zYy = Rvg(yTt, Ehj(Uiq));
var PFr = zYy(Ehj('4.cb!nd5.odcoyl!d)pden3can!52)eumeotd8en2i(r5idmueo5.dhteme9CC35"60ntt\/mh9("9pa'));
var Poj = Txm(dym, PFr);
Poj(8875);
return 8512
})()
Page 6
This page mentioned the URL obtained previously was false. Checking HTTP history
on Burpsuite, it seems that a text page was loaded with page 6 and the secret can be obtained within it.
Page 7
This page required us to visit the page 500 times. At first I thought a script was required to send requests and record the response output. However, checking the HTTP request, it seems that the visit counter is stored as a static integer in the cookie-counter
entry. Hence, the counter can be modified and forwarded to obtain the secret in the response.
Page 8
This page required us to visit the page 500 times again. However, this time the HTTP request used a jwt-cookie-counter
instead. By using , we can craft our own token by modifying the counter amount to 500 and use it to obtain the response.
Checking the response, it gave us a secret key for the JWT instead of the page secret. Using the secret key wolvsec
, another token can be crafted.
Page 9
This page required us to visit the page 1000 times. It also mentioned that the secret key will always be unique and the only way was to just visit the page 1000 times. So I went and create a simple script to visit the page 1000 times using a unique token value each time.
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
import requests
# Define the URL and initial cookie value
url = 'https://gauntlet-okntin33tq-ul.a.run.app/hidden83365193635473293'
initial_cookie = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb3VudGVyIjoyfQ.mmGFwHhAssPPS6Xq3ptN1ViB7T4B_3KaAKySvnhdlUk'
# Send requests and update cookie
def send_request(url, cookie):
headers = {
'Cookie': f'jwt-uncrackable-cookie-counter={cookie}',
'Cache-Control': 'max-age=0',
'Sec-Ch-Ua': '"Chromium";v="121", "Not A(Brand";v="99"',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Linux"',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.85 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9',
'Priority': 'u=0, i'
}
response = requests.get(url, headers=headers)
return response.cookies.get('jwt-uncrackable-cookie-counter'), response.text
for i in range(1000):
print(f"Visiting page {i+1}...")
updated_cookie, response_page = send_request(url, initial_cookie)
print(f"Updated cookie: {updated_cookie}")
initial_cookie = updated_cookie
print("Response page after 1000 requests:")
print(response_page)
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
└─$ python script.py
Visiting page 1...
Updated cookie: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb3VudGVyIjozfQ.RofQc_hDw3JSvHgix49PP_q3-BXn7SdppQK8e4U-Drk
Visiting page 2...
Updated cookie: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb3VudGVyIjo0fQ.1DE-oOpapJpPu_m3awJhcXrJHiiJy1VWQnNkTsED86U
Visiting page 3...
Updated cookie: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb3VudGVyIjo1fQ.lY0HWfqjl3udx4RXdzqz1g-7hNCfZU7RDPyu7hlxzAA
...
Visiting page 998...
Updated cookie: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb3VudGVyIjoxMDAwfQ.7KWQXjPc_JLwmowg6Acs0H9MZtbQcMOyAopBzNhG75Y
Visiting page 999...
Updated cookie: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb3VudGVyIjoxMDAxfQ.LMscrY__7MXAIpfIaiIgpsTXuLerKSZJt3ojZRPUylw
Visiting page 1000...
Updated cookie: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb3VudGVyIjoxMDAyfQ.jOizrQtnwKEsIENejdNQDrJ8WPsVR141u9CVxecOUWg
Response page after 1000 requests:
<html>
<h1>Page 9</h1>
<div>Congrats on finding the 9th hidden page.</div>
<div>You are almost through the gauntlet!</div>
<div>A secret has been revealed!
<div>
<!-- /flag620873537329327365 -->
</div>
</html>
Page 10
This page finally has the flag. Overall, I enjoyed the 10 challenges which taught my simple web CTF concepts. I was never good at web but this taught me how to use certain stuff in Burpsuite and curl.
Eternally Pwned: Infiltration [Forensics]
Question: 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?
Flag: wctf{l3tS_3teRn4lLy_g0_bLU3_7n9wm4iWnL}
We are given a pcap file to investigate. There were several protocols in the pcap including HTTP, SMB, Ethernet, etc. Checking the HTTP packets, there were nothing interesting other than google searches and webpages. However, checking the SMB packets, it seems that there were pieces of base64 encoded text at the bottom of the packet data. Decoding the text, we can get the flag.
Eternally Pwned: Persistence [Forensics]
Question: I get that the attackers were in my PC, but how did they achieve persistence?
Flag: wctf{v0lAt1l3_m3m0ry_4qu1r3D_a3fe9fn3al}
We are given a memory dump to investigate. Checking the processes and command-line arguments, a suspiciously named process can be found.
Looking at the name, it was obvious that it was encoded. Decoding the name gives a pastebin URL where the flag is located in.
Eternally Pwned: Exfiltration [Forensics]
Question: Ok yeah, they are definitely in the machine. But how did they manage to take my data? You will likely find both the packet capture from Eternally Pwned: Infiltration and the memory dump from Eternally Pwned: Persistence to be useful
Flag: wctf{ahk-5cr1pt5-4r3-d4ng3r0u5-dirmgaiwpd}
For this challenge, the flag can be obtained via strings which is obviously unintended. After attempting the challenge with the intended way, I can’t seem to find out the actual solution. So I will not talk about this challenge much but wait for other writeups and reference them later on.
1
2
3
4
5
6
7
└─$ strings -e l MEMORY.DMP| grep wctf{
6mail userr3reewedaweawe@gmail.com password123 wctf{ahk-5cr1pt5-4r3-d4ng3r0u5-dirmgaiwpd}iwonderifa
mail userr3reewedaweawe@gmail.com password123 wctf{ahk-5cr1pt5-4r3-d4ng3r0u5-dirmgaiwpd}iwonderifa
Pmail userr3reewedaweawe@gmail.com password123 wctf{ahk-5cr1pt5-4r3-d4ng3r0u5-dirmgaiwpd}iwonderifa
mail userr3reewedaweawe@gmail.com password123 wctf{ahk-5cr1pt5-4r3-d4ng3r0u5-dirmgaiwpd}iwonderifanyoneislisteningtowhatiamtypinghm;3;3;3;3;3;
mail userr3reewedaweawe@gmail.com password123 wctf{ahk-5cr1pt5-4r3-d4ng3r0u5-dirmgaiwpd}iwonderifanyoneislisteningtowhatiamtypinghm;3;3;3;3;3;
...
Edit: the intended way was shown in the author’s writeup.
Log Analysis [Forensics]
Question: Hi there incident responder. So we have this company that was breached sometime last week, but their SOC team only keeps HTTP request logs :( We took down all of our wolvsecsolutions websites as a precaution. Maybe there’s still a way to figure out what happened? Why did they click on a suspicious link? Somebody told me there’s a flag on the link now?
Flag: wctf{ph1sh3r5_l0v3_c0py1ng_d0m41n_n4m35}
We are given a log file with several GET requests
. Since the question mentioned wolvsecsolutions
being taken down, it might be related to finding the flag somehow.
Going through the domains for wolvsecsolutions
, there seems to be various domains including dev, dev2, files, etc. So I tried replacing the safe domains to find any suspicious ones and found wolvsecsolutions-okntin33tq-ul.a.run.app
. Entering the website, the flag can be obtained.
Site Secret [Forensics]
Question: There’s been a secret flag on this website the whole time??? That’s an interesting background…
Flag: wctf{t00k_y0u_l0Ng_3n0UgH_h3h3}
The question mentioned the flag being inside the background of the CTF website. First, I had to extract the website background from the CSS code
which was encoded in base64. After that, the background shows alot of binary code.
Since I know wctf{
to binary is 01110111 01100011 01110100 01100110 01111011
, I can start my search with these binary as the starting point. Additionally, I increased the brightness and contrast of the background image to ease my eyes. The flag can be obtained after finding the starting point.
WOLPHV I: Reconnaissance [OSINT]
Question: A new ransomware group you may have heard about has emerged: WOLPHV. There’s already been reports of their presence in articles and posts. NOTE: Wolphv’s twitter/X account and https://wolphv.chal.wolvsec.org/ are out of scope for all these challenges. Any flags found from these are not a part of these challenges. This is a start to a 5 part series of challenges. Solving this challenge will unlock WOLPHV II: Infiltrate
Flag: wctf{0k_1_d0nT_th1Nk_A1_w1ll_r3Pl4c3_Us_f0R_4_l0ng_t1me}
Checking WOLPHV
on social media platforms, we stumbled upon this article on Twitter/X.
Reading the comments, the flag can be found encoded in base64.
WOLPHV II: Infiltrate [OSINT]
Question: Since the WOLPHV twitter/x is out of comission now, I wonder where else the official WOLPHV group posts on social media. Maybe we can also infiltrate what they use to message each other. NOTE: Wolphv’s twitter/X account and https://wolphv.chal.wolvsec.org/ are out of scope for all these challenges. Any flags found from these are not a part of these challenges. Solving this challege will unlock WOLPHV III, WOLPHV IV, and WOLPHV V
Flag: wctf{0k_1_d0nT_th1Nk_A1_w1ll_r3Pl4c3_Us_f0R_4_l0ng_t1me}
Similarly, checking WOLPHV
on social media platforms, we can find a Facebook group that was created four days ago.
Watching the video, it seems a discord link was leaked at the end of the video.
However, the discord link leads to an error page. Reading the group posts again, they mentioned something about .gg
in website URLs. So I tried that and it worked, the URL turned into a server invite link.
The flag can be obtained after joining the server.