Hacking a Banksy with Bash and Varanid

Mike Benich
5 min readOct 25, 2019

--

The famous street artist Banksy recently opened up a physical storefront in Croydon, UK in order to maintain ownership over their name copyright. In classic Banksy fashion, the items were not for sale but a cryptic website popped up indicating that they might someday be purchasable online.

Coming Soon

In order to get the best chance of getting a genuine Banksy, I decided to leverage all the technical tools that I had at my disposal.

Recon

The core domain was grossdomesticproduct.com, so first I attempted to see what type of information was available that might hint towards a release date or time. Using a traditional bug bounty workflow, I enumerated all possible subdomains using a combination of MassDNS, Amass, Crt.sh, and Rapid7’s Project Sonar data.

"shops.myshopify.com,shop.grossdomesticproduct.com",
"shops.myshopify.com,checkout.grossdomesticproduct.com",
"104.20.37.146,www.grossdomesticproduct.com",
"104.20.38.146,www.grossdomesticproduct.com"

Based on this data, two things were immediately obvious. First, the shop was going to be hosted on Shopify’s cloud. Secondly, the ranges for the core website were hosted in Cloudflare, an indication that the site owners were expecting a lot of traffic and potentially had anti-botting measures in place.

Users browsing to shop.grossdomesticproduct.com were redirected to /password. Based on our experiences with Shopify, rate limiting and IP banning procedures exist so brute forcing the password was out.

Enter Password

The admin panel was available at /admin, but we didn’t want to cross any legal boundaries by brute forcing it.

While the store was protected with a password, it still was possible to view the source Javascript. It was clear that there were going to be 22 unique product links which aligned with the expectations from the store.

Monitoring

Checking out the first in a Shopify store meant being able to regularly track changes and alert us whenever new changes to the site were made. Because we were unsure the mechanism in which the password would be released, we set up our monitoring in a private Slack channel.

First, we set up RSS feeds for both @banksy and @banksygrossdomesticproduct. Although some sites exist for making RSS feeds out of Instagram pages (such as rss.app), it was faster to query the Instagram JSON endpoint ourselves (eg, https://www.instagram.com/banksy/?__a=1) and parse it using jq.

Annoyingly, Slack only fetches RSS feeds every 30 minutes. Our alerts already had 137 likes by the time new posts were fetched, so we quickly switched to manual alerts using a service called Pushover to read our RSS feed. We also turned on Instagram notifications just to ensure we didn’t miss out.

To track site changes, we used a new service called Varanid which could track changes and alert when a website was changed. Since we were already setting up a Slack channel, the webhook feature allowed us to receive notifications every 30 minutes with the differences.

It was clear that a 30 minute interval was not going to be fast enough for us, and even on the highest service tier a 5 minute delay was too long. Using Varanid as a starting point, we set up a quick bash script to alert us when the page hash changed.

Based on our findings from Varanid, certain lines were dynamically changing, such as the Cloudflare ID and session tokens. We decided to follow the below methodology to set the conditions for an alert:

  1. Check the page each minute with curl
  2. Using ripgrep, strip out any dynamic content from Cloudflare or Shopify’s CDN
  3. Calculate the MD5 hash and match it against the known calculation. If it matches, do nothing. If it does not, take a screenshot using Chromium, upload it to Imgur, and send the entire thing over as a notification to Slack.

The following script was set to execute every minute with Cron:

hashy=$(curl -sL https://grossdomesticproduct.com/ | rg -v "/cdn-cgi/" | md5sum | cut -d " " -f1)shopy=$(curl -sL -H 'Cache-Control: no-cache' https://shop.grossdomesticproduct.com/password | rg -v reqid | rg -v Trekki | rg -v cdn.shopify.com/s/files/1/0250/6224/4404/t/1/assets/ |rg -v integrity | md5sum | cut -d " " -f1)if [ "$hashy" = "<md5>" ]; then
echo "Samesiez"
else
echo "Changed! - Alerting"
chromium-browser --no-sandbox --headless --disable-gpu --window-size=1920,1080 --screenshot="$hashy.jpg" https://grossdomesticproduct.com
imgur_url=$(./imgur.sh "$hashy.jpg")
curl -X POST -H 'Content-type: application/json' --data '{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Changes detected! :wave: <!channel>"
}
},
{
"type": "image",
"title": {
"type": "plain_text",
"text": "Screenshot",
"emoji": true
},
"image_url": "'$imgur_url'",
"alt_text": "Screenshot"
}
]
}' https://hooks.slack.com/services/<ourwebhook>
fiif [ "$shopy" = "<md5>" ]; then
echo "Shop is the same"
else
echo "Shop Changed!"
chromium-browser --no-sandbox --headless --disable-gpu --window-size=1920,1080 --screenshot="$shopy.jpg" https://shop.grossdomesticproduct.com/password
imgur_url=$(./imgur.sh "$shopy.jpg")
curl -X POST -H 'Content-type: application/json' --data '{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Shop changes detected! :wave: <!channel>"
}
},
{
"type": "image",
"title": {
"type": "plain_text",
"text": "Screenshot",
"emoji": true
},
"image_url": "'$imgur_url'",
"alt_text": "Screenshot"
}
]
}' https://hooks.slack.com/services/<webhook>
fi

Results

On the day of release, we identified a change with Varanid that indicated the site was being manually worked on. It turned out that the instinct was right, and shortly thereafter we received a screenshot of the shop site that showed the password element was no longer on the page.

We quickly added the items to our cart, but the dialog box asked us to describe “what art means to us in 50 words or less”. Unfortunately, all of our monitoring eventually meant nothing as the terms of service indicated that the winners would somehow be manually verified and selected at random.

Oops.

Trolled Again

While technically there is still a chance, it’s looking more like our odds are better from winning the lottery.

--

--

Mike Benich

Physics geek. Security researcher. Former educator. Blog posts do not necessarily reflect the opinions of my employer.