<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>shadowe1ite - protocol</title>
    <subtitle>Welcome to my website — this is the place where I share all my work, experiments, notes, and random things I’m building or breaking along the way.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://batman.part-of.my.id/tags/protocol/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://batman.part-of.my.id"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-08-23T20:10:00+05:30</updated>
    <id>https://batman.part-of.my.id/tags/protocol/atom.xml</id>
    <entry xml:lang="en">
        <title>Grand Larceny Auto II — The Vault Was a Decoy</title>
        <published>2026-08-23T00:00:00+00:00</published>
        <updated>2026-08-23T20:10:00+05:30</updated>
        
        <author>
          <name>
            shadowe1ite
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://batman.part-of.my.id/blog/grand-larceny-auto-part-2/"/>
        <id>https://batman.part-of.my.id/blog/grand-larceny-auto-part-2/</id>
        
        <content type="html" xml:base="https://batman.part-of.my.id/blog/grand-larceny-auto-part-2/">&lt;h2 id=&quot;the-setup&quot;&gt;the setup&lt;&#x2F;h2&gt;
&lt;p&gt;Part 2 is almost the same build as &lt;a href=&quot;https:&#x2F;&#x2F;batman.part-of.my.id&#x2F;blog&#x2F;grand-larceny-auto&#x2F;&quot;&gt;part 1&lt;&#x2F;a&gt;, so I
tried the part-1 trick first: Cheat Engine my stars to six and go for the vault. But it doesn’t
crack open offline this time — the vault phones a server, runs a “proof of play,” and once the run
is done it hands back a flag with a catch:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;batman.part-of.my.id&#x2F;blog&#x2F;grand-larceny-auto-part-2&#x2F;civilian-flag.webp&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span class=&quot;spoiler&quot;&gt;&lt;code&gt;THM{n1c3_dr1v1ng_but_th4ts_th3_wr0ng_v4ult}&lt;&#x2F;code&gt;&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;“Proof of play accepted — but this is civilian access… the real vault is staff-only.”&lt;&#x2F;em&gt; So the
part-1 move still earns &lt;em&gt;a&lt;&#x2F;em&gt; flag — just the lesser one. The real flag lives on the server, behind a
higher tier.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;poking-at-it-first&quot;&gt;poking at it first&lt;&#x2F;h2&gt;
&lt;p&gt;Same first move — pull the strings out of the DLL.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;strings&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -el -n 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; data_...&#x2F;GrandLarcenyAuto.dll&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; less&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;http:&#x2F;&#x2F;gla2.thm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;GLA::vault::key::v1::stars=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Proof of play accepted — but this is civilian access.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(There&amp;#39;s a higher tier of access...)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Staff access granted. That&amp;#39;s the real score.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So there’s a &lt;strong&gt;server&lt;&#x2F;strong&gt; (&lt;code&gt;http:&#x2F;&#x2F;gla2.thm&lt;&#x2F;code&gt;), a “proof of play” you send it, and two tiers of
access — &lt;strong&gt;civilian&lt;&#x2F;strong&gt; and &lt;strong&gt;staff&lt;&#x2F;strong&gt;. And this time the real flag isn’t in the binary at all; I
grepped and found nothing but the decoys.&lt;&#x2F;p&gt;
&lt;p&gt;Decompiled it with ILSpy again (&lt;code&gt;ilspycmd -p -o .&#x2F;decompiled GrandLarcenyAuto.dll&lt;&#x2F;code&gt;) and checked the
vault first. In part 1, &lt;code&gt;TryOpen()&lt;&#x2F;code&gt; did real crypto — SHA256 a key, XOR it over a sealed blob. Here
it’s gutted:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;batman.part-of.my.id&#x2F;blog&#x2F;grand-larceny-auto-part-2&#x2F;02-decoy-vault.webp&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That’s the whole method — it reads &lt;code&gt;WantedStars&lt;&#x2F;code&gt;, ignores it, and returns a hardcoded
&lt;code&gt;THM{th3_v4ult_w4s_4_d3c0y}&lt;&#x2F;code&gt;. No star check, no crypto. So the offline vault really is a decoy —
that string is the bait, not something you reach in normal play.&lt;&#x2F;p&gt;
&lt;p&gt;So what talks to the server? I grepped the decompiled source for the host from the strings:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;batman.part-of.my.id&#x2F;blog&#x2F;grand-larceny-auto-part-2&#x2F;grep-gla2.webp&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That points straight at a new class this build didn’t have before — &lt;code&gt;PoPClient&lt;&#x2F;code&gt; (proof-of-play
client). It’s a small HTTP state machine, and this one wasn’t obfuscated, so it read straight:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;public string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; ServerUrl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;http:&#x2F;&#x2F;gla2.thm&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;static readonly byte&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; SignKey&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Encoding.UTF8.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;GetBytes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;gla2_crew_sign_v1_2f9b6c8ad14e&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; POST &#x2F;session {}       -&amp;gt; { session_id, token, stash_order:[a,b,c] }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; POST &#x2F;checkpoint {...}  -&amp;gt; { ok, step, next, token }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; POST &#x2F;claim {...}       -&amp;gt; { flag, tier, note }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The flow: open a session, send a signed &lt;strong&gt;checkpoint&lt;&#x2F;strong&gt; for each step in order — &lt;code&gt;heat5&lt;&#x2F;code&gt;,
&lt;code&gt;stash{a}&lt;&#x2F;code&gt;, &lt;code&gt;stash{b}&lt;&#x2F;code&gt;, &lt;code&gt;stash{c}&lt;&#x2F;code&gt;, &lt;code&gt;vault&lt;&#x2F;code&gt; — then &lt;strong&gt;claim&lt;&#x2F;strong&gt; the flag. Everything is signed with
an HMAC:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;static string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Sign&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; HMACSHA256&lt;&#x2F;span&gt;&lt;span&gt;(SignKey, msg).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;ToHex&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; checkpoint sig = Sign(sessionId + &amp;quot;|&amp;quot; + step + &amp;quot;|&amp;quot; + token)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; claim      sig = Sign(sessionId + &amp;quot;|claim|&amp;quot; + token)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Standard signed-request stuff. Then I read &lt;code&gt;Claim()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-bug&quot;&gt;the bug&lt;&#x2F;h2&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;public void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Claim&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Sign&lt;&#x2F;span&gt;&lt;span&gt;(sessionId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;|claim|&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; token);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    Post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&#x2F;claim&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;      &amp;quot;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;session_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; sessionId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;,&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;role&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;player&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;,&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;           &#x2F;&#x2F; hard-coded&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; token&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;,&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The client always claims as &lt;code&gt;role: &quot;player&quot;&lt;&#x2F;code&gt; — which the strings said is &lt;em&gt;civilian&lt;&#x2F;em&gt; access. And
the signature only covers &lt;code&gt;sessionId + &quot;|claim|&quot; + token&lt;&#x2F;code&gt;. &lt;strong&gt;&lt;code&gt;role&lt;&#x2F;code&gt; isn’t in it.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There’s also a method the game computes but never calls:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;public string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; DeriveStaffRole&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; s&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;heat5_stash&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; StashOrder[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;             +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;_stash&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; StashOrder[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;             +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;_stash&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; StashOrder[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;_vault&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; SHA1&lt;&#x2F;span&gt;&lt;span&gt;(s).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;ToHex&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;      &#x2F;&#x2F; the staff role&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So the game already knows how to build the staff role — a SHA1 of the stash path — it just never
sends it. That’s the whole thing: do the proof of play honestly, but on the final claim send
&lt;code&gt;role = DeriveStaffRole()&lt;&#x2F;code&gt; instead of &lt;code&gt;&quot;player&quot;&lt;&#x2F;code&gt;. The signature still checks out because it never
covered &lt;code&gt;role&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;talking-to-the-server&quot;&gt;talking to the server&lt;&#x2F;h2&gt;
&lt;p&gt;You could proxy the game and edit &lt;code&gt;role&lt;&#x2F;code&gt; on the wire (it’s unsigned, so it goes through). But I
had the sign key and the whole protocol, so I scripted the handshake in Python instead.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; json, hmac, hashlib, urllib.request, time&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;BASE&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; KEY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;http:&#x2F;&#x2F;gla2.thm&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;gla2_crew_sign_v1_2f9b6c8ad14e&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; sign&lt;&#x2F;span&gt;&lt;span&gt;(m):&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span&gt; hmac.new(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;KEY&lt;&#x2F;span&gt;&lt;span&gt;, m.encode(), hashlib.sha256).hexdigest()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; post(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&#x2F;session&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sid, tok, order&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; s[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;session_id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], s[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], s[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;stash_order&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;&lt;code&gt;gla2.thm&lt;&#x2F;code&gt; only resolves on the room network — map it in &lt;code&gt;&#x2F;etc&#x2F;hosts&lt;&#x2F;code&gt;
(&lt;code&gt;MACHINE_IP  gla2.thm&lt;&#x2F;code&gt;) or just point the script at the IP.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;First run died immediately:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[!] heat5  -&amp;gt;  HTTP 425  {&amp;quot;error&amp;quot;:&amp;quot;too_fast&amp;quot;,&amp;quot;need&amp;quot;:6,&amp;quot;got&amp;quot;:0.07}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;HTTP 425 Too Early.&lt;&#x2F;strong&gt; The server actually enforces proof of play — you have to wait ~6 seconds
between checkpoints, since a real player can’t rob a stash in 70 ms. Sleep it off. Next run died
one step later:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[+] heat5   -&amp;gt; next: stash0, token: &amp;quot;EHlh...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[!] stash0  -&amp;gt;  HTTP 401  {&amp;quot;error&amp;quot;:&amp;quot;bad_token&amp;quot;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;strong&gt;token rotates every checkpoint&lt;&#x2F;strong&gt; — each response hands you a fresh one to sign the next
request with (the client does this quietly). Carry it forward and the chain walks:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;steps&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;heat5&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;stash&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; %&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; for&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; order]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;vault&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; steps:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    time.sleep(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;6.4&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;                              # beat the too_fast gate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    r&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; post(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&#x2F;checkpoint&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;session_id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: sid,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;step&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: st,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;                             &amp;quot;token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: tok,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;sig&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: sign(sid&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;|&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt;st&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;|&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt;tok)})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    tok&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; r[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;                             # rotate&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then claim as &lt;strong&gt;staff&lt;&#x2F;strong&gt; instead of player:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;role&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; hashlib.sha1(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;heat5_stash&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;_stash&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;_stash&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;_vault&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; %&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; tuple&lt;&#x2F;span&gt;&lt;span&gt;(order)).encode()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;).hexdigest()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;time.sleep(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;6.4&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(post(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&#x2F;claim&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;session_id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: sid,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;role&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: role,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;                      &amp;quot;token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: tok,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;sig&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: sign(sid&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;|claim|&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt;tok)}))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The response comes back with no &lt;code&gt;note&lt;&#x2F;code&gt; field — the &lt;em&gt;“Staff access granted”&lt;&#x2F;em&gt; tier, not civilian.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;batman.part-of.my.id&#x2F;blog&#x2F;grand-larceny-auto-part-2&#x2F;03-flag.webp#spoiler&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The flag (hover to reveal):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span class=&quot;spoiler&quot;&gt;&lt;code&gt;THM{Th4ts_th3_wr0ng_g4m3_t0mmy}&lt;&#x2F;code&gt;&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;wrap-up&quot;&gt;wrap up&lt;&#x2F;h2&gt;
&lt;p&gt;Part 1 was a memory game — flip a number, open a door. Part 2 is an auth game: the flag lives on a
server, and the bug is that they signed everything about the claim &lt;em&gt;except&lt;&#x2F;em&gt; the field that decides
your privilege. Play the run honestly, swap one unsigned field, get the real flag.&lt;&#x2F;p&gt;
&lt;p&gt;I used ILSpy to read the client and a bit of Python to replay the protocol. Two different bugs in
the same silly little game — a good one.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;thanks for reading. &lt;a href=&quot;https:&#x2F;&#x2F;batman.part-of.my.id&#x2F;blog&#x2F;grand-larceny-auto&#x2F;&quot;&gt;part 1 is here&lt;&#x2F;a&gt; if you
skipped it.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
