Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication Bypass in GraphQL Queries for Users/Tokens Lacking Permissions #16292

Closed
kiraum opened this issue May 24, 2024 · 8 comments · Fixed by #17244
Closed

Authentication Bypass in GraphQL Queries for Users/Tokens Lacking Permissions #16292

kiraum opened this issue May 24, 2024 · 8 comments · Fixed by #17244
Assignees
Labels
severity: high Completely breaks certain functions, or substantially degrades performance application-wide status: accepted This issue has been accepted for implementation type: bug A confirmed report of unexpected behavior in the application

Comments

@kiraum
Copy link

kiraum commented May 24, 2024

Deployment Type

Self-hosted

NetBox Version

v4.0.3

Python Version

3.11

Steps to Reproduce

Test user does not have any permissions associated with it (I am using the admin token to make the query):

% curl -s -H "Authorization: Token 28a079fb8aa5e107583583dbeb7deb027121a15a" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/api/users/users/\?username\=test | jq -r '.results[] | .username, .permissions'
test
[]

Test user token:

% curl -s -H "Authorization: Token 28a079fb8aa5e107583583dbeb7deb027121a15a" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/api/users/tokens/ | jq -r '.results[] | select(.user.username == "test") | .user.username, .key'
test
4f745a0b1c13168c95883ee004f17df7ef96e42a

Now using the test user token without any permissions via Graphql:

% curl -s -H "Authorization: Token 4f745a0b1c13168c95883ee004f17df7ef96e42a" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/graphql/ \
--data '{"query": "query {asn(id:1) {asn}}"}'
{"data": {"asn": {"asn": 666}}}%
% curl -s -H "Authorization: Token 4f745a0b1c13168c95883ee004f17df7ef96e42a" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/graphql/ \
--data '{"query": "query {provider(id:1) {name}}"}'
{"data": {"provider": {"name": "test-p"}}}%

In my understanding, if a user/token has no permissions, it should reject by default.

Version:

% curl -s -H "Authorization: Token 28a079fb8aa5e107583583dbeb7deb027121a15a" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/api/status/ | jq -r '.'
{
  "django-version": "5.0.6",
  "installed-apps": {
    "debug_toolbar": "4.3.0",
    "django_filters": "24.2",
    "django_prometheus": "2.3.1",
    "django_rq": "2.10.2",
    "django_tables2": "2.7.0",
    "drf_spectacular": "0.27.2",
    "drf_spectacular_sidecar": "2024.5.1",
    "mptt": "0.16.0",
    "rest_framework": "3.15.1",
    "social_django": "5.4.1",
    "taggit": "5.0.1",
    "timezone_field": "6.1.0"
  },
  "netbox-version": "4.0.3",
  "plugins": {},
  "python-version": "3.11.6",
  "rq-workers-running": 2
}

This may be related with the issue#16228.

Expected Behavior

The system should reject GraphQL queries from users or tokens that do not have the necessary permissions.

Observed Behavior

During testing, it was discovered that if a user or token lacks permissions, the system does not enforce authentication for GraphQL queries. This was observed using a test user with no permissions, where GraphQL queries still returned sensitive data.

GraphQL queries are processed and data is returned even when the user or token has no permissions.

Further investigation is needed to confirm the scope and cause of the authentication bypass. Additional details will be provided upon request.

@kiraum kiraum added status: needs triage This issue is awaiting triage by a maintainer type: bug A confirmed report of unexpected behavior in the application labels May 24, 2024
@kiraum kiraum changed the title Authentication Bypass in GraphQL Queries for Users/Tokens Lackin Permissions Authentication Bypass in GraphQL Queries for Users/Tokens Lacking Permissions May 24, 2024
@arthanson
Copy link
Collaborator

@kiraum are you sure this is NetBox 4.0.3? This sounds like #16228 which was in NetBox 4.0.2 but fixed in 4.0.3

@kiraum
Copy link
Author

kiraum commented May 24, 2024

@arthanson, I was running 4.0.2, but pulled new image, and via API/UI, I do see version 4.0.3:
image
image
image

unit@45f666ac0bd0:/opt/netbox$ head -3 docs/release-notes/version-4.0.md
# NetBox v4.0

## v4.0.3 (2024-05-22)
docker image inspect f876940be42f | jq -r '.[].Config.Labels."netbox.git-ref"'
3f345cdbee016243ab5162456ea5415472a2f2df

3f345cd

Should I rely on those versions or do we have a better (more reliable) way to check it?

I've destroyed the environment, and recreated to see if there wasn't left overs from the old build, but still the same.

@DanSheps DanSheps added status: needs owner This issue is tentatively accepted pending a volunteer committed to its implementation severity: medium Results in substantial degraded or broken functionality for specfic workflows status: revisions needed This issue requires additional information to be actionable and removed status: needs triage This issue is awaiting triage by a maintainer status: needs owner This issue is tentatively accepted pending a volunteer committed to its implementation severity: medium Results in substantial degraded or broken functionality for specfic workflows labels May 27, 2024
@DanSheps
Copy link
Member

DanSheps commented May 27, 2024

I just tested this. I am not able to reproduce.

Please provide more comprehensive steps including what objects to create for testing against (I see you are querying ASN's but I don't see how to create that ASN specifically)

@kiraum
Copy link
Author

kiraum commented May 27, 2024

@DanSheps , thanks for checking, follow the step by step:

» git clone [email protected]:netbox-community/netbox-docker.git
» cd netbox-docker
» tee docker-compose.override.yml <<EOF
services:
  netbox:
    ports:
      - 8000:8080
EOF
services:
  netbox:
    ports:
      - 8000:8080
» vi docker-compose.yml
» git diff
diff --git a/docker-compose.yml b/docker-compose.yml
index 958561f..afc6905 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,6 +1,6 @@
 services:
   netbox: &netbox
-    image: docker.io/netboxcommunity/netbox:${VERSION-v4.0-2.9.1}
+    image: docker.io/netboxcommunity/netbox:${VERSION-v4.0.3-2.9.1}
     depends_on:
     - postgres
     - redis
» docker-compose pull
» docker-compose up -d
» docker image ls
REPOSITORY               TAG            IMAGE ID       CREATED      SIZE
netboxcommunity/netbox   v4.0.3-2.9.1   f876940be42f   3 days ago   707MB
redis                    7-alpine       cdb453bbf446   4 days ago   42MB
postgres                 16-alpine      1220ef94dbc4   5 days ago   250MB
» docker image inspect f876940be42f | jq -r '.[].Config.Labels."netbox.git-ref"'
3f345cdbee016243ab5162456ea5415472a2f2df
» docker compose exec netbox /opt/netbox/netbox/manage.py createsuperuser
# created an user test via UI, without group/permissions (not superadmin)
# created token b97beb333cf5f381025440a4037b3284373b3ab3 associated with user test
# created a provider, just to have something to query

Here, I can get data without permissions set to the user:

» curl -s -H "Authorization: Token b97beb333cf5f381025440a4037b3284373b3ab3" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/graphql/ \
--data '{"query": "query {provider(id:1) {name}}"}'
{"data": {"provider": {"name": "test-p"}}}%
» curl -s -H "Authorization: Token b97beb333cf5f381025440a4037b3284373b3ab3" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/api/status/ | jq '.'
{
  "django-version": "5.0.6",
  "installed-apps": {
    "debug_toolbar": "4.3.0",
    "django_filters": "24.2",
    "django_prometheus": "2.3.1",
    "django_rq": "2.10.2",
    "django_tables2": "2.7.0",
    "drf_spectacular": "0.27.2",
    "drf_spectacular_sidecar": "2024.5.1",
    "mptt": "0.16.0",
    "rest_framework": "3.15.1",
    "social_django": "5.4.1",
    "taggit": "5.0.1",
    "timezone_field": "6.1.0"
  },
  "netbox-version": "4.0.3",
  "plugins": {},
  "python-version": "3.11.6",
  "rq-workers-running": 1
}

If I try to get user details with the token without permissions, I do see a reject:

» curl -s -H "Authorization: Token b97beb333cf5f381025440a4037b3284373b3ab3" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/api/users/users/\?username\=test
{"detail":"You do not have permission to perform this action."}%

Using a superuser token, just to show the current permissions and tokens:

» curl -s -H "Authorization: Token 6065f2a66b6b6e08c4ea68b07976e998f6b29a67" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/api/users/users/\?username\=test | jq '.'
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": 2,
      "url": "http://localhost:8000/api/users/users/2/",
      "display": "test",
      "username": "test",
      "first_name": "",
      "last_name": "",
      "email": "",
      "is_staff": false,
      "is_active": true,
      "date_joined": "2024-05-27T20:25:24.362362Z",
      "last_login": null,
      "groups": [],
      "permissions": []
    }
  ]
}

» curl -s -H "Authorization: Token 6065f2a66b6b6e08c4ea68b07976e998f6b29a67" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/api/users/tokens/ | jq -r '.'
{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": 1,
      "url": "http://localhost:8000/api/users/tokens/1/",
      "display": "b97beb333cf5f381025440a4037b3284373b3ab3",
      "user": {
        "id": 2,
        "url": "http://localhost:8000/api/users/users/2/",
        "display": "test",
        "username": "test"
      },
      "created": "2024-05-27T20:26:12.198312Z",
      "expires": null,
      "last_used": "2024-05-27T20:31:55.628047Z",
      "key": "b97beb333cf5f381025440a4037b3284373b3ab3",
      "write_enabled": true,
      "description": "",
      "allowed_ips": []
    },
    {
      "id": 2,
      "url": "http://localhost:8000/api/users/tokens/2/",
      "display": "6065f2a66b6b6e08c4ea68b07976e998f6b29a67",
      "user": {
        "id": 1,
        "url": "http://localhost:8000/api/users/users/1/",
        "display": "admin",
        "username": "admin"
      },
      "created": "2024-05-27T20:36:55.815834Z",
      "expires": null,
      "last_used": "2024-05-27T20:38:40.961444Z",
      "key": "6065f2a66b6b6e08c4ea68b07976e998f6b29a67",
      "write_enabled": true,
      "description": "",
      "allowed_ips": []
    }
  ]
}

Thanks, and please let me know if you need something else.

@kiraum
Copy link
Author

kiraum commented May 28, 2024

Hi, I noticed that I left write enable during the latest test, follow the result with write_enable as false for the test token in the same environment/deploy:

» curl -s -H "Authorization: Token 6065f2a66b6b6e08c4ea68b07976e998f6b29a67" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/api/users/tokens/ | jq -r '.'
{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": 1,
      "url": "http://localhost:8000/api/users/tokens/1/",
      "display": "b97beb333cf5f381025440a4037b3284373b3ab3",
      "user": {
        "id": 2,
        "url": "http://localhost:8000/api/users/users/2/",
        "display": "test",
        "username": "test"
      },
      "created": "2024-05-27T20:26:12.198312Z",
      "expires": null,
      "last_used": "2024-05-27T20:31:55.628047Z",
      "key": "b97beb333cf5f381025440a4037b3284373b3ab3",
      "write_enabled": false,
      "description": "",
      "allowed_ips": []
    },
    {
      "id": 2,
      "url": "http://localhost:8000/api/users/tokens/2/",
      "display": "6065f2a66b6b6e08c4ea68b07976e998f6b29a67",
      "user": {
        "id": 1,
        "url": "http://localhost:8000/api/users/users/1/",
        "display": "admin",
        "username": "admin"
      },
      "created": "2024-05-27T20:36:55.815834Z",
      "expires": null,
      "last_used": "2024-05-28T04:34:33.458954Z",
      "key": "6065f2a66b6b6e08c4ea68b07976e998f6b29a67",
      "write_enabled": true,
      "description": "",
      "allowed_ips": []
    }
  ]
}

» curl -s -H "Authorization: Token b97beb333cf5f381025440a4037b3284373b3ab3" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
http://localhost:8000/graphql/ \
--data '{"query": "query {provider(id:1) {name}}"}'
{"data": {"provider": {"name": "test-p"}}}%

Copy link
Contributor

github-actions bot commented Jun 5, 2024

This is a reminder that additional information is needed in order to further triage this issue. If the requested details are not provided, the issue will soon be closed automatically.

@github-actions github-actions bot added the pending closure Requires immediate attention to avoid being closed for inactivity label Jun 5, 2024
@kiraum
Copy link
Author

kiraum commented Jun 5, 2024

@DanSheps do you need more details? Received a message from @github telling if details are not provided issue will be resolved.

@DanSheps DanSheps added status: under review Further discussion is needed to determine this issue's scope and/or implementation and removed status: revisions needed This issue requires additional information to be actionable pending closure Requires immediate attention to avoid being closed for inactivity labels Jun 7, 2024
@DanSheps DanSheps added status: accepted This issue has been accepted for implementation severity: high Completely breaks certain functions, or substantially degrades performance application-wide severity: medium Results in substantial degraded or broken functionality for specfic workflows and removed status: under review Further discussion is needed to determine this issue's scope and/or implementation severity: high Completely breaks certain functions, or substantially degrades performance application-wide severity: medium Results in substantial degraded or broken functionality for specfic workflows labels Aug 22, 2024
@DanSheps
Copy link
Member

Alright,

The fundamental issue is that for a single ASN query, we have:

class IPAMQuery:
    @strawberry.field
    def asn(self, id: int) -> ASNType:
        return models.ASN.objects.get(pk=id)

the models.ASN.objects.get(pk=id) is just simply not calling restrict. AFAIK, all models are impacted by this.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
severity: high Completely breaks certain functions, or substantially degrades performance application-wide status: accepted This issue has been accepted for implementation type: bug A confirmed report of unexpected behavior in the application
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants