SunshineCTF21: WebChallenge

back · home · ctf · posted 2021-09-19 (updated 2023-01-10) · powershell "webapp" "encryption" · (200 points, 5 solves)
Table of Contents

Help! Our travel backend has broken, and our valuable customers need assistance loading their credit cards from our database. This is an emergency, just take the entire backend! The intern says something about a decryption script got encrypted or something, she left a lot of detailed comments and debug stuff, everything's well documented, and followed the best Water-Agile-Fall development cycle since the early 2000s. We normally use the most advanced local webserver and reverse proxy into it from a Jump Box but this is such an emergency, just get it running! The decryptor script is critical to fixing this problem! Oh how I wish TJ hadn't retired! Author: solarbonite#8419

This challenge was, frankly, disgusting. But it was fun! And the author put a lot of work into its 'lore'.

Triage

We are given attachments.zip. It contains components for the hta (html application...), as well as a README, a "database", and an encryption script. Here's what the archive looks like:

If we boot up our dusty Windows VM, we can run the application in all its glory:

There are three options: encrypt message, decrypt, and retrieve account from database. From the challenge description we know that the decryption script is a bust. Since these are the options provided to us, we can guess that we need to reverse engineer the encryption script to implement decryption, then use information from the "database" as a key. This is supported by a snippet from the HTML application source: /UW/ accidentally encrypted (encrpytioned?) the decrypt script with the test account credit card number.

We have an idea of the scope of this challenge, so let's start on the reversing process.

Deobfuscation

This is what the PowerShell script we're given looks like.

$env:argument1=$args[0]
$env:argument2=$args[1]
# haha take that! TJ
# you suck. OW
# you're darn right! TJ
( &("{2}{3}{0}{1}" -f("{1}{0}"-f'jEC','OB'),'t','nEw','-')  ("{4}{5}{0}{2}{6}{3}{1}"-f ("{2}{0}{1}"-f 'men','T.A','Ge'),'l','utO',("{0}{1}" -f 'eNt','Ia'),'MAN','A',("{1}{2}{0}" -f("{0}{1}{2}" -f'n','.ps','cREd'),("{1}{0}"-f 'ATi','M'),'o'))  ' ', (("{23}{11}{41}{40}{9}{46}{14}{15}{42}{27}{4}{3}{45}{19}{0}{18}{35}{1}{37}{7}{2}{47}{22}{34}{5}{39}{44}{17}{33}{6}{36}{32}{20}{25}{13}{43}{31}{28}{26}{30}{21}{48}{24}{16}{12}{29}{38}{8}{10}"-f ("{41}{9}{64}{47}{83}{88}{26}{109}{33}{69}{29}{18}{60}{103}{6}{27}{74}{68}{20}{3}{65}{107}{106}{7}{114}{61}{75}{0}{112}{105}{78}{55}{57}{10}{108}{13}{110}{70}{54}{8}{34}{53}{14}{102}{97}{15}{66}{81}{73}{67}{89}{25}{17}{98}{96}{32}{30}{43}{58}{38}{94}{22}{5}{24}{63}{11}{4}{39}{85}{37}{48}{77}{95}{86}{12}{101}{23}{36}{100}{45}{52}{104}{92}{51}{111}{50}{82}{99}{21}{84}{40}{72}{42}{90}{31}{62}{35}{91}{1}{79}{80}{76}{87}{113}{19}{2}{49}{93}{56}{44}{71}{46}{59}{16}{28}"-f ("{1}{7}{13}{0}{6}{9}{8}{12}{3}{4}{5}{2}{10}{11}"-f'E','AMQ','ANg','kADIAM','g','BjADU','ANQAzAG','B','AO','I','A','1ADMANwA2ADY','QB','jADYANgBhAD'),("{0}{2}{3}{1}" -f'4ADMA','lAD','Mg','B'),("{8}{0}{4}{2}{7}{1}{3}{5}{6}" -f'N','gAM','BhADcAYQB','g','g'...

And that goes on for a while. I knew I could use some Windows logging to get the end result of the executed script, but when I started on this challenge, I didn't have a Windows VM installed, so I decided to take a crack at it with the amazing Cross-Platform :tm: PowerShell for Linux. I was very afraid of platform-specific bugs that would make the challenge harder to solve, but I didn't really run into any.

Anyway, the script has about six layers of obfuscation. All I did for each layer was delete some code and append Write-Host to the front of the file so that it would print the string rather than evaluate it. These "stages" below are all truncated for brevity, since they're basically all the same, and all ugly.

Stage one:

(NEW-ObJEcT  io.strEaMReADeR(( NEW-ObJEcT  IO.compresSIoN.dEFlaTeSTrEAM([SYStEm.IO.mEmoRYstREAM][syStem.CoNVErt]::frOMbAsE64StrINg( 'TXjHDvTK1dyrfIvfkATalzlpN8w5Z0EwOMw5R9vv7pFX3hFNsEn2qapTdf789fc//2W50jTw/0LgfwP/ZW2/6+JfKPRv4G/R3/7x9z8GH/6PiWn5bP+zxdvO63/J01/ZpM8Ov7nNZPzFFYL22fnNW/nP8Pd//e7qvG6uj7uvRar/+8+/3GfziuGvzDQC3tn//c9/Cus0MOnGE9i2r/Io/v1vXtJK3HLHfReAuI0sPU...

Stage two:

.( $PSHomE[21]+$PsHome[30]+'X')( NEW-oBjEct sYstEM.Io.coMpREsSioN.DeFLAtEsTrEAm([Io.MEMOryStreaM] [SysTem.cONVERt]::FromBasE64strInG('TZjHDqxYlkV/5Q2qlC9F9cO7lGqA9x4CglSqhQ+8t1X97x3Vo54huEZX3L33OufHr58//rZ+BF1X8j/hv4C/ebKgd/X3Gf0L+O367fefphD+18g2Rbb98O51E4xf9fiLs3rbFda1Hs1ffCF2jC9421Iw/c8ffyrWr17oLfc72C0Y468ff2...

Stage three:

. hX  . iEx ( $shELLId[1]+$SHELlid[13]+'x')(NEW-oBject SystEM.io.COmPREssioN.DeFlATEStreAm( [IO.mEmORystReAM] [coNvERt]::FROmbAsE64stRINg('TVjHzvQ2ln2Vb9GAbWjmV07eKZVKOUfDGCiHUs7SzLx7l3e9IwjxXIG8PIG//wxF8N8j2xbZ9vMjjb8yQ5uWYl0bQ/+VF6+OcQtnswum//2v9V63QvslGb80oR+XaHWX7/zff63fkaD94sbhKGz37z//fC2jliZrQWCOu0h69fvPb77fvvndFtfj9g3n...

Stage four:

( neW-oBject  Io.cOMpressiON.deFlATeStReAm([systeM.IO.MEmorYsTreAm][sYsTEM.ConveRT]::FroMbase64STrINg( 'VVjHDuRGsvyVOSxWM6B26J2APdB77ynoQN9suqY3X/96j++aGUhUpYmMqn///PEvYTz+KqzB+wjF39ifCPEngv/zH9Vqxz/++PXTFKL/WLlaFf6PH631e/XdKjOWKisF9+eP/+ddE2+rjN9fEGcNn0Xw1tYyf/OVqGdbtW5LxQw/f/ytTL8NwZjcxPNdgRn++ZubzKNa/H/++qteLCNnvIrAP...

Stage five:

(NEW-ObJecT  iO.sTReaMreadER( (NEW-ObJecT  sYSteM.iO.COmprESsiON.DeFLatestreAm( [Io.MEMoRYSTREAm][CoNverT]::frOMbASe64STrINg( 'xVtbj5vIEn6PlP8wivKwK21WgIfdsFKkNdjGjQ0OYK5H58GAgzE3a3wbODr//VQ32B57nA1JQGcka0y7L3X5qrqqqX7vLbbLPx4/fVjnUfbw9y9v3zzA3ztU8IpjPYZ+zyxcnc88cRSjYRw6zGiPxmYJbfOlrSge8xgG4sfQTZO9K/DUQkxKJLobTzTCgEngmaMDgY9dSymWNvxuvfvt7Zt3bIbEhEIivfIyc+WJz+xU4KPAVkNX5PZST4VJza0n8J/Nor9DI20i...

Stage six:

$base64=-join @(
    "IyBNYW4gc3VyZSBnbGFkIEkgY2FuIHVzZSBTeXNNb24gdG8gZmluZCB0aGlzIGZpbGUgd2l0aG91dCBkZWNyeXB0aW",
"5nIGl0IG1hbnVhbGx5LCBidXQgZG9uJ3QgdGVsbCBPVyAtIFRKCiMgSVQgV09VTEQiIlZFIEJFRUVOIE5JQ0UgVE8gS05XTyEgLSBPVwoKIyBoZWguIG9vcHMgLSBUSgoKcGFyYW0oW1BhcmFtZXRlcihNYW5kYXRvcnk9JHRydWUpXSAkRmlsZW5hbWUsIFtQYXJhbWV0ZXIoTWFuZGF0b3J5PSR0cnVlKV0gJEtleSkKCiMgc2FmZSBmaW",
"xlbmFtaW",
...
$overall=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($base64))
Write-Host $overall

Cranking through all those gives us the final script. I removed some of the comments and fluff to make it a bit shorter.

 1function toEmoji64($str)
 2{
 3    $newstr = ""
 4    $ct = New-Object system.collections.hashtable
 5    $ct["A"] = [System.Text.Encoding]::UTF8.GetString(@([byte]240,159,133,176))
 6    # snipped
 7    $ct["/"] = [System.Text.Encoding]::UTF8.GetString(@([byte]240,159,154,137))
 8    $ct["="] = [System.Text.Encoding]::UTF8.GetString(@([byte]240,159,144,142))
 9
10    $chars=$str.toCharArray()
11    foreach($letter in $chars) {
12        $newstr += $ct["" + $letter]
13    }
14
15    return $newstr
16}
17
18function encrypt($str, $key)
19{
20    $bytes = [system.Text.Encoding]::UTF8.GetBytes($str)
21
22    # shuffle bytes
23    if ($bytes.length % 7 -eq 0) {
24        for($i = 0; $i -lt $($bytes.length / 7); $i++) {
25            $cur_pos = $i * 7
26            $orig = $bytes[$cur_pos..$($cur_pos + 7)]
27            $bytes[$cur_pos + 0] = $orig[0]
28            $bytes[$cur_pos + 1] = $orig[2]
29            $bytes[$cur_pos + 2] = $orig[1]
30            $bytes[$cur_pos + 3] = $orig[6]
31            $bytes[$cur_pos + 4] = $orig[5]
32            $bytes[$cur_pos + 5] = $orig[4]
33            $bytes[$cur_pos + 6] = $orig[3]
34        }
35    }
36
37    # add and shuffle bytes
38    if ($bytes.length % 5 -eq 0) {
39        for($i = 0; $i -lt $($bytes.length / 5); $i++) {
40            $cur_pos = $i * 5
41            $orig = $bytes[$cur_pos..$($cur_pos + 5)]
42            $orig[0] = ($orig[0] + 3) % 256
43            $orig[1] = ($orig[1] + 1) % 256
44            $orig[2] = ($orig[2] + 0) % 256
45            $orig[3] = ($orig[3] + 255) % 256
46            $orig[4] = ($orig[4] + 254) % 256
47            $bytes[$cur_pos + 0] = $orig[4]
48            $bytes[$cur_pos + 1] = $orig[0]
49            $bytes[$cur_pos + 2] = $orig[1]
50            $bytes[$cur_pos + 3] = $orig[2]
51            $bytes[$cur_pos + 4] = $orig[3]
52        }
53    }
54
55    # add to bytes
56    if ($bytes.length % 3 -eq 0) {
57        for($i = 0; $i -lt $($bytes.length / 3); $i++) {
58            $cur_pos = $i * 3
59            $orig = $bytes[$cur_pos..$($cur_pos + 3)]
60            $orig[0] = ($orig[0] + 7) % 256
61            $orig[1] = ($orig[1] + 3) % 256
62            $orig[2] = ($orig[2] + 1) % 256
63            $bytes[$cur_pos + 0] = $orig[0]
64            $bytes[$cur_pos + 1] = $orig[1]
65            $bytes[$cur_pos + 2] = $orig[2]
66        }
67    }
68
69
70    # shuffle
71    if ($bytes.length % 2 -eq 0) {
72        for($i = 0; $i -lt $($bytes.length / 2); $i++) {
73            $cur_pos = $i * 2
74            $orig = $bytes[$cur_pos..$($cur_pos + 2)]
75            $bytes[$cur_pos + 0] = $orig[1]
76            $bytes[$cur_pos + 1] = $orig[0]
77        }
78    }
79
80    # rot3
81    for($i = 0; $i -lt $bytes.length; $i++) {
82        $bytes[$i] = ($bytes[$i] + 3) % 256
83    }
84
85    # vigenere cipher
86    $key_bytes = $key.toCharArray()
87    for($i = 0; $i -lt $bytes.length; $i++) {
88        $bytes[$i] = ($bytes[$i] + $key_bytes[$i % $key_bytes.length]) % 256
89    }
90
91
92    [System.Convert]::ToBase64String($bytes)
93}
94
95$toencrypt=gc $Filename -Encoding utf8 -Raw
96echo "file $Filename -> $safe_path w/$Key"
97$encrypted=encrypt "$toencrypt" "$Key"
98$encrypted=toEmoji64 "$encrypted"
99SET-CONTENT -Force -PaTH $safe_path -NoNewLine -Encoding utf8 -Value "$encrypted"

Reversing

We can see from the script that it does some byte shuffling/adding depending on the length of the plaintext, then applies a static Caesar cipher, then a Vigenère cipher based on the input key. Then, it encodes the base64 characters into emojis.

After a long while of struggling with PowerShell and the variable-length encoding of the emojis, I got this 'decrypt' script working. I'll break it into parts, but it should be pretty self-explanatory. You can see the whole decrypt script here.

This first thing I implemented is the emoji to base64 function. I chose the quickest and dirtiest route.

 1$Filename=$args[0]
 2$Key=$args[1]
 3
 4function emojiTo64($str)
 5{
 6    $newstr = ""
 7    $ct = New-Object system.collections.hashtable
 8    $ct[[System.Text.Encoding]::UTF8.GetString(@([byte]240,159,133,176))] = "A"
 9    $ct[[System.Text.Encoding]::UTF8.GetString(@([byte]240,159,133,177))] = "B"
10    # snipped
11    $ct[[System.Text.Encoding]::UTF8.GetString(@([byte]240,159,154,137))] = "/"
12    $ct[[System.Text.Encoding]::UTF8.GetString(@([byte]240,159,144,142))] = "="
13
14    $bytes = [system.Text.Encoding]::UTF8.GetBytes($str)
15    for($i = 0; $i -lt $bytes.length; ) {
16        $byte = $bytes[$i]
17        if ($byte -eq 240) {
18
19            if ($bytes[$($i+3)] -eq 153) {
20                $offset = 12
21            } elseif ($bytes[$($i+3)] -eq 140) {
22                if ($bytes[$($i+12)] -eq 140) {
23                    $offset = 16
24                } else {
25                    $offset = 15
26                }
27            } elseif ($bytes[$($i+3)] -eq 177) {
28                if ($bytes[$($i+2)] -ne 133) {
29                    $offset = 10
30                } else {
31                    $offset = 3
32                }
33            } else {
34                $offset = 3
35            }
36            $subsection = $bytes[$i..$($i+$offset)]
37            $emoji = [System.Text.Encoding]::UTF8.GetString($subsection)
38            $newstr += $ct[$emoji]
39            $i = $i+$offset+1
40        } elseif ($byte -eq 194) {
41            $subsection = $bytes[$i..$($i+1)]
42            $emoji = [System.Text.Encoding]::UTF8.GetString($subsection)
43            $newstr += $ct[$emoji]
44            $i = $i+2
45        } elseif ($byte -eq 226) {
46            $subsection = $bytes[$i..$($i+2)]
47            $emoji = [System.Text.Encoding]::UTF8.GetString($subsection)
48            $newstr += $ct[$emoji]
49            $i = $i+3
50        } else {
51            $subsection = $bytes[$i..$($i+6)]
52            $emoji = [System.Text.Encoding]::UTF8.GetString($subsection)
53            $newstr += $ct[$emoji]
54            $i = $i+7
55        }
56    }
57    return $newstr
58}

The decrypt function is composed of the above encrypt function, with its operations reversed. I ran into a silly PowerShell quirk(?), where it will not "correctly" modulo negative numbers. Thus, I had to add check cases with a workaround for negative numbers. But, the real decrypt script I read afterwards didn't have these, so I might just be dim. Whatever, mine runs faster anyway.

142function decrypt($str, $key)
143{
144    $bytes = [System.Convert]::FromBase64String($str)
145    $key_bytes = $key.toCharArray()
146
147    # vigenere cipher
148    for($i = 0; $i -lt $bytes.length; $i++) {
149        $key_byte = $key_bytes[$i % $key_bytes.length]
150        $thing = $bytes[$i]
151        if ($thing -lt $key_byte) {
152            $thing2 = ((($thing - $key_byte) % 256) + 256) % 256
153        } else {
154            $thing2 = ($thing - $key_byte) % 256
155        }
156        $bytes[$i] = $thing2
157    }
158
159    # caesar rot23
160    for($i = 0; $i -lt $bytes.length; $i++) {
161        if ($bytes[$i] -lt 3) {
162            $bytes[$i] = ((($bytes[$i]- 3) % 256) + 256) % 256
163        } else {
164            $bytes[$i] = ($bytes[$i] - 3) % 256
165        }
166    }
167
168    # reverse shuffle
169    if ($bytes.length % 2 -eq 0) {
170        for($i = 0; $i -lt $($bytes.length / 2); $i++) {
171            $cur_pos = $i * 2
172            $orig = $bytes[$cur_pos..$($cur_pos + 2)]
173            $bytes[$cur_pos + 1] = $orig[0]
174            $bytes[$cur_pos + 0] = $orig[1]
175        }
176    }
177
178    # subtract from bytes
179    if ($bytes.length % 3 -eq 0) {
180        for($i = 0; $i -lt $($bytes.length / 3); $i++) {
181            $cur_pos = $i * 3
182            $orig = $bytes[$cur_pos..$($cur_pos + 3)]
183
184
185            if ($orig[0] -lt 7) {
186                $orig[0] = ((($orig[0] - 7) % 256) + 256) % 256
187            } else {
188                $orig[0] = ($orig[0] - 7) % 256
189            }
190
191            if ($orig[1] -lt 3) {
192                $orig[1] = ((($orig[1] - 3) % 256) + 256) % 256
193            } else {
194                $orig[1] = ($orig[1] - 3) % 256
195            }
196
197            if ($orig[2] -lt 1) {
198                $orig[2] = ((($orig[2] - 1) % 256) + 256) % 256
199            } else {
200                $orig[2] = ($orig[2] - 1) % 256
201            }
202
203            $bytes[$cur_pos + 0] = $orig[0]
204            $bytes[$cur_pos + 1] = $orig[1]
205            $bytes[$cur_pos + 2] = $orig[2]
206        }
207    }
208
209    # subtract and unshuffle bytes
210    if ($bytes.length % 5 -eq 0) {
211        for($i = 0; $i -lt $($bytes.length / 5); $i++) {
212            $cur_pos = $i * 5
213            $orig = $bytes[$cur_pos..$($cur_pos + 5)]
214            if ($orig[1] -lt 3) {
215                $orig[1] = ((($orig[1] - 3) % 256) + 256) % 256
216            } else {
217                $orig[1] = ($orig[1] - 3) % 256
218            }
219            if ($orig[2] -lt 1) {
220                $orig[2] = ((($orig[2] - 1) % 256) + 256) % 256
221            } else {
222                $orig[2] = ($orig[2] - 1) % 256
223            }
224            $orig[3] = ((($orig[3] - 0) % 256) + 256) % 256
225            if ($orig[0] -lt 255) {
226                $orig[4] = ((($orig[4] - 255) % 256) + 256) % 256
227            } else {
228                $orig[4] = ($orig[4] - 255) % 256
229            }
230            if ($orig[0] -lt 254) {
231                $orig[0] = ((($orig[0] - 254) % 256) + 256) % 256
232            } else {
233                $orig[0] = ($orig[0] - 254) % 256
234            }
235            $bytes[$cur_pos + 0] = $orig[1]
236            $bytes[$cur_pos + 1] = $orig[2]
237            $bytes[$cur_pos + 2] = $orig[3]
238            $bytes[$cur_pos + 3] = $orig[4]
239            $bytes[$cur_pos + 4] = $orig[0]
240        }
241    }
242
243    # unshuffle bytes
244    if ($bytes.length % 7 -eq 0) {
245        for($i = 0; $i -lt $($bytes.length / 7); $i++) {
246            $cur_pos = $i * 7
247            $orig = $bytes[$cur_pos..$($cur_pos + 7)]
248            $bytes[$cur_pos + 2] = $orig[1]
249            $bytes[$cur_pos + 1] = $orig[2]
250            $bytes[$cur_pos + 6] = $orig[3]
251            $bytes[$cur_pos + 5] = $orig[4]
252            $bytes[$cur_pos + 4] = $orig[5]
253            $bytes[$cur_pos + 3] = $orig[6]
254        }
255    }
256    return [System.Text.Encoding]::UTF8.GetString($bytes)
257}
258
259$toencrypt=gc $Filename -Encoding utf8 -Raw
260echo "file $Filename -> $safe_path w/$Key"
261$plaintext=emojiTo64 "$toencrypt"
262$plaintext=decrypt "$plaintext" "$Key"
263echo $plaintext
264SET-CONTENT -Force -PaTH "${Filename}.decrypted" -NoNewLine -Encoding utf8 -Value "$plaintext"

I finally got this script working on the test files I encrypted with the provided script, so it was time to find the key.

Finding Database Key

The database.bat file was a Frankenstein's monster of shotty Windows technologies. The file decoded yet another base64 string (at this point, I have a terminal dedicated to base64 -d):

35$f15 = "IHVzaW5nIFN5c3RlbTsKIHVzaW5nIFN5c3RlbS5Db2xsZWN0aW9uczsKIHVzaW5nIFN5c3RlbS5UZXh0LlJlZ3VsYXJFeHByZXNzaW9uczsKIHVzaW5nIFN5c3RlbS5EaWFnbm9zdGljczsKIG5hbWVzcGFjZSBIZWxsb1dvcmxkIHsKICAgICBwdWJsaWMgY2xhc3MgSUxvdmVDU2hhcnAgewogICAgIC8vLyBvaCBJIGxvdmUgQyMgVEoKIHB1YmxpYyBzdGF0aWMgdm9pZCBGd..." # (26892 chars)
36
37mkdir -Force $env:AppDatA\junkstuff2
38$nascar | Set-Content -Force -Path $env:AppDATA\junkstuff2\csharp_stage.cs
39# give it some time to sync OW
40start-sleep 3

When decoded, it's a C# program that runs a URL encoded script:

47highlight powershell "linenos=inline,linenostart=47"
48"24     var stringUsed = \"'%0A%27Module%20super%0A%27%20source%20MSFT%20https%3A//docs.microsoft.com/en-us/previous-versions//yab2dx62%28v%3Dvs.85%29%0A%27%20modified%20for%20our%20needs%20OW%0AFunction%20RegReplace%28ByRef%20string1%2C%20pattern%2C%20string2%29%0A%20%20%20Dim%20regEx%2C%20Match%20%20%20%27%20Create%20variable.%0A%20%20%20Set%20regEx%20%3D%20New%20RegExp%20%20%20%27%20Create%20a%20regular%20expression.%0A%20%20%20%20regEx.Pattern%20%3D%20pattern%20%20%20%27%20Set%20pattern.%0A%20%20%20%20...' # (11605 chars)
49"45     // new hotness - TJ",
50"80         fso = new ActiveXObject(\"Scripting.FileSystemObject\");",
51"61                 alert(\"Bad Base64 passed!\")",
52"72         alert(\"TJ's retired!\")",
53"92         ts.Write(string)",
54"59             else",

And ugh, when that is decoded (I didn't screw up the tabs here, that's just how it looks)...

59' 2017/11/05 Added test credentials for automated testing TJ
60    ' 2011/29/09 Added Bob2 OW
61      ' 2011/29/09 Added Regime OW
62           ' 2011/29/09 Added Regime OW
63    ' 2011/29/09 Added Tudor OW
64       ' 2011/29/09 Huge rush of cusomers today, whew OW
65                 ' 2011/04/10 Automated additionas to fiel OW
66    ' 2011/05/01 Automated transfer of records to file comment
67           ' 2011/05/02 Automated transfer of records to file comment
68       ' 2011/05/03 Automated transfer of records to file comment
69               ' 2011/05/04 Automated transfer of records to file comment
70           ' 2011/05/07 Disabled customer add transitioned to new system OW
71                       ' 2011/05/07 New customers on paper OW
72                  ' 2017/11/09 Stripped files of so many customers
73    ' 2017/11/09 Old customers and test account data prior to 2011/09/29 grandfathered TJ
74    Dim databaseEntries(9)
75
76    Set databaseEntries(0) = getCustomer("fred", "4469746386451847")
77    ',
78        Set databaseEntries(1) = getCustomer("tina ", "4838678336427052")
79            Set databaseEntries(2) = getCustomer("Wilson", "5163109945115757")
80        Set databaseEntries(3) = getCustomer("Bob", "5444343912315204")
81            Set databaseEntries(4) = getCustomer("test-cc", "4263435718112958")
82        Set databaseEntries(5) = getCustomer("Bob2", "4564723402048547")
83                Set databaseEntries(6) = getCustomer("Regime", "5550054028145738")
84            Set databaseEntries(7) = getCustomer("Regime", "5550057643774014")
85        Set databaseEntries(8) = getCustomer("Tudor", "5402154154173510")
86    ' gave customers here some extra encryption TJ
87    Dim cut
88    Dim i
89    For i = 0 to 8
90        ' upped it to 4 rounds, 1 better than 3DES TJ
91        Dim temp
92        Set temp = databaseEntries(i)
93        temp.CreditCardNumber = databaseEnc(temp.CreditCardNumber)
94        temp.CreditCardNumber = databaseEnc(temp.CreditCardNumber)
95        temp.CreditCardNumber = databaseEnc(temp.CreditCardNumber)
96        temp.CreditCardNumber = databaseEnc(temp.CreditCardNumber)
97        Set databaseEntries(i) = temp
98    Next

We get the test account credit card information. We can corroborate this is the "test credentials" based on the chronology of comments. Also of note is the databaseEnc function, which does literally nothing when it's used twice.

Why Doesn't It Work?

I have my decrypt script working on every file I throw at it, but when I run it on the real decrypt script, I get garbage.

It's almost all printable, so I feel like I'm close, but maybe I just have the wrong numbers? Maybe there's something done to the number before being used as the key? I tried running it in my new Windows environment, thinking it was an issue there, but I got the same output.

I was stuck here for a long time, quadruple checking my script, giving it more test cases, trying to brute force the number, and receiving jeers from my teammate.

Decrypting

Finally, in a moment of enlightenment, I decide to pump it through the actual "webapp", and it works! The decrypt script outputs the real decrypted script, with the flag :)

But, I knew I would be wondering for the rest of my life why that worked. After reading the "webapp" source more closely, this line:

```powershell
    <script FOR="Decrypt" EVENT="onClick" LANGUAGE="VBScript">
      Set WshShell = CreateObject("WScript.Shell")
      ' NOOOOOOOOOOOOOOOO was testing encryptiong of files with the automated test account CC info! IT ENCRYPTED THE SCRIPT should be backupc though UW 11/05/20
      ' there's no backup of decrypt.ps1 UW 11/06/20
      WshShell.Run "cmd.exe '/c ""powershell.exe -ExecutionPolicy Bypass -file decrypt.ps1 -Filename "& getuserdatafromform.decrypt_input.value & " -Key ^'" & getuserdatafromform.decrypt_password.value & "^' & timeout 5"""'"
    </script>
```

It passes the key with single quotes around it...

I guess it really was a web challenge.

Flag: sun{emoji_web_challenge_\ud83e\udd47_moral_just_use_git_never_use_zip_backups}

If you have any questions or feedback, please email my public inbox at ~sourque/public-inbox@lists.sr.ht.