Security hack when using aliases

Hello all,

Today i discover some hack.

I have user, that is allowed to do everything on abc_* indices.

I have another indices like foo_, bar_, those should stay private to the users.

When user create alias abc_something and point it to the foo_something, then he can read the whole index.

Is this known or not ? I see is as a big issue.

Im using the elasticsearch 6.5.0 and readonlyrest 1.18.9.

Regards,
Denis.

Hi @Sinedko,

I understand your case as follows:

- name: "admin block"
  auth_key: admin:admin

- name: "user1 block"
  auth_key: user1:pass
  indices: [abc_*] 

ES has defined following indices:
foo_ & bar_ & abc_something

User1 should not have an access to the first two indices.

Now, indices rules can have:

  • index names
  • index patterns
  • alias names
  • alias patterns

At the moment ROR doesn’t distinguish if configured abc_* is alias pattern or index pattern.

So, when your admin adds alias:

curl -kv -u admin:admin -XPOST -H "Content-Type: application/json" "http://localhost:9200/_aliases" -d '{"actions":[{"add":{"index":"foo_","alias":"abc_something"}}]}'

he is allowed to do so (first block matched) - the alias is created and now user1 has access to foo_ though abc_something because abc_something matches abc_* pattern.

This is correct and desired behaviour.

Notice that, at the same time, user1 is not able to create such alias:

curl -kv -u user1:pass -XPOST -H "Content-Type: application/json" "http://localhost:9200/_aliases" -d '{"actions":[{"add":{"index":"foo_","alias":"abc_something"}}]}'

We should see 403 as a response.

Hmm, i will review that tommorow again, but im pretty sure that user1 is allowed to create that alias, this is my concern, this is because i wrote this.

I know that I can restrict some actions to do not create aliases but still.

But I see there is other issue. User1 can do this:

curl -kv -u admin:admin -XPOST -H "Content-Type: application/json" "http://localhost:9200/_aliases" -d '{"actions":[{"add":{"index":"abc_something","alias":"not_allowed_alias"}}]}'

There is no check if user1 can create alias with not_allowed_alias. He should not be able to do that, because there is no such name in indices rule array.

But I think this is not so scary scenario (don’t worry, we will fix it), because then user1 won’t be able to use not_allowed_alias. But by doing this, he can accidentally give another user access to abc_something, which has not_allowed_alias configured.

Ok so i investigated this more with our developers and we are pretty sure that user1 can create whatever alias and point it to the whatever index and then he can use this alias. So in other words, he can have access to everything if he knows this flaw.

So in our defined case the user1 succefully created the alias abc_something and pointed it to the foo_

Regards,
Denis.

And if the user create this alias succesfuly, after that he can see the foo index also in GET _cat/indices

@Sinedko

please take a look at my tests:

readonlyrest.yml

readonlyrest:

  access_control_rules:
    - name: "CONTAINER ADMIN"
      verbosity: "error"
      type: "allow"
      auth_key: "admin:container"

    - name: "user1 block"
      auth_key: user1:pass
      indices: [abc_*]

Admin creates indices:

curl -v -u admin:container -XPUT -H "Content-Type: application/json" "http://127.0.0.1:9200/foo_/_doc/1" -d '{"example":"test"}
{"_index":"foo_","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}

curl -u admin:container -XPUT -H "Content-Type: application/json" "http://127.0.0.1:9200/bar_/_doc/1" -d '{"example":"test"}'                            
{"_index":"bar_","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}

curl -u admin:container -XPUT -H "Content-Type: application/json" "http://127.0.0.1:9200/abc_something/_doc/1" -d '{"example":"test"}'                   
{"_index":"abc_something","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}

curl -u admin:container "http://localhost:9200/_cat/indices"                                                                                             
yellow open abc_something 75UfaNFXSgCnet_MkoadsQ 1 1 1 0 3.4kb 3.4kb
yellow open bar_          vUKExE2WRoKdR5lZl6kEzg 1 1 1 0 3.4kb 3.4kb
yellow open foo_          mK8yElFNR7uLyZaCzZkudQ 1 1 1 0 3.4kb 3.4kb

curl -u admin:container "http://localhost:9200/_cat/aliases"
// empty response

User1 doesn’t have an access to bar_ and foo_:

curl -u user1:pass "http://localhost:9200/_cat/indices"                                                                                               
yellow open abc_something 75UfaNFXSgCnet_MkoadsQ 1 1 1 0 3.4kb 3.4kb

User1 tries to create abc_alias to one of non-allowed indices (eg. foo_):

first method:

curl -u user1:pass -XPOST -H "Content-Type: application/json" "http://localhost:9200/_aliases" -d '{"actions":[{"add":{"index":"foo_","alias":"abc_alias"}}]}'
{"error":{"root_cause":[{"reason":"forbidden","due_to":["OPERATION_NOT_ALLOWED"]}],"reason":"forbidden","due_to":["OPERATION_NOT_ALLOWED"],"status":401}}

[2020-07-02T15:51:07,797][INFO ][t.b.r.a.l.AccessControlLoggingDecorator] [n1_it] FORBIDDEN by default req={  ID:1036056043-82774905#183,  TYP:IndicesAliasesRequest,  CGR:N/A,  USR:user1 (attempted),  BRS:true,  KDX:null,  ACT:indices:admin/aliases,  OA:127.0.0.1/32,  XFF:null,  DA:::1/32,  IDX:foo_,  MET:POST,  PTH:/_aliases,  CNT:{"actions":[{"add":{"index":"foo_","alias":"abc_alias"}}]},  HDR:Accept=*/*, Authorization=Basic dXNlcjE6cGFzcw==, Content-Length=58, Content-Type=application/json, Host=localhost:9200, User-Agent=curl/7.54.0,  HIS:[CONTAINER ADMIN-> RULES:[auth_key->false], RESOLVED:[indices=foo_]], [user1 block-> RULES:[auth_key->true, indices->false], RESOLVED:[user=user1;indices=foo_]]  }

second method:

curl -u user1:pass -XPUT "http://localhost:9200/foo_/_alias/abc_alias2"                                                                                  
{"error":{"root_cause":[{"reason":"forbidden","due_to":["OPERATION_NOT_ALLOWED"]}],"reason":"forbidden","due_to":["OPERATION_NOT_ALLOWED"],"status":401}}

[2020-07-02T16:03:46,233][INFO ][t.b.r.a.l.AccessControlLoggingDecorator] [n1_it] FORBIDDEN by default req={  ID:61700229-1673001928#452,  TYP:IndicesAliasesRequest,  CGR:N/A,  USR:user1 (attempted),  BRS:true,  KDX:null,  ACT:indices:admin/aliases,  OA:127.0.0.1/32,  XFF:null,  DA:::1/32,  IDX:foo_,  MET:PUT,  PTH:/foo_/_alias/abc_alias2,  CNT:<N/A>,  HDR:Accept=*/*, Authorization=Basic dXNlcjE6cGFzcw==, Host=localhost:9200, User-Agent=curl/7.54.0, content-length=0,  HIS:[CONTAINER ADMIN-> RULES:[auth_key->false], RESOLVED:[indices=foo_]], [user1 block-> RULES:[auth_key->true, indices->false], RESOLVED:[user=user1;indices=foo_]]  }

As we can see indices rule reject the request.
So, I cannot confirm your objections. It works as expected. User still cannot access forbidden indices.

But user1 is allowed to create alias for index which he is allowed to see:

curl -u user1:pass -XPOST -H "Content-Type: application/json" "http://localhost:9200/_aliases" -d '{"actions":[{"add":{"index":"abc_something","alias":"abc_alias"}}]}'
{"acknowledged":true}

curl -u user1:pass "http://localhost:9200/_cat/aliases"                                                                                                  
abc_alias abc_something - - - -

curl -u user1:pass "http://localhost:9200/abc_alias"                                                                                                     
{"abc_something":{"aliases":{"abc_alias":{}},"mappings":{"properties":{"example":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}},"settings":{"index":{"creation_date":"1593697592955","number_of_shards":"1","number_of_replicas":"1","uuid":"75UfaNFXSgCnet_MkoadsQ","version":{"created":"7070199"},"provided_name":"abc_something"}}}}

As I mentioned before, there is one problem. User can create alias with forbidden name (should be rejected by indices rule):

curl -u user1:pass -XPOST -H "Content-Type: application/json" "http://localhost:9200/_aliases" -d '{"actions":[{"add":{"index":"abc_something","alias":"should_be_forbidden"}}]}'
{"acknowledged":true}

curl -u user1:pass "http://localhost:9200/_cat/aliases"
abc_alias           abc_something - - - -
should_be_forbidden abc_something - - - -

curl -u user1:pass "http://localhost:9200/should_be_forbidden"                                                                                    
{"abc_something":{"aliases":{"abc_alias":{},"should_be_forbidden":{}},"mappings":{"properties":{"example":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}},"settings":{"index":{"creation_date":"1593697592955","number_of_shards":"1","number_of_replicas":"1","uuid":"75UfaNFXSgCnet_MkoadsQ","version":{"created":"7070199"},"provided_name":"abc_something"}}}}

As you can see I’m not able to reproduce the issue you’ve reported. Maybe your case is a little bit different, so I’d be nice if you could analyse the scenario above once again and compare with your tests scenario.

Let me know. If I’ll be able to reproduce the issue, fixing should not be a problem.

Cheers

Okay, so i did the test again, we are using kibana for that test.

We have following access block for that user:

- name: "USER - user1 - Kibana"
  type: allow
  auth_key: user1:pass
  indices: [".kibana*"]
  actions: ["indices:data/read/*","indices:data/write/*"] 

- name: "USER - user1"
  type: allow
  auth_key: user1:pass
  indices: ["abc_*","xyz_*"]

Indices response:

GET _cat/indices

green open xyz_user_v2          UrydIWcYTCWZJEG_HdM6EA 5 1    5271    741  8.6mb   4.4mb
green open abc_master_v1        wVwy3MpTRqKpDU4Co9CQKQ 3 1 1348187 389481    2gb     1gb

Aliases response:

GET _cat/aliases

abc_master_live_alias            abc_master_v1        - - -

Creating alias that user should not create even with not allowed name:

POST _aliases 
{
  "actions": [
    {
      "add": {
        "index": "foo_foo",
        "alias": "bar_test_alias"
      }
    }
  ]
}

{
  "acknowledged" : true
}

Deleting that alias:

DELETE foo_foo/_alias/bar_test_alias

{
  "acknowledged" : true
}

Creating new alias with allowed name:

POST _aliases 
{
  "actions": [
    {
      "add": {
        "index": "foo_foo",
        "alias": "abc_test_alias"
      }
    }
  ]
}

{
  "acknowledged" : true
}

Aliases response:

GET _cat/aliases

abc_master_live_alias            abc_master_v1        - - -
abc_test_alias                   foo_foo            - - -

Getting all documents:

GET abc_test_alias/_search

all documents from foo_foo

Deleting that alias:

DELETE foo_foo/_alias/abc_test_alias

{
  "acknowledged" : true
}

All of this is achieved by user1. We are using elastic 6.5.0 and RoR 1.18.9-re1 (just discovered we are using pre version, it can be because of that ? )

Hope it helps.

@Sinedko oh, wow. I tested it with 1.20.0 (I did’t notice that you’ve mentioned what version you use in first post ;)) This one was probably fixed in 1.19.0. But I recommend to update ROR to the newest version.

sure, will update it to the latest, thanks.

1 Like