Dangerous developments: An overview of vulnerabilities in coding services

Development and workflow management tools represent an entire class of programs whose vulnerabilities and misconfigs can turn into a real trouble for a company using such software. For a pentester, knowledge of these bugs is a way to successful exploitation; while for an admin, it’s a great opportunity to enhance the protection. This article discusses vulnerabilities discovered in Jira, Confluence, Asana, Docker, GitLab, and other similar products.

info

The Editorial Board is grateful to the Hackerdom team for assistance in the production of this article.

A typical development cycle involves multiple stages: planning, analysis, design, development, testing, integration, support, etc. At each stage, developers, admins, DevSecOps, and other specialists use various tools. The safety of the product directly depends on the correct configuration of these tools. In many cases, incorrect settings can be easily turned into vulnerabilities.

For demonstration purposes, I will take several popular services used at different development stages. Most of these vulnerabilities were discovered back in 2019; so, don’t expect to repeat what I have done with current versions of these programs: the majority of the bugs have already been fixed, and my goal is just to show how to detect them.

warning

All information provided in this article is intended for educational purposes only. Neither the author nor the Editorial Board can be held liable for any damages caused by improper usage of this publication.

Jira

Jira is a powerful task tracker developed by Atlassian. It can also be used as a bug tracker, a task distribution system for employees, etc. Millions of organizations use this product, thus, becoming potential targets for attackers. Too bad, not all admins are concerned about security; many of them sincerely believe that their organizations are of no interest to anyone and no one will attack them. What a dangerous delusion!

The list of vulnerabilities identified in Jira is pretty long; I will start from the simplest one.

Data disclosure

Imagine that you have found a Jira service at the address jira.company.hack. Then a call to the URL https://jira.company.hack/rest/api/2/[]/ will display the stack trace as in the screenshot below.

Jira stack trace
Jira stack trace

What’s of interest there? First, you can see what plugins are installed in Jira. As you are aware, plugins are often much more vulnerable than the app itself. Jira contains plenty of bugs of various severity; so, you can find the program version in the logs and check whether a handy RCE is available for it.

info

Jira ports (from 8000 to 8100) are also useful for an attacker: you can use them to directly control the installed system: restart it, stop, manipulate the admin panel, etc. In addition, Jira can be easily detected from the Internet.

Like any industrial system, Jira is scalable. For this purpose, it uses plugins written both by the official developers (i.e. Atlassian) and third parties.

One of the most frequently used such components is Agile Board.

Jira Agile Board
Jira Agile Board

Agile is a popular approach to development, and Jira Agile Board makes it possible to create dashboards for Agile and use them to work on different projects in a team or in several teams. The idea is simple: there is a board, there are tickets on it, and you can see when they are completed and by whom, what are the deadlines for them, what are the sprints, etc.

When I started researching Jira in our company, I noticed that all this information is available without any authentication. In other words, the plugin created by the official developer lives its own life – even though it contains sensitive information. You can just follow an URL in the https://jira.company.hack/secure/ManageRapidViews.jspa format and view any board of any project.

Viewing boards without authentication
Viewing boards without authentication

IDOR

Jira even contains an IDOR (insecure direct object references) vulnerability that allows you to open any project (either a private or a public one) and view the tasks and their implementation progress.

By trying different parameter values in the URL, you can open all boards created in Jira.

Blind JQL

Another exciting bug. Its exploitation doesn’t require authentication and allows to read any tickets.

The Jira core expectedly includes various APIs. The following addresses are of special interest for an attacker:

  • https://jira.company.hack/jira/rest/api/2/mypermissions;
  • https://jira.company.hack/jira/rest/api/2/issue/1; and 
  • https://jira.company.hack/jira/rest/api/2/search?jql=status%3Dopen and description="12345".

As you can see, at the search endpoint, a request is passed in the jql parameter, and different answers are given to existing and nonexistent requests. This allows to completely restore the contents of tickets using blind SQL injection techniques.

No answer exists
No answer exists
There is an answer
There is an answer

For a successful search, you have to guess the correct value of the id field in the request text. This is an integer that represents the ticket number in Jira. After that, you can completely restore the ticket contents by brute-forcing the description field.

One of the problems with IDs is that they are out of order. To guess an ID, use the following simple script:

for $i in $(seq 10000 11000); do curl https://jira.company.hack/rest/api/2/issue/$i; done;
Script output
Script output

As can be seen in the screenshot, tickets 10000 and 10001 exist, but there is no access to them; while 10002 doesn’t exist. This means that you can apply blind JQL only to tickets 10000 and 10001.

In some cases, this may not work. Atlassian has two access distribution modes: personal and group. The group mode is applied immediately to the entire group of developers; while the personal mode, to one specific developer. If the personal access mode is enabled, the attack cannot be reproduced.

The error message is always the same
The error message is always the same

Confluence

Confluence is a team workspace, and it’s especially handy for remote working: team members can together accumulate knowledge in it.

The product is plagued by the same problems as Jira (stack trace and control ports), but there is one more bug in Confluence.

This time, the following URLs are worth your attention:

  • https://confluence.company.hack/rest/api/space;
  • https://confluence.company.hack/rest/api/content;
  • https://confluence.company.hack/rest/api/space/{SPACEKEY}; and 
  • https://confluence.company.hack/rest/api/content/{ContentID}.

Open spaces can be read without authentication; so, it might seem that there is nothing wrong here.

Still, there is a vulnerability in the product, and the problem is very similar to the blind JQL that affects Jira. Since the Confluence built-in language is called CQL, the attack technique is called blind CQL.

The impact of the bug is the same: you can restore the contents of closed spaces. The precondition for a successful attack is the same: group access permissions (i.e. not individual ones), which is normal for large companies.

info

Don’t rush to search for vulnerable Jira and Confluence versions on the Internet. In Jira, the problem has been recently fixed, and now it remains only in systems not updated for many months. The same applies to Confluence.

Redmine

Redmine is another popular task tracker.

The program seems to be safe, but I managed to find one small bug: XSS.

It occurs when you insert executable code instead of a link as shown in the screenshot.

Exclamation marks are the program’s special tags: inside them, you transmit a link, parentheses, and payload.

At the first glance, it seems to be an ordinary stored XSS: you create a malicious ticket, the victim opens it, clicks on your injected code, and it’s executed on the visitor’s PC.

But the point is that this bug makes it possible to sniff passwords!

On a host under your control, you add basic authentication to a PHP script, and this authentication request is loaded to Redmine from your malicious resource. This attack vector is well-known, but it’s still efficient.

The victim opens your ‘charged’ entry and sees an authentication request. The unsuspecting user thinks that this is the corporate Redmine server and enters the login and password that are transmitted to your server. You decode the string from Base64 and get the login:password pair of a Redmine user (or even credentials of a corporate account if LDAP is used.

Asana

This is another popular task tracker. Just look at its cookies:

soft_signup_user_id=5166376;
soft_signup_email=hac126%40mail.ru;
xi_ip=97.12.23.123; soft_signup_invitation_token=numeric_token-5166375-672796;
gtm_u_id=5166376;
dapulseUserId=5166375;
gtm_u_is_admin=true;
gtm_a_paying_value=0.0;

No doubt that soft_signup_user_id is the user’s ID, while soft_signup_email is the user’s e-mail address. This is a classical authentication bypass and potentially an IDOR!

If you know that a person with a certain e-mail address is registered in a specific system (you can check this directly in the system), you can brute-force the user’s ID and get access to that person’s account.

This is exactly what I did.

HiTask

There is also an XSS here, but it’s gross! It’s located in tags and can potentially be deployed not only on a specific page, but throughout the entire site.

When a tag is created, it spreads throughout the entire site and becomes accessible everywhere; accordingly, you can use it to target all users.

Demonstration
Demonstration

Another bug in HiTask is a classical LFI that has an interesting feature: the name of the loaded file is passed not in the parameter of the loading script, but in its name.

It works as follows: there is a script inside that parses the variable name from the script name. It includes prop_act_upload1, prop_act_upload2, etc., but if you pass a string there, it will read the file located at the transmitted address.

prop_act_upload../../../../../../../../etc/passwd%00
prop_act_upload../../../../../../../../etc/passwd%00

The exploitation is simple: you create a ticket with an attached file (attachment), intercept and alter the path to it in Burp, and, an internal file becomes attached to it. Then you download and read this file.

LFI triggered
LFI triggered

Team Foundation Server

TFS is a Visual Studio component well-known to developers. The trick is that TFS can import third-party Git repositories.

Just imagine: you can download something from the specified address on behalf of Team Foundation Server. I went through all the known private addresses (subnet 192.168.0.0/24, 10.0.0.0/8, etc.) checking them for the presence of a .git folder and found one internal repository containing some code.

This is how Team Foundation Server can be used to retrieve proprietary code from private corporate subnets.

TeamCity

TeamCity is about deployment. The program allows you to automatically deploy applications: when you write some code and commit it, it goes to TeamCity and is deployed on a server. In some cases, you code immediately goes to production.

The platform uses so-called artifacts: they represent any changes submitted to TeamCity. It includes a standard guest account; using it, you log in and review open artifacts in some projects.

The point is that you can use this guest account to manipulate the API that provides information about closed projects, including those inaccessible to the guest. For instance, this API makes it possible to see build Ids; using them, you can get a list of all related artifacts and direct links to them.

Then you use these direct links to download artifacts. Only Base64- (basic-) authentication can stop an attacker, i.e. there is no role-based access model there.

Docker

Docker (and LXD) are too popular to omit them. Just to remind: Docker is a containerization tool making it possible to deploy apps in an isolated box.

Of course, you can escape from Docker if you want, but I am going to demonstrate one widespread misconfig that allows to manipulate the host system without even leaving your container.

If the docker.sock file is forwarded to the container, then you can use it to directly access the Docker API on the host. In other words, you use the standard curl to manipulate the API, create new containers, etc. The problem occurs in the following startup line:

docker run -v /var/run/docker.sock:/var/run/docker.sock debian ls

The string that starts curl for interaction with the API is as follows:

curl -s --unix-socket /var/lib/lxd/unix.socket a/1.0/containers/
{
"type": "async",
"status": "Operation created",
"status_code": 100,
"metadata": {
"id": "439bf4a1-e056-4b76-86ad-bff06169fce1",
"class": "task",
"created_at": "2021-04-18T22:56:22.590239576

Below is an example with LXD. Everything is the same, and the only differences are that the Docker API is on port 2375; LXD on port 8443; and a different path has to be specified when you use a Unix socket.

curl -s --unix-socket /var/lib/lxd/unix.socket a/1.0/containers/
{
"type": "async",
"status": "Operation created",
"status_code": 100,
"metadata": {
"id": "439bf4a1-e056-4b76-86ad-bff06169fce1",
"class": "task",
"created_at": "2016-04-18T22:56:22.590239576

GitLab

The first issue relates to source code interception from other runners. As you are aware, these runners use Docker, which automatically transfers the vulnerabilities discussed in the previous section to GitLab CI runners.

To save time, let’s go straight to the exploitation mechanisms that are more than real (provided that you can manipulate scripts in runners).

  1. Scan your subnet for port 2375 (to find other runners). If this isn’t possible, search for docker.sock;
  2. Access the API and execute your commands in the discovered containers; and 
  3. Retrieve the code from them.

Simple, isn’t it?

Access permissions in GitLab are a big mystery because anyone can read snippets using the API. Snippets are code chunks left by someone and apparently used somewhere. Why not grab them?

The API also discloses other interesting stuff. For instance, you can extract project metadata (authors, creation dates, descriptions, etc.) without any authentication.

Project API
Project API

Use wfuzz to search for projects as follows:

wfuzz -c -z range,1-10000 --sc=200 https://gitlab.company.hack/api/v4/projects/FUZZ
Wfuzz found something
Wfuzz found something

Another GitLab issue is that it allows you to list all of its users using the same buggy API.

https://gitlab.company.hack/api/v4/users/
https://gitlab.company.hack/api/v4/users/
https://gitlab.company.hack/api/v4/users/2
https://gitlab.company.hack/api/v4/users/2

info

The trick is still working, you can go to the official GitLab site and try it.

In addition, you can make the request https://gitlab.company.hack/%username%.keys (where %username% is the nickname of the target user) to get the public key of that user (provided that the user has configured these keys).

Too bad, you have to know the user’s name; so, this trick is unsuitable for full-scale enumeration.

SaltStack

SaltStack is a centralized command execution system for multiple servers.

Its architecture includes a master server and so-called minions, and you distribute the required commands to them.

There are three authentication methods in SaltStack: login/password, plain text (from a file), and LDAP. LDAP authentication is of utmost interest for pentesters.

Once I noticed that if you pass an empty password when you log in over LDAP, the authentication will be successful, and a valid X-Auth-Token will be returned in the response. In other words, you gain all the permissions and can do whatever you want.

curl -si POST https://saltstack.company.hack/login -H "Accept: application/json" -d username="<LDAP LOGIN>" -d password="" -d eauth='ldap'

info

The issue was reported to the developers and fixed, its ID is CVE-2018-15751.

It’s very easy to exploit this bug, and its impact is gross: you can execute code on all minions.

  1. Use the access token mentioned at the beginning of this section to get the list of keys and servers:

    curl -i -H 'X-Auth-Token: 694a7dfa16.........cacfcb0d26650d21bd' https://saltstack.company.hack/keys
  2. Execute the command on minions:

    curl -i -H 'X-Auth-Token: 694a7dfa168........cfcb0d26650d21bd' -H 'Content-type: application/json' https://<host>/ -d '[{"client":"local", "tgt":"*", "fun":"cmd.run", "kwarg":{"cmd":"id"}, "eauth":"auto"}]'
    I am root
    I am root

As you can see, on some minions, you immediately gain the root privileges. An excellent foothold for further attacks!

Sentry

Sentry is a centralized error storage system. You connect a project (or projects) to it, and if any errors occur in these projects, you use Sentry to track these errors.

The system has a guest account, but it’s useless for hacking purposes.

Guest account in Sentry
Guest account in Sentry

However, if you log in to the guest account and follow the direct URL, you can create a new token for yourself with any permissions.

Keys
Keys

Using this token, you can get the keys and do anything you want: read errors, control projects, etc.

Conclusions

Manufacturing software is plagued by numerous bugs and misconfigs, and it’s imperative for a pentester to know at least the most common ones. The more examples you review, the higher are the chances that you find something. Good luck and may the Force be with you!


Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>