Monthly Archives: August 2023

Kusto Detective Agency Season 2: Case 8 – Catchy Run


Click for challenges

  • Onboarding: Here
  • Challenge 1: Here
  • Challenge 2: Here
  • Challenge 3: Here
  • Challenge 4: Here
  • Challenge 5: Here
  • Challenge 6: Here
  • Challenge 7: Here
  • Challenge 8: This article
  • Challenge 9: Coming soon
  • Challenge 10: Coming soon

While I did find this challenge to be quite fun there was a minor issue with the answer submission which prevented me from solving the case until much later in the day. Initially you had to get the right location to within 25cm which is a little difficult while running around Barcelona.

General advice

The clues for this case are decent, even with them it can be a little tricky to get started and the “Train me” for this case is very specific as we learned the geo skills in the previous challenge.

Challenge: Case 8

Case 8 challenge text

Hi again, Detective,

First things first, let’s give you a round of applause for your jaw-dropping detective skills on the previous case! You totally nailed it when you pointed to Barcelona as Krypto’s secret hideout. We were blown away by your incredible intuition and deductive prowess. Seriously, you’re like a real-life Sherlock Holmes, but even cooler!

Now, let’s dive into the current situation. We were thiiis close to catching Krypto, but he slipped through our fingers in the bustling city of Barcelona. Our intelligence sources provided a lead indicating that Krypto is a die-hard runner, hitting the pavement 3-4 times a week and running 8-12 kms each time. We managed to obtain fitness data from the past two weeks of all runners in Barcelona, thanks to our National Security Office’s (NSO) extensive reach. However, despite our best efforts, we couldn’t pinpoint where he starts his runs. It’s up to you, detective, to crack this case wide open!

But wait, there’s more. We intercepted a message sent by Krypto to his Kuanda associates. As expected, he encrypted the message using a sophisticated Krypto-code, rendering it unreadable without the key. It appears that the Kuanda possess a tribal knowledge of how to obtain these keys based on specific cities. While our agents have managed to decipher half of the required 16 numbers, we’re still missing the full Barcelona city code.
Good luck, detective, and may your wits guide you through this challenge!

NSO Agent Stas Fistuko

Right lets get tracking

Secret Message Hint

To get started first we need to decode the message, we know in order to do this we need the missing numbers from the grid, and we suspect it may have something to do with the Sagrada Familia, particularly the Nativity and Passion facades, keep those eyes open.

Spoilers below

Secret Message Spoiler

Over the entrance of the Passion facade there is a very well-known magic square, which looks to match our number grid lets decode that message.

The decoded message reads as follows:

Listen up, esteemed members of Kuanda, for we have encountered a slight hiccup in our grand scheme.
I can sense your concern, as rumors of our true intentions have reached the ears of the KDA.
But fear not, my loyal comrades, for we shall not waver from our path! If anything, we shall intensify our efforts until the KDA crumbles beneath our feet.
I cannot share too much at this time, but rest assured, we are running our “smoke tests”, both figuratively and quite literally.
They shall expose the KDA’s weaknesses and herald its epic downfall.

Now, let us address the matter of my well-being. I understand that there is a great deal of curiosity regarding my safety.
Let me assure you, it was all a matter of impeccable timing. No doubt my connecting flight was an experience of a lifetime!
Too bad my luggage failed to join me on this thrilling journey! 🙂

But fear not, my friends, leaving things to chance is not my style. I have assembled a team of loyal bodyguards,
who move with me like elusive phantoms, ensuring my invincibility. At any given time, at least two of them discreetly
shadow my every move, even during my exhilarating runs through the city. Truly, I feel untouchable. And let me tell you,
this city is a hidden gem! It offers an abundance of marvelous spots where one can indulge in a refreshing shake after
conquering a breathtaking 10K run. It is a perfect blend of mischief and rejuvenation, a delightful concoction that fuels my strength.

