{"id":27706240,"url":"https://github.com/kpcyrd/badtouch","last_synced_at":"2025-04-26T05:01:59.684Z","repository":{"id":42993779,"uuid":"125433240","full_name":"kpcyrd/authoscope","owner":"kpcyrd","description":"Scriptable network authentication cracker (formerly `badtouch`)","archived":false,"fork":false,"pushed_at":"2023-12-19T14:50:40.000Z","size":448,"stargazers_count":410,"open_issues_count":22,"forks_count":45,"subscribers_count":13,"default_branch":"main","last_synced_at":"2025-04-21T05:06:30.645Z","etag":null,"topics":["concurrency","cracking","credential-stuffing","dictionary-attack","lua","password","password-cracker","rust"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/authoscope","language":"Rust","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/kpcyrd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":["kpcyrd"]}},"created_at":"2018-03-15T22:27:56.000Z","updated_at":"2025-04-16T13:26:58.000Z","dependencies_parsed_at":"2024-06-19T02:56:08.093Z","dependency_job_id":"6a17c45f-29ca-4274-af3e-625234769fc8","html_url":"https://github.com/kpcyrd/authoscope","commit_stats":{"total_commits":174,"total_committers":7,"mean_commits":"24.857142857142858","dds":0.03448275862068961,"last_synced_commit":"1317aece6faa2696624c5c024c8ef182747c4c06"},"previous_names":["kpcyrd/badtouch"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpcyrd%2Fauthoscope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpcyrd%2Fauthoscope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpcyrd%2Fauthoscope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpcyrd%2Fauthoscope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kpcyrd","download_url":"https://codeload.github.com/kpcyrd/authoscope/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250935455,"owners_count":21510552,"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":["concurrency","cracking","credential-stuffing","dictionary-attack","lua","password","password-cracker","rust"],"created_at":"2025-04-26T05:01:41.663Z","updated_at":"2025-04-26T05:01:59.552Z","avatar_url":"https://github.com/kpcyrd.png","language":"Rust","readme":"# authoscope [![Crates.io][crates-img]][crates]\n\n[crates-img]:   https://img.shields.io/crates/v/authoscope.svg\n[crates]:       https://crates.io/crates/authoscope\n\nauthoscope is a scriptable network authentication cracker. While the space for\ncommon service bruteforce is already [very][ncrack] [well][hydra]\n[saturated][medusa], you may still end up writing your own python scripts when\ntesting credentials for web applications.\n\n[ncrack]: https://nmap.org/ncrack/\n[hydra]: https://github.com/vanhauser-thc/thc-hydra\n[medusa]: https://github.com/jmk-foofus/medusa\n\nThe scope of authoscope is specifically cracking custom services. This is done\nby writing scripts that are loaded into a lua runtime. Those scripts represent\na single service and provide a `verify(user, password)` function that returns\neither true or false. Concurrency, progress indication and reporting is\nmagically provided by the authoscope runtime.\n\n[![asciicast](https://asciinema.org/a/Ke5rHVsz5sJePNUK1k0ASAvuZ.png)](https://asciinema.org/a/Ke5rHVsz5sJePNUK1k0ASAvuZ)\n\n## Installation\n\n\u003ca href=\"https://repology.org/project/authoscope/versions\"\u003e\u003cimg align=\"right\" src=\"https://repology.org/badge/vertical-allrepos/authoscope.svg\" alt=\"Packaging status\"\u003e\u003c/a\u003e\n\nIf you are on an Arch Linux based system, use\n\n    pacman -S authoscope\n\nIf you are on Mac OSX, use\n\n    brew install authoscope\n\nTo build from source, make sure you have [rust](https://rustup.rs/) and `libssl-dev` installed and run\n\n    cargo install\n\nVerify your setup is complete with\n\n    authoscope --help\n\n### Debian\n1. Install essential build tools\n```\nsudo apt-get update \u0026\u0026 sudo apt-get dist-upgrade\nsudo apt-get install build-essential libssl-dev pkg-config\n```\n2. Install rust\n```\ncurl -sf -L https://static.rust-lang.org/rustup.sh | sh\nsource $HOME/.cargo/env\n```\n3. Install authoscope\n```\ncd /path/to/authoscope\ncargo install\n```\n\n## Scripting\n\nA simple script could look like this:\n\n```lua\ndescr = \"example.com\"\n\nfunction verify(user, password)\n    session = http_mksession()\n\n    -- get csrf token\n    req = http_request(session, 'GET', 'https://example.com/login', {})\n    resp = http_send(req)\n    if last_err() then return end\n\n    -- parse token from html\n    html = resp['text']\n    csrf = html_select(html, 'input[name=\"csrf\"]')\n    token = csrf[\"attrs\"][\"value\"]\n\n    -- send login\n    req = http_request(session, 'POST', 'https://example.com/login', {\n        form={\n            user=user,\n            password=password,\n            csrf=token\n        }\n    })\n    resp = http_send(req)\n    if last_err() then return end\n\n    -- search response for successful login\n    html = resp['text']\n    return html:find('Login successful') != nil\nend\n```\n\nPlease see the reference and [examples](/scripts) for all available functions.\nKeep in mind that you can use `print(x)` and `authoscope oneshot` to debug your\nscript.\n\n## Reference\n- [base64_decode](#base64_decode)\n- [base64_encode](#base64_encode)\n- [clear_err](#clear_err)\n- [execve](#execve)\n- [hex](#hex)\n- [hmac_md5](#hmac_md5)\n- [hmac_sha1](#hmac_sha1)\n- [hmac_sha2_256](#hmac_sha2_256)\n- [hmac_sha2_512](#hmac_sha2_512)\n- [hmac_sha3_256](#hmac_sha3_256)\n- [hmac_sha3_512](#hmac_sha3_512)\n- [html_select](#html_select)\n- [html_select_list](#html_select_list)\n- [http_basic_auth](#http_basic_auth)\n- [http_mksession](#http_mksession)\n- [http_request](#http_request)\n- [http_send](#http_send)\n- [json_decode](#json_decode)\n- [json_encode](#json_encode)\n- [last_err](#last_err)\n- [ldap_bind](#ldap_bind)\n- [ldap_escape](#ldap_escape)\n- [ldap_search_bind](#ldap_search_bind)\n- [md5](#md5)\n- [mysql_connect](#mysql_connect)\n- [mysql_query](#mysql_query)\n- [print](#print)\n- [rand](#rand)\n- [randombytes](#randombytes)\n- [sha1](#sha1)\n- [sha2_256](#sha2_256)\n- [sha2_512](#sha2_512)\n- [sha3_256](#sha3_256)\n- [sha3_512](#sha3_512)\n- [sleep](#sleep)\n- [sock_connect](#sock_connect)\n- [sock_send](#sock_send)\n- [sock_recv](#sock_recv)\n- [sock_sendline](#sock_sendline)\n- [sock_recvline](#sock_recvline)\n- [sock_recvall](#sock_recvall)\n- [sock_recvline_contains](#sock_recvline_contains)\n- [sock_recvline_regex](#sock_recvline_regex)\n- [sock_recvn](#sock_recvn)\n- [sock_recvuntil](#sock_recvuntil)\n- [sock_sendafter](#sock_sendafter)\n- [sock_newline](#sock_newline)\n- [Examples](/scripts)\n- [Configuration](#configuration)\n- [Wrapping python scripts](#wrapping-python-scripts)\n\n### base64_decode\nDecode a base64 string.\n```lua\nbase64_decode(\"ww==\")\n```\n\n### base64_encode\nEncode a binary array with base64.\n```lua\nbase64_encode(\"\\x00\\xff\")\n```\n\n### clear_err\nClear all recorded errors to prevent a requeue.\n```lua\nif last_err() then\n    clear_err()\n    return false\nelse\n    return true\nend\n```\n\n### execve\nExecute an external program. Returns the exit code.\n```lua\nexecve(\"myprog\", {\"arg1\", \"arg2\", \"--arg\", \"3\"})\n```\n\n### hex\nHex encode a list of bytes.\n```lua\nhex(\"\\x6F\\x68\\x61\\x69\\x0A\\x00\")\n```\n\n### hmac_md5\nCalculate an hmac with md5. Returns a binary array.\n```lua\nhmac_md5(\"secret\", \"my authenticated message\")\n```\n\n### hmac_sha1\nCalculate an hmac with sha1. Returns a binary array.\n```lua\nhmac_sha1(\"secret\", \"my authenticated message\")\n```\n\n### hmac_sha2_256\nCalculate an hmac with sha2_256. Returns a binary array.\n```lua\nhmac_sha2_256(\"secret\", \"my authenticated message\")\n```\n\n### hmac_sha2_512\nCalculate an hmac with sha2_512. Returns a binary array.\n```lua\nhmac_sha2_512(\"secret\", \"my authenticated message\")\n```\n\n### hmac_sha3_256\nCalculate an hmac with sha3_256. Returns a binary array.\n```lua\nhmac_sha3_256(\"secret\", \"my authenticated message\")\n```\n\n### hmac_sha3_512\nCalculate an hmac with sha3_512. Returns a binary array.\n```lua\nhmac_sha3_512(\"secret\", \"my authenticated message\")\n```\n\n### html_select\nParses an html document and returns the first element that matches the css\nselector. The return value is a table with `text` being the inner text and\n`attrs` being a table of the elements attributes.\n```lua\ncsrf = html_select(html, 'input[name=\"csrf\"]')\ntoken = csrf[\"attrs\"][\"value\"]\n```\n\n### html_select_list\nSame as [`html_select`](#html_select) but returns all matches instead of the\nfirst one.\n```lua\nhtml_select_list(html, 'input[name=\"csrf\"]')\n```\n\n### http_basic_auth\nSends a `GET` request with basic auth. Returns `true` if no `WWW-Authenticate`\nheader is set and the status code is not `401`.\n```lua\nhttp_basic_auth(\"https://httpbin.org/basic-auth/foo/buzz\", user, password)\n```\n\n### http_mksession\nCreate a session object. This is similar to `requests.Session` in\npython-requests and keeps track of cookies.\n```lua\nsession = http_mksession()\n```\n\n### http_request\nPrepares an http request. The first argument is the session reference and\ncookies from that session are copied into the request. After the request has\nbeen sent, the cookies from the response are copied back into the session.\n\nThe next arguments are the `method`, the `url` and additional options. Please\nnote that you still need to specify an empty table `{}` even if no options are\nset. The following options are available:\n\n- `query` - a map of query parameters that should be set on the url\n- `headers` - a map of headers that should be set\n- `basic_auth` - configure the basic auth header with `{\"user, \"password\"}`\n- `user_agent` - overwrite the default user agent with a string\n- `json` - the request body that should be json encoded\n- `form` - the request body that should be form encoded\n- `body` - the raw request body as string\n\n```lua\nreq = http_request(session, 'POST', 'https://httpbin.org/post', {\n    json={\n        user=user,\n        password=password,\n    }\n})\nresp = http_send(req)\nif last_err() then return end\nif resp[\"status\"] ~= 200 then return \"invalid status code\" end\n```\n\n### http_send\nSend the request that has been built with [`http_request`](#http_request).\nReturns a table with the following keys:\n\n- `status` - the http status code\n- `headers` - a table of headers\n- `text` - the response body as string\n\n```lua\nreq = http_request(session, 'POST', 'https://httpbin.org/post', {\n    json={\n        user=user,\n        password=password,\n    }\n})\nresp = http_send(req)\nif last_err() then return end\nif resp[\"status\"] ~= 200 then return \"invalid status code\" end\n```\n\n### json_decode\nDecode a lua value from a json string.\n```lua\njson_decode(\"{\\\"data\\\":{\\\"password\\\":\\\"fizz\\\",\\\"user\\\":\\\"bar\\\"},\\\"list\\\":[1,3,3,7]}\")\n```\n\n### json_encode\nEncode a lua value to a json string. Note that empty tables are encoded to an\nempty object `{}` instead of an empty list `[]`.\n```lua\nx = json_encode({\n    hello=\"world\",\n    almost_one=0.9999,\n    list={1,3,3,7},\n    data={\n        user=user,\n        password=password,\n        empty=nil\n    }\n})\n```\n\n### last_err\nReturns `nil` if no error has been recorded, returns a string otherwise.\n```lua\nif last_err() then return end\n```\n\n### ldap_bind\nConnect to an ldap server and try to authenticate with the given user.\n```lua\nldap_bind(\"ldaps://ldap.example.com/\",\n    \"cn=\\\"\" .. ldap_escape(user) .. \"\\\",ou=users,dc=example,dc=com\", password)\n```\n\n### ldap_escape\nEscape an attribute value in a relative distinguished name.\n```lua\nldap_escape(user)\n```\n\n### ldap_search_bind\nConnect to an ldap server, log into a search user, search for the target user\nand then try to authenticate with the first DN that was returned by the search.\n```lua\nldap_search_bind(\"ldaps://ldap.example.com/\",\n    -- the user we use to find the correct DN\n    \"cn=search_user,ou=users,dc=example,dc=com\", \"searchpw\",\n    -- base DN we search in\n    \"dc=example,dc=com\",\n    -- the user we test\n    user, password)\n```\n\n### md5\nHash a byte array with md5 and return the results as bytes.\n```lua\nhex(md5(\"\\x00\\xff\"))\n```\n\n### mysql_connect\nConnect to a mysql database and try to authenticate with the provided\ncredentials. Returns a mysql connection on success.\n```lua\nsock = mysql_connect(\"127.0.0.1\", 3306, user, password)\n```\n\n### mysql_query\nRun a query on a mysql connection. The 3rd parameter is for prepared\nstatements.\n```lua\nrows = mysql_query(sock, 'SELECT VERSION(), :foo as foo', {\n    foo='magic'\n})\n```\n\n### print\nPrints the value of a variable. Please note that this bypasses the regular\nwriter and may interfer with the progress bar. Only use this for debugging.\n```lua\nprint({\n    data={\n        user=user,\n        password=password\n    }\n})\n```\n\n### rand\nReturns a random `u32` with a minimum and maximum constraint. The return value\ncan be greater or equal to the minimum boundary, and always lower than the\nmaximum boundary. This function has not been reviewed for cryptographic\nsecurity.\n```lua\nrand(0, 256)\n```\n\n### randombytes\nGenerate the specified number of random bytes.\n```lua\nrandombytes(16)\n```\n\n### sha1\nHash a byte array with sha1 and return the results as bytes.\n```lua\nhex(sha1(\"\\x00\\xff\"))\n```\n\n### sha2_256\nHash a byte array with sha2_256 and return the results as bytes.\n```lua\nhex(sha2_256(\"\\x00\\xff\"))\n```\n\n### sha2_512\nHash a byte array with sha2_512 and return the results as bytes.\n```lua\nhex(sha2_512(\"\\x00\\xff\"))\n```\n\n### sha3_256\nHash a byte array with sha3_256 and return the results as bytes.\n```lua\nhex(sha3_256(\"\\x00\\xff\"))\n```\n\n### sha3_512\nHash a byte array with sha3_512 and return the results as bytes.\n```lua\nhex(sha3_512(\"\\x00\\xff\"))\n```\n\n### sleep\nPauses the thread for the specified number of seconds. This is mostly used to\ndebug concurrency.\n```lua\nsleep(3)\n```\n\n### sock_connect\nCreate a tcp connection.\n```lua\nsock = sock_connect(\"127.0.0.1\", 1337)\n```\n\n### sock_send\nSend data to the socket.\n```lua\nsock_send(sock, \"hello world\")\n```\n\n### sock_recv\nReceive up to 4096 bytes from the socket.\n```lua\nx = sock_recv(sock)\n```\n\n### sock_sendline\nSend a string to the socket. A newline is automatically appended to the string.\n```lua\nsock_sendline(sock, line)\n```\n\n### sock_recvline\nReceive a line from the socket. The line includes the newline.\n```lua\nx = sock_recvline(sock)\n```\n\n### sock_recvall\nReceive all data from the socket until EOF.\n```lua\nx = sock_recvall(sock)\n```\n\n### sock_recvline_contains\nReceive lines from the server until a line contains the needle, then return\nthis line.\n```lua\nx = sock_recvline_contains(sock, needle)\n```\n\n### sock_recvline_regex\nReceive lines from the server until a line matches the regex, then return this\nline.\n```lua\nx = sock_recvline_regex(sock, \"^250 \")\n```\n\n### sock_recvn\nReceive exactly n bytes from the socket.\n```lua\nx = sock_recvn(sock, 4)\n```\n\n### sock_recvuntil\nReceive until the needle is found, then return all data including the needle.\n```lua\nx = sock_recvuntil(sock, needle)\n```\n\n### sock_sendafter\nReceive until the needle is found, then write data to the socket.\n```lua\nsock_sendafter(sock, needle, data)\n```\n\n### sock_newline\nOverwrite the default `\\n` newline.\n```lua\nsock_newline(sock, \"\\r\\n\")\n```\n\n## Configuration\n\nYou can place a config file at `~/.config/authoscope.toml` to set some\ndefaults.\n\n### Global user agent\n\n```toml\n[runtime]\nuser_agent = \"w3m/0.5.3+git20180125\"\n```\n\n### RLIMIT_NOFILE\n\n```toml\n[runtime]\n# requires CAP_SYS_RESOURCE\n# sudo setcap 'CAP_SYS_RESOURCE=+ep' /usr/bin/authoscope\nrlimit_nofile = 64000\n```\n\n## Wrapping python scripts\n\nThe authoscope runtime is still very bare bones, so you might have to shell out\nto your regular python script occasionally. Your wrapper may look like this:\n\n```lua\ndescr = \"example.com\"\n\nfunction verify(user, password)\n    ret = execve(\"./docs/test.py\", {user, password})\n    if last_err() then return end\n\n    if ret == 2 then\n        return \"script signaled an exception\"\n    end\n\n    return ret == 0\nend\n```\n\nYour python script may look like this:\n\n```python\nimport sys\n\ntry:\n    if sys.argv[1] == \"foo\" and sys.argv[2] == \"bar\":\n        # correct credentials\n        sys.exit(0)\n    else:\n        # incorrect credentials\n        sys.exit(1)\nexcept:\n    # signal an exception\n    # this requeues the attempt instead of discarding it\n    sys.exit(2)\n```\n\n# License\n\nGPLv3+\n","funding_links":["https://github.com/sponsors/kpcyrd"],"categories":["Tools","Web Exploitation","应用 Applications","Web and Cloud Security","应用","Applications","Web"],"sub_categories":["Binary files examination and editing","Penetration Testing Report Templates","安全工具 Security tools","Pentesting","安全工具","Security tools","Web Exploitation","Social Engineering Tools"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpcyrd%2Fbadtouch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkpcyrd%2Fbadtouch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpcyrd%2Fbadtouch/lists"}