Published on

WolvCTF 2023 – WannaFlag Series

Authors

WannaFlag Banner

Table of Contents

Intro

Over the last weekend, I played in WolvSec’s second CTF iteration with Project SekaiWolvCTF 2023. We placed first in the open division, and throughout the solving process I became intrigued by a specific series of challenges placed under the OSINT category: WannaFlag. Telling a story of a supposed ransomware group which had been terrorizing the CTF community for the past several months, these series of challenges offered an opportunity for players to track down this group’s means of operation. Ultimately, the goal was to find WannaFlag’s kingpin through all possible methods.

Project Sekai was the first to blood the entire series. Here was our thought process, notes, and conclusions.


WannaFlag I: An Introduction

Welcome to WolvCTF’s OSINT Category! We have a bunch of great OSINT lined up, assuming nothing goes wrong hahahhahahhahah but why would it?
For this challenge, find where the image was taken, and look at the Google Maps reviews!
Note: Flags can be found in standard format wctf{...} for ALL OSINT challenges
Attachments: image.png

We’re first given a little bit of a warmup: find the location of the following object, and to view its Google Reviews:

Image of a statue of a black cube, balanced on one of its corners in a university plaza

Simple task! These types of challenges, often called GEOINT (geospacial intelligence), can be trivial if there is a landmark object situated within the image—in this case, we have some public art resembling a black cube. We can use Google Lens to identify it:

Screenshot of Google lens output of provided image

Looks like Google’s given us a hit: this is “The Cube,” a public art installation in the University of Michigan. Let’s take a look at the Google Reviews:

Screenshot of Google reviews for “The Cube”

This netcat wanna-flag-i dot wolvctf dot io one three three seven can be converted to a command: nc wanna-flag-i.wolvctf.io 1337. Let’s connect to this server to see what it has to say:

Asciinema-produced GIF of netcat server output

Well... that was unexpected. Let’s first digest what in the world just happened to this netcat:

  • Our flag is cut off midway, and WannaFlag supposedly “pwns” the server
  • We’re told to pay 500,000 Goerli to the wallet 0x08f5AF98610aE4B93...
  • This hashtag #YourFlagsBelongToUs is spammed everywhere throughout the message
  • The group tells “John OSINTs” to leave them alone on Twitter

There’s a small connection here, but it’s not immediately obvious. We can actually search for this hashtag on Twitter, revealing a tweet from none other than @JohnOSINT_ himself:

I’ve been getting reports of a new ransomware group that have been specifically targeting CTFs. They’ve been posting #YourFlagsBelongToUs to their victims and demanding crypto in exchange for the flags and infra back. This is big..... – John OSINT (@JohnOSINT_) March 11, 2023

If you think you’re one to go after them, consider these OSINT tools: osintframework.com

I've only exfiltrated one flag from them so far: d2N0Znt1aGhoX3doM3IzX2QxZF80bGxfMHVyX2ZsNGdzX2cwP30= – John OSINT (@JohnOSINT_) March 11, 2023

A simple base64 decode of the exfiltrated string gives us our first flag: wctf{uhhh_wh3r3_d1d_4ll_0ur_fl4gs_g0?}


WannaFlag II: Payments

Ok well.........................something may have gone wrong
WannaFlag’s ransom demand is insane, there’s no way we are paying that. Can you figure out which address the money is being funneled to?
From the ransom note: send 500,000 Goerli to 0x08f5AF98610aE4B93cD0A856682E6319bF1be8a6

Our next step involves figuring out where this ransom money is being funneled to. We can use Etherscan’s Goerli Testnet Explorer for transactions involving the address 0x08f5AF98610aE4B93cD0A856682E6319bF1be8a6:

Screenshot of Etherscan transactions for the address 0x08f5AF...

We can see the transactions this account has made so far, but the only ones that are relevant would be the ones done before the competition started (since those would be part of the challenge creation process). These are the following transactions the account has made within this reasonable scope:

Screenshot of 0x08f5AF’s transactions within a reasonable scope

Looks like there’s something that stands out—although there are a lot of “IN” transactions, there’s only one “OUT” transaction. This is the transaction that we’re looking for, and it’s a pretty big one being sent to 0xA01FD0.... Let’s follow the breadcrumb:

Screenshot of Etherscan transactions for the address 0xA01FD0...

From here, it seems as if the money is being distributed into several different accounts. Upon checking these transactions, though, these addresses actually just loop back the crypto into the original 0x08f5AF... wallet. Here is an example of one of these “dummy” accounts, 0xc527ad...; the second transaction on this list sends money back in a seeming “loop”:

Screenshot of Etherscan transactions for the address 0xc527ad...

However, one outlier amongst these “OUT” transactions exists: the address 0x80710E..., which funnels these payments into three different accounts:

Screenshot of Etherscan transactions for the address 0x80710E...

Once again, two of these accounts are dummies, and will send money back into 0x08f5AF.... However, one of these accounts, 0x64E69A..., will send money to a completely new address, 0x79616B..., where the trail ends:

Screenshot of Etherscan transactions for the address 0x79616B...

There’s a suspicious “SELF” transaction on this address. Hovering over it, we can see that Etherscan believes there is a hidden message in the “Input Data” field:

Screenshot of “Transfer*” text bubble appearing on-hover, stating “This transaction includes data in the Input Data field which may indicate a message in UTF-8”

If we take a look at the transaction hash and click on the “More Details” button, we find the flag in the “Input Data” section when decoding it into UTF-8:

Screenshot of “Input Data” field converted into UTF-8, revealing flag

If you’re lost, I made a visualization of the transactions which took place, highlighting the breadcrumbs which eventually led to the flag:

Diagram of all covered transactions in a spider-web/node-style layout

The flag is wctf{g1v3_m3_b4cK_mY_cRypT0!!11!}!


WannaFlag III: Infiltration

We have some solid leads so far. However, we need our flags back. Find a way to locate their communication and infiltrate their private ransom service, and submit the stolen flag we wanted to use for the first OSINT!
From outside intelligence, we know the group sometimes goes by w4nn4_fl4g

We’re now given a keyphrase to work with: w4nn4_fl4g. We can search for the specific term on Google by wrapping it in quotes, and our first result is a subreddit, r/w4nn4_fl4g:

Screenshot of Google Search results for the query “w4nn4_fl4g”

Looking through the small amount of posts on this locked subreddit, we can find three users in particular which have access to post permissions (alongside one moderator): u/w4nn4fl4g_admin, u/RemarkableDiamond443, and u/[deleted]. None of the posts and memes were particularly interesting or relevant, but one thing stuck out in particular: the deleted user. We can find the username of the user through querying camas.unddit.com for a specific comment in r/w4nn4_fl4g; let’s utilize the comment “sorry” they left under this post:

Screenshot of Unddit query for comments with term “sorry” in r/w4nn4_fl4g

We found the original username of the deleted user: u/Chemical_Bread1558! We can now query for posts the user made under the subreddit:

Screenshot of Unddit query for posts from user u/Chemical_Bread1558 in r/w4nn4_fl4g

We’ve got a hit on a moderator-deleted post. Let’s check it out:

Screenshot of moderator-deleted meme post on subreddit, the meme reading “hey guys how do i access the website; change my mind”

Well, that doesn’t really help. However, we can see the post’s original content by using the Unddit tool once again—simply replace the reddit in the URL with unddit to see deleted comments:

Screenshot of Unddit website reversing deleted comments from r/RemarkableDiamond443 which reads: “bruhhhh again NOT a meme. just post it as a question; anyways it’s wanna-flag-d60bf7cd-012a-4fcc-9a4c-e60eca6b653f-tlejfksioa-ul.a.run.app”

The secret website provided leads us to this:

Screenshot of secret WannaFlag website, which reveals a flag

We’ve managed to recover the flag from the first OSINT challenge, but it’s actually meant to be submitted as part of the third. The flag is wctf{sp1nnnNn_tH3_cUb333e3E}!


WannaFlag IV: Exfiltration

Now that we’ve successfully gotten into their website - I say we figure out what other data they have.
Find and crack the master flag list, and submit the flag you see of ours on the list.

First blood! We’re now tasked with analyzing the contents of their website. Scouring through the source code, we find two extra pages: /ctfs.html and /prices.html:

Screenshot of /ctfs.html from WannaFlag website

Screenshot of /prices.html from WannaFlag website

We’ve also got a footer with various social medias (which are all unsurprisingly rickrolls):

Screenshot of footer from WannaFlag website

However, on the /ctfs.html page, upon further inspection of the source code we find a secret page, /data.html, linked under the Instagram icon:

ctfs.html
      <footer class="w3-center w3-black w3-padding-64 w3-opacity w3-hover-opacity-off">
         <button onclick="topFunction()" class="w3-button w3-light-grey" title="Go to top"><i class="fa fa-arrow-up w3-margin-right w3-light-grey"></i>To Top</button>
         <div class="w3-xlarge w3-section">
            <!-- small logo with redirecting link -->
            <a href="https://youtu.be/dQw4w9WgXcQ" <i class="fa fa-twitter w3-hover-opacity"></a>
            <a href="data.html" <i class="fa fa-instagram w3-hover-opacity"></a>
            <a href="https://youtu.be/dQw4w9WgXcQ" <i class="fa fa-github w3-hover-opacity"></a>
         </div>
         <!-- page link -->
         </p>
      </footer>

Accessing the secret page reveals a secret download link:

Screenshot of /data.html from WannaFlag website

Downloading the file, we find a flaglist.xlsx, a Microsoft Excel document. There is an issue, however—the spreadsheet is completely encrypted via password protection, and we cannot import it to Google Sheets:

Screenshot of failed .xlsx import into Google Drive

Let’s see what we can do about this. Inspecting the source code of /data.html, we actually find that the file is being sourced from a GitHub account, fl4gpwners:

data.html
<h1 class="left topp">
  If you made it to this page, you must be an 3l1t3 h4x0r that’s one of us. Nobody else wouldve
  sifted through those rickrolls. The stolen flag list is below.
</h1>
<a
  href="https://raw.githubusercontent.com/fl4gpwners/flaglist/main/flaglist.xlsx"
  class="button leftmar "
  >List of Flags</a
>
<p>It’s encrypted so you still need the password duhhh</p>

Screenshot of GitHub user fl4gpwners

There happens to be an extraordinarily convenient password-repo repository on this GitHub account, which reveals a passwords_stub.lst file:

Screenshot of GitHub repo fl4gpwners/password-repo’s passwords_stub.lst

Unfortunately, none of the passwords in this list were accepted by the spreadsheet. However, it reveals extraordinarily useful information: the password list is a stub (as hinted by part of the filename), and the real password of the spreadsheet was likely part of the original document.

Let’s further analyze the passwords which were given to us. We have the name of a U.S. city/town, followed by a number of some kind. Googling the city followed by the number reveals that the number is actually the population of the city which proceeds it; this means that Concord_city has a population of 105186, Baton_Rouge_city has a population of 225128, etc. We can use this information to our advantage to reassemble the password list, assuming it is every U.S. city/town followed by its population.

Upon further research, the population of the city was discovered to be sourced from the U.S. 2020 Census. Census.gov, their official website, has various datasets of “Annual Estimates of the Resident Population for Incorporated Places of 50,000 or More.” Importing the data into Google Sheets, we can see that this was the exact dataset which was used to generate the stub:

Screenshot of U.S. census data imported into Google Sheets

Note: See how the term “city,” “munincipality,” and “village” were lowercase in password_stub.lst? This is because the census uses these terms to categorize urban areas in the United States, and are not actually part of the area’s name. That is also why there are some entries which have two “cities” in their name (e.g. Oklahoma City city), as city is both part of the official city name and categorization.

Let’s create a wordlist from this data. Although we can export it as a .csv and run a Python script to filter out the passwords, we can simply create a spreadsheet formula on another column to create the wordlist:

=SUBSTITUTE(SUBSTITUTE(LEFT(B5,FIND(",",B5)-1)," ","_"),",","")&"_"&SUBSTITUTE(C5,",","")

Screenshot of new column in Google Sheets created as a result of above formula

We can now copy the column into a text file, wordlist.txt, and use it to crack the spreadsheet. We’ll be utilizing the john tool to achieve this.

Firstly, we need the hash of the spreadsheet. We can use the office2john.py tool to extract the proper hash:

Asciinema-produced GIF of office2john.py usage

office2john.py output

$ python3 office2john.py flaglist.xlsx flaglist.xlsx:$office$*2007*20*128*16*fc1b72a9c6a0f44e944534547b08d387*9c6e0ab272092ccd21181da4f3465dfd*223ecfbb03444d6a78149e26196e427bc448b5f6

After we pipe it into a file, hash.txt, we can now finally run john:

Asciinema-produced GIF of successful john usage, resulting in a successfully generated password

john output

$ ./john --wordlist=wordlist.txt hash.txt Using default input encoding: UTF-8 Loaded 1 password hash (Office, 2007/2010/2013 [SHA1 256/256 AVX2 8x / SHA512 256/256 AVX2 4x AES]) Cost 1 (MS Office version) is 2007 for all loaded hashes Cost 2 (iteration count) is 50000 for all loaded hashes Will run 12 OpenMP threads Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status Great_Falls_city_60506 (flaglist.xlsx) 1g 0:00:00:00 DONE (2023-03-21 14:25) 7.142g/s 4800p/s 4800c/s 4800C/s Palatine_village_67771..Hanford_city_57932 Use the "--show" option to display all of the cracked passwords reliably Session completed.

Note: For some reason, john when installed with apt-get doesn’t have Office support (at least for me). Make sure that you are compiling from source if you want to use this tool (or just use hashcat)!

The password is Great_Falls_city_60506, which we can now use to open the spreadsheet:

Screenshot of successfully opened flaglist, containing flag

The flag is wctf{y0ur_fl4gS_b3l0nG_t0_m3_;)}!


WannaFlag V: The Mastermind

Alright, I don’t know about you but I’m kinda sick of this WannaFlag group. I say we take them down once and for all. Maybe there’s a way to figure out who is behind the whole operation...
Consider all possible leads and clues so far. This challenge may be the most complex so far.
No games or programs need to be downloaded, or users messaged.

This challenge remained unsolved for a long while until the first hint was released:

Perhaps we can find the Mastermind’s email...

This immediately gave us a starting path to work with. We can find the email address of the user from the commit history of fl4gpwners; I found it from the patch of the commit which uploaded flaglist.xlsx to the flaglist repository:

Screenshot of GitHub commit .patch from fl4gpwners/flaglist repository

We’ve found a [email protected]—from here, although various email OSINT strategies yielded no results (e.g. Epieos), a simple keyword search on Google resulted in a GameBanana profile:

Screenshot of Google results from query “civilianengi3421” reveals GameBanana profile

Let’s take a look at this user. They currently have one submission, a map titled jump_forklift for the game Team Fortress 2:

Screenshot of GameBanana map preview of TF2 workshop map jump_forklift

Check out that screenshot: it has a snippet of a Discord link, discord.gg/aY4Wuy.... It seems cut off, however, so we’ll have to try and recover the rest of the invite.

Although we actually attempted to brute force the invite code (simply a two-character combination of A-Z, a-z, 0-9), we ended up completely IP rate-limited by Discord. So, like any logical person would do, I tried to open the map in the game itself.

To download maps into TF2, you need to subscribe to its respective workshop on Steam. Although GameBanana never explicitly provided the Steam account for this user (or so I believe), their Steam account conveniently had the same as their GameBanana, civilianengi3421:

Screenshot of Steam profie of user rocketjumper3005

Here is the workshop item associated with jump_forklift:

Screenshot of Steam workshop map of jump_forklift

After subscribing to the item, I booted up TF2 for the first time in a couple of years to check out what was going on.

Note: The challenge explicitly states that you do not need to download any games or programs. I just simply took the easy route and did so, anyways!

We can navigate to the “Create Server” menu and select the map at the bottom:

Screenshot of “(UGC) jump_forklift” appearing in TF2 map list

I entered the map and lo and behold, the Discord invite was fully visible:

Screenshot of Discord link appearing in TF2 map: discord.gg/aYWuynAJ8

Let’s join the server... or not, I guess:

Screenshot of “Invite Invalid” message upon clicking Discord invite link

Although this hiccup had my team scratching their heads for a while, we eventually stumbled upon a discrepancy in the invite link presented in the screenshot and the one in the map—the screenshot’s initial characters are aY4Wuy..., while the map has aYWuyn.... Let’s try adding the missing character 4 to the invite link:

Screenshot of successful Discord embed upon finding correct invite link

We’ve successfully gained access to the server! Let’s take a look around:

Screenshot of locked Discord server, focused on conversation in the channel #jump_forklift

Although there’s nothing of relevance in any of the channels, we see that the server has two individuals who have interacted with each other: rocketjumper3005 and s0llym41n3006, who had left the server earlier. Let’s run a Sherlock search on these two users and see what we can find:

$ python3 sherlock.py rocketjumper3005
[*] Checking username rocketjumper3005 on:

[+] Coil: https://coil.com/u/rocketjumper3005

[*] Search completed with 1 results
$ python3 sherlock.py s0llym41n3006
[*] Checking username s0llym41n3006 on:

[+] Coil: https://coil.com/u/s0llym41n3006
[+] Pastebin: https://pastebin.com/u/s0llym41n3006

[*] Search completed with 2 results

Coil was a false-positive, but that Pastebin account for the second user was a hit. Visiting their account reveals an interesting paste:

Screenshot of PasteBin from s0llym41n3006 reveals message: “"rocket jumper" dude uses lame rocket jumping server for schoolwork; https://cdn.discordapp.com/attachments/1085911652085551114/1085911678811643964/Screenshot_2023-03-16_075249.png; seen with discord hidden channel viewer”

The paste reveals that the rocketjumper3005 user had been keeping their schoolwork in the Discord server, and it had been visible using BetterDiscord’s ShowHiddenChannels plugin. The following image was attached:

Screenshot of Discord server using hidden channel viewer provided in PasteBin

We’re given a couple hints to pick at: rocketjumper3005’s real name is Corey, and he had been working on his application to “TNISO University.” Let’s run a Google search for “corey tniso university” on DuckDuckGo:

Screenshot of querying “TNISO University” on DuckDuckGo

We’ve got him! This “Corey Jacobs” actually has a LinkedIn profile:

Screenshot of user “Corey Jacobs” on LinkedIn

Expanding the “About” section reveals... some interesting text:

Expanded “About” section on Corey's LinkedIn reveals flag

A base64 decode reveals our final flag: wctf{y0u_c4n_r0ck3tjUmp_bUt_y0u_c4nt_h1d3}!