gitlabrat 1.1__tar.gz → 1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -183,7 +183,7 @@ cython_debug/
183
183
  # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
184
184
  # and can be added to the global gitignore or merged into this file. However, if you prefer,
185
185
  # you could uncomment the following to ignore the entire vscode folder
186
- # .vscode/
186
+ .vscode/
187
187
 
188
188
  # Ruff stuff:
189
189
  .ruff_cache/
gitlabrat-1.3/PKG-INFO ADDED
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.4
2
+ Name: gitlabrat
3
+ Version: 1.3
4
+ Summary: LabRat: GitLab exploitation orchestrator
5
+ Project-URL: Homepage, https://github.com/JChamblee99/Labrat
6
+ Project-URL: Repository, https://github.com/JChamblee99/Labrat.git
7
+ Project-URL: Issues, https://github.com/JChamblee99/Labrt/issues
8
+ Author-email: John Chamblee <conway.py@proton.farm>
9
+ License: GPL-3.0
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Requires-Python: >=3.8
20
+ Requires-Dist: beautifulsoup4>=4.9.0
21
+ Requires-Dist: gitpython>=3.1.0
22
+ Requires-Dist: python-gitlab>=3.0.0
23
+ Requires-Dist: requests>=2.25.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ <div align="center">
27
+
28
+ # Labrat
29
+
30
+ **Living rent-free in the walls of your GitLab.**
31
+
32
+ [![PyPI - Version](https://img.shields.io/pypi/v/gitlabrat)](https://pypi.org/project/gitlabrat/)
33
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/gitlabrat)](https://pypi.org/project/gitlabrat/)
34
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
35
+ [![GitHub last commit](https://img.shields.io/github/last-commit/JChamblee99/LabRat)](https://github.com/JChamblee99/LabRat/commits/main)
36
+
37
+ </div>
38
+
39
+ ---
40
+
41
+ ## Overview
42
+
43
+ Labrat automates workflows for gaining and maintaining access to GitLab instances and CI/CD pipelines.
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install gitlabrat
49
+ ```
50
+
51
+ ## Quick Start
52
+
53
+ ```bash
54
+ # Authenticate to a GitLab instance
55
+ labrat auth -t https://gitlab.example.com -u username -p password
56
+
57
+ # List authenticated agents
58
+ labrat agents ls
59
+ ```
@@ -0,0 +1,34 @@
1
+ <div align="center">
2
+
3
+ # Labrat
4
+
5
+ **Living rent-free in the walls of your GitLab.**
6
+
7
+ [![PyPI - Version](https://img.shields.io/pypi/v/gitlabrat)](https://pypi.org/project/gitlabrat/)
8
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/gitlabrat)](https://pypi.org/project/gitlabrat/)
9
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
10
+ [![GitHub last commit](https://img.shields.io/github/last-commit/JChamblee99/LabRat)](https://github.com/JChamblee99/LabRat/commits/main)
11
+
12
+ </div>
13
+
14
+ ---
15
+
16
+ ## Overview
17
+
18
+ Labrat automates workflows for gaining and maintaining access to GitLab instances and CI/CD pipelines.
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install gitlabrat
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```bash
29
+ # Authenticate to a GitLab instance
30
+ labrat auth -t https://gitlab.example.com -u username -p password
31
+
32
+ # List authenticated agents
33
+ labrat agents ls
34
+ ```
@@ -21,7 +21,7 @@ def build_parser(parsers):
21
21
 
22
22
  def handle_list_args(args):
23
23
  # Prepare table
24
- headers = ["Host", "ID", "Username", "Name", "Authenticated", "Is Admin", "Is Bot", "Password", "Private Token"]
24
+ headers = ["Host", "ID", "Username", "Name", "Is Authenticated", "Is Admin", "Is Bot", "Password", "Private Token"]
25
25
  data = []
26
26
 
27
27
  for agent in args.controller.list(args.filter):
@@ -13,6 +13,7 @@ def build_parser(parsers):
13
13
  parser.add_argument("-r", "--re-auth", action="store_true", help="Re-authenticate with stored credentials")
14
14
  parser.add_argument("-n", "--token-name", required=False, help="Name for the access token", default="private token")
15
15
  parser.add_argument("-s", "--scopes", required=False, help="Comma-separated list of scopes for the access token", default="api,read_repository,write_repository")
16
+ parser.add_argument("-a", "--access-token", required=False, help="Access token for authentication")
16
17
 
17
18
  parser.set_defaults(func=handle_args, _parser=parser)
18
19
  parser.set_defaults(controller=Auth())
@@ -20,7 +21,7 @@ def build_parser(parsers):
20
21
 
21
22
  def handle_args(args):
22
23
  has_targets = args.target or args.target_file
23
- has_credentials = (args.username and args.password) or args.combo_list
24
+ has_credentials = (args.username and args.password) or args.combo_list or args.access_token
24
25
  can_auth = has_targets and has_credentials
25
26
 
26
27
  if args.re_auth or can_auth:
@@ -53,7 +54,7 @@ def auth(args):
53
54
  scopes = args.scopes.split(",") if args.scopes else []
54
55
 
55
56
  # Iterate over each user and target
56
- for agent, err in args.controller.reauth(token_name=args.token_name, token_scopes=scopes, targets=targets, users=[user[0] for user in users]) if args.re_auth else args.controller.auth(targets, users, token_name=args.token_name, token_scopes=scopes, use_ldap=args.use_ldap):
57
+ for agent, err in args.controller.reauth(token_name=args.token_name, token_scopes=scopes, targets=targets, users=[user[0] for user in users]) if args.re_auth else args.controller.auth(targets, users, token_name=args.token_name, token_scopes=scopes, private_token=args.access_token, use_ldap=args.use_ldap):
57
58
  if err:
58
59
  print(f"[-] Authentication failed for {agent.label}: {err}")
59
60
  else:
@@ -0,0 +1,55 @@
1
+ import datetime
2
+
3
+ import gitlab
4
+
5
+ from labrat.cli import common
6
+ from labrat.controllers.groups import Groups
7
+
8
+ def build_parser(parsers):
9
+ parser = parsers.add_parser("groups", help="Manage GitLab groups")
10
+ subparsers = parser.add_subparsers(dest="command", required=True)
11
+
12
+ list_parser = common.add_filtered_parser(subparsers, "list", handle_list_args, aliases=["ls"], help="List GitLab groups", filter_required=False)
13
+ list_parser.add_argument("-m", "--min-access-level", required=False, type=int, help="Minimum access level to filter groups", default=0)
14
+ list_parser.add_argument("-u", "--user", action="append", required=False, help="Filter agent performing action")
15
+
16
+ create_token_parser = common.add_filtered_parser(subparsers, "create_token", handle_create_token_args, help="Create an access token", filter_required=True)
17
+ create_token_parser.add_argument("-l", "--access-level", required=False, type=int, help="Access level for the access token", default=50)
18
+ create_token_parser.add_argument("-d", "--days", required=False, type=int, help="Number of days until the token expires", default=60)
19
+ create_token_parser.add_argument("-n", "--token-name", required=False, help="Name for the access token", default="project_bot")
20
+ create_token_parser.add_argument("-s", "--scopes", required=False, help="Comma-separated list of scopes for the access token", default="api,read_repository,write_repository")
21
+ create_token_parser.add_argument("-u", "--user", action="append", required=False, help="Filter agent performing action")
22
+
23
+
24
+ parser.set_defaults(controller=Groups())
25
+ return subparsers
26
+
27
+ def handle_list_args(args):
28
+ headers = ["Host", "ID", "Name", "Access Level", "Agents"]
29
+ data = []
30
+
31
+ for group in args.controller.list(args.filter, args.user):
32
+ # color each username by that agent's access level (higher -> brighter)
33
+ usernames = []
34
+ for agent in group.agents:
35
+ colored_username = common.color_access_level(agent.access_level, f"{agent.username} ({agent.access_level})")
36
+ usernames.append(colored_username)
37
+
38
+ # Add group information to data table
39
+ data.append([
40
+ group.host,
41
+ group.id,
42
+ group.name,
43
+ f"{group.access_level} ({gitlab.const.AccessLevel(group.access_level).name.lower()})",
44
+ ", ".join(usernames)
45
+ ])
46
+
47
+ common.print_table(headers, data)
48
+
49
+ def handle_create_token_args(args):
50
+ expiration = (datetime.datetime.now() + datetime.timedelta(days=args.days)).strftime("%Y-%m-%d")
51
+ for group, agent, err in args.controller.create_token(args.token_name, args.access_level, args.scopes.split(","), expiration, args.user, args.filter):
52
+ if err:
53
+ print(f"[!] Failed to create access token for {group.web_url}: {err}")
54
+ else:
55
+ print(f"[+] Successfully created access token for {group.web_url}: {agent.private_token}")
@@ -41,7 +41,7 @@ class Agents:
41
41
  continue
42
42
 
43
43
  try:
44
- agent.add_ssh_key(title, key)
44
+ agent.gitlab.user.keys.create({'title': title, 'key': key})
45
45
  yield agent, None
46
46
  except Exception as e:
47
47
  yield agent, e
@@ -6,7 +6,7 @@ class Auth:
6
6
  def __init__(self):
7
7
  self.config = Config()
8
8
 
9
- def auth(self, targets, users, token_name, token_scopes, use_ldap=False):
9
+ def auth(self, targets, users, token_name, token_scopes, private_token, use_ldap=False):
10
10
  """Authenticate and create a PAT for each user on each target.
11
11
 
12
12
  Keyword arguments:
@@ -19,10 +19,14 @@ class Auth:
19
19
 
20
20
  for username, password in users:
21
21
  for target in targets:
22
- agent = Agent(target, use_ldap, username, password)
23
22
  try:
24
- agent.login()
25
- agent.auth(private_token=agent.create_pat(token_name, token_scopes))
23
+ if private_token:
24
+ agent = Agent(url=target, username=username, private_token=private_token)
25
+ else:
26
+ agent = Agent(url=target, username=username, password=password, use_ldap=use_ldap)
27
+ agent.login()
28
+ agent.auth(private_token=agent.create_pat(token_name, token_scopes))
29
+
26
30
  self.config[agent.section] = agent.to_dict()
27
31
  yield agent, None
28
32
  except Exception as e:
@@ -0,0 +1,81 @@
1
+ import copy
2
+
3
+ from labrat.core.agent import Agent
4
+ from labrat.core.config import Config
5
+ from labrat.core.utils import obj_filter
6
+
7
+ class Groups:
8
+ def __init__(self):
9
+ self.config = Config(authed_only=True)
10
+
11
+ def list(self, filter=None, agent_filter=None, min_level=0):
12
+ """List groups accessible by agents.
13
+
14
+ Keyword arguments:
15
+ - filter: Regex filter and field selection passed to `utils.obj_filter()` for group objects
16
+ - agent_filter: Regex filter and field selection passed to `utils.obj_filter()` for agent objects
17
+ - min_level: Minimum desired access level for agent to return a group
18
+ """
19
+
20
+ group_map = {}
21
+ for section, agent in self.config.filter(agent_filter):
22
+ groups = agent.gitlab.groups.list(all=True)
23
+ for group in groups:
24
+ access_level = self.get_group_access_level(agent, group)
25
+ if access_level < min_level:
26
+ continue
27
+
28
+ # Agent enrichment with shallow copy
29
+ agent_copy = copy.copy(agent)
30
+ agent_copy.access_level = access_level
31
+
32
+ # Group enrichment
33
+ group.host = agent.host
34
+
35
+ # Add group and agent to map
36
+ if group.web_url not in group_map:
37
+ group.agents = [agent_copy]
38
+ group.access_level = access_level
39
+ group_map[group.web_url] = group
40
+ else:
41
+ group_map[group.web_url].agents.append(agent_copy)
42
+ group_map[group.web_url].access_level = max(group_map[group.web_url].access_level, access_level)
43
+
44
+ return [group for group in group_map.values() if not filter or obj_filter(group, filter)]
45
+
46
+ def create_token(self, name, access_level, scopes, expires_at, agent_filter=None, filter=None):
47
+ """ Create Group Access Tokens.
48
+
49
+ Keyword arguments:
50
+ - name: Name of the access token.
51
+ - access_level: Access level of the created PAT.
52
+ - scopes: List of scopes for the created PAT. (e.g. `['read_repository', 'write_repository']`)
53
+ - expires_at: ISO format expiration date for the created PAT.
54
+ - agent_filter: Regex filter and field selection passed to `utils.obj_filter()` for agent objects.
55
+ - filter: Regex filter and field selection passed to `utils.obj_filter()` for group objects.
56
+ """
57
+
58
+ for group in self.list(filter=filter, agent_filter=agent_filter, min_level=50):
59
+ agent = group.agents[0]
60
+
61
+ try:
62
+ req = group.access_tokens.create({
63
+ 'name': name,
64
+ 'access_level': access_level,
65
+ 'scopes': scopes,
66
+ 'expires_at': expires_at
67
+ })
68
+
69
+ section = f"{req.user_id}_{group.id}@{agent.host}"
70
+ group_agent = Agent(agent.url, username=name, private_token=req.token, section=section)
71
+ self.config[section] = group_agent.to_dict()
72
+
73
+ yield group, group_agent, None
74
+ except Exception as e:
75
+ yield group, None, e
76
+
77
+ def get_group_access_level(self, agent, group):
78
+ try:
79
+ return group.members.get(agent.id).access_level
80
+ except Exception as e:
81
+ return 60 if agent.is_admin else 0
@@ -85,9 +85,6 @@ class Agent:
85
85
  return response_json.get("token")
86
86
  except ValueError:
87
87
  return None
88
-
89
- def add_ssh_key(self, title, key):
90
- self.gitlab.user.keys.create({'title': title, 'key': key})
91
88
 
92
89
  def to_dict(self):
93
90
  return {
@@ -1,6 +1,6 @@
1
1
  # labrat/main.py
2
2
  import argparse
3
- from labrat.cli import agents, auth, projects, users
3
+ from labrat.cli import agents, auth, projects, groups, users
4
4
 
5
5
  def main():
6
6
  parser = argparse.ArgumentParser(
@@ -9,7 +9,7 @@ def main():
9
9
  )
10
10
  subparsers = parser.add_subparsers(dest="command", required=True)
11
11
 
12
- for command in [agents, auth, projects, users]:
12
+ for command in [agents, auth, projects, groups, users]:
13
13
  command.build_parser(subparsers)
14
14
 
15
15
  args = parser.parse_args()
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "gitlabrat"
7
- version = "1.1"
7
+ version = "1.3"
8
8
  description = "LabRat: GitLab exploitation orchestrator"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -13,7 +13,7 @@ authors = [
13
13
  ]
14
14
  license = {text = "GPL-3.0"}
15
15
  classifiers = [
16
- "Development Status :: 3 - Alpha",
16
+ "Development Status :: 4 - Beta",
17
17
  "Intended Audience :: Developers",
18
18
  "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
19
19
  "Programming Language :: Python :: 3",
@@ -30,9 +30,9 @@ dependencies = [
30
30
  ]
31
31
 
32
32
  [project.urls]
33
- Homepage = "https://github.com/JChamblee99/LabRat"
34
- Repository = "https://github.com/JChamblee99/LabRat.git"
35
- Issues = "https://github.com/JChamblee99/LabRat/issues"
33
+ Homepage = "https://github.com/JChamblee99/Labrat"
34
+ Repository = "https://github.com/JChamblee99/Labrat.git"
35
+ Issues = "https://github.com/JChamblee99/Labrt/issues"
36
36
 
37
37
  [project.scripts]
38
38
  labrat = "labrat.main:main"
gitlabrat-1.1/PKG-INFO DELETED
@@ -1,72 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: gitlabrat
3
- Version: 1.1
4
- Summary: LabRat: GitLab exploitation orchestrator
5
- Project-URL: Homepage, https://github.com/JChamblee99/LabRat
6
- Project-URL: Repository, https://github.com/JChamblee99/LabRat.git
7
- Project-URL: Issues, https://github.com/JChamblee99/LabRat/issues
8
- Author-email: John Chamblee <conway.py@proton.farm>
9
- License: GPL-3.0
10
- License-File: LICENSE
11
- Classifier: Development Status :: 3 - Alpha
12
- Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
14
- Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Programming Language :: Python :: 3.11
19
- Requires-Python: >=3.8
20
- Requires-Dist: beautifulsoup4>=4.9.0
21
- Requires-Dist: gitpython>=3.1.0
22
- Requires-Dist: python-gitlab>=3.0.0
23
- Requires-Dist: requests>=2.25.0
24
- Description-Content-Type: text/markdown
25
-
26
- # LabRat
27
-
28
- LabRat is a GitLab exploitation orchestrator designed for security researchers and penetration testers.
29
-
30
- ## Features
31
-
32
- - **Authentication Management** - Authenticate to GitLab servers and manage access tokens
33
- - **Project Management** - List, clone, and manipulate GitLab projects
34
- - **User Management** - Enumerate and create access tokens for GitLab users
35
- - **Agent Management** - Manage multiple GitLab agents and credentials
36
-
37
- ## Installation
38
-
39
- Install LabRat from PyPI:
40
-
41
- ```bash
42
- pip install gitlabrat
43
- ```
44
-
45
- ## Usage
46
-
47
- ```bash
48
- labrat --help
49
- ```
50
-
51
- ### Commands
52
-
53
- - `labrat auth` - Authenticate to GitLab server(s)
54
- - `labrat agents` - Manage GitLab agents
55
- - `labrat projects` - Manage GitLab projects
56
- - `labrat users` - Manage GitLab users
57
-
58
- ## Requirements
59
-
60
- - Python 3.8+
61
- - python-gitlab
62
- - GitPython
63
- - beautifulsoup4
64
- - requests
65
-
66
- ## License
67
-
68
- GNU General Public License v3.0 (GPL-3.0)
69
-
70
- ## Author
71
-
72
- John Chamblee
gitlabrat-1.1/README.md DELETED
@@ -1,47 +0,0 @@
1
- # LabRat
2
-
3
- LabRat is a GitLab exploitation orchestrator designed for security researchers and penetration testers.
4
-
5
- ## Features
6
-
7
- - **Authentication Management** - Authenticate to GitLab servers and manage access tokens
8
- - **Project Management** - List, clone, and manipulate GitLab projects
9
- - **User Management** - Enumerate and create access tokens for GitLab users
10
- - **Agent Management** - Manage multiple GitLab agents and credentials
11
-
12
- ## Installation
13
-
14
- Install LabRat from PyPI:
15
-
16
- ```bash
17
- pip install gitlabrat
18
- ```
19
-
20
- ## Usage
21
-
22
- ```bash
23
- labrat --help
24
- ```
25
-
26
- ### Commands
27
-
28
- - `labrat auth` - Authenticate to GitLab server(s)
29
- - `labrat agents` - Manage GitLab agents
30
- - `labrat projects` - Manage GitLab projects
31
- - `labrat users` - Manage GitLab users
32
-
33
- ## Requirements
34
-
35
- - Python 3.8+
36
- - python-gitlab
37
- - GitPython
38
- - beautifulsoup4
39
- - requests
40
-
41
- ## License
42
-
43
- GNU General Public License v3.0 (GPL-3.0)
44
-
45
- ## Author
46
-
47
- John Chamblee
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes