Author

Colin is a data analyst, currently working in Whitehorse, Yukon.

Showing blog posts written by: Colin Luoma

Change Ubuntu Root Password Using Dirty COW



If you've been on the internet over the past couple of weeks then you've likely heard of the so-called Dirty COW bug recently discovered in Linux. The story behind it is actually kind of funny. It was discovered and patched by Linus himself over 11 years ago! The fix was then undone in subsequent code commits due to issues with new code. So the vulnerability has been exploitable since for about 9 years, since 2007. The silver lining is that no examples of this exploit being used have been found in the wild and most popular Linux distros have pushed fixes. Patch your system if you haven't already!

If you're not familiar with what dirty COW does it allows a non-privileged user to write to files that are usually read-only except by root. This is pretty significant and since "everything in Unix is a file" this opens up lots of doors for exploitation. There are plenty of resources online to learn more about how exactly the exploit works, like here, so I won't go into much detail about the exploit itself.

Along with knowledge of the exploit, proof-of-concept code was also released. Basically a skeleton example with just enough code to show how writing to a root-protected file is possible. This is what I used to create a simple command line program that I call Madcow. It takes advantage of the dirty COW exploit to change the root password on an Ubuntu system to whatever the user provides.

Ubuntu stores passwords (sort of) in a file located at '/etc/passwd'. Here is the first couple lines of the one found on my machine:

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin

This file is read-only to all users except root. There's lots of stuff here but the most important information is after the first colon of each line. Right at the top we see 'root' then a colon, a lowercase 'x', and another colon. There can be two types of information here: an encrypted password (storing plaintext passwords in files readable by all users would be silly), or a lowercase 'x'. The lowercase 'x' indicates that the encrypted version of the password is stored in a separate file '/etc/shadow'. That file is readable only by root so changing it is impossible.

So if we want to change the root password all we need to do is replace the single lowercase 'x' with an encrypted password of our choosing. And this is exactly what madcow does. Here is a quick example:

$ head -1 /etc/passwd
root:x:0:0:root:/root:/bin/bash
$ ./madcow new_password
mmap 7f4b13aba000
madvise 0
procselfmem 2648940
$ head -1 /etc/passwd
root:xxTumM4gi9IDM:0:0:root:/root:/bin/bash

Not very interesting on the surface, but this is a write-protected file that we, as a normal user, have just written to! Now it's simply a matter of starting a root shell using the new password and the entire system is ours.

That's basically it. I'm sure there are a lot more interesting things that could be done with this exploit but this is the most obvious and powerful. The code is available on my Github if you want to take a closer look. Obviously I take no responsibility for any damage caused to your or others' systems from running this code. And please don't run it outside of a VM or an installation you don't mind losing.

Tags:


Using Julia for Dynamic Charts

The more I play around with Julia the better things seem to get. With web-stuff on my mind lately from working on MiniHTTP I thought it would be cool to create a simple web API for adding charts to any webpage. Thanks to a couple packages (HttpServer.jl and Plots.jl) this was dead easy in Julia.

The basis for handling requests for charts is by creating a simple HTTP Handler. This checks the URL that was supplied and, if it matches, dispatches the correct chart.

http = HttpHandler() do req::Request, res::Response
  if ismatch(r"^/randomchart",req.resource)
    Response(random_chart(), headers("image/png"))
  elseif ismatch(r"^/linechart",req.resource)
    Response(line_chart(req.resource), headers("image/png"))
  else
    Response(400)
  end
end

Above there are two endpoints that we can match. The first one is if the URL starts with ‘/randomchart’, in this case the function ‘random_chart()’ is called which generates some random numbers and plots them to a chart. The chart at the top of this post in made this way and, since it is random, every time this page is refreshed the chart will be slightly different.

The second endpoint is for generating a custom line chart. If the URL starts with ‘/linechart’ then the function ‘line_chart(::AbstractString)’ is called. This function expects that a query string is attached to the end of the supplied URL that contains a list of x and y coordinates to plot on a line graph. For instance, the URL ‘/linechart?x=1,2,3,4,5,6,7,8,9,10&y=1,2,2.5,2,3,1,1.5,3,0.5,2’ was used to generate the plot below.

