@mrexcessive WHA
& Rob Laverick WHA

The problem

```Open the box at
nc 52.17.65.252 18324
```

The solution
NO binary provided - probably a good thing ?!

Rob and I worked on this separately for first three challenges, Rob solved fourth and fifth and we shared work solving final challenge required both of us.

Let's take a look...

```\$ nc 52.17.65.252 18324
You need to open the box!
Valid characters are: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!"#\$%&'()*+,-./:;<=>?@[\]^_`{|}~ "
Max length is: 63 characters.
Let's try some easy boxes:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!"#\$%&'()*+,-./:;<=>
Expected [XXm\$!>=%%+\$i|^iv(-=<]
```

We find:

• You get 3 tries
• You get told how your string encrypts
• You are looking for plaintext to match their encrypted output

So ... send in a charstring
See encrypted result and target result

Are we playing blackbox ?
en.wikipedia.org/wiki/BlackBox(game)
I have played that before, but not so big a board....
We can send in 'many rays' in each of 3 turns... so should be not too hard...
however this wasn't the case ;(

So general approach is

• Send first salvo - seeking results
• Gather info
• Deduce things - possibly tough
• Send answer or possibly second salvo if needed

OK gets first one right

Built a wrapper to handle these... Core of my code - incorporating Rob's insights and algos - is:

```def SendAnswer(p,state):      # send appropriate answer     problem not currently in p, is in state and globals
global characters, expected      # charset and expected result (our target)
global mappings,diffs, nextchar  # mappings never used
global Box5Sendbase              # Box5Sendbase just a global to 'pull from the left'
modulus = len(characters)
Log("SendAnswer state %i passed [%s]" % (state,p))
if state == 0: # or \
if boxCounter in [0,1,2,3]:
sending = characters[:50]
elif boxCounter in [4]:
sending = "A" * 36
else: # boxCounter == 5
sending = Box5Sendbase
nextchar = len(sending)
sending += "A" * (62 - len(sending))
diffs = []
mappings = {}
elif state == 1 or state == 2:      # XXX state 2 here...
if boxCounter == 0 or boxCounter == 1:
ofs = []             # check for simple offset mod len(characters)
if boxCounter == 0:     # BOX 0: fixed offset across
Log("Box 0 state %i checking modOfs with:\nsent[0] = [%s]\n   recd = [%s]" % (state,sent[state-1],received[state-1]))
for i, cSent in enumerate(sent[state-1]):
idxSent = characters.find(cSent)
tryofs = (idxRecd - idxSent) % modulus
ofs.append(tryofs)      # this should work for all the same or fixed pattern
elif boxCounter == 1:   # BOX 1: offset for char @ posn N+1 is index of char @posn N in character set
Log("Box 1 state %i calculating ofs with :\n   expected = [%s]" % (state,expected))
ofsTotal = 0
for cExpected in expected:
idxExpected = characters.find(cExpected)
ofsToSend = ofsTotal
ofs.append(ofsToSend)      # this should work for all the same or fixed pattern
ofsTotal += idxExpected
Log("Offsets determined %s" % ofs)
sending = ""
iofs = 0
for cExpected in expected:
ofsExpected = characters.find(cExpected)
ofsRequired = (ofsExpected - ofs[iofs]) % modulus
cToSend = characters[ofsRequired]
sending += cToSend
iofs += 1
elif boxCounter == 2:   # BOX 2:
# so mapping is to reverse() of charset plus an offset plus message length
if state == 1:       # send a string of the right length, just to get the correct offset..
sending = "BCD" + "A" * (len(expected) -3)
elif state == 2:     # now calculate offset
Log("Box 2 state %i checking modOfs with:\nsent[0] = [%s]\n   recd = [%s]" % (state,sent[state-1],received[state-1]))
recdOffset = characters.find(received[1][0])      # what does first 'A' map to
sentOffset = characters.find(sent[1][0])              # this should be an A
encodingOffset = recdOffset - sentOffset     # offset for the messasge which was sent to test
Log("recdOffset = %i, sentOffset = %i, encodingOffset = %i" % \
(recdOffset,sentOffset,encodingOffset))
sending = ""
for cExpected in expected:
cipOfs = (characters.find(cExpected) - encodingOffset) % modulus
sending += characters[cipOfs]
sending = sending[::-1]       # reverse the item to send
elif boxCounter == 3:      # BOX 3:       # this block based on RobLaverick's code
if state == 1:
for i in xrange(1,len(sent[state-1])):
sentOffsetThis = characters.find(sent[0][i])
sentOffsetPrev = characters.find(sent[0][i-1])
ofs = (recdOffsetThis - sentOffsetThis) - (recdOffsetPrev - sentOffsetPrev)
if ofs > -1:
Log("Found offset %i" % ofs)
break
sending = ""
total = 0
for i in xrange(0,len(expected)):
cidx = characters.find(expected[i])
sending += characters[cidx - total]
total = (total + ofs) % len(characters)
elif boxCounter == 4:      # BOX 4:        # again Rob's algo
if state == 1:
sentOffsetThis = characters.find(sent[0][0])
total = recdOffsetThis - sentOffsetThis
Log("Initial offset %i" % total)
sending = ""
for i in xrange(0,len(expected)):
cidx = characters.find(expected[i])
sending += characters[cidx - total]
total = (total + cidx) % len(characters)
sending = sending[::-1]
elif boxCounter == 5:      # BOX 5:
Log("Box 5 state %i with:" % state,True)
Log("sent = [%s]" % sent[state-1] ,True)
Log("recd = [%s]" % received[state-1] ,True)
Log(" ANS = [%s]" % expected,True)
if state == 1:
if nextchar % 4 == 0 or nextchar % 4 == 1:
wanted = characters.find(expected[nextchar])
elif nextchar % 4 == 2:
wanted = characters.find(expected[nextchar+1])
elif nextchar % 4 == 3:
wanted = characters.find(expected[nextchar-1])
change = wanted - recd
sending = ""
for i in xrange(0,len(expected)):
if i == nextchar:    # if next char to resolve then fix it
newchar = characters[change % len(characters)]
Box5Sendbase += newchar
sending += newchar
else:                # otherwise use previous value
sending += sent[0][i]
else:
diffdifferences = []
for i in xrange(0,len(diffs[state-1])):
diffdifferences.append((diffs[1][i] - diffs[0][i]) % len(characters))
print diffdifferences
print "----"
return False      # ask for restart, just collecting this info.

sending = "I am still a test"

sent.append(sending)
s.send("%s\n" % sending)
return True
```

Some of the decodes were quick, Rob was way ahead solving 4 and 5 (which I called 3 and 4 for some zero-numbering-fetish reasons...)

We both battled with box 6 - it was unpleasant

Some insights we gleaned: 36 char blocks; characters affect their own cell by a simple offset in the character set, but they also affect all characters to their right within a 36 char block; need to treat each block of 4 chars as a unit - chars +0 and +1 map to themselves, chars +2 and +3 swap their mapping (you can see this used in code above)

So difference from char 'A' in the plaintext, in a particular position, has no complex impact on that single char position - the impact propogates to the right and mixes with all other differences.

The second char position is affected by the first char only in an involved way... not the second char. It has first-char-difference-from-A difference (so B-> + 1), etc. but mod 95

So to send a valid sequence... for each 36 chars in expected
Look at expected[0] you need to send a char[0] which is the difference in this position mod 95.
Look at expected[1] in message. It will be fecked with according to above pattern by your char[0]... you need to unfeck it (TM) by adding working out to where it maps from char[0] and sending the appropriate char to undo both feckings !
All 36 chars accumulate feckings (probably)

Time for another test...
So can I get char[0] right !

OK Yes

```GetProblem state 2 passed [Password [OKrV^=m%:81aMZIzRjw*CFXLvADBHP"fg\$,k0KrV^=m%:8
Expected [Orr[Y3RFCoa|)1QxECH=go.V66ZL7[uSrF{Zo}nIC<i#,0]

Box 5 state 2 checking modOfs with:
sent[0] = [TAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
sent = [TAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [I~{}];e!9cU5p@y+GN3bQho&>'\q/6tW|_)?u~{}];e!9c]
ANS = [If{8X1JB>I_bEQ6)_.Oku'}:}fM0hgcJHc^*i=]<B:a85U]
```

So... first char correct... (recd[0] = ANS[0])
That's the hard part, right ?

Second char...
Will be affected by multiples of first char difference from 'A'
According to our extracted sequence of multiples...
`[0, 1, 4, 2, 8, 16, 64, 32, 33, 66, 74, 37, 53, 11, 44, 22, 88, 81, 39, 67, 78, 61, 54, 27, 13, 26, 9, 52, 18, 36, 49, 72, 3, 6, 24, 12]`
I need to take into account offset from first char * 1, and then adjust second char using a multiple of 1 on it's own char...

So maybe that is this:

```            recd = characters.find(received[0][0])
wanted = characters.find(expected[0])
changes[0] = wanted - recd       # this char pos should be easy...
wanted = characters.find(expected[1])
changes[1] = wanted + (1*changes[0]) - recd
; sending code for reference...
sending = ""
for i in xrange(0,len(expected)):
sending += characters[changes[i] % len(characters)]
```

Hmmm not quite right:

```sent = [T*AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [R^TJn<i#(s[`-2dO7YExJT<n#is(`[2-OdY7xETJn<i#(s]
ANS = [Rx&B+4VHK4{cIY{]5NzHZaT_pY<m+P3)9psCrD"Uy=m%>']

and again...
sent = [TAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [I~{}];e!9cU5p@y+GN3bQho&>'\q/6tW|_)?u~{}];e!9c]
ANS = [If{8X1JB>I_bEQ6)_.Oku'}:}fM0hgcJHc^*i=]<B:a85U]
```

Much more messing, exchanges of code and thoughts with Rob and then realised could just pull characters one at a time from left to right - because the complicated fecking impact of the characters to the left is already absorbed, so provided we calculate one char at a time should be easy.

Combined with Rob's insight that every four-char block has second pair input-swapped... and we get to pull the whole thing!

```Box 5 state 1 with:
sent = [ThAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [PtW6t|?_)u}~{]!;e95cUp+@yGbN3Q&ho>q'\/W6t|?_)u}~{]!;e95cUp+@yG]
ANS = [Ptz_o^y(':<)>.J~\(\Y~&_.[PrUA9x&xRrxp vMSQ&hs`]
Box 5 state 2 with:
sent = [TheAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [Pt0_KV^r=%:m8aM1ZzRIj*CwFLvXABHDPfg"\$k0,KV^r=%]
ANS = [Ptz_o^y(':<)>.J~\(\Y~&_.[PrUA9x&xRrxp vMSQ&hs`]
[0, 0, 0, 30, 60, 25, 5, 50, 10, 20, 80, 40, 65, 35, 45, 70, 90, 85, 55, 75, 15, 30, 25, 60, 50, 5, 20, 10, 40, 80, 35, 65, 70, 45, 85, 90, 75, 55, 30, 15, 60, 25, 5, 50, 10, 20]
----
peter@KaliA:~/CTFs/LegitBS/blackbox\$ ./try.py
Box 5 state 1 with:
sent = [TheAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [Pt0_KV^r=%:m8aM1ZzRIj*CwFLvXABHDPfg"\$k0,KV^r=%:m8aM1ZzRIj*CwFL]
ANS = [Ptz_o^y(':<)>.J~\(\Y~&_.[PrUA9x&xRrxp vMSQ&hs`]
Box 5 state 2 with:
sent = [The AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [Ptz_JT<n#is(`[2-OdY7xETJn<i#(s[`-2dO7YExJT<n#i]
ANS = [Ptz_o^y(':<)>.J~\(\Y~&_.[PrUA9x&xRrxp vMSQ&hs`]
[0, 0, 94, 1, 94, 93, 87, 91, 79, 63, 62, 31, 29, 58, 42, 21, 84, 73, 7, 51, 14, 28, 17, 56, 34, 68, 82, 41, 69, 43, 77, 86, 59, 23, 92, 46, 89, 83, 47, 71, 94, 93, 87, 91, 79, 63]
----
peter@KaliA:~/CTFs/LegitBS/blackbox\$ ./try.py
Box 5 state 1 with:
sent = [The AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [Ptz_JT<n#is(`[2-OdY7xETJn<i#(s[`-2dO7YExJT<n#is(`[2-OdY7xETJn<]
ANS = [Ptz_o^y(':<)>.J~\(\Y~&_.[PrUA9x&xRrxp vMSQ&hs`]
Box 5 state 2 with:
sent = [The fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [Ptz_oyNGb3hQ&o'>q\6/Wt_|?)~u}{;]!ec95U@p+yNGb3]
ANS = [Ptz_o^y(':<)>.J~\(\Y~&_.[PrUA9x&xRrxp vMSQ&hs`]
[0, 0, 0, 0, 0, 31, 29, 62, 58, 21, 84, 42, 73, 51, 14, 7, 28, 56, 34, 17, 68, 41, 69, 82, 43, 86, 59, 77, 23, 46, 89, 92, 83, 71, 94, 47, 93, 91, 79, 87, 63, 31, 29, 62, 58, 21]
----
peter@KaliA:~/CTFs/LegitBS/blackbox\$ ./try.py
Box 5 state 1 with:
sent = [The fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [c*8t6]!;e95cUp+@yGbN3Q&ho>q'\/W6t|?_)u}~{]!;e95cUp+@yGbN3Q&ho>]
ANS = [c*8t6cEu*@I?i/NA~\24fm&Rqa^q_v_ng;C/2Z4#k0VKvA]
Box 5 state 2 with:
sent = [The flAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [c*8t6cpU@+GyNbQ3h&>o'q/\6W|t_?u)~}]{;!9ec5pU@+]
ANS = [c*8t6cEu*@I?i/NA~\24fm&Rqa^q_v_ng;C/2Z4#k0VKvA]
[0, 0, 0, 0, 0, 0, 74, 37, 53, 11, 44, 22, 88, 81, 39, 67, 78, 61, 54, 27, 13, 26, 9, 52, 18, 36, 49, 72, 3, 6, 24, 12, 48, 1, 4, 2, 8, 16, 64, 32, 33, 66, 74, 37, 53, 11]
----
```

So then I wired it up to add the character automatically and just loop

```peter@KaliA:~/CTFs/LegitBS/blackbox\$ ./try.py
Box 5 state 1 with:
sent = [The flAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [EX6s2UJETn#<i(`s[-O2d7xYEJnT<#(is`-[2O7dYxJETn#<i(`s[-O2d7xYEJ]
ANS = [EX6s2U&eHy%Q3X][x}?)^3oz@NjQ<dQD;.\)e+2"gs[`;!]
Box 5 state 2 with:
sent = [The flaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [EX6s2Uje*wFCLXAvBDPHf"\$gk,K0Vr=^%m8:a1ZMzIjR*w]
ANS = [EX6s2U&eHy%Q3X][x}?)^3oz@NjQ<dQD;.\)e+2"gs[`;!]
----
Box 5 state 1 with:
sent = [The flAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [zW2quEo&>'\q/6tW|_)?u~{}];e!9cU5p@y+GN3bQho&>'\q/6tW|_)?u~{}];]
ANS = [zW2quED~(<^-CMq!4LrDJ`>Y)?a79_!a.(96:*y9YcU5t|]
Box 5 state 2 with:
sent = [The flaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
ANS = [zW2quED~(<^-CMq!4LrDJ`>Y)?a79_!a.(96:*y9YcU5t|]
```

A minute later ...

```Box 5 state 1 with:
sent = [The flag is: Gr3aT j0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [Rx&B+4VHK4{cIY{]5NzHZ                                         ]
ANS = [Rx&B+4VHK4{cIY{]5NzHZaT_pY<m+P3)9psCrD"Uy=m%>']
Box 5 state 2 with:
sent = [The flag is: Gr3aT j0bAAAAAAAAAAAAAAAAAAAAAAAA]
ANS = [Rx&B+4VHK4{cIY{]5NzHZaT_pY<m+P3)9psCrD"Uy=m%>']
----
Box 5 state 1 with:
sent = [The flag is: Gr3aT j0bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
recd = [i@N(7eMy^UpzFS%-JMvFRK[`-2dO7YExJT<n#is(`[2-OdY7xETJn<i#(s[`-2]
ANS = [i@N(7eMy^UpzFS%-JMvFRKy4I1FBTELw'3Fe8lJ_l2dO"g]
Box 5 state 2 with:
sent = [The flag is: Gr3aT j0b!AAAAAAAAAAAAAAAAAAAAAAA]
ANS = [i@N(7eMy^UpzFS%-JMvFRKy4I1FBTELw'3Fe8lJ_l2dO"g]
```

and finally

```Box 5 state 1 with:
sent = [The flag is: Gr3aT j0b! BlackBox Ma\$Ter\$@!!!%AAAAAAAAAAAAAAAAA]
recd = [@]Z.@;Oz|c,&(zIt@\$(Q9DWqvkg!,R"-:,:%OoV 9HfP&(`s[-O2d7xYEJnT<#]
ANS = [@]Z.@;Oz|c,&(zIt@\$(Q9DWqvkg!,R"-:,:%OoV 9HfP&o]
Box 5 state 1 with:
sent = [The flag is: Gr3aT j0b! BlackBox Ma\$Ter\$@!!!%%AAAAAAAAAAAAAAAA]
recd = [oA9~8gU2I0-U*3Y1UioxD:hA(=S3t4d5JgI<%x5Rm4lS<#g"\$k0,KV^r=%:m8a]
ANS = [oA9~8gU2I0-U*3Y1UioxD:hA(=S3t4d5JgI<%x5Rm4lS<#]
```

Flag submitted 2 points