So, my fellow rogues, let us keep our eyes fixed on the target. I will reveal more details about our plans in due time.
Prepare yourselves to witness the spectacular downfall of the KDA, as we relentlessly drill into its core at full speed!


Now on to finding Krypto!

Query Hint

We have the runner’s data and some information about Kryptos habits. Lets try use the geo_point_to_s2cell and between commands to track him and his bodyguards.

Solution – Spoilers below

We know where each runner starts their run and we know Krypto likes to get a shake at the end and runs tailed by at least two bodyguards. We also know it’s a 10km run so let’s see what we can do.

Query Case 8

//We’re going to determine the runners that are running a 10km, two to three times a week and then see which ones are running with a group of at least 3 and see where they start.

let potentials=
| where Distance between (8 .. 12)
| summarize runs=make_set(Timestamp) by RunnerID, startofweek(Timestamp)
| extend numberofruns=array_length(runs)
| where numberofruns in (3,4)
| distinct RunnerID;
let bodyguards=
| where RunnerID in~ (potentials)
| summarize make_set(RunnerID) by geo_point_to_s2cell(StartLon, StartLat,22), startofday(Timestamp)
| where array_length(set_RunnerID) >= 3
| extend set_RunnerID=tostring(set_RunnerID)
| summarize count() by set_RunnerID
| where count_ > 2
| mv-expand todynamic(set_RunnerID)
| extend RunnerId=tostring(set_RunnerID)
| distinct RunnerId;
| where RunnerID in (bodyguards)
| where Distance between (8 .. 12)
| summarize make_set(RunnerID) by Cell=geo_point_to_s2cell(StartLon, StartLat,22)
| join kind=inner(Runs
| where RunnerID in (bodyguards)
| where Distance between (8 .. 12)
| extend Cell=geo_point_to_s2cell(StartLon, StartLat,22)
) on Cell
| distinct StartLat, StartLon
//| distinct round(StartLat,5), round(StartLon,5) //round for answer

//This gives us some locations which when entered into our VirtualTourLink reveal a pretty cool place to grab a post-run shake!

VirtualTourLink(41.384673980870026, 2.1833562706513296)

Well look at that I think we’ve got him, great job detectives!

This challenge was far less frustrating than the previous one, the clues were interesting, and it was great to get a look around Barcelona as well. Overall not my favorite case but definitely fun.


Kusto Detective Agency Season 2: Case 7 – Mission ‘Connect’


Click for challenges

  • Onboarding: Here
  • Challenge 1: Here
  • Challenge 2: Here
  • Challenge 3: Here
  • Challenge 4: Here
  • Challenge 5: Here
  • Challenge 6: Here
  • Challenge 7: This article
  • Challenge 8: Here
  • Challenge 9: Coming soon
  • Challenge 10: Coming soon

I really enjoyed this week’s challenge, it has a fun story element and works well with some of the more interesting KQL features. I definitely get to keep my Kusto card after this one!

General advice

This time arounds the clues are not only crucial but also pretty cool, this case also has some similarities to case 4 from season 1 but if you aren’t super familiar with this KQL capability the “train me” does a good job of laying the groundwork.

Challenge: Case 7

Case 7 challenge text

Hi Detective,

It’s been awesome witnessing your progress. Seriously, you’ve climbed to new heights in uncovering the misdeeds of sly cyber-criminal, Krypto. We, the National Security Office (NSO), had our eyes on him for ages, and thanks to your information, we finally managed to track him down. I’ll spare you the thrilling details, but guess what? Turns out our guy held a high-ranking position as City Manager in the Mayor’s office, and he was tight with Ms. Gaia Budskott, the Mayor of Digitown. And yes, he’s also the mastermind behind the infamous KUsto ANti-Detective Agency ( that you brilliantly exposed. However, here’s the unfortunate part: he slipped through the fingers of Digitown’s law enforcement. Given the new international nature of the case, we (the NSO) are taking over.

So, let’s cut to the chase. Time is of the essence, and we need your expertise and experience to help us find the final destination of Krypto.
While we have gathered significant information about him, it is not enough to capture him. Our sources indicate that he was spotted at the Doha airport on August 11, 2023, between 03:30 AM and 05:30 AM (UTC). However, by the time our agents arrived, he had already made his escape, presumably utilizing a private jet. We have deployed dozens of officers to all potential landing destinations, but he has evaded us so far. We have a single lead that suggests Krypto may have attempted a plane-to-plane jump, given his skills as a wingsuit expert. Here is where we got stuck.

Fortunately, we have you (and full access to the public and private jet plane schedules on this day). Your mission, should you choose to accept it, is to determine the destination to which Krypto has fled.

Hoping to hear back from you soon,
NSO Agent Stas Fistuko

Alright detectives lets find that fugitive!

Query Hint

This is a geo challenge and you’re going to need to check out the geo_point_to_h3cell and geo_point_to_s2cell commands for their awesome capabilities.

Solution – Spoilers below

Well, we know the point and time of departure and we also know some pretty fancy flying would have to take place to allow Krypto to jump between planes Mission Impossible style.

Query Case 7

//First we need to find the first airport, you can use the code but the municipality works just as well

| where municipality == “Doha”
| project lat, lon

//Then we need to find flights leaving from Doha, between 03:30 AM and 05:30 AM, that have a close encounter with another plane where our suspect flight is the one flying above the other plane.

let doha=
| where Timestamp between (datetime(2023-08-11T03:30:00Z) .. datetime(2023-08-11T05:30:00Z))
| where onground==true
| summarize callsign=make_set(callsign) by geo_point_to_s2cell(51.608056,25.273056,12)
| mv-expand callsign
| extend callsign=tostring(callsign)
| distinct callsign;
let potentialplanes=
| where Timestamp between (datetime(2023-08-11T05:30:00Z) .. now() )
| where callsign in (doha)
| summarize Planes=make_set(callsign), Heights=make_set(geoaltitude) by geo_point_to_s2cell(lat,lon,15), bin(Timestamp, 1m)
| extend CountofPlanes=array_length(Planes),HeightCount=array_length(Heights)
| where CountofPlanes == 2
| where HeightCount == 2
| extend H1=toint([‘Heights’][0]), H2=toint([‘Heights’][1])
| extend HeightDelta=H1-H2
| where HeightDelta >=0 and HeightDelta<=10
| mv-expand Planes
| extend callsign=tostring(Planes)
| distinct callsign;
| summarize arg_max(Timestamp, *) by callsign
| where onground== true
| where callsign in~ (potentialplanes)
| extend key=geo_point_to_s2cell(lon, lat, 13)
| join kind=inner(
| extend key=geo_point_to_s2cell(lon, lat, 13)
) on key
| distinct municipality

//Bingo one suspect and it’s flying to Barcelona!

Bonus observation: I noticed the name of our new friend NSO Agent Stas Fistuko looked a little strange, turned out it’s an anagram for Kusto is fast and I couldn’t agree more!

Great job detectives, with 3 cases to go we’re hot on the trail of Krypto and Kuanda, hopefully we can catch them before they do any real damage!

Note: There are some fun things you can do with mapping the flights onto a map in order to see the two places meet up and where they go, there’s a couple of ways to do this too and it’s a fun one to try and figure out.


Kusto Detective Agency Season 2: Case 6 – Hack this rack!


Click for challenges

  • Onboarding: Here
  • Challenge 1: Here
  • Challenge 2: Here
  • Challenge 3: Here
  • Challenge 4: Here
  • Challenge 5: Here
  • Challenge 6: This article
  • Challenge 7: Here
  • Challenge 8: Here
  • Challenge 9: Coming soon
  • Challenge 10: Coming soon

I must say I enjoy a good puzzle as much as the next Kusto Detective, but this week was a real head scratcher.

General advice

For this challenge I found the clues to be of almost no help at all, the “train me” though is critical as is having a lot of patience for the final riddle.

Challenge: Case 6

Case 6 challenge text

Hey there! I’ve got some juicy details for you regarding the elusive

So, the bad news is that despite my best efforts, I still don’t have a ton of info on these guys. But, the good news is that I did stumble upon a lead that might just crack this case wide open! You ready for this? isn’t just some run-of-the-mill fishing organization. Nope, my sources tell me it’s a brand spanking new cyber organization that’s all about digital data repositories. Talk about cutting-edge technology, am I right?

But wait, it gets even better. They’ve been recruiting cyber-crime specialists like there’s no tomorrow. Which means that this organization is serious about their work, and they have something big planned. And here’s the kicker – every new member has to spend a week at the National Gallery of Art! Yeah, you heard that right. The same National Gallery of Art that houses all those fancy paintings and sculptures. What could they possibly be doing in there for a whole week? Studying Leonardo da Vinci’s brushstrokes? I smell something fishy, and it’s not just the art restoration chemicals.

And to top it all off, my sources managed to snag some instructions for the new recruiters. If you can decode them, you might just have a shot at infiltrating their system and finding out what they’re really up to. Who knows, you might even find the smoking gun that proves they’re behind all those cyber-crimes. Good luck, detective – I sense you will need one!

El Puente

Decrypting the message is the first stop and there are a couple of different ways to do this, I took a more manual approach due to early morning lack of coffee.

Query Hint

You can take a basic start with the extract_all command or you can tackle the entire message at once with replace_string. Either way works and you’ll end up with an interesting yet cryptic message

Solution – Spoilers below

The community keeps growing around these challenges, I was part of many debates as tot he meaning of certain phrases in the secret message so lets have a look.

Query to decrypted the message

//This is the manual way by substitution each pair of numbers into the ObjectId and Index section of the query

| where ObjectId == “46081”
| extend Words=extract_all(@'(\w+)’, ProvenanceText)
| mv-expand with_itemindex=Index Words
| project Index, Words
| where Index == 105

//Here is the much smarter way of doing this which I must credit to Aviv Yaniv who operates much better on far less coffee than I do.

let RecruitInstructions =
“`12204/497 62295/24 50883/678 47108/107 193867/3,
45534/141 hidden 100922/183 143461/1 1181/505 46187/380.
41526/155 66447/199 30241/114, 33745/154 12145/387 46437/398 177191/131:
293/64 41629/1506 210038/432, 41612/803 216839/1.
404/258 rules 40/186 1472/222 122894/2 46081/105:
41594/650 32579/439 44625/141 184121/19 33254/348 357/273 32589/821,
46171/687 punctuations 62420/10 50509/48 1447/128,
176565/82’56721/591 561/225 insensitive, 30744/129 76197/32.
1319/42 41599/216 68/457 136016/146, 42420/126’46198/389 42429/158 40091/108 41667/252,
1515/555 177593/223 176924/73 45889/65 159836/96 35080/384 32578/199.
1607/167 124996/9 71/56, 1303/187 45640/1114 72328/247 75802/11,
1168/146 163380/12 57541/116 206122/738 365/267 46026/211 46127/19.
119295/425 45062/128 12198/133 163917/238 45092/8 54183/4 42453/82:
561/433 9/387 37004/287 1493/118 41676/38 163917/238 3159/118 63264/687
1/905 1493/109 43723/252, 136355/1 1159/134 40062/172 32588/604,
158574/1 45411/8 10/892 127587/175 – 633/9 72328/247 1514/615 42940/138.
164958/84 221014/479 151526/7 111124/138, 41668/206 34109/46 1514/555,
147789/2 3228/152 993/323 166477/167 178042/167, 50753/91’207786/8 12/372.
1108/158’42423/150 12/309 66154/9 213566/11 44981/158 1197/300
40184/149 92994/63-71071/179 75093/7 211718/18 74211/5 46144/399.“`;
let ProvenanceTextWords = materialize(
| extend Tokens = extract_all(@'(\w+)’, ProvenanceText));
let CleanedRecruitInstructions = replace_string(
                                “-“, ” – “),
                                “‘”, ” ‘ “),
                                “,”, ” , “),
                                “.”, “”),
                                “\r\n”, ” \n “),
                                “:”, ” : “);
let CleanedRecruitInstructionsTokens = split(CleanedRecruitInstructions, ” “);
let TokensCount = array_length(CleanedRecruitInstructionsTokens);
let DecipheredRaw = tostring(toscalar(
range token_index from 0 to TokensCount step 1
| extend original_token = CleanedRecruitInstructionsTokens[token_index]
| extend cipher_indexes = split(original_token, “/”)
| extend should_translate = array_length(cipher_indexes) == 2
| extend ObjectId = tolong(cipher_indexes[0])
| extend WordIndex = tolong(replace_string(tostring(cipher_indexes[1]), “,”, “”))
| join kind=leftouter ProvenanceTextWords on ObjectId
| extend Word = iff(should_translate, Tokens[WordIndex], original_token)
| sort by token_index asc
|  summarize array_strcat(make_list(Word), ” “)));
let Deciphered = replace_string(replace_string(replace_string(replace_string(DecipheredRaw, ” : “, “:”), ” ‘ “, “‘”), ” , “, “, “), ” – “, “-“);

Decrypted message

in catalogue of titles Grand,
three hidden words Demand your Hand
when found all, they form A line:
A clear timeline, simply Fine

words rules are simple to Review:
at least three Letters have in view,
all punctuations Mark the End,
they’re case insensitive, my friend

to find all words, you’ll need some skill,
seeking the popular will guide you still
below The King, the first word mounts,
the Second shares with Third their counts

reveal the last word with Wise thought:
take first two letters from word most sought
into marked dozen, and change just one,
and with those two – the word is done

so search the titles, high and low,
and when you find it, you’ll know
you’ve picked the Image that revealed
the pass-code to the World concealed

Now a word of warning, how you proceed here could lead you down many hours of dead ends because depending on how you sort the words you might not get the right list to work off. Let’s break this down, section by section.

1. We’re looking for three words that form a timeline
2. The words are case insensitive, at least 3 letters long and end in punctuation
3. The first word is below King
4. The second word shares it’s count with the word Third (this took me longer than I’d care to admit)
5. The third word needs us to do some work with the first and 12th word
6. We’re looking for an image what will reveal a passcode

Query Case 6

//As I mentioned before sorting this correctly, makes a world of difference, my solution was anything but elegant so in this case I’d rather present this steamlined piece of art curtesy of Aviv

let punctuations = “`[[:punct:]]“`;
| extend TitleUnified = toupper(Title)
| extend TitleWords = extract_all(@'(\w+)’, TitleUnified)
| mv-expand TitleWords to typeof(string)
| extend punctuation_index = indexof_regex(TitleWords, punctuations)
| extend last_index = iff(punctuation_index>=0, punctuation_index,
| extend Word = substring(TitleWords, 0, last_index)
| where strlen(Word) >= 3
| summarize WordCount=count() by Word
| sort by WordCount desc

//look at that a timeline we can look for, along with a strange piece of art!900,900/0/default.jpg
| where Title has_all(“day”, “month”, “year”)

We’re not done yet, lets head over to and see what we can find. In order to join the club we need a passcode

What happens if we use our image as the login hint?

A-ha! We’re onto something now, lets try unscrambling the letters being held by the octopus and we get the nefarious stopkusto!

And we’re in and can see all the trickery that Kuanda has been up to so far including the name of their leader.

Another case solved Detectives well done!

This case was very challenging, and a lot of the difficulty comes down the interpretation of the riddle, even assuming that all your KQL was spot on, making the wrong assumptions about which words to look for could take hours of hunting. While I did feel like I needed to hand in my Kusto card for a while I did get there eventually.