And that’s really all there is to it. The full code (which is less than 50 lines!) has been uploaded to my Github if you would like to take a further look. With a little extra work, this could be fleshed out to a very nice and easy-to-use web graphing API for people who just want to be able to quickly add some charts to a website.

Update(20-02-2017): I've shut down one of my Amazon EC2 instances so the charts shown above are now static images instead of dynamically generated from Julia.


Tags:


MiniHTTP - Raspberry Pi Webserver



Finally! After over a year of wondering what I should do with my Raspberry Pi, and a forgotten attempt at a weather station, I have found a permanent use for the Raspberry Pi.

Recently I took my website off blogger and wrote my own from scratch. This started a chain reaction and got me thinking: “If I’m already writing a website, why not write a webserver to go along with it?” And as a result, I’ve been spending the majority of my spare time learning about and writing a C webserver from scratch. A huge thanks to Beej's guide that got me rolling on this project.

It’s called MiniHTTP (not very original I know) and it’s finally in a semi-working state. Good enough for my purposes anyways. It’s more of a prototype and a learning project than a piece of code that should ever be used by anybody else for anything other than education. It’s rather inefficient due to forking processes to handle each request and it supports only a small subset of HTTP. But I wanted to keep it small a purpose-built for my application since it will be running on a Raspberry Pi I didn’t want the project to get too big.

Now the Raspberry Pi comes into play. My version 2 has been sitting in the closet for quite some time now. Soon after starting to work on MiniHTTP I made the decision to host everything on my Pi for a couple of reasons. First, while Amazon AWS is pretty awesome, it’s quite expensive for hosting my small site that barely gets any traffic. And second, because it’s awesome! Hosting my own site on my own hardware, that only cost 35 bucks! Actually twice that with the cost of the PiDrive. But still pretty dang cheap. And if it holds up for more than 7 months it will have been cheaper than continuing to pay for an EC2 instance on Amazon.



And of course it isn’t a true Raspberry Pi project without an unnecessary glowing blue LCD display. So along with the webserver a small python script is also running in the background that reads from the server log file to show hourly, daily, and weekly website hits.

Tags:


Mission: Economy - Düsseldorf Hackathon



Over the past weekend I participated in my first ever hackathon here in Dusseldorf. The theme described on the event page was rather vague and didn't really have any clear direction so we weren't sure what exactly to expect. Before arriving we had decided that we wanted to do something interesting and/or fun with the Panama papers data that was released publicly.

Eventually we decided to create a simple website to 'Build Your Own Offshore Corporation', poking fun at the ridiculousness of the companies (if you can even call them that) mentioned in the papers. Using the actual data pulled from the papers we allow the user to generate a name, locate an intermediary to help set up their corporation, and decide which tax jurisdiction to have the company operate in. We used R Shiny to build a quick prototype. It isn't hosted online anywhere but if you clone our github repo you can run it locally.

The funniest part for us was seeing how silly the actual companies names are. A couple of our group member put together the graphic above, using the most common words found in the names. Give it a try to see for yourself!

At the conclusion of the event we ended up taking home the prize for 'Best Pitch'. Which was quite a surprise to us as it seemed that our project was probably not what the judges were really looking for. So all-in-all not a bad way to spend a couple days. It was definitely an interesting experience for my first hackathon.

Finally I'd like to thank everybody in our group that participated! Without them this would not have been quite so successful or fun.

Tags:


Objective Gametypes and KDA

Something that often gets said regarding objective gametypes (capture the flag, strongholds, etc.) is that KDA (kill-deaths-assists) does not matter as long as you're getting the objectives. So I wanted to dig through some of my recent Halo 5 matches to see if there's any truth to this statement. Using a sample of recent objective games that I completed, a logistic regression was performed using Kills, Assists, and Deaths on the dependent variable of winning or losing. Basically I want to know how much each of these variables influences the probability of winning or losing a game.

This was also a bit of an excuse to play around some more with the language Julia. I've been starting to use it more at work and am really enjoying what it offers. Although it's tough to compete with the whole R ecosystem, thanks to the RCall package it makes the transition pretty painless. Head over to Julia-Lang if you want to know more.

First the non-Julia stuff. Since I want Halo 5 data I'll be using RCall to interface with my R package to get some data. Below is a simple wrapper function to get the match data I want using my h5api package.

using DataFrames, RCall, GLM

api_key = "itsamystery"
slayer_id = "257a305e-4dd3-41f1-9824-dfe7e8bd59e1"

R"library(h5api);library(data.table)"
function getRecentMatches(player, modes, start, count, key)
  R"recent_matches <- getRecentMatches(player = $player,
                                        modes = $modes,
                                        start = $start,
                                        count = $count,
                                        key = $key)"
  r_data = R"cbind(rbindlist(lapply(recent_matches$Results$Players, flatten)),
                  id=recent_matches$Results$GameBaseVariantId)"
  return rcopy(DataFrame, r_data)
end

Next, just using a simple loop I'll grab data from 250 of my most recent matches. CLWakaLaka is my gamertag, so you can either use mine again or try your own if you play Halo 5.

match_data = DataFrame()
for i in 0:9
  match_data = [
    match_data;
    getRecentMatches("CLWakaLaka", "arena", i*25, 25, api_key)
  ]
end

This performs a little cleaning up before performing the regression. Besides a win or a lose, it's possible for a match to end in a tie or disconnect. So first only definite win/lose matches are considered. Slayer games are also filtered out. Since kills are the objective in this game type it goes without saying that KDA directly influences your likelihood of winning. Finally the result is changed to a simple 1-0 variable. 1 meaning win and 0 meaning lose.

match_data = match_data[((match_data[:Result] .== 1) | (match_data[:Result] .== 3)) & (match_data[:id] .!= slayer_id), :]
match_data[:Result] = map(match_data[:Result]) do x
  if x == 3
    return 1
  else
    return 0
  end
end
match_data[:Result] = convert(Array{Float64, 1}, match_data[:Result])

Finally, using the GLM package, a logistic regression is performed in the data with the following results:

my_lm = glm(Result ~ TotalDeaths + TotalAssists + TotalKills,
            match_data,
            Binomial(), LogitLink())

Formula: Result ~ 1 + TotalDeaths + TotalAssists + TotalKills
Coefficients:
               Estimate Std.Error   z value Pr(>|z|)
(Intercept)   0.0405304  0.531324 0.0762819   0.9392
TotalDeaths   -0.202424 0.0572149  -3.53796   0.0004
TotalAssists   0.202192 0.0941433   2.14771   0.0317
TotalKills     0.121854  0.049335   2.46993   0.0135

So what does this tell us? Well the logistic function looks like this: 1 / (1 + exp(-( intercept + b1x1 + b2x2 + etc. ))) Where bi's are the estimates above and xi's are the data points. For example, if I had a game with 10 kills, 4 assists, and 8 deaths, then my estimated probability of winning that game would be: 1 / (1 + exp(-( 0.0405 -0.2028 +0.2024 + 0.122*10 ))) = 0.61 or 61%

From the estimates above: deaths negatively influence the probability of a win and kills and assists influence the probability of a win positively. All three variable estimates have a p-value of less than 0.05 which suggests they are significant factors in the overall outcome of a game (obviously). The intercept, however, is not significant which makes sense since we likely have no data for a 0/0/0 game.

Interestingly, deaths and assists coefficients have roughly equal magnitude while the coefficient for kills is slightly less. This would suggest that the relative importance of these actions corresponds to Deaths = Assists > Kills. Meaning that the most important factors towards getting the win are (in this order): not dying, always shooting/helping teammates, then getting kills.

So there we go, it seems there is a little kernel of truth in the idea that KDA in an objective-based gametype is not everything... Although it certainly helps, and feels so good.


Tags: